Info Guide #11
05 июля 2015

Дикий ум - ловля багов: самые типичные ошибки, при разработке на ассемблере Z80 (часть 2).

<b>Дикий ум</b> - ловля багов: самые типичные ошибки, при разработке на ассемблере Z80 (часть 2).
        Баги: практика
  Теперь  перейдём собственно к "типичным
ошибкам". Без всяких высасываний из пальца
прямо разберём все ошибки,которые возника─
ли при написании и серии оптимизаций эффе─
кта"ball record"в New View 48K. Это, ко─ 
нечно,не большая программа со сложным раз─
ветвлённым функционалом, но я хотел бы по─
смотреть на того, кто сможет так же подро─
бно документировать процесс разработки че─
го-то более крупного :)

   Начато03.10.2014. Этапы - это  внесён─
ные изменения, не имеющие отношения к баг─
фиксам.Баги пронумерованы,для каждого бага
указано, как  он  проявился  и  как найден
(иногда это одно и то же). Разработка про─
изводилась  в SjAsmPlus с автосборкой (из─
начально  в TRD) с помощью упаковщика mhmt
и утилиты  trdtool. Я всегда использую ав─
тосборку,чтобы не отлаживать отдельно сна─
пшот, а потом писать лоадер и заново отла─
живать. Первая  пустая  сборка  сделана из
исходников Chaos Zoomer 1K.

 - (этап 1)Нарисовал спрайт шарика.
 - (этап 2)Графика  вручную перенесена в
нолики и единички. Написаны простые проце─
дуры  вывода  спрайтов. Протестированы  на
координатах от балды(y+=9,x++).
 1. В середине  маски была точка (случайно
поставилась цифра уже после рисования???)-
заметил только при перемещении спрайта.
 - (этап 3)Процедура  вывода заменена на
набор  из  8x8  генерируемых  выводилок (в
#c000..#ffff -  для  каждой комбинации x 
mod 8,  y mod 8),  генератор  выводилок -
genprspr.
 2. Забыл инициализировать hl перед циклом
вызова genprspr - нашёл трассировкой.
 3. jr z  вместо jr nz  в genprbyte (часть
genprspr) - нашёл  просмотром сгенерённого
кода.
 4. Забыл случай, когда после маски ничего
не накладывается - нашёл просмотром сгене─
рённого кода. Оптимизировал.
 5. Забыл  случай, когда маска перекрывает
весь  байт - нашёл просмотром сгенерённого
кода (и первый раз поставил переход непра─
вильно, второй раз баг из-за того, что пе─
ренёс ветку из другого места).
 6. Не  работал  вывод  по зигзагу - нашёл
первым запуском сгенерённого кода  (и пер─
вый  раз неправильно написал метку, второй
раз не сохранил,третий раз зигзаг не в той
фазе - торопился исправить и исправлял раз
за разом неправильно,но эти циклы шли один
за другим и очень быстро).
 7. Не работал  down hl в сгенерённом коде
(ld a,l вместо ld l,a, ошибка copy-paste)-
нашёл первым запуском сгенерённого кода.
 - (этап 4)В генераторе выводилок сдела─
ны ветки не только дляand:or(реальноor:
xor),но и дляor,xor,and.Спрайт инверти─
рован для удобства написания всех веток.
 8. Ошибочно  поставил  единицы  вне маски
(потому что  инвертировал  маску  и спрайт
автозаменой) - нашёл  печатью спрайтов без
фона (до этого печатал поверх копии ПЗУ).
 - (этап 5) В генераторе  выводилок  до─
бавлен   оптимизатор  вывода  одного  бита
(genprsprres). 
 9. В  genprsprres  был  пропущен   inc hl
(скопировал  не  целиком из другой ветки -
ошибка copy-paste) - нашёл просмотром сге─
нерённого кода.
 - (этап 6)В генераторе выводилок добав─
лен  оптимизатор  конструкций  типаinc l:
down hl:dec l(removeЗinc).
 10. В removeЗinc  не обслужена одна ветка
выхода - нашёл  запуском выводилки с пара─
метрами.
 (С этого момента правильно заработала вы─
водилка.) 
 - (этап 7) Написан генератор восстанав─
ливалок  (стиралок) genrespr (на  основе 
генератора  выводилок) - стиралки по форме
спрайта,черезldi/lddиз копии экрана, 8x8 
штук в#8000..#bfff.В связи с этим актив─ 
ная  часть программы отделена от инициали─
зации  и помещена в#7800,а в#6000поме─ 
щена  копия  экрана. При этом пришлось ис─
правлять адреса в депакере (depkmain.asm).
 11. В genprsprxor (ветка genprspr, реаль─
но не используется)  пропущено inc c - на─
шёл  просмотром  кода, когда делал из него
другую процедуру.
 12. Обвязку  распаковки  depkmain.asm ис─
