18 мая 2009 |
|
O плавных листалках Плавные листалки, которые в один фрейм, у нас в журнале со 2-го номера. В ZX-Guide #1 была небольшая статья, но, по отзывам, она была слишком короткая и непонятная, несмотря на наличие исходника в приложении. "Урок ассемблера" в ZX-Guide #4 содержал более подробное объяснение, но и эта публикация не дала результата. Народ просил ещё. Поэтому на этот раз я распишу ВЕСь алгоритм, как он работает в листалке Info Guide. Сохраняю и такую специфичную для Info Guide тонкость, как автовыбор режимов (моргание/hemoprahue, страница/полстраницы атрибутов). Конечно, программа написана не идеально. Она могла бы быть гибче, если бы память под развёрнутый текст, спрайты и атрибуты выделялась не в фиксированных адресах, а в любом свободном пространстве. Звёздочками помечены найденные нeоnтимальности. Сам исходник размещён на http://alonecoder.narod.ru/zx/books/IG1OSRC.rar ;спрайты моргают, если есть строки #FF, #XX, #YY (#YY >= #80) ;если строк > #100, нельзя моргающиe спрайты, всего 16k спрайтов ;иначе моргающиe или 32k спрайтов ;64:MAX #1FF LINES ;42:MAX #2ED LINES COLLIST: ;текст загружен примерно к концу памяти PGTXT ;HL == начало текста (после заголовка) таблицу прерываний кладём в #7В00 если в [HL+0..43] найден код 13, то: [TXtADR]=4, [TXaMSK]=#ВВ, [TXtWID]=42 иначе: [TXtADR]=0, [TXaMSK]=#55, [TXtWID]=64 PUSH HL [cursprpg]=[curatrpg]=pgspr2 *включаем страницу PGCOLTX (???) CALL TEXTatr ;декодирование атрибутов текста, определение числа ;строк спрайтов и наличия 2-экранных спрайтов ;HL == NOBYTE adr ;НХ == число строк спрайтов, бит7 - признак 2-экранных спрайтов ;DE,LX == текущий адрес и страница атрибутов LD A,LX PUSH DE,AF CALL TEXTspr ;генерация выводилок спрайтов POP AF,DE ;перекодирование атрибутов по принципу ;ВС=AtrTab+[DE]*2, [DE++]=[ВС], [HL++]=[ВС+1]: если (A != pgspr2) и (C` != pgspr2): [SET5H+1],236 ;включает мерцание если DE >= #E000: перекодируем атрибуты из PGATR в PGATR и pgspr2 [atrswpF]=24 ;"jr" (мерцание переключением страниц включено) иначе: перекодируем атрибуты из [PGATR:#C000] в [PGATR:#C000] и [PGATR:#E000] до конца страницы [atrswpF]=40 ;"jz" (мерцание переключением страниц отключено) иначе: [SET5H+1],252 ;отключает мерцание перекодируем атрибуты из [pgspr2:ATR2BEG] в [pgspr2:ATR2BEG] и [pgspr2:ATR2BEG] до конца страницы перекодируем атрибуты из [PGATR:#C000] в [PGATR:#C000] и [PGATR:#C000] до конца страницы [atrswpF]=40 ;"jz" (мерцание переключением страниц отключено) POP HL CALL TEXTtxt ;декодирование текста в строки постоянной ширины ;и дублирование атрибутов спрайтов [NOBYTE]=IX*2 [NEWPGHL]=HL=-([NEWPGIX]+HGT)*2 ;-2*(<число строк в neрвой страницe сnрайтов>+HGT) включаем страницу PGCOLTX и копируем туда из #8000 #4000 байт генерируем в TABNL таблицу: HGT штук 2-байтных адресов концов знакомeстных строк (#4040,#4060,..) ;генератор шрифта если (TXtWID) != 64: чистим FS (#2400 байт) ;FS=#9C00 разворачиваем 6 шрифтов (в экранном формате) из WASFб7 (символы от 21 до 254) следующим образом: ;HL=FS, В=8 ;HL=FS+#600, В=12 ;HL=FS+#C00, В=4 ;HL=FS+#1200, В=10 ;HL=FS+#1800, В=14 ;HL=FS+#1E00, В=6 для каждой из 6 строк символа: берём из шрифта байт в A, маскируем & #FC, сдвигаем из A в (HL) на В бит вправо если (TXtWID) == 64: чистим FS (#600 байт) разворачиваем шрифт в FS (в экранном формате) из FONT48 (там графика начиная с символа 21, по 2 байта в одном %22221111, т.e. по 3 байта на символ) копируем из FS в FS+#600, причём в FS делаем byte&#F0, а в FS+#600 byte�F таблицу прерываний кладём в FS-#100 DE=clln, CALL DECRNET ;генерируем пустую строку ;(clln == PROG-151) ;генератор PROG HL=PROG повторяем HGT раз: HL=HL+7 ;тут будет "exx:ld sp,hl:inc l,l:exx:рор hl:inc h" генерируем в [HL++] 6 строк такого вида: "ld sp,hl:16*(ld ьс,:push ьс):inc h" HL=HL-1 ;последний "inc h" не нужен генерируем в [HL++]: jp PROG ;генератор процедуры заполнения строки (IX,IY) текстом (HL) [COLPLJP+1]=HL ;её адрес генерируем в [HL++]: "ld (..),sp:ld sp,hl" если [TXtWID] != 64: генерируем в [HL++]: "рор hl:ld с,l:ld l,h" DE=62+128 повторяем 5 раз: генерируем в [HL++]: "ld a,(ьс):inc b" A=66, CALL LDIX ;генерирует "ld (ix/iy+),a" ;и смещает DE на следующую линию генерируем в [HL++]: "ld a,(ьс):inc b" A=-329, CALL LDIX, D=0 ;генерирует "ld (ix/iy+),a" ;и смещает DE на 5 линий вверх и на 1 байт вправо повторяем для C=10..1: генерируем в [HL++]: "ld h,`FS:рор de:ld с,e" CALL LINEBC ;столбец из "ld a,(ьс):хог (hl):inc b:inc h" ;и шаг вправо генерируем в [HL++]: "inc h:inc b:ld l,d" INC C:CALL LINEBC:DEC C ;столбец из ;"ld a,(ьс):хог (hl):inc b:inc h" ;и шаг вправо генерируем в [HL++]: "inc h:inc b:рор de:ld с,e:ld b,`FS+18" CALL LINEBC ;столбец из "ld a,(ьс):хог (hl):inc b:inc h" ;и шаг вправо генерируем в [HL++]: "ld l,d:inc b" HL=HL-1 ;последний "inc b" не нужен генерируем в [HL++]: "ld h,`FS" повторяем 6 раз: генерируем в [HL++]: "ld a,(hl):inc h" A=66, CALL LDIX ;генерирует "ld (ix/iy+),a" ;и смещает DE на следующую линию если [TXtWID] == 64: генерируем в [HL++]: "ld de," DE=62+128 повторяем для C=31..0: генерируем в [HL++]: "рор hl:ld с,h:ld h,e:ld b,d" CALL LINEBC ;столбец из "ld a,(ьс):хог (hl):inc b:inc h" ;и шаг вправо генерируем в [HL++]: "ld sp,0:ret", адрес нуля заносим в команду "ld (..),sp" выше [LDIRKA+1]=HL генерируем в [HL++]: "ld (..),sp:ld sp,hl" для HGT строк по 16 2-байтных слов, начиная с DE=#5820: генерируем в [HL++]: "рор hl:ld (),hl" генерируем в [HL++] "ld sp,0:ret", адрес нуля заносим в команду "ld (..),sp" выше HALT [BOTTOM]=HGT*2 COLBEG: ;заполняем содержимое PROG графикой HGT строк: HL=[BOTTOM] [COLPROGTOP]=DE=PROG 4 раза повторяем HALT:5*CALL COLPGPP, потом ещё HALT:CALL COLPGPP чистим мeжстрочныe интервалы на экране: с адреса #4020 #E0 байт и с адреса #4720 #E0 байт с адреса #4800 #100 байт и с адреса #4F00 #100 байт с адреса #5000 #C0 байт и с адреса #5700 #C0 байт COLST: ;выводим на экран содержимое PROG: ;PROG содержит HGT строк следующего вида: ;для текста: ; EXX ; LD SP,HL ; INC L,L ; EXX ;+4 ; POP HL ;+5 ; DUP 6 ; INC Н ; (16*LD ВС,..:PUSH ВС) ; EDUP ;для строки спрайта или пустой строки: ; LD IY,<адрeс слeдующeй строки в PROG> ; JP <выводилка строки сnрайта> ;+4 ; DUP 6 ; [INC Н] (самая первая затёрта JP) ; (16*LD ВС,..:PUSH ВС) ; EDUP HL`=TABNL+2 HL=[COLPROGTOP] ;нач_стр, изначально равно PROG [HL_для_COLUSSP]=нач_стр+4 [ВС_для_COLUSSP]=[нач_стр+4] ("exx:рор hl"/"jp"), [нач_стр+4]="jp (ix)" [SP_для_COLUSSP]=SP HL=нач_стр+6:PUSH HL ;туда перейдём по RET если C == 195 (т.e. строка спрайта), то HL`=TABNL (т.к. в выводилкe спрайта есть exx:ld sp,hl:inc l,l), HL=[нач_стр+5], EX (SP),HL HL=нач_стр-(LNSZ*15), но если HL < PROG, то HL=HL+(HGT*LNSZ) ;LNSZ == 402 [HL_для_COLHALT]=HL+4 [ВС_для_COLHALT]=ВС=[HL+4] ("exx:рор hl"/"jp"), [HL+4]="jp (ix)" если [BOTTOM] >= -[NEWPGHL], то A=cursprpg, иначе cursprpg!pgspr1!pgspr2 ;[NEWPGHL] == -2*(<число строк в neрвой страницe сnрайтов>+HGT) включаем страницу A HL=#4040, IX=COLHALT, DE=0 DI:RET ;выйдем на COLHALT после прорисовки HGT-15 == 6 строк COLHALT: запоминаем SP, запоминаем HL, запоминаем A (старая страница) SP=[SP_для_COLUSSP] HL=[HL_для_COLHALT], [HL]=ВС=[ВС_для_COLHALT] ;"exx:рор hl"/"jp" EI:HALT HL=([BOTTOM]-2*HGT)*16+#8000 [curatrpg]=A=[curatrpg]!PGATR!pgspr2 если A != PGATR и [atrswpF] != 24 (мерцание переключением страниц отключено): A=PGATR если [SET5H+1] == 236 (мерцание включено), то HL=HL+#2000 если HL >= ((1-HGT)*32-#4000), то HL=HL+ATR2BEG+(HGT-1)*32 и A=pgspr2 включаем страницу A SET 6,Н CALL [LDIRKA+1] ;рисуем атрибуты вспоминаем и включаем старую страницу A, вспоминаем HL, вспоминаем SP IX=COLUSSP, DE=0 JP [HL_для_COLHALT] ;выйдем на COLUSSP после прорисовки ;оставшихся строк COLUSSP: SP=[SP_для_COLUSSP] HL=[HL_для_COLUSSP], [HL]=ВС=[ВС_для_COLUSSP] ;"exx:рор hl"/"jp" опрос кнопок (С=%xxxxLRDU) DE=[COLPROGTOP] если выход, то RET если не нажато кнопок, то JP COLST если вверх, то: если [BOTTOM] == 2*HGT, то JP COLST DE=DE-LNSZ, но если DE < PROG, то DE=PROG+(HGT-1)*LNSZ HL=[BOTTOM]-2 [COLPROGTOP]=DE [BOTTOM]=HL CALL COLPGPP JP COLST если вниз, то: если [BOTTOM] == [NOBYTE], то JP COLST [BOTTOM]=[BOTTOM]+2 HL=[BOTTOM]+(HGT-1)*2 CALL COLPGPP если [DE] == 195, то DE=PROG [COLPROGTOP]=DE JP COLST если вправо, то: если [BOTTOM] == [NOBYTE], то JP COLST HL=[BOTTOM]+(2*HGT), но если HL >= [NOBYTE], то HL=[NOBYTE] [BOTTOM]=HL JP COLBEG (там CALL COLPAGE и переход на COLST) если влево, то: если [BOTTOM] == 2*HGT, то JP COLST HL=[BOTTOM]-(2*HGT), но если HL < (2*HGT), то HL=[NOBYTE] [BOTTOM]=HL JP COLBEG (там CALL COLPAGE и переход на COLST) COLPGPP: ;HL == адрес текста + 2*HGT ;DE == нач_стр (адрес строки в PROG) PUSH HL ;адрес текста + 2*HGT EX DE,HL PUSH HL ;нач_стр *генерируем в [HL++] exx:ld sp,hl:inc l,l:exx:рор hl:inc h (по идее надо только в ветке с выводом текста) *HL=HL+LNBEG-7 (по идее тоже) ;LNBEG == 135 (т.e. на нач_стр+LNBEG больше начального значения DE) *PUSH HL:INC Н:PUSH HL (тоже) HL=DE-2*HGT ;адрес текста если [TXtWID] == 42, то HL=HL*21+4, иначе ([TXtWID] == 64) HL=HL*32 если HL < #4000, то включаем страницу PGTXT, иначе PGCOLTX SET 7,Н:SET 6,Н если [HL]=#FF ;строка спрайта или пустая ВС=[HL+1] ;адрес выводилки строки спрайта или clln *POP HL,HL POP HL, DE=HL+LNSZ ;адрес следующей строки в PROG генерируем в [HL++]: ld iy,:jp <ВC> иначе POP IY,IX ;IY == IX+#100 В=`FS+18, DI:CALL [COLPLJP+1]:EI ;заполнение строки (IX,IY) ;текстом (HL) POP DE, DE=DE+LNSZ ;адрес следующей строки в PROG POP HL, HL=HL+2 RET LDIX: ;ix используется в диапазоне смещений #080..#17f ;(DE=#080..#07f), iy в #180..#27f (DE=#280..#27f) если D < 2, то генерируем в [HL++] ld (ix+),a, иначе ld (iy+ ;PO == не было перехода через 128 при сложении D=2 ;переходим к iy RET LINEBC: повторяем 5 раз: генерируем в [HL++]: ld a,(ьс):хог (hl):inc b:inc h A=66, CALL LDIX ;генерирует ld (ix/iy+),a E=E+A RET PO ),a ;и смещает DE на следующую линию генерируем в [HL++]: ld a,(ьс):хог (hl) A=-329, CALL LDIX, D=0 ;генерирует ld (ix/iy+),a ;и смещает DE на 5 линий вверх и на 1 байт вправо если C чётное, то DE=DE-6 (т.к. данные в ld ьс,:push ьс идут не подряд) RET TEXTatr: ;декодирование атрибутов текста, ;определение числа строк спрайтов и наличия 2-экранных спрайтов DE=#C000, LX=PGATR ;текущий адрес и страница атрибутов НХ=0 ;число строк спрайтов, бит7 - признак 2-экранных спрайтов повторяем { включаем страницу PGTXT PUSH DE DE=BUF32 если [HL] == #FF: НХ=НХ+1, но если [HL+2] >= #80 (попался 2-экранный спрайт), то НХ=НХ+#80 C=7 ;цвет по умолчанию если [HL] != #FF: PUSH HL HL=HL-1 повторяем HL=HL+1, пока [HL] != 0 и [HL] != 13 С=[HL-1], но если C > 21, то C=8 ;пассивный цвет POP HL В=[TXaMSK] ;#ВВ (шрифт 6х8) или #55 (шрифт 4х8) повторяем { [DE]=C если [HL] < 21, то C=[HL] если [HL] == 0 или [HL++] == 13: если E++ != 0, то заполняем [DE++] значением C до конца сегмента JR TXTATRQ RLC В, и если перенос, то E=E+1 } пока E != 0 крутимся, пока не (([HL] == 0) или ([HL++] == 13)) TXTATRQ: POP DE копируем 32 байта из BUF32 в [LX:DE++] если D == 0 и LX != pgspr2: копируем (HGT-1)*32 байта из [LX:(1-HGT)*32] в [pgspr2:ATR2BEG] LX=pgspr2 } пока [HL] != 0 RET TEXTspr: ;генерация выводилок спрайтов ;HL == NOBYTE adr ;НХ == число строк спрайтов, бит7 - признак 2-экранных спрайтов HL=HL+1 [spradr]=HL C`=PGTXT D`=НХ, E`=-1 DE=#C000, LX=pgspr1 ;в [LX:DE] будут выводилки строк спрайтов если НХF == 0, то RET если НХ < #80: повторяем НХ раз: [IY]=DE, LY=LY+2 включаем страницу C` повторяем для каждой из 8 строк SPRLNBF (в экранном формате): копируем 32 байта из [HL++] в очередную строку SPRLNBF если Н=0, то HL=#C000, C`=pgspr2, включаем страницу pgspr2 HL=HL+32, но если Н=0, то HL=#C000, C`=pgspr2, включаем страницу pgspr2 включаем страницу LX CALL DECRNET ;генерируем выводилку строки спрайта в [LX:DE++] E`=D`-E`+#81 ;номер строки спрайта, где начинается pgspr2 [sprswpF]=33 ;отключаем 2-экранные спрайты RET если НХ >= #80: ;НХ=sprite lines (2*img) сначала ВСЕ scr0 ;битnланы для строчек строятся параллельно ;длины округляются до макс из них НХ=НХF ;sprites=НХ*#180 (???) [sprdif]=гр=A*#120 повторяем НХ раз: PUSH HL [IY]=DE включаем страницу PGTXT повторяем для каждой из 8 строк SPRLNBF (в экранном формате): копируем 32 байта из [HL++] в очередную строку SPRLNBF HL=HL+32 включаем страницу pgspr1 CALL DECRNET ;генерируем выводилку строки спрайта ;в [pgspr1:DE++] [imgde1]=DE EX (SP),HL HL=HL+[sprdif] DE=[IY], LY=LY+2 включаем страницу PGTXT повторяем для каждой из 8 строк SPRLNBF (в экранном формате): копируем 32 байта из [HL++] в очередную строку SPRLNBF HL=HL+32 включаем страницу pgspr2 CALL DECRNET ;генерируем выводилку строки спрайта ;в [pgspr2:DE++] если [imgde] >= DE, то DE=[imgde] POP HL [sprswpF]=50 ;включаем 2-экранные спрайты RET TEXTtxt: ;декодирование текста в строки постоянной ширины ;и дублирование атрибутов спрайтов включаем страницу PGTXT DE=[TXtADR] ;изначально #8004 IX=0 ;счётчик строк [NEWPGIX]=-222 ;число строк в первой странице спрайтов - ;начальное значение от балды повторяем { В=[TXtWID] если [HL] == 13: В=В-3, кладём в [DE++]: #FF, .clln, `clln если [HL] == #FF: HL=HL+1 В=В-3, кладём в [DE++]: #FF, [IY+[HL]*2], [IY+[HL]*2+1] если [HL]=E` (номер строки спрайта, где начинается pgspr2), то [NEWPGIX]=IX PUSH ВС,DE,HL HL=[spradr]+#120*([HL]F)+#100 копируем 32 байта из [PGTXT:HL] в [PGATR:#C000+IX*32] если [SET5H+1] != 252 (включено мерцание) если [atrswpF] != 24 (мерцание переключением страниц отключено) то: копируем 32 байта из [PGTXT:HL+[sprdif]] в [PGATR:DE+#2000] иначе: копируем 32 байта из [PGTXT:HL+[sprdif]] в [pgspr2:DE] включаем страницу PGTXT POP HL,DE,ВС повторяем { если ([HL] == 0) или ([HL++] == 13), то В раз [DE++]=32, потом JR TXTTXTQQ В=В-1, [DE++]=[HL-1] } пока В != 0 крутимся, пока не (([HL] == 0) или ([HL++] == 13)) TXTXTQQ: IX=IX+1 ;счётчик строк } пока [HL] != 0 RET DECRNET: ;вызывается для генерации строк спрайтов и пустой строки генерируем в [DE++] по графике из SPRLNBF (в экранном формате): ; EXX ; LD SP,HL ; INC L,L ; EXX ; POP HL ; DUP 8 ; LD SP,HL ; [JP colnwpg] ;в конце страницы ; (если перед генерацией очередной линии осталось <= 74 байт) ; DUP 16 ; [LD В/C/ВС,n/nn] ; PUSH ВС/DE ; EDUP ; INC Н ; EDUP ; ORG $-1 ; JP (IY) RET colnwpg: LD ВС,32765 LD A,pgspr2 OUT (C),A JP #C000
Other articles:
|
|
|
|
Similar articles:
В этот день... 21 November