Если помните, я очень долго писал и отлаживал алгоритм обнаружения точек, используя "генератор тестового изображения". Сначала проверял на симуляции, потом перебрался на "железо", но ещё без реального сигнала.
Самое время это исправить. Для начала "ручками" настроим порог обнаружения и расчётный диаметр точек, хотя в будущем хотелось бы, чтобы всё это работало автоматически в широком диапазоне дальностей. Но надо же с чего-то начинать!
Попросту поглядим в фотошопе на изображение из
предыдущего поста. И окажется, что порог нужно ставить 255 (!!!):
"штрих" наверху - это кромка плоского зеркала. Где это зеркало правильно ставить - до сих пор вопрос открытый. Оно нужно для "отладочных целей" - для нахождения нормали к плоскости мишени, чтобы знать, каковы реальные параметры сближения. В принципе, его можно чем-то закрывать во время работы макета. Повесить занавесочку бархатную :)
Пятна слева снизу - это лежит 6 винтов, держащих крышку макета. Их можно завинтить и надеяться, что по закону подлости не придётся тут же их отвинчивать.
Вспоминаем, как работало наше обнаружение точек, вот файл ProcessFrame.asm:
ProcessFrame proc
j 1 ;отныне и во веки веков!
;ждём кадрового синхроимпульса
ACQ VSync ;"застревает" до тех пор, пока кадровый импульс не придёт
;пропускаем "пустые строки" вверху изображения (нужно только для аналоговой камеры, в цифровой такой фигни нет вроде бы...)
k 9
Z D1
@@topRows: ACQ HSync
kLOOP @@topRows
;теперь k=0, пусть так и остаётся, наши [X+k], [Y+k], [Z+k] будут означать [X],[Y],[Z], очень надо для списков
;теперь начинаются информативные строки изображения, сейчас их 720, позже может стать 1024.
C [Z+k] ;ожидаемый диаметр точки, всё-таки штука весьма нужная, лучше держать локальной перем.
[SP+2j] 0 ;в [SP+2] храним номер строки. начнём всё-таки с максимума...
JMP @@FinalRange ;чтобы СНАЧАЛА отправить запрос, а потом уже смотреть результат, так почему-то удобнее
;можем заменить на JNO, переполнения здесь случаться не должно
;НАЧАЛО ЦИКЛА ПО СТРОКАМ
@@newRow: [SP+2j] Acc
X ActivePoints ;начинаем цикл по всем активным точкам
;даже если список пуст, первую половину цикла мы пройдём, она касается отрезка, предшествующего точке из списка
@@ActPointsStart: [SP+2j+1] GPUL ;яркость самой яркой точки на отрезке
[SP+1] GPUH ;соотв. координата
Acc Threshold ;поставили задом наперёд, чтобы избежать Hazard'а
SUB [SP+2j+1] ;вычитаем порог, положит. значение свидетельствует о новом найденном "пятне"
JL @@MidCycle ;пятна нет, не торопимся заказывать отрезок
;пятно есть. Надо проверить: не сливается ли оно с пятном слева или с пятном справа
;X сейчас указывает на точку, расположенную слева (возможно, фиктивную, нам не важно)
;коорд. текущей точки лежит в Acc, её яркость в C
Z X ;Чтобы в Merge всегда использовать Z, независимо от того, левая или правая точка оказались
i 1
@@checkCycle: Acc [SP+1]
SUB [Z+2j+k]
ABS Acc
[SP] Acc ;модуль разности - это и будет новый "диаметр" точки, если сработает слияние
DIV2S C ;не будем мудрить пока что, всё "в лоб"
DIV2S [Z+1]
JL @@DoMerge
Z [X+k]
iLOOP @@checkCycle
;если попали сюда, значит слияния не было, и точку всё-таки надо добавить!
;[SP+2j+1]=[SP+3] указывает яркость этой точки, [SP+2j]=[SP+2]: Y-координату, [SP+1]: X-координата
;а куда вставить, указывает рег. X
;в Acc содержится модуль разности между правой точкой и текущей, за вычетом двух радиусов
Y Heap
;добавит в X новую, пока что пустую точку.
[SP++] @@AfterListOp
;взять первый элемент из списка Y и перецепить его в начало списка X
;значения X,Y остаются неизменными,
;при вызове должно быть k=0, затираем регистр Z, Acc и флаг знака
;адрес возврата хранится в стеке, как положено
ListOp proc
ZAcc RoundZero
Z [Y+k]
SUB [Y+k]
[Y+k] [Z+k]
JGE @@OutOfMem
[Z+k] [X+k]
[X+k] Z
NOP 0
JMP [--SP]
@@OutOfMem: [--SP] Call(OutOfMemory) ;SP возвращается на место, но выпрыгиваем в другое место, на обработку ошибки
ListOp endp
;если добрались досюда, значит памяти хватило. X содержит адрес указателя на новую точку, а вот Z содержит адрес самой точки!
;наполним её содержимым!
@@AfterListOp: X Z ;для дальнейшей работы (после MidCycle)... Чтобы мы не взялись за только что добавленную точку!
[Z+1] C ;гориз. размер точки (будет расти при слияниях, а поначалу D, т.к мы сюда включаем и )
[SP++] @@MidCycle ;после вызова AcqQueue возвращаемся
;сразу на метку @@MidCycle
;"заказать" видеопроцессору два отрезка на обработку,
;первый - на поиск новой точки (т.е здесь мы пока не ожидаем ничего найти),
;второй - на уточнение координат существующей
;в [SP+1], теперь уже [SP], лежит X-координата
;в [SP+2j]=[SP+2], теперь уже [SP+1] - Y-координата точки,
;в [SP+2j+1]=[SP+3], теперь уже [SP+2j]=[SP+2] - яркость точки
;при вызове AcqAndUpdate обновим значения, при вызове AcqNoUpd - не будем
;в [X+1] лежит радиус точки,
;в [X+2j+k]=[X+2] - X-координата
;в [X+2j+1]=[X+3] - Y-координата
;в [X+4j+k]=[X+4] - яркость
AcqAndUpdate proc
i 2
NOP 0 ;избежать Hazard, ведь i участвует в следующей строке
@@updCycle: [X+2j+i] [SP+i]
iLOOP @@updCycle
AcqNoUpd: Acc [X+2j+k]
DIV2S [X+1] ;теперь в аккмуляторе у нас X - Ceil(D/2)
ACQ Acc ;первый отрезок
ADD [X+1] ;а вот теперь X + Floor(D/2)
ACQ Acc ;второй отрезок
JMP [--SP]
AcqAndUpdate endp
;а вот и слияние. В простейшем случае увеличиваем размер точки, и на этом считаем работу выполненной...
@@DoMerge: [Z+1] [SP] ;пока так
;теперь надо посмотреть - а есть ли точка? Или мы уже прошли последний отрезок между точками?
;но у нас в конце списка правая фиктивная точка - её надо игнорировать!
@@MidCycle: Y X
X [X+k]
ZAcc RoundZero
SUB [X+k]
JGE @@FinalRange ;наткнулись на NULL, значит, все точки обработали...
;запрашиваем отрезок, где ожидаем точку. Первой придёт яркость, как всегда
[SP+2j+1] GPUL ;яркость
Acc GPUL
SUB [X+4j+k]
[SP+1] GPUH ;пока не забыли - сразу второе слово запрашиваем (коорд точки), чтобы не сбить весь FIFO
[SP++] @@ActPointsStart ;нам по-любому придётся вернуться именно сюда!
JL AcqAndUpdate ;отсюда "выпрыгнем" сразу на метку @@ActPointsStart
;ветвь исполнения, если старая точка оказывалась ярче.
;Нужно сообразить: не пора ли ей на покой, то бишь в список AllPoints?
;в кои-то веки работаем с Y-координатой!
Acc [X+2j+1] ;координата точки (из списка)
DIV2A [X+1] ;прибавить половинку её диаметра
SUB [SP+1] ;вычесть текущий номер строки
JGE AcqNoUpd ;здесь ОБЯЗАН быть именно JGE, т.к флаг знака является одним из "аргументов" для AcqQueue
;пора эту точку перетаскивать в другой список...
X AllPoints
CALL ListOp
X Y ;теперь [X] ссылается уже не на обработанную и удалённую точку, а на следующую, так что всё верно
JMP [--SP] ;ура...
;до PixelsProcessed проскрипели, теперь последний участок запросить на обработку, и синхроимпульс для кучи
@@FinalRange: ACQ WholeRow
ACQ HSync
Acc [SP+2j]
SUB ImgHeight
ADD ImgHeightP1
JL @@newRow
;усё, отстрелялись...
;надо ещё все оставшиеся в ActivePoints точки "перецепить" в AllPoints
Y ActivePoints ;это заведомо фиктивная, и указывает либо на другую фиктивную, то ли на реальную
X AllPoints
Y [Y+k] ;это уже может быть реальная точка, а может быть правой фиктивной
@@Transfer: ZAcc RoundZero
SUB [Y+k] ;если [Y+k]=NULL, значит пора закругляться
[SP++] @@Transfer
JL ListOp
NOP [--SP] ;раз не было вызова - возвращаем стек в исходное состояние
ProcessFrame endp
Страшная штуковина... И в ней затесалась одна "магическая постоянная", 9, в самом начале. Это количество строк "выше экрана", которые мы пропускаем. Так было в "модельном изображении", но в реальности надо пропустить 29 строк, и в более новой ImageTransfer.asm (где картинка сохраняется в статическую память, а потом передаётся на компьютер) введена константа TopRows:
TopRows EQU 28 ;столько синхроимпульсов нужно пропустить, прежде чем начнутся "полезные" строки (минус 1, поскольку считаем до нуля)
Пригодится!
Остальные константы в ProcessFrame.asm не введены, они сидят в VideoProcessing.asm - файле, в который ProcessFrame подключается в виде отдельной "библиотеки". Вот он, VideoProcessing.asm:
;громко думаем, как организовать видеоообработку
;наилучшее качество получим с сигнала CVI, т.к у него длительность видимой строки максимальная, 43 мкс, и 10,3 мкс на обратный ход
;у нас есть не более 308 тактов для подготовки к следующей строке!
;в 1205ХВ014 логика будет существенно отличаться: сканирование "в два луча", всего 8 тактов на обратный ход
;но давайте решать проблемы по мере их поступления. Потренируемся на кошках, то бишь на аналоговых камерах высокой чёткости...
%include "QuatCoreConsts.inc"
%include "Win1251.inc"
.rodata
; VSync EQU 0x8000 ;ожидание кадрового синхроимпульса, значение для регистра произвольное
; HSync EQU 0x4000 ;ожидание строчного синхроимпульса, а затем ещё интервал front porch (между импульсом и началом картинки)
; WholeRow EQU 1023 ;для обработки всей строки целиком
; ImgHeight EQU 720 ;720p пока что
VSync EQU 0x8000 ;ожидание кадрового синхроимпульса, значение для регистра произвольное
HSync EQU 0x40EE ;ожидание строчного синхроимпульса, а затем ещё интервал back porch (между импульсом и началом картинки) в 240 тактов (239+1 такт на сброс счётчика + 2 такта задержки на ПЗУ)
WholeRow EQU 31 ;для проверки на симуляторе
ImgHeight EQU 32 ;вообще, 32, но вводим дополнительные строки, чтобы перенести все точки из ActivePoints в AllPoints
ImgHeightP1 EQU 33 ;не обучили свой компилятор обрабатывать "constexp"
Threshold EQU 0xDFF ;минимальная яркость пятен, сейчас поставили 1/8 от "насыщения" (4096), но с инверсией, так надо!
Nil EQU -32768 ;традиционно это 0, но у нас нет флага нуля! Поэтому куда удобнее гигантское отриц. значение, всяко у нас 65535 слов памяти не наберётся, надеемся что 256..512 слов вполне хватит
.code
main proc
%include "SetClock.asm"
SP Stack ;инициализация стека.
%include "ProcessFrame.asm"
;давайте передадим параметры обнаруженных точек по UART
;пока тупо по младшему байту
SIO UART
.rodata
PointsStr db 'Обнаруженные точки:',0x0D,0x0A,0x00
.code
X PointsStr
CALL print
Z AllPoints ;В списке AllPoints нет фиктивных точек. Но может вообще никаких не быть!
;Хотя сам AllPoints - это "заголовок", с него надо двинуться вперёд, если получится
i 0 ;номер точки (для отображения в терминале, поэтому нумерация с 1)
JMP @@moveOn ;перемещение на очередную точку
;к началу цикла мы переместились на одну из найденных
@@outLoop: X NumSign
Acc i
CALL CallThemAll
;назвали номер точки, теперь выдаём её параметры
.rodata
NumSign db '№',0x00
XStr db 'X:',0x00
YStr db 'Y:',0x00
LumStr db 'Яркость:',0x00
SizeStr db 'Диаметр:',0x00
OutOfMemStr db 'Out of heap memory',0x0D,0x0A,0x00
.code
;в [Z+1] лежит радиус точки,
;в [Z+2j+k]=[Z+2] - X-координата
;в [Z+2j+1]=[Z+3] - Y-координата
;в [Z+4j+k]=[Z+4] - яркость
X XStr
Acc [Z+2j+k] ;X-координата
CALL CallThemAll
X YStr
Acc [Z+2j+1] ;Y-координата
CALL CallThemAll
X LumStr
Acc 0xFFF ;инверсия 12 младших бит через вычитание
SUB [Z+4j+k] ;Яркость
CALL CallThemAll
X SizeStr
Acc [Z+1] ;размер точки
CALL CallThemAll
;продвигаемся на одну позицию, если только указатель не nil
@@moveOn: ZAcc RoundZero
SUB [Z+k]
Z [Z+k]
i++ 0
JL @@outLoop
JMP @@endless
OutOfMemory: X OutOfMemStr
CALL print
@@endless: JMP @@endless
main endp
;после 3 вызовов этой процедуры начинается экономия :)
CallThemAll proc
CALL print
CALL BetterBin2Bcd
OUT 13
OUT 10
JMP [--SP]
CallThemAll endp
%include "Print.asm"
%include "Bin2Bcd.asm"
.data
;расчётный диаметр пятна. Вообще, должен грубо устанавливаться исходя из экспозиции кадра, затем корректироваться по вычисленной дальности
;но пока, для отладки, сразу зададим 3
D1 dw 6
;ДИВАН
;в дальнейшем будет ещё R2 - радиус МДД, когда работаем в ближней зоне (они больше не используются для измерения, но нужно проверять их наличие)
ActivePoints dw APHeadDummyX
AllPoints dw Nil
APHeadDummyX dw Nil ;когда будем сверять с X-координатой ПРЕДЫДУЩЕЙ ТОЧКИ, это позволит не вводить лишних проверок!
Heap dw Elem0
APTailDummyX dw 32767
;возможно, и оставшиеся значения Y, Lum, Path могут пригодится, а пока пущай будут чем угодно
;область для списков точек, для начала она вся объединена в список Heap
Elem0:
Elem0Next dw Elem1
Elem0X dw ?
Elem0Y dw ?
Elem0L dw ?
Elem0P dw ?
Elem1 dw Elem2,?,?,?,?
Elem2 dw Elem3,?,?,?,?
Elem3 dw Elem4,?,?,?,?
Elem4 dw Elem5,?,?,?,?
Elem5 dw Elem6,?,?,?,?
Elem6 dw Elem7,?,?,?,?
Elem7 dw Elem8,?,?,?,?
Elem8 dw Elem9,?,?,?,?
Elem9 dw ElemA,?,?,?,?
ElemA dw ElemB,?,?,?,?
ElemB dw ElemC,?,?,?,?
ElemC dw ElemD,?,?,?,?
ElemD dw ElemE,?,?,?,?
ElemE dw ElemF,?,?,?,?
ElemF dw Elem10,?,?,?,?
Elem10 dw Elem11,?,?,?,?
Elem11 dw Elem12,?,?,?,?
Elem12 dw Elem13,?,?,?,?
Elem13 dw Elem14,?,?,?,?
Elem14 dw Elem15,?,?,?,?
Elem15 dw Elem16,?,?,?,?
Elem16 dw Elem17,?,?,?,?
Elem17 dw Elem18,?,?,?,?
Elem18 dw Elem19,?,?,?,?
Elem19 dw Elem1A,?,?,?,?
Elem1A dw Elem1B,?,?,?,?
Elem1B dw Elem1C,?,?,?,?
Elem1C dw Elem1D,?,?,?,?
Elem1D dw Elem1E,?,?,?,?
Elem1E dw Elem1F,?,?,?,?
Elem1F dw -32768,?,?,?,?
Stack dw ?,?,?,?
(пришлось убрать цвета - ЖЖ возмущается слишком большой записью)
Пора редактировать константы.
VSync остаётся какой и была, 0x8000, то есть только флажок "ожидать кадровый синхроимпульс", а сколько после этого тактов прождать - совершенно пофиг!
HSync - видим закомментированное значение 0x4000, то есть и здесь лишь флажок "ожидать строчный синхроимпульс", но оно переправлено на 0x40EE (пропустить 238 тактов!), тогда как в ImageTransfer имеем 0x4106 (пропустить 262 такта), и это уже "боевое" значение для стандарта CVI, с условием, что мы подрезаем наше поле зрения с 1280х720 до 1024х720. Пусть оно и будет.
WholeRow - было значение 1023, которое закомментировали и написали 31, очевидно, для "тестового изображения", чтобы совсем не подохнуть на симуляции. А в ImageTransfer у нас тоже 1023, так и поставим.
ImgHeight - было 720, переправили на 32, тогда как в ImageTransfer стоит константа UsefulRows = 719. Но там мы традиционно начинаем с 719 и уменьшаем до нуля, и по признаку отрицательного числа решаем выйти из цикла. Тут у нас был Over-Engineering, когда мы начинали с нуля, и в конце каждой итерации вычитали 720 и прибавляли 721, и по флагу знака решали, выходить ли нам из цикла.
Это было вынужденное решение, поскольку мы собирались взять ещё сотню-другую "лишних строк" внизу кадра, чтобы "волшебным образом" все точки, которые остались в списке ActivePoints, были бы перенесены в список AllPoints. Сделай мы счёт "сверху вниз" - и у нас получалась бы уродская координата Y от 200 до 919, или вроде того, поэтому решили - пусть всё-таки будет от 0 до 719, как и полагается. Хотя, мы-таки ошиблись на единицу и получили в итоге от 1 до 720.
Ладно, не будем пока "окончательно вылизывать" - не удивлюсь, если этот алгоритм ещё очень кардинально поменяется! Так что пусть будет ImgHeight=720, и ещё ImgHeightP1=721.
Threshold - стоит значение 0xDFF, что является 1/8 от насыщения при использовании 12-битного АЦП, и при условии, что к нам приходит инвертированное значение яркости. Почему так вышло - см
"нахождение второй суммы", если вкратце: это результат скрещивания ежа с ужом, то бишь сумматора с "обнаружителем максимума".
Но сейчас у нас 8-битный АЦП, и порог мы хотим 255. Поэтому выставим Threshold = 0x000 - ВНЕЗАПНО. Впрочем, давайте на всякий случай поставим 0x001, то есть порог 254 - нужно будет проверить, какое условие мы проверяем, "больше", "больше или равно", или в разных местах по-разному?
D1 (в сегменте .data внизу файла) - предполагаемый диаметр точек. У меня в компьютерной модели работало примерно так: сначала ставилась максимальная экспозиция (там её можно регулировать самостоятельно), и если в кадре где-то шло насыщение - экспозицию снижали вдвое. Наконец, по величине экспозиции ГРУБО определялась дальность, и по ней - размер пятен, с которыми мы будем иметь дело.
Но здесь мы не можем получить от аналоговой камеры информацию, какую экспозицию она использует, да и выставляет она её не так, как мне хотелось бы (слишком ярко всё равно!), поэтому такой подход не годится.
Может оказаться, однако, что этого и не надо! В конце концов, мы почти довели наш алгоритм обнаружения до того, что каждому пятну он будет сам присваивать радиус. То есть, может мы вообще уберём переменную D1, заменив её на константу, и всегда будем начинать с 6 (минимально возможное значение).
Но не сразу... Сейчас, если так сделать, я ожидаю обнаружения огромного количества пятен, десятки и сотни, поскольку мы не предусмотрели все варианты "слияния" нескольких пятен в одно. Поэтому пока выставим 110 - это чуть больше диаметра самого близкого пятна на нашем изображении. "Костыль", но пусть будет.
Также нам надо написать (скопипастить) обработчик прерываний и инициализировать ЖК-экранчик
Исходно в файле VideoProcessing.asm ничего этого нет. Но ничего страшного: для инициализации ЖК мы просто подключим файл LCD4wire.asm.
Ну а обработчик прерываний скопируем из ImageProcessing. Хотя и для него, если подумать, можно было отдельный файлик создать.
Так теперь выглядит VideoProcessing.asm:
;громко думаем, как организовать видеоообработку
;наилучшее качество получим с сигнала CVI, т.к у него длительность видимой строки максимальная, 43 мкс, и 10,3 мкс на обратный ход
;у нас есть не более 308 тактов для подготовки к следующей строке!
;в 1205ХВ014 логика будет существенно отличаться: сканирование "в два луча", всего 8 тактов на обратный ход
;но давайте решать проблемы по мере их поступления. Потренируемся на кошках, то бишь на аналоговых камерах высокой чёткости...
%include "QuatCoreConsts.inc"
%include "Win1251.inc"
.rodata
; ImgHeight EQU 720 ;720p пока что
VSync EQU 0x8000 ;ожидание кадрового синхроимпульса, значение для регистра произвольное
HSync EQU 0x4106 ;формат CVI, 9,5 мкс от синхроимпульса до начала строки, и ещё 1 мкс прибавляем, т.к хотим 1024 вместо 1280 (а вообще при 25 МГц полезная строка занимает 1075 пикселей)
WholeRow EQU 1023 ;для обработки всей строки целиком
ImgHeight EQU 720 ;вообще, 32, но вводим дополнительные строки, чтобы перенести все точки из ActivePoints в AllPoints
ImgHeightP1 EQU 721 ;не обучили свой компилятор обрабатывать "constexp"
Threshold EQU 0x01 ;минимальная яркость пятен, сейчас поставили 1/8 от "насыщения" (4096), но с инверсией, так надо!
Nil EQU -32768 ;традиционно это 0, но у нас нет флага нуля! Поэтому куда удобнее гигантское отриц. значение, всяко у нас 65535 слов памяти не наберётся, надеемся что 256..512 слов вполне хватит
.code
main proc
%include "SetClock.asm"
SP Stack ;инициализация стека.
.rodata
CommonError dw 0x101,0x106,'Ошибка: ',0
PointsStr db 'Обнаруженные точки:',0x0D,0x0A,0x00
NumSign db '№',0x00
XStr db 'X:',0x00
YStr db 'Y:',0x00
LumStr db 'Яркость:',0x00
SizeStr db 'Диаметр:',0x00
OutOfMemStr db 'Out of heap memory',0x0D,0x0A,0x00
NoVideoSignal db 'нет сигнала',0
UnderflowInt db 'исчерпание ','заданий GPU',0
OverflowInt db 'переполнение','результатов GPU',0
%include "LCD4wire.asm"
%include "ProcessFrame.asm"
;давайте передадим параметры обнаруженных точек по UART
;пока тупо по младшему байту
SIO UART
.code
X PointsStr
CALL print
Z AllPoints ;В списке AllPoints нет фиктивных точек. Но может вообще никаких не быть!
;Хотя сам AllPoints - это "заголовок", с него надо двинуться вперёд, если получится
i 0 ;номер точки (для отображения в терминале, поэтому нумерация с 1)
JMP @@moveOn ;перемещение на очередную точку
;к началу цикла мы переместились на одну из найденных
@@outLoop: X NumSign
Acc i
CALL CallThemAll
;назвали номер точки, теперь выдаём её параметры
;в [Z+1] лежит радиус точки,
;в [Z+2j+k]=[Z+2] - X-координата
;в [Z+2j+1]=[Z+3] - Y-координата
;в [Z+4j+k]=[Z+4] - яркость
X XStr
Acc [Z+2j+k] ;X-координата
CALL CallThemAll
X YStr
Acc [Z+2j+1] ;Y-координата
CALL CallThemAll
X LumStr
Acc 0xFFF ;инверсия 12 младших бит через вычитание
SUB [Z+4j+k] ;Яркость
CALL CallThemAll
X SizeStr
Acc [Z+1] ;размер точки
CALL CallThemAll
;продвигаемся на одну позицию, если только указатель не nil
@@moveOn: ZAcc RoundZero
SUB [Z+k]
Z [Z+k]
i++ 0
JL @@outLoop
JMP @@endless
OutOfMemory: X OutOfMemStr
CALL print
@@endless: JMP @@endless
main endp
;обработка прерываний
GPU_WDT: C NoVideoSignal
JMP IntHandler
GPU_UFLO: C UnderflowInt
JMP IntHandler
GPU_OFLO: C OverflowInt
IntHandler: SIO LCD
X CommonError
CALL print
X C
CALL print
@@endless: JMP @@endless
;после 3 вызовов этой процедуры начинается экономия :)
CallThemAll proc
CALL print
CALL BetterBin2Bcd
OUT 13
OUT 10
JMP [--SP]
CallThemAll endp
%include "Print.asm"
%include "Bin2Bcd.asm"
.data
;расчётный диаметр пятна. Вообще, должен грубо устанавливаться исходя из экспозиции кадра, затем корректироваться по вычисленной дальности
;но пока, для отладки, сразу зададим 3
D1 dw 110
;ДИВАН
;в дальнейшем будет ещё R2 - радиус МДД, когда работаем в ближней зоне (они больше не используются для измерения, но нужно проверять их наличие)
ActivePoints dw APHeadDummyX
AllPoints dw Nil
APHeadDummyX dw Nil ;когда будем сверять с X-координатой ПРЕДЫДУЩЕЙ ТОЧКИ, это позволит не вводить лишних проверок!
Heap dw Elem0
APTailDummyX dw 32767
;возможно, и оставшиеся значения Y, Lum, Path могут пригодится, а пока пущай будут чем угодно
;область для списков точек, для начала она вся объединена в список Heap
Elem0:
Elem0Next dw Elem1
Elem0X dw ?
Elem0Y dw ?
Elem0L dw ?
Elem0P dw ?
Elem1 dw Elem2,?,?,?,?
Elem2 dw Elem3,?,?,?,?
Elem3 dw Elem4,?,?,?,?
Elem4 dw Elem5,?,?,?,?
Elem5 dw Elem6,?,?,?,?
Elem6 dw Elem7,?,?,?,?
Elem7 dw Elem8,?,?,?,?
Elem8 dw Elem9,?,?,?,?
Elem9 dw ElemA,?,?,?,?
ElemA dw ElemB,?,?,?,?
ElemB dw ElemC,?,?,?,?
ElemC dw ElemD,?,?,?,?
ElemD dw ElemE,?,?,?,?
ElemE dw ElemF,?,?,?,?
ElemF dw Elem10,?,?,?,?
Elem10 dw Elem11,?,?,?,?
Elem11 dw Elem12,?,?,?,?
Elem12 dw Elem13,?,?,?,?
Elem13 dw Elem14,?,?,?,?
Elem14 dw Elem15,?,?,?,?
Elem15 dw Elem16,?,?,?,?
Elem16 dw Elem17,?,?,?,?
Elem17 dw Elem18,?,?,?,?
Elem18 dw Elem19,?,?,?,?
Elem19 dw Elem1A,?,?,?,?
Elem1A dw Elem1B,?,?,?,?
Elem1B dw Elem1C,?,?,?,?
Elem1C dw Elem1D,?,?,?,?
Elem1D dw Elem1E,?,?,?,?
Elem1E dw Elem1F,?,?,?,?
Elem1F dw -32768,?,?,?,?
Stack dw ?,?,?,?
(пришлось обойтись без подкрашивания цветом, ЖЖ жалуется на слишком большую запись)
По-моему, это самая крупная из наших программ на данный момент!
Получилось 186 слов кода и 323 слова данных, что уже требует 8-битной адресации ПЗУ и 9-битной адресации ОЗУ.
Кроме того, используется 5 адресов процедур и 45 "непосредственных значений".
Синтез проходит без проблем: 1588 ЛЭ, или 16% от доступных на 5576ХС4Т, и ещё 12 288 бит памяти, или 13% от доступных. С таймингами проблем нет: предельная частота 27 МГц.
Одна маленькая проблемка: при запуске вообще не подаёт никаких признаков жизни - экранчик не инициализирует, там пустота, и никаких сообщений на компьютер не посылает. С нахрапу не получилось - придётся сейчас уйти глубоко в отладку.
Продолжение следует.