правил  не  в  том каталоге (другая панель
TotalCmd) - программа не запустилась.
 13. Забыл скопировать экран для восстана─
вливалки - нашёл  запуском восстанавливал─
ки.
 14. Забыл присвоить de для восстанавлива─
лки - нашёл просмотром исходника.
 (Прошёл  час и 40 минут  после  того, как
заработала  выводилка; с этого момента за─ 
работала стиралка, уже 858 строк.) 
 15. Вместо ld a,(hl):ld (de),a  стояло ld
a,(de):ld (hl),a - увидел после фикса пре─
дыдущего бага (баг был и в трёх комментах,
хотя рядом было написано ldi,ldd).
 - (этап 8) Написана  обвязка  вывода по
синусу  (mainprO)  и   обвязка  стирания 
(mainreO) через  использование  значений,
рассчитанных  обвязкой  вывода  (черезix=
COORDS+n). Число шариков занесено в конс─
танту BALLS.Проверяю работоспособность и 
число  тактов  на  тесткейзах приnomove=1
(т.е. не двигать спрайты).
 16. Вместо ld ix,COORDS+128 (работа через
-128,+127) стояло ld ix,COORDS - программа
вывалилась.
 17. Пропущена  метка BALLS (хотя откомпи─
лировалось) - программа сбросилась.
 18. jp nz,mainreO  вместо   jp nz,mainprO
(ошибка copy-paste)  -  программа  сброси─
лась.
 19. А надо было  jp p  и там, и там (т.к.
использовалось  ix+128) - глючило. Переде─
лал на ix, ix-BALLS, ix-(BALLS*2).
 (Через 50 минут после того,как заработала
стиралка; один баг на >45 строк.) 
 - (этап 9)Оптимизировал выводилку через
пересчёт таблицы синусов дляXв значения, 
нужные  для  вывода:x/8иf(x&7)=8*(x&7)+
#c0(циклrexsinO), а таблицы синусов для
Y в таблицу экранных адресов этихY(цикл
reysinO). До  этого  использовалась  про─
цедура  расчёта  экранных  координат в ПЗУ
(8881).
 20. Было COORDS=#8Off-BALLS вместо #8100-
BALLS - глючило.
 21. ld (ix+),a  после оптимизации было не
там - глючило.
 (Прошёл  ещё час, на этот момент написано
1100 строк - один баг на 50 строк, включая 
№24.) 
 - (этап 10)Смотрел  скорость при разных
рисунках шариков. Выбрал самый быстрый ва─
риант.

   Второй день:
 - (этап 11)Добавлял оптимизатор стирал─
ки -genreopt(запускается послеgenrespr,
исправляет уже сгенерированную процедуру -
ld a,(hl):ld (de),a:inc l:inc e заменяется
наldi,и ld a,(hl):ld (de),a:dec l:dec e
заменяется наldd). 
 22. В  genreopt  не  был  сделан  пропуск
