Баги: практика
Теперь перейдём собственно к "типичным
ошибкам". Без всяких высасываний из пальца
прямо разберём все ошибки,которые возника─
ли при написании и серии оптимизаций эффе─
кта"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 ysind - 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 лежит в приложении.
* * *
Кажется, всё самое главное я рассказал.
Но тема интересная и перспективная. Ведь
если умеешь отлаживать - можешь рискнуть
писать большой код. Если не умеешь - не
можешь. Конечно, надо ещё иметь фантазию,
усидчивость и определённый кругозор,но эти
качества присущи практически любой творче─
ской специальности.
Осталось только пожелать вам приятных
глюков и успешной разработки кода с боль─
шой буквы!
Other articles: