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
Na tom je asi nejzajimavejsi to, ze LDIR mel byt tim spravnym resenim, ale asi bylo nejjednodussi ho implementovat tak, ze se vlastne porad opakuje jeho nacitani z pameti, Jako asi to chapu - resi se tak problem preruseni apod. - ale pres zasobnik to tusim jde napsat skoro az 2x rychleji.
(na 6502 to je uplne jina debata, tam se bez automodifukujiciho kodu asi neda rozumne obejit - zadny LDIR nemame :/).
No, ono to kopírovaní přes zásobník není až tak velká výhra. Ten statickej příklad v článku je opravdu rychlej, ale v praxi chce člověk mít to kopírování přece jen ve smyčce, obzvlášt když přenáší několik kilobajtů dat. A tam narazí na to, že pro každou iteraci potřebujete počítat adresy zdroje a cíle a znovu je vložit do registru SP. Ve výsledku je kopírování přes zásobník přinejlepším jen o trochu rychlejší než rozvinutá smyčka LDI.
Když se na to člověk podívá v kontextu toho, že limitem reálného 8 bitového počítače je rychlost paměti, tak na 6502 je obrovský problém s tím, že na kopírování jednoho bajtu (jeden read, jeden write) je potřeba hodně čtení instrukcí (opcode fetch). Na stejně rychlé paměti nejde prostě kopírovat tak rychle jako pomocí LDI.
6809 má podobný problém, ale má dvě výhody: dva zásobníky a zvláštní implementaci push a pull ze zásobníku. Jde o to, že PSH,PUL instrukce jsou implementované jako jeden opcode pro PUL a jeden PSH, a za nimi následuje parametrický bajt. Každý bit parametrického bajtu určuje, který registr se vloží/načte ze zásobníku. Takže PSH/PUL má na 6809 nejlepší poměr přenesených dat a načtených instrukcí. I tak je rychlá kopírovací rutina přes dva zásobníky jen o malinko rychlejší než LDI a má samozřejmě problém s přerušením.
Nástupci 6502 a 6809, tedy 65816 a 6309 už mají dedikované instrukce pro blokové kopírování.
Děkuju za další pěkný článek.
Když jsem se po zkušenostech s 8080, 8086, 8051 (no a trochu 6502) dostal k 8-bitovému atmelu tak moje první reakce na instrukční sadu byla že autoři si vzali to nejlepší z Intelu, Motoroly a možná i jiných.
Hlavně se mi líbily instrukce pro čtení/zapis z/do paměti, které automaticky inkremetují/dekrementují ukazatele.
ldi r26,low(SCREEN_ADR) ;nastaveni ukazatele X
ldi r27,high(SCREEN_ADR)
ldi r28,low(SECOND_SCREEN_BLOCK) ;nastaveni ukazatele Y
ldi r29,high(SECOND_SCREEN_BLOCK)
ldi r18,low(SCREEN_BLOCK_SIZE) ;nastaveni pocitadla opakovani
ldi r19,high(SCREEN_BLOCK_SIZE)
cyklus: ld r16,X+ ;nacteni hodnoty z pameti 2
st Y+,r16 ;zapis do pameti 2
dec r18 ;snizeni pocitadla 1
brne cyklus ;opakovani dokud R18<>0 2
dec r19 ;snizeni pocitadla
brne cyklus ;opakovani dokud R19<>0
Vnitřní smyčka trvá tedy pouze 7 taktů (a vzhledem k běžné frekvenci 16MHz je tedy asi 6x rychlejší). Je vidět že zkušenosti více než 20 let s prvními mikroporcesory se při návrhu zúročily. Předpokládám že svoji roli tady hraje také dvojúrovňový pipelining.
2. 5. 2023, 16:30 editováno autorem komentáře
to se vám možná bude líbit Uzebox: https://www.root.cz/clanky/herni-konzole-uzebox-s-osmibitovym-mikroradicem-atmega644/
(ten video výstup by bylo lepší upravit na něco modernějšího, ale kompozitní vstup se dá ještě najít, nebo nějakej konvertor pro něj).
Uzebox je pěkná hračka. (Četl jsem všechny články o hardware....)
Učím na střední škole a asi před 5 roky jsem měl žáka který se zajímal o herní konzole a v rámci praktické práce si chtěl pořídit Uzebox a zkusit si do něj napsat nějakou hru. Bohužel už se stavebnice nedala koupit. Jako náhradní program nakonec vznikla vektorová "herní konzole" (2x 8bitD/A - R-2R sít přímo na dvou portech a výstupy operačních zesilovačů připojené na X a Y vstupy CRT osciloskopu).
Pěkná hračka je taky emulátor čipu AY-3-8500 pro TV hry založený na arduinu http://searle.x10host.com/AVRPong/index.html (použitý byl upravený návrh od Nostalcompa). Po připojení na dataprojektor málokdo odolal....
tak to musim gratulovat k dobremu zaku! ze mel zajem, ktery presahoval hrani Minecraftu :-)
K te vektorove konzoli: takze ty operacni zesilovace byly pouzity jako linearni interpolatory? Tj. "semtam" se zapsaly nove souradnice [x,y] a pockalo se, az to paprsek dokresli? To by bylo pekne reseni, jeste bych asi pridal Z vystup s intenzitou paprsku (pokud to osciloskop umi).
Mě šlo o to, že třeba na Vectrexu to je takto řešeno. Program musí generovat jen koncové body vektorů a zbytek si dopočítají integrátory (na vstupu asi bude výstup z toho r2r žebříku). Takže ten program nakonec může být docela pomalý a určitě nemusí počítat nějakého Bresenhama atd.
Je to vidět tady
https://www.root.cz/clanky/historie-vyvoje-pocitacovych-her-16-cast-herni-konzole-vectrex/#k07
hlavně na schématu https://i.iinfo.cz/images/391/7720.png
Takhle dokonalý jako Vectrex to není. Vykreslování je realizováno softwarově jeden 8.bitový port X a druhý Y. Aby se vykreslila čára musí se kreslit "pomalu" 1b za 5us (0,2b/us) což prři opakovací frekvenci 150Hz vychází na celkovou délku čáry 1300 bodů. Na herní ploše 160x160 jsou vykreslovány objekty o velikosti 20x20 (délka čáry 80-120) no a víc jak 10 se jich na obrazovku nevejde. Žádné složité výpočty program nedělá, objekty se skládají z vodorovné, svislé a šikmé (45°) čáry takže na všechno stačí jednoduchá smyčka s DEC a INC
ukázka vykreslovaní:
.macro CaraL ;Cara vlevo
ldi r21,@0
cal: out PORTB,r8 ;vystup X
out PORTD,r9 ;vystup Y
dec r8 ;zmena souradnice
call delay_cara ;zpozdeni
dec r21 ;snizeni pocitadla
brne cal ;cyklus
.endmacro
;..............................................................
.macro CaraUR ;Sikma cara v pravo nahoru
ldi r21,@0
caur: out PORTB,r8 ;vystup X
out PORTD,r9 ;vystup Y
inc r8 ;zmena souradnice do prava
inc r9 ;zmena souradnice nahoru
call delay_cara ;zpozdeni
dec r21 ;snizeni pocitadla
brne caur ;cyklus
.endmacro
;-------Vykresleni hrace---------------------------------------
Hrac: mov r8, r5
ldi r20, YHrac
mov r9, r20
CaraDR VelObj/2
CaraD VelObj/4
CaraDL VelObj/4
CaraL VelObj/2
CaraUL VelObj/4
CaraU VelObj/4
CaraUR VelObj/2
ret
;-------Objekt04 Ctverec X-------------------------------------
.org AdrObj04*0x100
ldi r20, VelObj/2
sub r8, r20
CaraR VelObj
CaraU VelObj
CaraL VelObj
CaraD VelObj
CaraUR VelObj
ldi r20, VelObj
sub r8, r20
CaraDR VelObj
ret3. 5. 2023, 20:51 editováno autorem komentáře
Na to by ale dva operaky skutocne nestacili. Podla toho co vidim v tej scheme k vectrexu, tak sa to chova podobne ako turtle grafika s tym ze jediny sposob ako ovladat zelvu (elektronovy luc, kresliaci na luninofor) je nastavovat napatie ktore udava cielove suradnice do ktorych sa ma zelva dostat. Zelva ma 2 rychlosti - vykreslovanie a "teleport" (analogovy spinac v spatnej vazbe integratoru, tvoreny 4066). Naviac procesor nema spatnu vezbu kde sa v skutocnosti zelva nachadza, vie len to kde by sa nachadzat mala. Pride mi ze kod procesoru bude za tychto podmienok o dost zlozitejsi ako jednoduche priame hybanie zelvou. Ale umozni to vykreslit komplikovanejsiu grafiku.
Ten operak tvoriaci prudovo napatovy konvertor mate vo vectrexu pripojeny na vystup MC1408. V spominanom skolskom projekte budu predpokladam tie r2r prevodniky dva a tym padom aj dva operaky ktore prevadzaju prudovy vystup (vysoka impedancia) na napatovy vystup (nizka impedancia).