Diky za clanek.
Pouzit u skoku PE je svatecni pocit. .)
V kapitole 3. mas napsano:
ld bc, SCREEN_ADR
ld de, SECOND_SCREEN_BLOCK
ld hl, SCREEN_BLOCK_SIZE
call mem_copy
Poznámka: volba registrových párů není náhodná, jak uvidíme v navazujících kapitolách.
To muze nekoho zmast, protoze uz v kapitole 6 je:
ld hl, SCREEN_ADR ; adresa zdrojového bloku ld de, SECOND_SCREEN_BLOCK ; adresa cílového bloku ld bc, SCREEN_BLOCK_SIZE ; velikost přenášených dat ldir
Te mem_copy rutine z kapitoly 16 se neda vubec nic vytknout, imho je i lepsi nez hrani se zasobnikem. Tam je spousta problemu. Vypinat interupty. Pokud uz si hrajeme o nejvyssi rychlost, tak se tam jeste pridavaji IX a IY registrove pary. Takze pocet prenaseneho bloku neni nasobek 2. Pak se jeste musi resit, jestli to chces fakt rozbalit pres celou kopirovanou oblast a mit kod tim padem o podobne velikosti, nebo nad tim udelat nejakou smycku a pak resit, ze jednou jde ukazatel zasobniku nahoru a podruhe dolu. Takze muzes mit i zdroj prevraceny v bufferu. Same komplikace.
U toho elegantniho rozbaleneho LDI bych jen podotknul, ze nemusis mit
ld a, b ; kontrola BC na nulu
or c
ret z ; při prázdném bloku podprogram ukončíme
ld a, 16 ; na základě hodnoty v C vypočteme, kolik
sub c ; insturkcí LDI se má na začátku přenosu přeskočit
and 15 ; maximálně se přeskočí 15 instrukcí ze 16
add a, a ; vynásobit dvěma protože LDI je dvoubajtová instrukce
ld (jump_address), a ; uložíme offset do tohoto paměťového místa
jr $ ; relativní skok přečte offset z ^
jump_address equ $-1 ; trik jak zasáhnout do operandu instrukce JR
dokud je ta velikost prenasenych dat konstanta.
Pak to jen volas misto:
call mem_copy
napriklad takto:
call repeat+2*((16-SCREEN_BLOCK_SIZE)& 15)
a nechas si spocitat ten skok prekladacem.
dik za doplneni.
jinak na okraj: zrovna ted v sobotu jsem zpovidal dva sinclairisty (jeden dela pekna dema) a ti rikali, ze kdyz jde fakt o kopie cele VRAM, tak si nechaji ty rozbalene smycky s push/pull vygenerovat za behu. Chce to ale zhruba 2x tolik pameti, nez kopirovana data :) Ale ze vetsinou to neni tak kriticke a staci rozbaleni LDI.
Jo pro demo je to pouzitelne, ale kdyz si predstavim ze mam 48kb volne ram. Z toho si odectu nejake misto na zasobnik a buffer obrazovky tak jsem nekde kolem 40kb. Pak misto na tu rutinu a kdyby to bylo ldi tak jsem na 26.5 kb a to je sakra malo, pokud delam hru a spoustu mista mi zabira grafika. To fakt neni 64kb.
Schvalne jsem se snazil prepsat tu samomodifikujici rutinu na variantu, kterou muze pouzit i ROM rutina a vzhledem k tomu, ze pri volani je obsazene i HL a nejde to diky LDI zmenit tak je to zajimavy problem, protoze "jp HL" (oficialne to je "jp (HL)") nejde pouzit. Ale pomohl jsem si pres RET.
Prepsal jsem i co se kopiruje na atributy, protoze u textu, ktery ani nedosahuje do konce tretiny obrazovky to neni prukazne. A aby to bylo citelnejsi tak je lepsi si pomoct i v basicu tiskem na obrazovku predtim.
10 FOR a=0 TO 16 20 PRINT AT a,0;a 30 NEXT a 40 RANDOMIZE USR 32768 50 PAUSE 0
; Example #88x:
; Print color on screen + copy it to second part of screen using unrolled loop.
ATTR_ADR equ $5800
SCREEN_BLOCK_SIZE equ 130
SECOND_SCREEN_BLOCK equ $5901
ENTRY_POINT equ $8000
org ENTRY_POINT
; Vstupní bod celého programu
start:
call draw_attr ; vyplnění prvni tretiny barvou
ld hl, ATTR_ADR ; adresa zdrojového bloku
ld de, SECOND_SCREEN_BLOCK ; adresa cílového bloku
ld bc, SCREEN_BLOCK_SIZE ; velikost přenášených dat
call mem_copy
ret
repeat:
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
jp pe, repeat ; ukončit blokový přenos při BC==0
ret ; návrat z podprogramu
mem_copy:
ld a, b ; kontrola BC na nulu
or c
ret z ; při prázdném bloku podprogram ukončíme
ld a, 16 ; na základě hodnoty v C vypočteme, kolik
sub c ; insturkcí LDI se má na začátku přenosu přeskočit
and 15 ; maximálně se přeskočí 15 instrukcí ze 16
add a, a ; vynásobit dvěma protože LDI je dvoubajtová instrukce
exx
add a, low repeat
ld c, a
if ((0xFF & repeat) >= (256-2*16))
adc a, high repeat
sub c
ld b, a
else
ld b, high repeat
endif
push bc ; menit stinove BC je bezpecne pro navrat do basicu
exx
ret
draw_attr:
ld a,1*8+2
ld de,ATTR_ADR
ld b,0
draw_loop:
ld (de),a ; zápis hodnoty na adresu (DE)
inc de
djnz draw_loop ;
ret ; návrat z podprogramu
end ENTRY_POINT
Podporuje to pasmo, kterym se prekladaji ty priklady zrovna. Ale umi to i sjasmplus.
pasmo ma vse 16 bitove a unsigned
sjasmplus je myslim 32 bit signed a umi vic operatoru, ale na rozdil od pasma neumi ?
pasmo ma chybu, a nebo featuru v tom ze uvodni - vyhodnocuje skoro nakonec takze:
-5+3 =-8
https://pasmo.speccy.org/pasmodoc.html
## (see note) $, NUL, DEFINED *, /, MOD, %, SHL, SHR, <<, >> +, - (binary) EQ, NE, LT, LE, GT, GE, =, !=, <, >, <=, >= NOT, ~, !, +, - (unary) AND, & OR, |, XOR && || HIGH, LOW ?
Pokud chces v pasmu nasobit ve vyrazu nejake dve 16 bitova cisla, ale signed a vysledek ma byt 32 bitovy signed, tak je to docela orisek jak to udelat... :) kdyz pasmo umi jen 16 bit unsigned.
dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'PUSH(abc,def) MMUL'
;[8:42] abc def m* ( -- +((abc)>>8)*((def)>>8)+(low(abc))*((def)>>8)>>8+((abc)>>8)*(low(def))>>8+((low((low(abc))*((def)>>8)))+(low(((abc)>>8)*(low(def))))+(low(abc))*(low(def))>>8)>>8-((abc)>>15)*(def)-((def)>>15)*(abc) +(abc)*(def) )
push DE ; 1:11 abc def m*
push HL ; 1:11 abc def m*
ld DE, +((abc)>>8)*((def)>>8)+(low(abc))*((def)>>8)>>8+((abc)>>8)*(low(def))>>8+((low((low(abc))*((def)>>8)))+(low(((abc)>>8)*(low(def))))+(low(abc))*(low(def))>>8)>>8-((abc)>>15)*(def)-((def)>>15)*(abc); 3:10 abc def m*
ld HL, +(abc)*(def); 3:10 abc def m*
; seconds: 0 ;[ 8:42]
O neco kratsi by to bylo kdyby jsi chtel u16*u16=u32
dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'PUSH(abc,def) UMMUL'
;[8:42] abc def um* ( -- +((abc)>>8)*((def)>>8)+(low(abc))*((def)>>8)>>8+((abc)>>8)*(low(def))>>8+((low((low(abc))*((def)>>8)))+(low(((abc)>>8)*(low(def))))+(low(abc))*(low(def))>>8)>>8 +(abc)*(def) )
push DE ; 1:11 abc def um*
push HL ; 1:11 abc def um*
ld DE, +((abc)>>8)*((def)>>8)+(low(abc))*((def)>>8)>>8+((abc)>>8)*(low(def))>>8+((low((low(abc))*((def)>>8)))+(low(((abc)>>8)*(low(def))))+(low(abc))*(low(def))>>8)>>8; 3:10 abc def um*
ld HL, +(abc)*(def); 3:10 abc def um*
; seconds: 0 ;[ 8:42]
Pro signed deleni 16 bitove konstanty 16 bitovou konstantou a jeste ziskanim zbytku by vypadalo takto:
dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'PUSH(abc,def) DIVMOD'
;[8:42] abc def /mod ( -- +((((abc)|(def))>>15) xor 1)*(abc)mod(def)+(((~(abc))&(def))>>15)*(abc)mod(-(def))-(((abc)&(~(def)))>>15)*(-(abc))mod(def)-(((abc)&(def))>>15)*(-(abc))mod(-(def)) +((((abc)|(def))>>15) xor 1)*(abc)/(def)-(((~(abc))&(def))>>15)*(abc)/(-(def))-(((abc)&(~(def)))>>15)*(-(abc))/(def)+(((abc)&(def))>>15)*(-(abc))/(-(def)) )
push DE ; 1:11 abc def /mod
push HL ; 1:11 abc def /mod
ld DE, +((((abc)|(def))>>15) xor 1)*(abc)mod(def)+(((~(abc))&(def))>>15)*(abc)mod(-(def))-(((abc)&(~(def)))>>15)*(-(abc))mod(def)-(((abc)&(def))>>15)*(-(abc))mod(-(def)); 3:10 abc def /mod
ld HL, +((((abc)|(def))>>15) xor 1)*(abc)/(def)-(((~(abc))&(def))>>15)*(abc)/(-(def))-(((abc)&(~(def)))>>15)*(-(abc))/(def)+(((abc)&(def))>>15)*(-(abc))/(-(def)); 3:10 abc def /mod
; seconds: 0 ;[ 8:42]
No a pak jsou jeste vetsi hruzy :D
Nekdy to 32 bitove cislo musi mit nejaky pocet nejvyssich bitu nulovy a je potreba prave ten ? operator
dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'PUSH(hi,lo,abc) UMDIVMOD'
.warning Pasmo does not support 32 bit numbers and M4 does not know all values(They can only emulate 28-bit/12-bit or 16-bit/16-bit without checking the range): "hi<<16+lo abc um/mod"
;[8:42] hi lo abc um/mod ( -- +((((((hi) mod (abc)<<4)mod (abc)<<4)mod (abc)<<4)mod (abc)<<4)mod (abc)+(lo) mod (abc))mod (abc) +((hi) mod (abc)<<4+(lo)>>12)/(abc)<<12+(((hi) mod (abc)<<4+(lo)>>12)mod (abc)<<4+(lo)<<4>>12)/(abc)<<8+((((hi) mod (abc)<<4+(lo)>>12)mod (abc)<<4+(lo)<<4>>12)mod (abc)<<4+(lo)<<8>>12)/(abc)<<4+(((((hi) mod (abc)<<4+(lo)>>12)mod (abc)<<4+(lo)<<4>>12)mod (abc)<<4+(lo)<<8>>12)mod (abc)<<4+(15& (lo)))/(abc) )
push DE ; 1:11 hi lo abc um/mod
push HL ; 1:11 hi lo abc um/mod
ld DE, +((((((hi) mod (abc)<<4)mod (abc)<<4)mod (abc)<<4)mod (abc)<<4)mod (abc)+(lo) mod (abc))mod (abc); 3:10 hi lo abc um/mod
ld HL, +((hi) mod (abc)<<4+(lo)>>12)/(abc)<<12+(((hi) mod (abc)<<4+(lo)>>12)mod (abc)<<4+(lo)<<4>>12)/(abc)<<8+((((hi) mod (abc)<<4+(lo)>>12)mod (abc)<<4+(lo)<<4>>12)mod (abc)<<4+(lo)<<8>>12)/(abc)<<4+(((((hi) mod (abc)<<4+(lo)>>12)mod (abc)<<4+(lo)<<4>>12)mod (abc)<<4+(lo)<<8>>12)mod (abc)<<4+(15& (lo)))/(abc); 3:10 hi lo abc um/mod
; seconds: 0 ;[ 8:42]
dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'PUSH(hi,lo,abc) FMDIVMOD'
.warning Pasmo does not support 32 bit numbers and M4 does not know all values(They can only emulate 16-bit/16-bit without checking the range): "hi<<16+lo abc fm/mod"
;[8:42] hi lo abc fm/mod ( -- +((((hi)|(abc))>>15) xor 1)*(lo)mod(abc)+(((~(hi))&(abc))>>15)*(((lo)mod(-(abc))!=0)&((abc)+(lo)mod(-(abc))))+(((hi)&(~(abc)))>>15)*((((-(lo))mod(abc))!=0)&((abc)-(-(lo))mod(abc)))-(((hi)&(abc))>>15)*(-(lo))mod(-(abc)) +((((hi)|(abc))>>15) xor 1)*(lo)/(abc)-(((~(hi))&(abc))>>15)*((lo)/(-(abc))-(((lo)mod(abc))!=0))-(((hi)&(~(abc)))>>15)*((-(lo))/(abc)-(((lo)mod(abc))!=0))+(((hi)&(abc))>>15)*(-(lo))/(-(abc)) )
push DE ; 1:11 hi lo abc fm/mod
push HL ; 1:11 hi lo abc fm/mod
ld DE, +((((hi)|(abc))>>15) xor 1)*(lo)mod(abc)+(((~(hi))&(abc))>>15)*(((lo)mod(-(abc))!=0)&((abc)+(lo)mod(-(abc))))+(((hi)&(~(abc)))>>15)*((((-(lo))mod(abc))!=0)&((abc)-(-(lo))mod(abc)))-(((hi)&(abc))>>15)*(-(lo))mod(-(abc)); 3:10 hi lo abc fm/mod
ld HL, +((((hi)|(abc))>>15) xor 1)*(lo)/(abc)-(((~(hi))&(abc))>>15)*((lo)/(-(abc))-(((lo)mod(abc))!=0))-(((hi)&(~(abc)))>>15)*((-(lo))/(abc)-(((lo)mod(abc))!=0))+(((hi)&(abc))>>15)*(-(lo))/(-(abc)); 3:10 hi lo abc fm/mod
; seconds: 0 ;[ 8:42]
dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'PUSH(hi,lo,abc) SMDIVREM'
.warning Pasmo does not support 32 bit numbers and M4 does not know all values(They can only emulate 28-bit/12-bit or 16-bit/16-bit without checking the range): "hi<<16+lo abc sm/rem"
;[8:42] hi lo abc sm/rem ( -- +(((((((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)+((hi)>>15?-(lo):lo) mod (((abc)>>15)?-(abc):abc))mod (((abc)>>15)?-(abc):abc))xor((hi)>>15*(-1)))+((hi)>>15) +(((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)/(((abc)>>15)?-(abc):abc)<<12+((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<4>>12)/(((abc)>>15)?-(abc):abc)<<8+(((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<4>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<8>>12)/(((abc)>>15)?-(abc):abc)<<4+((((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<4>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<8>>12)mod (((abc)>>15)?-(abc):abc)<<4+(15& ((hi)>>15?-(lo):lo)))/(((abc)>>15)?-(abc):abc))xor(((hi) xor (abc))>>15*(-1)))+(((hi) xor (abc))>>15) )
push DE ; 1:11 hi lo abc sm/rem
push HL ; 1:11 hi lo abc sm/rem
ld DE, +(((((((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)+((hi)>>15?-(lo):lo) mod (((abc)>>15)?-(abc):abc))mod (((abc)>>15)?-(abc):abc))xor((hi)>>15*(-1)))+((hi)>>15); 3:10 hi lo abc sm/rem
ld HL, +(((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)/(((abc)>>15)?-(abc):abc)<<12+((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<4>>12)/(((abc)>>15)?-(abc):abc)<<8+(((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<4>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<8>>12)/(((abc)>>15)?-(abc):abc)<<4+((((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<4>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<8>>12)mod (((abc)>>15)?-(abc):abc)<<4+(15& ((hi)>>15?-(lo):lo)))/(((abc)>>15)?-(abc):abc))xor(((hi) xor (abc))>>15*(-1)))+(((hi) xor (abc))>>15); 3:10 hi lo abc sm/rem
; seconds: 0 ;[ 8:42]
Popravde to byla docela dobra vyzva co vsechno tim jde udelat.
3. 5. 2023, 02:40 editováno autorem komentáře
pekne dik, c65 pro 6502 (a 65816) taky docela umi, ale mel jsem za to, ze starsi assemblery ne (https://cc65.github.io/doc/ca65.html#s11)
Na jp (ix) a jp (iy) jsem uplne zapomnel!
Zda se ze automaticky preferuji jednobajtove instrukce, protoze bez IX se jde vetsinou obejit.
Ten kod jsem psal narychlo nez jsem bezel do prace a cestou me napadlo, ze tam vubec nemusim pouzivat stinove registry, zvlast v ROMce, kde to spis prepinam zase zpet a ze bych mel uchovavat ten pouzity registr a s ex (sp),hl to dokonce bude i kratsi!
original
ld (jump_address), a ; 3:13 uložíme offset do tohoto paměťového místa
jr $ ; 2:12 relativní skok přečte offset z ^
;[5:25]
stinovy a niceny BC
exx ; 1:4
add a, low repeat ; 2:7
ld c, a ; 1:4
if ((0xFF & repeat) >= (256-2*16))
adc a, high repeat ; 2:7
sub c ; 1:4
ld b, a ; 1:4
else
ld b, high repeat ; 2:7
endif
push bc ; 1:11 menit stinove BC je bezpecne pro navrat do basicu
exx ; 1:4
ret ; 1:10
;[9:47]
;[11:55]
niceny IX (pouzivam instrukce co umi originalni ZX, ale ne vsechny Z80 klony, slo by to resit i jinak mozna i rychleji, ale nechce se me v tehle variante moc vrtat)
add a, low repeat ; 2:7
ld ixl, a ; 2:8
if ((0xFF & repeat) >= (256-2*16))
adc a, high repeat ; 2:7
sub ixl ; 2:8
ld ixh, a ; 2:8
else
ld ixh, high repeat ; 3:11
endif
jp (ix) ; 2:8 menit stinove BC je bezpecne pro navrat do basicu
;[9:34]
;[12:46]
Moje volba by byla pouzit zasobnik a ex (sp),hl. Kratke ale pomalejsi.
push hl ; 1:11
add a, low repeat ; 2:7
ld l, a ; 1:4
if ((0xFF & repeat) >= (256-2*16))
adc a, high repeat ; 2:7
sub l ; 1:4
ld h, a ; 1:4
else
ld h, high repeat ; 2:7
endif
ex (sp),hl ; 1:19
ret ; 1:10
;[8:58]
;[10:66]
A jeste jedna varianta do poctu. Jen kdyz je rozbalene LDI mimo predel "segmentu". Navic tahle by vyzadovala inicializaci (zapsani 2 bajtu), takze dalsi takty a bajty.
add a, low repeat ; 2:7
ld (in_ram+1), a ; 3:13
if ((0xFF & repeat) >= (256-2*16))
.error ldi deli 256 segment!
endif
jp in_ram ; 3:10
;[11:40]
...
in_ram:
jp repeat ; 3:10