inc l:inc e - программа  висла  (и  первый
раз неправильно исправил, оставил ld a,#ed
наряду с ld (hl),#ed ).
 23. В genreoptn (ветка genreopt) был про─
пущен ld a,(hl)  перед сравнением (прежде─
временная оптимизация) - программа сбрасы─
валась  (и первый  раз скомпилил исправле─
ние, не сохранив).
 - (этап 12)Добавил компиляцию в tap.

   Через 2 дня:
 - (этап 13) Оптимизировал обвязку рисо─
валки и стиралки через хранение параметров
цикла в регистрах, которые не портятся при
вызове.
 24. В rexsinO использовался l вместо (hl)
- шарики  дёргались (первый раз понял при─
чину неправильно, потом  исправил неправи─
льно).
 25. Добавил  ld c,0  после ld ($+3+2),a -
висло. Если  бы  было сделано через метку,
глюка бы не было.
 - (этап 14) Оптимизировал  рисовалку  и
стиралку через константы в регистрах.
 26. Изменил prdhlsize, но забыл исправить
в removeЗreinc (в  removeЗinc  исправил) -
артефакты на экране (и первый раз исправил
неправильно, т.к. надо  в respr свой dhl -
портится de).
[Ложный баг:сначала оптимизация dhl через
константы  казалась проигрышной (забыл за─
ранее посчитать, нашёл просмотром тактов в
отладчике), в итоге потратил время на №26;
но  впоследствии  в  новом коде этот новый
вариант dhl выиграл.]
 27. Добавил     jr z,genprsprorxorconst_c
вместо genprsprorxor2const_c (ошибка copy-
paste) - висло при const_b=0.
 28. Написал 'xsin вместо xsin/256 - SjAsm
не скомпилил.

   Через несколько дней:
 - (этап 15)Написал вывод фона (до этого
фон был из ПЗУ).
 29. Была  опечатка:  pgbg2, pgbgЗ  вместо
prbg2,prbgЗ (причём prbgO,prbg1 было напи─
сано  правильно) - ошибка  компиляции,  но
запустилось с неправильной фоновой картин─
кой.

   Ещё через несколько дней:
 - (этап 16) Оптимизировал обвязку рисо─
валки и стиралки через сохранение парамет─
ров в стек, а вызов через ret:pop hl(это 
экономит  регистры  на  параметры  цикла и
константы отрисовки).
 30. В  genprspror  ошибочно  был  переход
на  genprsprorxorconst_c - вместо  нужного
genprsprorconst_c (только один из трёх ря─
дом  стоящих  неверный!) - нашёл по ошибке
компилятора (длинный jr), но всё работало!
 31. Был ещё один длинный jr на genprspror
- нашёл по ошибке компилятора,но всё рабо─
тало!

   При оптимизации:
 - (этап 17) Написал другой вариант сти─
ралок, которые генерируются прямо из текс─
туры фона (настройкаbgtype=2),им не нуж─ 
на копия экрана в#6000.Процедура называ─ 
етсяgenrebg.
 32. Комментарий (e&7)*8+#c0 вместо (x&7)*
8+#c0 - нашёл при изменении исходника.Этот
баг  в комментарии  был  бы очень чреватым
при последующих изменениях.
 33. Удалил  вызов genreopt, но забыл уда─
лить вызов genrespr - сбрасывалось.
 34. Перед  genrebgsO  написал ld hl,PROGS
вместо ld hl,PROGS-#4000 - висло с артефа─
ктами на экране.
 35. Было jr nz,genrebgs1 вместо genrebgsO
(ошибка copy-paste) - висло  с артефактами
на экране.
 36. ld a,b:dec a:call nz,dde  вместо call
dde:ld a,b:dec a:call nz,genrebgdhl  (пер─
вый раз исправил наоборот).
 37. IMVEC  и IMER затирались новыми длин─
ными процедурами  rebg - сбрасывалось. Пе─
ренёс в другие адреса (где выводилки).
 38. Забыл  в genrebg  в начале сгенериро─
вать pop hl - сбрасывалось.
 39. Забыл  push hl  в новой ветке mainreO
(запуск стиралок из главного цикла)- сбра─
сывалось.
 40. Размеры процедур rebg могут превышать
#80 байт  (на 1 байт)  -  нашёл спецтестом
при  генерации, исправил  фоновую  графику
(#fe на #ff в углу).
 - (этап 18) Пробовал сделать восстанав─
ливалку черезpush(bgtype=0)- невыгодно 
(нужно маленькое окно), но пригодилось для
следующих изменений.
 41. Когда сделал три варианта bgtype, по─
ставил if..else без endif, не компилирова─
лось  (unexpected end of file) - нашёл вы─
кидыванием  частей исходника (оборачивание
if..endif не помогает).
 (На тот момент написано 1436 строк - один
баг на 35 строк.) 

   Ещё через время:
 - (этап 19) Новый скоростной расчёт па─
раметров для вывода (pop hl:ld bc:add hl,
bc:ld (),hl- причём  в стеке для стирания
патчится  не  слово адреса  процедуры  или
слово адреса экрана, а по одному  байту от
того  и того). Для этого всеYдля шариков 
зациклены,для каждой из 8 фаз входа в таб─
лицу Yгенерируются "скрипты" (в#6000), 
где Y сортируются сверху вниз. ТаблицаX
копируется  в стек  с циклическим сдвигом.
Настройкаrex=1.
 42. При генерации таблиц для Y брались не
синусы, а  пересчитанные  в адреса (поверх
того  же  места  в  памяти) - сбрасывалось
(нашёл трассировкой и просмотром памяти).
 43. Забыл  присвоить sp при входе в новый
расчёт  параметров вывода  -  сбрасывалось
(нашёл трассировкой и просмотром памяти).
 44. Вместо PROGS&255 в новом расчёте сто─
яло 0 - висло (нашёл трассировкой).
 45. После нового расчёта не присваивались
регистры перед вызовом шариков - сбрасыва─
лось (нашёл трассировкой).
[Ложный баг: для нового расчёта после со─
ртировки  шариков по Y надо по идее читать
X-координаты не подряд, а в порядке сорти─
ровки  (нашёл, когда  начал добавлять сор─
тировку). Но потом отсортировал запись (не
чтение) по порядку X  и вернул чтение под─
ряд.]
 46. Когда добавлял номер шарика в порядке
сортировки (т.е. 2 байта вместо 1 в списке
y), забыл пропустить этот байт при состав─
лении  стека - артефакты  на экране, нашёл
постепенным возвратом изменений,первый раз
исправил  неправильно  (пропустил  не  тот
байт).
 47. При добавлении сортировки поставил jr
nc,fillreysortskip ;skip if (hl)>=min вме─
сто jr c,fillreysortskip ;skip if (hl)>min
- сбрасывалось,нашёл трассировкой.
 (С этого момента на Скорпионе всё выводи─
тся без резания лучом. 32 шарика для круг─ 
лого счёта, чтобы y=ysin[i],i+=8. Пока что 
используется стиралка через push.) 

 - (этап 20)Оптимизировал стиралку через
push- на1,2,7строках  чётных  знакомест
всё заливаем одним регистром,т.к.в графике
там нули или ff-ки.
 48. При оптимизации стиралки один jr z не
компилировался, но всё запускалось - нашёл
трассировкой  сгенерированной  стиралки  и
просмотром исходника.
 - (этап 21)Придумал, как быстро генери─
ровать  параметры для старой стиралки (ге─
нерируемой из фона).Чтобы вернуться к ней,
стал  тестировать  условную  компиляцию  с
разными комбинациямиrexиbgtype- убеди─ 
ться,что всё ещё работает перед написанием
нового расчёта параметров для стиралки.
 49. В условной  компиляции  в двух местах
стояло if trex==0 вместо if rex==0 - выва─
ливалось, нашёл при возврате к старому ме─
тоду стирания.
 50. reysin  (перекодирование  таблицы  Y)
была выброшена  за пределы процедур (возв─
ратил на место) - сбрасывалось,нашёл трас─
сировкой и просмотром содержимого таблицы.
 - (этап 22) Приблизил адресацию восста─
навливалок  к адресации рисовалок переста─
новкой  битов адреса (настройкиREYYYYXXX,
SWAP2310- сначала  отлаживал  одну, потом
добавил вторую).
 51. При добавлении второго варианта адре─
сации  в одном  из двух  if REYYYYXXX были
перепутаны  ветки - артефакты  на  экране,
нашёл  переключением  REYYYYXXX (артефакты
остались) и просмотром исходника.
 - (этап 23)Теперь вход в восстанавлива─
лки всегда  по#nnOO,без#nn80.Для этого 
в#c000..#ffOOставитсяjp nzна восстана─ 
вливалку#nn80,в  конце  восстанавливалки 
всегдаNZ,а в конце выводилки добавилxor
a.НастройкаPRJP. 
 - (этап 24)Вместо генератора параметров
восстанавливалки сделал патч стека выводи─
лки (прямо в выводилке), чтобы  превратить
его в стек стиралки - настройкаPRPATCHRE.
С новым вариантом адресации меняется всего
один байт из 4 на каждый шарик.
 52. При добавлении патча стека в выводил─
ку пропустил  inc sp:inc sp - сброс, нашёл
трассировкой.
 53. При добавлении стиралки по патченному
стеку поставил ld a,l вместо ld a,(ysinst)
- нашёл  трассировкой во время поиска сле─
дующего бага.
 54. В стеке патчился адрес не текущего, а
следующего шарика (ошибка проектирования)-
артефакты на экране, нашёл трассировкой.
 55. В генераторе  рисовалки  координата Y
для  патча  бралась  не из того регистра -
артефакты на экране,нашёл просмотром исхо─
дника.
 56. Координата  Y бралась в конце шарика,
когда она уже не соответствовала - артефа─
кты на экране, нашёл просмотром исходника.
 57. В mkscript (генератор скрипта по таб─
лице Y) inc e:ld (hl),e  вместо ld (hl),e:
inc (hl) и из-за копипасты ещё одно  inc e
дальше,в результате в стеке не было адреса
выхода - сброс, нашёл трассировкой и прос─
мотром памяти.
 (С этого  момента  вернулся  к стиралкам,
сгенерированным из фона.) 

 - (этап 25) Переделал  патч  стека  для
стиралок  через (hl')вместо ex (sp),hl,
теперь патчится адрес нужного шарика, а не
следующего.
 58. Когда возвращал pop hl ;scr  в начало
re (раньше  я переместил  его  в конец для
исправления  фикса адреса следующего шари─
ка), перепутал  ветки условной компиляции,
так что оно осталось в конце - сброс,нашёл
трассировкой.
 59. В новом вызове стиралки не присваива─
лись  константы  в регистры - артефакты на
экране, нашёл просмотром исходника.
 60. В tap  не  чистился  экран - висло на
генераторе стиралки,нашёл трассировкой.
 (С этого  момента  32 шарика работают без
проблем на 48K - в эмуляторе Fuse.) 

 - (этап 26)Поскольку  в эмуляторе  Fuse
не увидеть число тактов в процедуре, доба─
влен  настраиваемый пустойldir,чтобы ви─ 
деть, сколько осталось свободного времени.
Потом он сделан автонастраиваемым, находит
по  прерыванию  худший  случай - настройка
SPEEDMETER.
 - (этап 27) Переделал  патч  стека  для
стиралок через(de)вместо(hl').
 61. При   убирании  констант   const_d  и
const_e  написал ifdef вместо if - заметил
по неизменению числа тактов.
 62. В mkscript (генератор скрипта по таб─
лице Y) при замене hl' на de пропустил inc
hl  в патче - артефакты  на  экране, нашёл
трассировкой.
 63. В genprspr ret оказался в ветке усло─
вной  компиляции  после добавления xor a -
заметил  просмотром исходника при убирании
xor a, когда менял hl' на de.
 64. Убирать  xor a  было нельзя, т.к. она
давала Z - артефакты на экране,нашёл трас─
сировкой, исправил  добавлением  DRAWUP  и
сменой Z на C.
 - (этап 28)НастройкаDRAWUP- рисование
спрайтов снизу вверх, чтобы в восстанавли─
валках  всегда  было CY=0 (раньше всегда 
былоNZ,но дляZв выводилках надо лишнюю 
команду xor a,а дляCY=1не надо). Потом 
сделал, чтобы спрайт переворачивался в со─
ответсвии с этой настройкой.
 65. После вставки DRAWUP не оптимизирова─
лись inc l:dhl:dec l - нашёл подсчётом та─
ктов.
 - (этап 29)Оптимизировал  генерациюX-
координат  пересортировкой таблицы синусов
через 7 значений  и сразу по 2 байта -x/8
иf(x&7)(настройкаSORTX7). 
 66. После   добавления   SORTX7   вызывал
fillrexO, хотя   добавил  перед  ней   код
(fillrex) - сбрасывалось, нашёл трассиров─
кой.
 - (этап 30)ОптимизировалfillrexO (пе─
ренос данных  для X из таблицы синусов в 
линейную таблицу, с одновременной цикличе─
ской  перестановкой шариков) - копирование
не черезld,а черезldi.
 67. При замене  в fillrexO копирования из
ld  на  ldi  забыл  сохранить  регистр c -
сбрасывалось, первый раз исправил неправи─
льно (ld c,b, хотя ldi были парами).
 - (этап 31)Убрал fillrexO,заменил  на
ldir.Исходную  таблицу  синусов  зациклил
методом добавления копии начала в конец.
 68. Делал  это копирование не к +512, а к
+256 - примерно каждый 3-й кадр искажался,
нашёл просмотром исходника.
 - (этап 32) Пересортировал расчёт пара─
метров вывода по порядку ball number,чтобы
читать данные дляXиз стека,как и задумы─ 
валось.
 69. При добавлении пересортировки по ball
number  в поиске  был  цикл без выхода при
нахождении - сбрасывалось,нашёл просмотром
исходника.
 (На  этот момент  написано 2696 строк в 8
модулях - один баг на 39 строк.) 

 - (этап 33) Оптимизировал  в  стиралках
down hlна нечётных знакоместах черезset/ 
res 5,l.
 70. При  оптимизации dhl на нечётных зна─
коместах в ветке DRAWUP написал set вместо
res - артефакты на экране, нашёл трассиро─
вкой.
 (На  этот  момент  написано 2774 строки -
один  баг  на  40 строк; около 7000 тактов 
свободно при 32 шариках.) 

 - (этап 34) 28.11.2014.Искал самые уда─
чные  палитры для шариков и фона, учитывая
мнение населения на работе.
 - (этап 35) 02.12.2014.Добавлял  шарики
сверх  32 - для  этого  таблица  синусовY
увеличена сверх 256 байт (8*BALLSбайт). 
 71. Когда добавлял шарики сверх 32, напи─
сал ysinst вместо ysin в двух местах - ви─
сло, нашёл просмотром исходника.
 72. Тогда же  запортил hl  в сортировке X
(на глаз не было видно, что он использует─
ся, преждевременная  оптимизация) - висло,
нашёл просмотром исходника.
 73. Пересчитывал  ysinst (фаза  синуса по
Y) до того,как с ним была сделана половина
действий  (преждевременная  оптимизация) -
каждый  восьмой кадр дёргался, нашёл прос─
мотром исходника.
 74. Было  неправильное  зацикливание  при
BALLS>32 - нашёл случайно просмотром исхо─
дника при поиске предыдущего бага.
 75. Проверял    if ysin&#100   -  forward
reference,  не компилировалось,  переписал
математикой, первый  раз  ошибочно  ld hl,
ysinst вместо ld hl,(ysinst).
 - (этап 36)Сразу получилось 36 шариков.
Оптимизировал  подбором  траекторий поXи 
Y,чтобы успел 37-й.
 - (этап 37)Оптимизировал  начало стира─
лок  методом  устраненияjp #8080+#nnOOна 
месте выводилок, которые не используются в
траекториях - туда кладётся сама стиралка,
на  которую  раньше  переходили (настройка
MOVERE).
 - (этап 38)Когда шариков стало 37, сде─
лал шаг по  таблице  синусовX не 7, а 9, 
иначе получалось кольцо из шариков.
 76. Когда  делал шаг по X не 7, а 9, неп─
равильно  посчитал  0,9,...,252, (29 шт) -
кадры мелькали,нашёл тестовыми изменениями
и просмотром исходника.
 - (этап 39)Оптимизировал доступ к стеку
при вайтах методом переноса стека на место
выводилок,которые не используются в траек─
ториях (настройкаMOVESCRIPT)- пока несо─ 
вместимо сMOVERE.
 - (этап 40)Оптимизировал вывод (настро─
йкаPRFORWAIT)- вместо..ld (hl),a:inc l:
ld a,(hl):or reg:xor n:ld (hl),a...теперь
будет...ld (hl),a:inc l:ld a,reg:or (hl):
xor n:ld (hl),a...,так лучше укладывается
в вайты.
 (На  этот  момент  написано 2932 строки -
один баг на 39 строк.) 

 - (этап 41)Оптимизировал вход в восста─
навливалки - не вычислять зановоSP(наст─ 
ройкаPRPATCHRESP). 
 77. Когда  добавлял PRPATCHRESP, не заме─
тил, что стек уже переприсвоен в mainprret
- сбрасывалось, нашёл трассировкой.
 78. Тогда же забыл сбросить флаг CY - ар─
тефакты на экране, нашёл просмотром исход─
ника.
 - (этап 42)Оптимизировал  выход из вос─
станавливалок - без  использования  адреса
выхода  вix,просто  через условный пере─ 
ход, как при объединении выводилок со сти─
ралками.
 (На этот  момент  по оптимистичной оценке
свободно порядка 500 тактов при 37 шариках 
- не считая выбросов на отдельных кадрах.) 

   Ещё через время(04.12.2014):
 - (этап 43)Реализовал возможность одно─
временно включить оптимизацииMOVESCRIPTи 
MOVERE(хотя последняя  всего на 46 тактов
- при совмещении ещё меньше), заодно доба─
влена настройкаEVENY- использовать толь─ 
ко чётные  номераY,чтобы осталось больше 
места дляMOVERE.
 - (этап 44)Попробовал ещё более простой
рисунок  шарика. Теперь  почти  38 шариков
(на некоторых кадрах не успевает).
 - (этап 45)Небольшие  оптимизации глав─
ного  цикла - перенёс xsinstв освободив─ 
шийся lx,убрал  сохранение spиз цикла, 
перенёс ysinst в используемыйld hl.Всё 
ещё не успевает.Сколько тактов не успевает
- понять невозможно (SPEEDMETERвычисляет 
только оставшееся время, причём сам тратит
много тактов).
 - (этап 46)Стал выкидывать неиспользуе─
мые  настройки  (перемещать  куски  кода в
main_nu.asm),чтобы было лучше видно акту─ 
альный код.Убраны веткиrex=0,PRPATCHRE=0,
PRPATCHRESP=0,PRJP=0,SORTX7=0.
 - (этап 47)Убрал jp mainloop, ускорил
ветвление наmainprret,убрал переприсваи─ 
вание bcпри входе в стиралки. Всё ещё не 
успевает.
 - (этап 48)В mainprret вместо ld hl,
-(BALLS*4+2):add hl,sp(что, к слову, даёт
неправильный CY) теперь поставил ld e,
tscriptstkshift&255:ex de,hl.
 - (этап 49)Убрал mainprret в  быструю
память, т.к. он выполняется  в поле экрана
(хотя там всего 7 команд).
 - (этап 50)Убрал  две парыpush...popв
подготовке данных дляXперед выводом. 
 79. На промежуточной  фазе этой оптимиза─
ции запортил de (преждевременная оптимиза─
ция) - висло, нашёл просмотром исходника.
 - (этап 51)Использовал стекtrexвместо
стандартного на время прерывания - сэконо─
мил одно переприсваиваниеsp.
 80. Забыл присвоить sp на первой итерации
- глючило и сбрасывалось, нашёл трассиров─
кой.
 - (этап 52)Ускорил работу  сysinstдля
случаяEVENY=1(использовать только чётные 
номераY в таблице синусов, самYлюбой - 
старое  поведение  этой настройки ошибочно
осталось): поскольку реально данные из та─
блицыYне читаются, нужен только номер, и 
он в этом случае делится на 2 и становится
однобайтным.Следующий номер теперь берётся
без ветвлений  по таблице черезld l,(hl).
Уже почти успевает 38 шариков с новой гра─
фикой - мигает ОЧЕНЬ редко.
 - (этап 53)Вместоei:haltпоставил ei:
<код, терпимый к прерываниям>:ld a,r:jp pe
(po=di,т.е. прерывание было  захвачено), 
или дажеld a,r:ret po:jp.Всё это для то─ 
го, чтобы  одиночный медленный кадр не дал
пропуска  следующего кадра поhalt.Теперь 
с новой графикой не мигает на 38 шариках и
почти успевает 39.
 81. Когда убирал  команду ld b,0: условие
if reconst_b!=0  не компилилось  SjAsm'ом,
заменил на if !reconst_b, но на самом деле
надо  было  наоборот: if reconst_b - нашёл
просмотром кода.
 - (этап 54)Оптимизировал ld l иld bc
на основании bc=0 послеldirпри входе в 
скрипты.
 82. Написал  условие  if !tscripts вместо
if !(tscripts&255) - нашёл  просмотром ко─
да.
 - (этап 55)Оптимизировалld bcна входе
в рисовалки.
 (На  этот  момент написано 3154 строки, в
среднем  один баг на 38 строк; все оптими─ 
зации кода за этот день дали выигрыш всего 
около 400 тактов (есть задел на ещё неско─ 
лько  тактов  в трёх местах), но изменение 
графики и алгоритма захвата прерываний да─ 
ли существенный выигрыш.) 

   Через большое время(30.01.2015):
 83. Из-за убирания ld b,0 нельзя было вы─
ключить EVENY и bgtype - висло,нашёл трас─
сировкой, временно  вернул ld b,0, оставил
TODO на сложное условие.
 84. Одновременно обнаружил, что две наст─
ройки  для  sinx??opt были включены, а для
bgtype=0 инклюдилась таблица без opt.
 85. Фаза X лежала  в lx, а он был занят в
bgtype=0 - заметил по  поведению  шариков,
нашёл просмотром исходника, исправил ix на
iy в repush.asm.
 - (этап 56)Сделал в окошке и убрал патч
стека дляbgtype=0- теперь с новой графи─ 
кой успевает 40 шариков.

  03.03.2015:
 86. Заметил, что верхнюю и нижнюю строчки
экрана не надо чистить из-за рисунка шари─
ков - нашёл просмотром экрана, выиграл 332
такта.
 - (этап 57) Заменил  "оптимизированную"
таблицу Y на простой синус - скорость не 
упала, зато  стало  видно одновременно все
шарики (раньше они перекрывали друг друга,
и не было такого скриншота, где было видно
все). Подгонкой  окошка добился 42 шариков
с небольшим запасом на музыку.

   А дальше  всё завертелось - до пати ос─
тавалось всё меньше  и меньше времени, и я 
перестал  документировать. Добавлял  плеер 
музыки, появление/стирание  фона, фикс под 
музыку, но основной код и таблицы остались 
неизменными. 

                  * * *

   Статистика (по  ball record  и  цветным
чанкам):

  - в среднем  один  баг у меня приходится
на  каждые  40  добавленных строк. Поэтому
надо быть осторожным при добавлении кусков
по 100 строк  и более - лучше  этого избе─
гать, чтобы не ловить несколько  багов од─
новременно.
  - из  105 зафиксированных багов 20 отно─
сятся  к упомянутым выше типам: 9 являются
ошибками  copy-paste, 7 - ошибками прежде─
временной оптимизации, 4 - ошибками проек─
тирования. А из оставшихся, кажется, самые
популярные: неполные изменения (исправил в
одном  месте и не исправил в другом), про─
пущенные строки, ошибочные выражения.
  - Если я не ошибся  в подсчётах, 9 багов
найдены  компилятором, 19 - просмотром ис─
ходника (в том числе при изменениях), 23 -
запуском (в том числе подсчётом тактов), 3
- просмотром  скомпилированного  кода, 8 -
изменением тесткейза, 31 - трассировкой (в
том числе 6 - просмотром памяти, 4 - прос─
мотром сгенерённого кода), 1 - выкидывани─
ем частей исходника, 1 - постепенным отка─
том изменений.

                  * * *

   Я записал  на  видео один пример, как я
пишу и отлаживаю код - в реальном времени,
со   всеми  ошибками.  Это  мультиколорный
скроллер картинки для фирменного 48K:
http://www.youtube.com/watch?v=eQacAM9VRmЧ
   Сама  программа  с  графикой  от Trixs/
Conscience лежит в приложении. 

                  * * *

  Кажется, всё самое главное я рассказал.
Но тема  интересная  и перспективная. Ведь
если умеешь  отлаживать - можешь  рискнуть
писать  большой  код. Если  не умеешь - не
можешь. Конечно, надо  ещё иметь фантазию,
усидчивость и определённый кругозор,но эти
качества присущи практически любой творче─
ской специальности.
  Осталось  только  пожелать вам приятных
глюков  и успешной разработки кода с боль─
шой буквы!



Другие статьи номера:

Об оболочке - журнал состоит из разделов, а разделы из статей.

От авторов - предисловие: Прошло 8 лет с момента выхода прошлого номера Info Guide. Что изменилось на Спектруме за это время?

Комьюнити - Spectrum в глубинке: в городе, население которого не превышает 15 тысяч человек, появление компьютера было сравнимо с изготовлением атомной бомбы в гараже.

Комьюнити - Forever 2015: отчет с демопати для всех 8-битных компьютеров.

Комьюнити - DiHalt 2015: отзывы от Lilka и Louisa.

Комьюнити - Как это было в Бразилии: история развития Спектрума в Бразилии от Paulo Silva.

Комьюнити - Беседа с Tiboh/Debris - программистом из Красноярска, долгие годы занимавшимся обработкой архивов спектрумовских программ.

Комьюнити - интервью с Raver/Phantasy взятое на irc.forestnet.org

Code - Этюды: Вызов функции по номеру, Поиск текста по номеру, Определение наличия музыкального сопроцессора, Установка пикселя на ATM Turbo 2, Библиотеки процедур в ALASM, Короткий генератор случайных чисел, Ускорение LD:PUSH.

Code - точка зрения: проекция пространства на экран из одной точки.

Code - чанковый эффект: Magnets stretching

Code - О мерцающем бордере: использование мерцание для повышения разрешения на бордере.

Code - Скриптование в демо: синхронизация эффектов под музыку и не только.

Графика - режиссура в демо: палитра изобразительных средств в Демомейкинге.

Графика - Мини-опрос художников: Dimidrol, Einar Saukas, Sand, Rion, riskej.

Графика - интервью с художником RayNoa/MAYhEM.

Музыка - Синхронизация музыки: nq рассказывает о создании треков под таймлайн.

Музыка - Беседа с MmcM/Sage group, известным AY-музыкантом, о его знаменитой технике.

Музыка - Беседа с Manwe/SandS - известным композитором, одним из старейших демосценеров России.

Музыка - Однобитная музыка: почему бипер ZX Spectrum продолжает вызывать восхищение?

Музыка - Горизонты турбосаунда: Cj Splinter делится опытом работы с TurboSound.

Музыка - Снова о плейерах Pro Tracker 3.x

Музыка - Музыкальный движок Muse 128b.

Системки - Как приручить IAR C Compiler.

Системки - Оберон для ZX Spectrum: Тонкости при разработке на Обероне в среде ZXDev (часть 1).

Системки - Оберон и ассемблер: Сопряжение с ассемблером (часть 2).

Системки - ZX-Basic Compiler: расширяемый кросс-компилятор.

Системки - Программы с поддержкой HDD, или "Linux" для Спектрума с винтом (или SD-картой).

Системки - iS-DOS/TASiS: о базовых принципах программирования под ОС iS-DOS/TASiS (часть 1).

Системки - iS-DOS/TASiS: как писать игры под iS-DOS/TASiS (часть 2).

Системки - iS-DOS/TASiS: Работа с палитрой и переключение графических режимов в TASiS (часть 3).

Металлолом - о строении экрана 6912 с аппаратной точки зрения.

Металлолом - Палитра для ZX Spectrum в различных графических режимах.

Металлолом - Эмуляция контроллера дисковода 1818ВГ93.

Дикий ум - Генерация и оптимизация кода в компилятора (часть 1)

Дикий ум - Генерация и оптимизация кода в компилятора (часть 2).

Дикий ум - ловля багов: самые типичные ошибки, при разработке на ассемблере Z80 (часть 1).

Дикий ум - ловля багов: самые типичные ошибки, при разработке на ассемблере Z80 (часть 2).

Дикий ум - алгоритм сжатия видео - 16 цветов на точку.

Игрушки - Разработка игр на Evo SDK (часть 1).

Игрушки - Разработка игр на Evo SDK (часть 2).

Игрушки - секрет успеха игры Jet Set Willy выпущенной в 1984 году.

Игрушки - Metal Man Reloaded: История создания от Oleg Origin.

Игрушки - Строение скриптового движка игры на примере L7 script engine.

Мыльница - Секретные кнопки в играх: Project ROBO, Ninjajar!, Uwol, Quest for Money, Zooming Secretary, Game About Squares.

Мыльница - письма: Kq, elfh, mig'95, wbr^NOT-Soft.

Мыльница - errata: Работа над ошибками.

Мыльница - об авторах журнала.


Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Железо - IBM PC Keyboard (часть 2).
Умора - Инструкция пользования ударными артиллерийского полка.
Разберемся - Описание игры THE GREAT ESCAPE.
Юмор - ЗАПИСКИ ИДИОТКИ ( ИЗ ЖИЗНИ THE LEVT)
Послесловие - Что ж, вот и конец OPEХ#0...

В этот день...   9 декабря