nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

QuatCore: сортировка против часовой стрелки

Первую часть аффинного алгоритма мы-таки попробовали запустить - работает.
Пора взяться за следующую часть.

Теперь код практически удвоился (63 слова), вызываемых процедур стало 3, появились команды умножения, умножения с накоплением, "плюс-минус".

Ширина регистра PC (и сопутствующих схем) возросла с 5 до 6 бит, таблица QuatCoreCallTable стала немножко толще, в результате всего этого весь QuatCore занимает уже не 431 ЛЭ, а 443 ЛЭ, хотя при более грамотном составлении таблицы можно было бы обойтись 440 ЛЭ.


В первую очередь посмотрим листинг кода:
main proc
00  FD47              SP          StackAdr
01  8AFC              C           [SP]
02  FD83              SP          C
03  F3B0              CALL        AffineAlgorithm
04  B804  @@endless:  JMP         @@endless
main endp
AffineAlgorithm     proc
    Associate4Points    proc
        FindMDD3    proc
05  CD18                      X           Points2D
06  F000                      [SP+1]      0   ;максимальная отдалённость, инициализируем нулём
07  A103                      j           3
08  A003          @@j_loop:   i           3   ;также от 0 до 3, чтобы все расстояния просуммировать
09  FC00                      [SP]        0   ;здесь будет храниться текущий максимум
0A  A201          @@i_loop:   k           1   ;от 0 до 1, т.е значения X и Y
0B  80C9          @@k_loop:   Acc         [X+2i+k]    ;загрузили одно значение
0C  83CA                      SUB         [X+2j+k]    ;вычитаем второе
0D  9C80                      SQRD2       Acc         ;возводим в квадрат
0E  82FC                      ADD         [SP]        ;прибавляем к пред. значению
0F  FC80                      [SP]        Acc
10  AA7B                      kLOOP       @@k_loop        ;теперь то же самое для второй координаты
11  A879                      iLOOP       @@i_loop
12  83F0                      SUB         [SP+1]  ;можно и "пожертвовать" значением в Acc,
13  B004                      JL          @@skip
14  8AFC                      C           [SP]
15  F083                      [SP+1]      C
16  DDA1                      Y           j
17  A971          @@skip:     jLOOP       @@j_loop
18  A0DD                      i           Y
19  DD18                      Y           Points2D
1A  ED18                      Z           Points2D
1B  F3B2                      CALL        SwapPoints  ;потёрли текущий максимум (лежал в [SP])-и хрен с ним
        FindMDD3    endp
        SortCCW     proc
1C  DD1A                      Y           Fx1 ;чтобы индекс от 2 до 0 соотв. точкам (Fx1,Fy1) ... (Fx3, Fy3)
1D  ED1C                      Z           Fx2 ;чтобы иметь сдвинутую на 1 адресацию
1E  A301                      Inv         1   ;пока что выкинули команды ijk, без них чуть толще, но даже понятнее
1F  F3B1                      CALL        ShiftOrigin
20  A101                      j           1
21  A0A1          @@j_loop:   i           j
22  8AEA          @@i_loop:   C           [Z+2j+k]
23  90D1                      MUL         [Y+2i+1]
24  8AE2                      C           [Z+2j+1]
25  93D9                      FMS         [Y+2i+k]    ;нахождение "векторного произведения"
26  B002                      JL          @@skip
27  F3B2                      CALL        SwapPoints
28  A87A          @@skip:     iLOOP       @@i_loop
29  A978                      jLOOP       @@j_loop
2A  A001                      i           1
2B  A101                      j           1
2C  F3B2                      CALL        SwapPoints
2D  A300                      Inv         0
2E  F3B1                      CALL        ShiftOrigin     
        SortCCW     endp
    Associate4Points endp
    Compute4PointAffine proc
    Compute4PointAffine endp
    FindRoll    proc
    FindRoll    endp
    FindScale   proc
    FindScale   endp
    FindVector  proc
    FindVector  endp
