Info Guide
#11
05 июля 2015 |
|
Дикий ум - ловля багов: самые типичные ошибки, при разработке на ассемблере 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 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 лежит в приложении. * * * Кажется, всё самое главное я рассказал. Но тема интересная и перспективная. Ведь если умеешь отлаживать - можешь рискнуть писать большой код. Если не умеешь - не можешь. Конечно, надо ещё иметь фантазию, усидчивость и определённый кругозор,но эти качества присущи практически любой творче─ ской специальности. Осталось только пожелать вам приятных глюков и успешной разработки кода с боль─ шой буквы!
Другие статьи номера:
Похожие статьи:
В этот день... 21 ноября