Info Guide
#13
01 апреля 2021 |
|
Code - этюды по программированию на ZX Spectrum
Этюды Alone Coder Если выполняется долгая операция и негде поставить честный процентомер (на─ пример, это сильно замедлило бы работу),то можно написать так: ld a,r add a,a call showprogress Теперь процентомер будет вызываться в 128 раз реже. Используется в ZXRar и в графическом редакторе Scratch. * * * Переворот строки байтов (de=начало, bc=длина): ld h,b ld l,c add hl,de ;hl=конец+1 srl b rr c ;bc=wid/2 mirrorbytes0 dec hl ld a,(de) ldi dec hl ld (hl),a jp pe,mirrorbytes0 * * * В прошлый раз мы не показали,как подго─ товиться к циклу djnz:dec c:jr nz: ld a,c dec bc inc b ld c,b ;bc=1..256 => c=1, ;bc=257..512 => c=2... ld b,a * * * В дополнение к "Этюдам" в #11 (там было определение AY ) - определение наличия TurboSound FM: оно важно,потому что плейер под TFM работает с ожиданием регистра ста─ туса, которого нет на обычном AY. Алгоритм: - включаем FM режим и режим чтения ста─ туса. - пишем в 0-й регистр число с установ─ ленным битом 7. - немного ожидаем. - читаем регистр, смотрим 7-й бит. Если нолик, то это TFM, и он готов. А если нет (то есть прочиталось записанное число или пустая шина), то это не TFM. В Evo SDK это выглядит так: LD BC,#FFFD LD A,%11111000 OUT (C),A ;FM on,status rg read on ld d,b dec d jr nz,$-1 ;pause XOR A OUT (C),A ;select rg 0 dec d jr nz,$-1 ;pause LD B,#BF OUT (C),B ;write some data in rg 0 dec d jr nz,$-1 ;pause inc a ;a=1 LD B,#FF INF ;read status (P=ready) JP P,tfmpresent ;a=1 xor a ;TFM not present tfmpresent ld (TURBOFMON),a NB: при текущей реализации вайтов в ZX Evo нельзя работать с TFM на частоте 14 МГц. Нужно временно включать 3,5 МГц при работе с портами TFM! Иначе звук портится. В эмуляторе это обнаружить нельзя! * * * Наши постоянные читатели помнят услов─ ное сложение с константой за фиксированное время: ;CY=flag ;a=исходное значение jc $+4 jr $+4 add a,что условно прибавить ;a=result :19 t Дополним его условным сложением с реги─ стром за фиксированное время: ;CY=flag ;a=исходное значение ;b=что условно прибавить jr c,$+6 nop jp $+5 ret nc add a,b ;a=result :21 t Или так: ;CY=flag ;с=исходное значение ;b=что условно прибавить sbc a,a and b [add a,c] ;не надо, если исходно 0 ld c,a ;a=result :12(16) t * * * Как перейти на следующий столбец EGA экрана на АТМ-Turbo (если экран подключен в 4000, 8000): ld a,0x9f ;0xa0,если не используем ;верхние 8 линий cp h ld bc,0x4000 adc hl,bc jp pe,nextstq ;в половине случаев ;8000->с000 (надо 6000) ;или a000->e001 (надо 4001) inc a ;если используем верх.8 линий xor h ld h,a nextstq Если экран подключен в 8000, c000, то переход можно делать так: bit 6,h set 6,h jr z,$+2+4+2+2+1 ld a,h xor 0x60 ld h,a and 0x20 jr nz,$+3 inc hl В некоторых случаях выгоднее для всех 4 вариантов расположения спрайта по X сде─ лать свою ветку запоминания адресов в стек, например (в Супер Марио ): macro COUNTSCRADDR add a,(hl) inc h ld c,a adc a,(hl) sub c ld l,c endm macro NEXTCOLUMNS0 ld h,a ;.00 push hl set 6,h ;.10 push hl xor 0x20 ld h,a ;.01 push hl set 6,h ;.11 endm macro NEXTCOLUMNS1 ld c,a xor 0x40 ld h,a ;.10 push hl xor 0x60 ld h,a ;.01 push hl set 6,h ;.11 push hl ld h,c ;.00 inc hl endm macro NEXTCOLUMNS2 ld h,a set 5,h ;.01 push hl set 6,h ;.11 push hl ld h,a ;.00 inc hl push hl set 6,h ;.10 endm macro NEXTCOLUMNS3 ld c,a xor 0x60 ld h,a ;.11 push hl ld h,c ;.00 inc hl push hl set 6,h ;.10 push hl ld a,h ;не старое, т.к. был inc hl xor 0x60 ld h,a ;.01 endm prcharxy ;de=gfx ;la=yx ;CY=0 ld h,tytoscr/256 rra jr c,prcharxy_nextcolumns13 rra jr c,prcharxy_nextcolumns2 prcharxy_nextcolumns0 COUNTSCRADDR NEXTCOLUMNS0 jp prcharxy_scrok prcharxy_nextcolumns2 COUNTSCRADDR NEXTCOLUMNS2 jp prcharxy_scrok prcharxy_nextcolumns13 srl a jr c,prcharxy_nextcolumns3 prcharxy_nextcolumns1 COUNTSCRADDR NEXTCOLUMNS1 jp prcharxy_scrok prcharxy_nextcolumns3 COUNTSCRADDR NEXTCOLUMNS3 prcharxy_scrok Запоминается 3 адреса,четвёртый остаёт─ ся в регистрах. Ширина спрайта в этой игре равна 8 пикселей, но это не ограничение метода, а особенность кода самой игры. В Evo SDK каждый слой экрана отрисовывается целиком, и там тоже запоминается 4 адреса. В залитом 3D движке под ATM-Turbo 2 ис─ пользуется такой код: ld (hl),a ;0x0000 set 6,h ld (hl),a ;0x4000 add hl,bc ;bc=0xe000 ld (hl),a ;0x2000 set 6,h ld (hl),a ;0x6000 add hl,de ;de=0xa001 Можно для экономии регистровой пары его переписать на sbc hl,bc ... add hl,bc: inc hl или на res 6,h:set 5,h ... add hl,bc. * * * Как возвращать ошибки из процедур на─ верх? Я обычно по ошибке выхожу в исходную точку программы с восстановлением стека.Но пусть мы хотим обработку ошибок с правиль─ ным освобождением ресурсов. Пусть ошибка возвращается как CY=1. Простейший способ в стиле iS-DOS: <делаем что-то> call proc1 ret c <делаем что-то> call proc2 ret c <делаем что-то> jp proc3 При этом можно даже вернуть тип ошибки в каком-нибудь регистре через все про─ цедуры. Во многих практических случаях всё со─ держимое <делаем что-то> - просто присваи─ вания типа ld rp,() и ld (),rp, которые не меняют флаги. Поэтому можно писать так: <присваивания> call proc1 <присваивания> call nc,proc2 ret c <присваивания> jp proc3 (Использовалось в GIF converter by DIS/ XPJ .) Но тут, если мы хотим вернуть тип ошиб─ ки, придётся запретить использовать этот регистр в присваиваниях. * * * Как можно сделать процедуру захвата ре─ сурса (например, открытие файла), которая сама освободит его на выходе из блока: openresource ;if success, autopush closeresource ;out: nz=error ...open resource (out: nz=error)... ret nz ;error ld hl,closeresource ex (sp),hl jp (hl) closeresource ;out: nz=error ...close resource (out: nz=error)... ret Использование: work_with_resource ;out: nz=error call openresource1 ret nz ;error call openresource2 ret nz ;error ...work with resources 1 and 2... ret Применяется в Nedovigator'е. * * * Опять-таки в дополнение к "Этюдам" в #11 - ещё один способ вызова функции по номеру (номера заняты не подряд): ;a=команда ld hl,tcmds ;адрес списка команд ld bc,ncmds ;число команд в списке cpir jp nz,ошибка add hl,bc add hl,bc add hl,bc ld a,(hl) inc hl ld h,(hl) ld l,a jp (hl) Список команд: tcmds db 'a' db 's' db 'd' ... db 'x' ncmds=$-tcmds ;дальше в обратном порядке лежат адреса ;обработчиков: dw PROC_x ... dw PROC_d dw PROC_s dw PROC_a Используется в NedoOS - из-за поддержки нумерации команд от CP/M и MSX-DOS. Самые частые команды лежат в начале списка CPIR. Когда номера команд идут подряд, CPIR уже не нужен, достаточно перехода по таблице. Это значительно быстрее! Вот вариант от NEO SPECTRUMAN'а: ld h,tab ;7 ld h,(hl) ;7 jp (hl) ;4 А вот из Супер Марио под NedoOS: ;ld l,a or 0xc0 ;7 ld h,a ;4 jp (hl) ;4
Другие статьи номера:
Похожие статьи:
В этот день... 21 ноября