2F  B8FF              JMP         [--SP]
AffineAlgorithm     endp
SwapPoints  proc
30  A201              k           1
31  80EA  @@swap:     Acc         [Z+2j+k]
32  8AD9              C           [Y+2i+k]
33  EA83              [Z+2j+k]    C
34  D980              [Y+2i+k]    Acc
35  AA7C              kLOOP       @@swap
36  B8FF              JMP         [--SP]
SwapPoints  endp    
ShiftOrigin proc
37  A002                  i           2
38  A201  @@i_loop:       k           1   ;как всегда, чтобы Y и X
39  80D9  @@k_loop:       Acc         [Y+2i+k]
3A  81C8                  PM          [X+k]
3B  D980                  [Y+2i+k]    Acc
3C  AA7D                  kLOOP       @@k_loop
3D  A87B                  iLOOP       @@i_loop
3E  B8FF                  JMP         [--SP]
ShiftOrigin endp


Мы чуть изменили процедуру FindMDD3 - если раньше мы "ручками" поменяли местами нулевую точку и наиболее отдалённую, то теперь вызвали процедуру SwapPoints, чем сэкономили целых 3 слова (6 байт).

Данная процедура меняет местами точки, лежащие по адресам [Y+2i] и [Z+2j]. Она потом применяется при сортировке оставшихся точек против часовой стрелки, а потом вызывается ещё раз (ЭТОГО НЕ БЫЛО РАНЕЕ), чтобы расположить точки в порядке МДД3-МДД1-МДД2-МБД. (МДД-мишень дальней дистанции, МБД - мишень ближней дистанции).

Их финальный порядок был не так уж важен для аффинного алгоритма - как определишься, так и будет (главное, чтобы каждый раз он был один и тот же). А вот с алгоритмом сопровождения есть подлянка. На больших дистанциях отдельные элементы мишени ближней дистанции сливаются в одно пятно, которое мы и обзываем МБД, а на малых дистанциях мы начинаем видеть отдельные точки, называемые МБД1, МБД2, МБД3, и т.д. В итоге, я решил расположить реальные координаты точек в памяти следующим образом:

МБД1-МБД2-МБД3-...-МБД8-МДД3-МДД1-МДД2-МБД.

Тогда работа на дальней и на ближней дистанции будет отличаться только выбором смещения и количества элементов. На дальней дистанции мы работаем с 4 последними элементами, на ближней дистанции - с 11 первыми.

В остальном, алгоритм не сильно изменился за это время.

Компилятор формирует файлы QuatCoreCode.mif (memory initialization file), под спойлером его содержимое

[Spoiler (click to open)]
WIDTH=16;
DEPTH=63;

ADDRESS_RADIX=UNS;
DATA_RADIX=HEX;

CONTENT BEGIN
	0	:	FD47;
	1	:	8AFC;
	2	:	FD83;
	3	:	F3B0;
	4	:	B804;
	5	:	CD18;
	6	:	F000;
	7	:	A103;
	8	:	A003;
	9	:	FC00;
	10	:	A201;
	11	:	80C9;
	12	:	83CA;
	13	:	9C80;
	14	:	82FC;
	15	:	FC80;
	16	:	AA7B;
	17	:	A879;
	18	:	83F0;
	19	:	B004;
	20	:	8AFC;
	21	:	F083;
	22	:	DDA1;
	23	:	A971;
	24	:	A0DD;
	25	:	DD18;
	26	:	ED18;
	27	:	F3B2;
	28	:	DD1A;
	29	:	ED1C;
	30	:	A301;
	31	:	F3B1;
	32	:	A101;
	33	:	A0A1;
	34	:	8AEA;
	35	:	90D1;
	36	:	8AE2;
	37	:	93D9;
	38	:	B002;
	39	:	F3B2;
	40	:	A87A;
	41	:	A978;
	42	:	A001;
	43	:	A101;
	44	:	F3B2;
	45	:	A300;
	46	:	F3B1;
	47	:	B8FF;
	48	:	A201;
	49	:	80EA;
	50	:	8AD9;
	51	:	EA83;
	52	:	D980;
	53	:	AA7C;
	54	:	B8FF;
	55	:	A002;
	56	:	A201;
	57	:	80D9;
	58	:	81C8;
	59	:	D980;
	60	:	AA7D;
	61	:	A87B;
	62	:	B8FF;
END;



Второй файл: QuatCoreData.mif - то же самое, но это начальное содержание оперативной памяти.

И наконец, QuatCoreCallTable.v. Сейчас он такой:
//адреса для вызова процедур, "вшитые" в модуль QuatCorePC
module QuatCoreCallTable (input [7:0] SrcAddr, output [5:0] addr);
wire [1:0] index = SrcAddr[1:0];
	assign addr = 
		(index==0)? 6'd5:		//AffineAlgorithm
		(index==1)? 6'd55:		//ShiftOrigin
		(index==2)? 6'd48:		//SwapPoints
		6'bxxxxxx;
endmodule


Здесь у нас есть очень большая свобода действий, в каком порядке расположить эти адреса для вызова процедур. Сейчас они идут тупо в алфавитном порядке, без "прорех", но чтобы хоть чуть-чуть потенциально снизить размер модуля, из входных битов задействуется только минимально необходимое количество, в данном случае 2.

Эта функция синтезируется в 2 ЛЭ, поскольку мы имеем следующее:
00: 000101
01: 110111
10: 110000
11: xxxxxx


Оказывается, что 0-й и 2-й бит результата совпадают и используют первый ЛЭ, 1-й бит совпадает с нулевым битом адреса, 3-й бит просто нулевой, а 4-й и 5-й биты снова совпадают и используют 2-й ЛЭ.

Но мы могли бы вообще обойтись здесь без ЛЭ:
module QuatCoreCallTable (input [7:0] SrcAddr, output [5:0] addr);
	assign addr[0] = SrcAddr[0];
	assign addr[1] = SrcAddr[1];
	assign addr[2] = SrcAddr[0];
	assign addr[3] = 1'b0;
	assign addr[4] = SrcAddr[2];
	assign addr[5] = SrcAddr[2];
endmodule


Теперь для вызова AffineAlgorithm нужно использовать младшие 3 бита: 001, для ShiftOrigin: 111, для SwapPoints: 100. И как показывает результат синтеза, это каким-то образом экономит нам 3 ЛЭ.

Но пока мы не торопимся внедрять эти эвристики - далеко не факт, что они смогут сработать по мере усложнения программы, когда вызываемых функций станет больше, и адреса их станут длиннее.

Пора запускать симуляцию и посмотреть, что там происходит.


Начинаем с того места, на котором новый код отличается от старого.
i Y - готовимся поменять местами нулевую точку и максимально отдалённую. На шине данных число 3 - максимально отдалена именно третья точка.

Y 0x18 - аргумент для процедуры SwapPoints. По сути, у нас сейчас все 3 регистра X,Y,Z будут ссылаться на одно и то же место, начало массива точек.
Z 0x18 - вот-вот.
[SP++] CALL2 - вызываем процедуру SwapPoints. Она у нас 2-я (при нумерации от нуля) в таблице вызовов. На шине данных - адрес возврата, 0x1C, который отправляется в память по адресу 0x60 - это текущее значение SP. И действительно, мы переходим в процедуру.

k 1 - как всегда, цикл от 1 до 0, чтобы единообразно поменять местами две координаты, X и Y. LOOP Unrolling - не для нас :) У нас, как это ни странно, времени много, а вот памяти кот наплакал.
Acc [Z+2j+k] - берём Y-координату нулевой точки. Эффективный адрес 19 - всё верно.
C [Y+2i+k] - берём Y-координату третьей точки. Эффективный адрес 1F, это мы уже проходили.
[Z+2j+k] C - на место нулевой точки перемещаем третью.
[Y+2i+k] Acc - на место третьей перемещаем нулевую.
kLOOP -4 - прыгаем в начало цикла.
Acc [Z+2j+k]
C [Y+2i+k]
[Z+2j+k] C
[Y+2i+k] Acc - всё то же самое, но эффективные адреса на 1 меньше, поэтому обмениваются X-координаты.
kLOOP -4 - вышли из цикла.
JMP [--SP] - возврат из процедуры. Извлекли из стека значение 0x1C.

Y 0x1A
Z 0x1C - теперь у нас регистры X,Y,Z идут "через 2" - X указывает на нулевую точку, Y - на первую, Z - на вторую. Так нужно, чтобы "переместить начало координат" в нулевую точку, а затем комфортно провести сортировку "выбором".
Inv 1 - "включаем инверсию", чтобы при "перемещении начала координат" было вычитание координат нулевой точки. Видим по "осциллограмме", что действительно теперь Inv=1.
[SP++] CALL1 - вызываем процедуру ShiftOrigin - как раз перемещение начала координат. Всё как надо - в стеке лежал старый адрес возврата 0x1C, а сейчас кладём новый - 0x20. И прыгаем куда надо.

i 2 - нам нужно всего 3 точки переместить, от первой до третьей, нулевую не трогаем! Но циклы удобно устраивать до нуля, поэтому используем регистр Y, сдвинутый на 1 точку вправо, а для вычитания координат нулевой точки - регистр X.
k 1 - как всегда, итерация по номеру координаты, сначала замучиваем игреки, потом иксы. Кстати, такие вложенные циклы в большинстве случаев можно поменять местами, сделать внешний цикл по k, а внутренний по i. И это выгодно: реже придётся инициализировать переменную! Так мы 3 раза выполняем инструкцию k 1, а можно было бы взамен 2 раза выполнить i 2 :) Ладно, потом переставим и порадуемся сэкономленным тактам.

Acc [Y+2i+k] - уже знакомая нам Y-координата 3-й точки, адрес 1F. Загрузили значение 0xFFF9, то есть -7.
PM [X+k] - команда "плюс-минус". При Inv=0 должна прибавить значение, но у нас Inv=1 - значит отнять. Мы берём Y-координату нулевой точки (адрес 19), 0xFC1B, т.е -997.

Следующий скриншот!


Тут мы крутимся в двух вложенных циклах, поэтому подписывать каждую команду - дело неблагодарное. Подчеркнул только конечные результаты и то, куда они заносятся. -7 - (-997) = 990 = 0x3DE - команда PM отработала правильно! И мы обновили Y-координату 3-й точки.

Затем, загружаем X-координату (адрес 0x1E, значение 0x35= 53), вычитаем X-координату нулевой точки (адрес 0x18, значение 0x190=400), получается -347 = 0xFEA5, обновляем X-координату 3-й точки.

Далее берёмся за 2-ю точку, сначала за Y-координату (адрес 0x1D), -302 - (-997) = 695 = 0x2B7. Потом за X-координату (адрес 0x1C), -539 - 400 = -939 = 0xFC55.

Следующий скриншот!


Добиваем 1-ю точку, сначала Y-координату (адрес 0x1B), -30 - (-997) = 967 = 0x3C7, затем X-координату (адрес 0x1A), 480-400 = 80 = 0x50. Всё правильно.

Выходим из двух вложенных циклов, возвращаемся из процедуры. Настало время отсортировать эти точки. Внешний цикл: j от 1 до 0. Внутренний цикл: i от j до 0, т.е он выполняется сначала дважды, потом ещё ОДИН РАЗ. Это логично: на первом цикле мы выбираем самую "большую" точку (в смысле выбранного нами оператора упорядочивания), для чего нужно произвести ДВА сравнения (точек-то три). Её мы помещаем на 3-ю позицию, после чего остаётся лишь сравнить две оставшиеся.

Самое интересное здесь - оператор упорядочивания, по сути векторное произведение на плоскости. Первый раз мы сравниваем 2-ю и 3-ю точки. Дальше в адресах будет маячить регистр k, но он РАВЕН НУЛЮ.
C [Z+2j+k] - загружаем X-координату 3-й точки, адрес 0x1E, значение теперь: -347 (0xFEA5).
MUL [Y+2i+1] - умножаем на Y-координату 2-й точки, адрес 0x1D, значение 695 (0x2B7). Результат умножения мы нигде не видим - он хранится в аккумуляторе и "наружу" не выходит.



C [Z+2j+1] - загружаем Y-координату 3-й точки, адрес 0x1F, значение 990 (0x3DE).
FMS [Y+2i+k] - Fused Multiply-Subract, т.е из аккумулятора вычитаем результат произведения C на текущее значение. Мы подаём сюда X-координату 2-й точки, адрес 0x1C, значение -939 (0xFC55).

Увы, и результат этой операции мы не знаем! И, похоже, не узнаем, т.к используется лишь её ЗНАК. Давайте хоть знак проверим... Мы должны посчитать выражение

-347 * 695 - 990 * (-939) = 688445. Если иметь в виду формат чисел 1.15 (когда -347 на самом деле означает -347/32768 и т.д.), надо результат поделить на 32768, получается 21. Очевидно, это больше нуля, поэтому условный переход по команде JL (Jump Less) не должен случиться.

Так оно и есть, и мы вызываем процедуру, чтобы поменять местами две точки, которые сейчас сравнивали.

Но всё-таки как-то неуютно. Давайте добавим безобидную "отладочную" команду, которая покажет нам содержание аккумулятора, на неё как раз осталось одно вакантное место, 63 команды :) Пусть это будет ADD Acc. Разумеется, после перекомпиляции нужно "подсунуть" квартусу и обновлённый файл QuatCoreCallTable.v, т.к адреса процедур также сместились на единичку.

Итак:


Да, результатом вычисления становится 0x15 = 21, всё верно! Работают наши умножения и умножения с накоплением!

Число это положительное, поэтому JL 2 не "перепрыгивает" через вызов процедуры. Мы вызываем SwapPoints, который меняет местами 2-ю и 3-ю точку. Её работу мы уже наблюдали ранее, поэтому сейчас вполглаза глядим на адреса - всё хорошо, поехали дальше.

Заканчивается одна итерация по i, он уменьшается до нуля. А j по-прежнему ссылается на 3-ю точку, так и должно быть - это сортировка выбором, а не пузырьком! Не соседние сравниваем и меняем местами, а находим максимальный элемент, сравнивая его со всеми остальными.

Так что начинаем следующее сравнение. Сначала загружаем X-координату 3-й точки (Fx3), адрес 1E, значение 0xFC55 = -939. (это была 2-я точка, но мы же их только что поменяли местами). Умножаем на Fy1, адрес 1B, значение 0x3C7 = 967.


Теперь вычитаем Fy3 (адрес 1F, значение 0x2B7=695) умноженное на Fx1 (адрес 1A, значение 0x50 = 80).

Результатом вычислений должно стать -939 * 967 - 695 * 80 = -29 (с учётом формата 1.15) = FFE3.
Всё чётко! Здесь ни о какой потере точности не может идти и речи - накопление идёт в 32 битах!
Число это отрицательное, поэтому условный переход срабатывает, и процедура обмена не вызывается.
Выходим из внутреннего цикла, начинаем новую итерацию внешнего цикла с уменьшением j до нуля.

Забавно: к началу этого цикла уже имеем i=0, поэтому можно было бы "хитрючее" присвоение i = j заменить тривиальным i = 1, и прыгать во внешнем цикле на единицу ниже, сразу к строке C [Z+2j+k]. На этом сэкономится целый 1 такт :)

И теперь нам осталось сравнить первую и вторую точку и, при необходимости, поменять их местами.
Загружаем Fx2 (адрес 0x1C, значение 0xFEA5 = -347), умножаем на Fy1 (адрес 0x1B, значение 0x3C7 = 967).


Вычитаем Fy2 (адрес 0x1D, значение 0x3DE = 990) умноженное на Fx1 (адрес 0x1A, значение 0x50 = 80).

Результатом должно стать:
-347 * 967 - 990 * 80 = -13 (в представлении 1.15) = 0xFFF3.
Всё в порядке.

Число отрицательное - перепрыгиваем через вызов процедуры, завершаем оба цикла.
Затем мы меняем местами 2-ю и 3-ю точку, чтобы МБД был с самого краю.
Сейчас гляжу на это безобразие и вижу, что можно на одну команду укоротить: вместо
i 1
j 1
можно написать
Y Fx3

А дальше снова вызывается SwapPoints. Верим, что эта процедура сделает всё верно.


Регистр Inv сбрасывается в ноль - всё в порядке.
Вызываем процедуру ShiftOrigin, в этот раз она должна вернуть начало координат на место. Ща проверим.

Загрузили значение 0x03DE = 990, прибавили 0xFC1B = -997, получили 0xFFF9 = -7 - да, в этот раз у нас действительно сложение!
Адреса очень удачно идут в порядке убывания, так что теперь можно собрать, что же у нас получилось.





F1 (480,-30),
F2 (-539, -302),
F3 (53, -7).

Сравниваем с тем, что когда-то получали на эмуляторе:


(справа в "содержимом памяти" видим Fx0, Fy0, и т.д.)
Действительно, после двух "сдвигов начала координат" мы вернулись к исходным числам, и отсортировали их как в прошлый раз, не считая того, что две последних точки поменяли местами ради будущего алгоритма сопровождения.

По окончании сдвига начала координат, мы возвращаемся сначала в процедуру SortCCW, а из неё сразу в процедуру main, где входим в бесконечный цикл, где и сидим, пока что-нибудь с нами не сделают. Пока идея в том, что как только "видеопроцессор" отправляет нам новые координаты ярких точек, он "жмёт нам на Reset", мы запускаемся, делаем своё дело - и снова застреваем в бесконечном цикле. Возможно, в итоге сделаем чуточку хитрее, а пока и этого хватит...

Ещё можно заметить, что вызов процедуры в самом конце другой процедуры можно сделать эффективнее - так называемый Tail Call, когда мы НЕ ЗАПИСЫВАЕМ В СТЕК АДРЕС ВОЗВРАТА, чтобы вернуться одним прыжком, а не двумя. Но мы не стали этого делать, поскольку программа очевидно не завершена - после SortCCW мы должны продолжать выполнение аффинного алгоритма - вычисление матрицы, нахождение крена, масштаба и вектора параллельного переноса. А то ещё сейчас поставлю этот tail call, затем забуду про него и начну громко думать - почему оно не работает???

Пока что, при тактовой частоте 4 МГц, на выполнение этой программы уходит 335 мкс, при времени отведённом на все вычисления: 200 мс.

Что приятно, на этот раз ничего "дорабатывать напильником" не пришлось - все команды сразу заработали как надо, и каких-то неожиданностей не дали.

Но ещё всё впереди: мы не проверяли разные типы беззнаковых операций, команду ABS, не посмотрели на построение знака для FMPM (там не просто Inv, а хитрая комбинация Inv, i, k) и хитрые варианты адресации.


Дальше по списку: нахождение матрицы преобразования.

UPD. Чуть подрихтовал программу: поменял местами циклы по k и по i, укоротил на 1 команду внутренний цикл в сортировке, поменял местами вызов процедур ShiftOrigin и SwapPoints в конце процедуры сортировки, и вместо
i 1
j 1
написали
Y Fx3
(именно для этого надо было процедуры местами поменять - запустить ShiftOrigin, пока Y не переиначили.

В итоге, укоротили программу на 1 слово, а время выполнения - аж на 7,75 мкс, что вообще-то 2,3% ускорение :) И результат тот же выходит.
Листинг подправленной программы:
[Spoiler (click to open)]
main proc
00  FD47              SP          StackAdr
01  8AFC              C           [SP]
02  FD83              SP          C
03  F3B0              CALL        AffineAlgorithm
04  B804  @@endless:  JMP         @@endless
main endp
AffineAlgorithm     proc
    Associate4Points    proc
        FindMDD3    proc
05  CD18                      X           Points2D
06  F000                      [SP+1]      0   ;максимальная отдалённость, инициализируем нулём
07  A103                      j           3
08  A201          @@j_loop:   k           1   ;также от 0 до 3, чтобы все расстояния просуммировать
09  FC00                      [SP]        0   ;здесь будет храниться текущий максимум
0A  A003          @@k_loop:   i           3   ;от 0 до 1, т.е значения X и Y
0B  80C9          @@i_loop:   Acc         [X+2i+k]    ;загрузили одно значение
0C  83CA                      SUB         [X+2j+k]    ;вычитаем второе
0D  9C80                      SQRD2       Acc         ;возводим в квадрат
0E  82FC                      ADD         [SP]        ;прибавляем к пред. значению
0F  FC80                      [SP]        Acc
10  A87B                      iLOOP       @@i_loop        ;теперь то же самое для второй координаты
11  AA79                      kLOOP       @@k_loop
12  83F0                      SUB         [SP+1]  ;можно и "пожертвовать" значением в Acc,
13  B004                      JL          @@skip
14  8AFC                      C           [SP]
15  F083                      [SP+1]      C
16  DDA1                      Y           j
17  A971          @@skip:     jLOOP       @@j_loop
18  A0DD                      i           Y
19  DD18                      Y           Points2D
1A  ED18                      Z           Points2D
1B  F3B2                      CALL        SwapPoints  ;потёрли текущий максимум (лежал в [SP])-и хрен с ним
        FindMDD3    endp
        SortCCW     proc
1C  DD1A                      Y           Fx1 ;чтобы индекс от 2 до 0 соотв. точкам (Fx1,Fy1) ... (Fx3, Fy3)
1D  ED1C                      Z           Fx2 ;чтобы иметь сдвинутую на 1 адресацию
1E  A301                      Inv         1   ;пока что выкинули команды ijk, без них чуть толще, но даже понятнее
1F  F3B1                      CALL        ShiftOrigin
20  A101                      j           1
21  A001                      i           1
22  8AEA          @@loop:     C           [Z+2j+k]
23  90D1                      MUL         [Y+2i+1]
24  8AE2                      C           [Z+2j+1]
25  93D9                      FMS         [Y+2i+k]    ;нахождение "векторного произведения"
26  B002                      JL          @@skip
27  F3B2                      CALL        SwapPoints
28  A87A          @@skip:     iLOOP       @@loop
29  A979                      jLOOP       @@loop
2A  A300                      Inv         0
2B  F3B1                      CALL        ShiftOrigin     
2C  DD1E                      Y           Fx3
2D  F3B2                      CALL        SwapPoints
        SortCCW     endp
    Associate4Points endp
    Compute4PointAffine proc
    Compute4PointAffine endp
    FindRoll    proc
    FindRoll    endp
    FindScale   proc
    FindScale   endp
    FindVector  proc
    FindVector  endp
2E  B8FF              JMP         [--SP]
AffineAlgorithm     endp
SwapPoints  proc
2F  A201              k           1
30  80EA  @@swap:     Acc         [Z+2j+k]
31  8AD9              C           [Y+2i+k]
32  EA83              [Z+2j+k]    C
33  D980              [Y+2i+k]    Acc
34  AA7C              kLOOP       @@swap
35  B8FF              JMP         [--SP]
SwapPoints  endp    
ShiftOrigin proc
36  A201                  k           1
37  A002  @@k_loop:       i           2   ;как всегда, чтобы Y и X
38  80D9  @@i_loop:       Acc         [Y+2i+k]
39  81C8                  PM          [X+k]
3A  D980                  [Y+2i+k]    Acc
3B  A87D                  iLOOP       @@i_loop
3C  AA7B                  kLOOP       @@k_loop
3D  B8FF                  JMP         [--SP]
ShiftOrigin endp

Tags: ПЛИС, математика, программки, работа, странные девайсы
Subscribe

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments