Zkusil jsem si napsat podprogram pro získání kodu klavesy. V akumulátoru je vrácen poziční kod - není to nijak zvlášt optimalizované. Formát je 5 bitů sloupec a 3 bity číslo řádku.
keyb: push bc ;A - kod klavesy
push de
ld d,8 ;pocitadlo radku
ld c,0xF0 ;adresa portu
ld b,0xFE ;adresa prvniho radku
keyb01: in a,(c) ;nacteni portu
cpl ;negace
and 0x1F ;nastaveni hornich bitu
jp nz,keyb02 ;je stisknuta
ld a,b ;adresa dalsiho radku
rla
ld b,a
dec d ;pocitadlo radku
jp nz,keyb01 ;dalsi radek
pop de
pop bc
ret ;neni stisknuta zadna (A=00)
keyb02: rla ;nalezena prvni stisknuta klavesa
rla ;posun o 3 bity
rla
dec d
add a,d ;pricteni cisla radku
pop de
pop bc
ret ;stisknuta klavesa (A=sssssrrr)
Pro otestovvání kodů klaves jsem využil "print_hex_number"
ENTRY_POINT equ $8000
ROM_OPEN_CHANNEL equ $1601
AT equ $16
;-------------------------------------------------------------------------------
GOTO_XY macro X,Y
ld A, AT ; řídicí kód pro specifikaci pozice psaní
rst 0x10 ; zavolání rutiny v ROM
ld A, Y ; y-ová souřadnice
rst 0x10 ; zavolání rutiny v ROM
ld A, X ; x-ová souřadnice
rst 0x10 ; zavolání rutiny v ROM
endm
;-------------------------------------------------------------------------------
ZNAK_XY macro X,Y,kod
ld A, AT ; řídicí kód pro specifikaci pozice psaní
rst 0x10 ; zavolání rutiny v ROM
ld A, Y ; y-ová souřadnice
rst 0x10 ; zavolání rutiny v ROM
ld A, X ; x-ová souřadnice
rst 0x10 ; zavolání rutiny v ROM
ld A, kod ; kód znaku '*' pro tisk
rst 0x10 ; zavolání rutiny v ROM
endm
;-------------------------------------------------------------------------------
org ENTRY_POINT
start:
ld A,2 ; číslo kanálu
call ROM_OPEN_CHANNEL ; otevření kanálu číslo 2 (screen)
main: GOTO_XY 5,5
call keyb
call print_hex_number
jp main
;-------------------------------------------------------------------------------
keyb: push bc ;A - kod klavesy
push de
ld d,8 ;pocitadlo radku
ld c,0xF0 ;adresa portu
ld b,0xFE ;adresa prvniho radku
keyb01: in a,(c) ;nacteni portu
cpl ;negace
and 0x1F ;nastaveni hornich bitu
jp nz,keyb02 ;je stisknuta
ld a,b ;adresa dalsiho radku
rla
ld b,a
dec d ;pocitadlo radku
jp nz,keyb01 ;dalsi radek
pop de
pop bc
ret ;neni stisknuta zadna (A=00)
keyb02: rla ;nalezena prvni stisknuta klavesa
rla ;posun o 3 bity
rla
dec d
add a,d ;pricteni cisla radku
pop de
pop bc
ret ;stisknuta klavesa (A=sssssrrr)
;-------------------------------------------------------------------------------
print_hex_number:
push AF ; uschovat A pro pozdější využití
rrca ; rotace o čtyři bity doprava
rrca
rrca
rrca
and $0f ; zamaskovat horní čtyři bity
call print_hex_digit; vytisknout hexa číslici
pop AF ; obnovit A
and $0f ; zamaskovat horní čtyři bity
call print_hex_digit; vytisknout hexa číslici
ret ; a návrat z podprogramu
;-------------------------------------------------------------------------------
print_hex_digit:
cp 0x0a ; test, jestli je číslice menší než 10
jr c, print_0_to_9 ; ok, hodnota je menší než 10, budeme tedy tisknout desítkovou číslici
add A, 65-10-48 ; ASCII kód znaku 'A', ovšem začínáme od desítky, ne od nuly (+ update pro další ADD)
print_0_to_9:
add A, 48 ; ASCII kód znaku '0'
rst 0x10 ; zavolání rutiny v ROM pro tisk jednoho znaku
ret ; návrat ze subrutiny
;-------------------------------------------------------------------------------
end ENTRY_POINT
No a ještě jeden program: klávesy E, S, D, X pohybují znakem po obrazovce
ENTRY_POINT equ $8000
ROM_OPEN_CHANNEL equ $1601
ROM_CLS equ $0DAF
AT equ $16
X0 equ 15
Y0 equ 10
XMIN equ 0
XMAX equ 31
YMIN equ 0
YMAX equ 21
ZNAK equ "#"
MEZERA equ " "
;-------------------------------------------------------------------------------
GOTO_XY macro X,Y
ld A, AT ; řídicí kód pro specifikaci pozice psaní
rst 0x10 ; zavolání rutiny v ROM
ld A, Y ; y-ová souřadnice
rst 0x10 ; zavolání rutiny v ROM
ld A, X ; x-ová souřadnice
rst 0x10 ; zavolání rutiny v ROM
endm
;-------------------------------------------------------------------------------
ZNAK_XY macro X,Y,kod
ld A, AT ; řídicí kód pro specifikaci pozice psaní
rst 0x10 ; zavolání rutiny v ROM
ld A, Y ; y-ová souřadnice
rst 0x10 ; zavolání rutiny v ROM
ld A, X ; x-ová souřadnice
rst 0x10 ; zavolání rutiny v ROM
ld A, kod ; kód znaku '*' pro tisk
rst 0x10 ; zavolání rutiny v ROM
endm
;-------------------------------------------------------------------------------
org ENTRY_POINT
start: call ROM_CLS
ld h,X0 ;pocatecni nastaveni X,Y
ld l,Y0
ZNAK_XY h,l,ZNAK
main: call delay
m01: call keyb
cp 0x26
call z,vpravo
cp 0x16
call z,vlevo
cp 0x25
call z,nahoru
cp 0x27
call z,dolu
jp main
;-------------------------------------------------------------------------------
keyb: push bc ;A - kod klavesy
push de
ld d,8 ;pocitadlo radku
ld c,0xF0 ;adresa portu
ld b,0xFE ;adresa prvniho radku
keyb01: in a,(c) ;nacteni portu
cpl ;negace
and 0x1F ;nastaveni hornich bitu
jp nz,keyb02 ;je stisknuta
ld a,b ;adresa dalsiho radku
rla
ld b,a
dec d ;pocitadlo radku
jp nz,keyb01 ;dalsi radek
pop de
pop bc
ret ;neni stisknuta zadna (A=00)
keyb02: rla ;nalezena prvni stisknuta klavesa
rla ;posun o 3 bity
rla
dec d
add a,d ;pricteni cisla radku
pop de
pop bc
ret ;stisknuta klavesa (A=sssssrrr)
;-------------------------------------------------------------------------------
vpravo:
ZNAK_XY h,l,MEZERA
ld a,h
cp XMAX
jr Z,vp0
inc h
vp0: ZNAK_XY h,l,ZNAK
ret
;-------------------------------------------------------------------------------
vlevo:
ZNAK_XY h,l,MEZERA
ld a,h
cp XMIN
jr Z,vl0
dec h
vl0: ZNAK_XY h,l,ZNAK
ret
;-------------------------------------------------------------------------------
dolu:
ZNAK_XY h,l,MEZERA
ld a,l
cp YMAX
jr Z,do0
inc l
do0: ZNAK_XY h,l,ZNAK
ret
;-------------------------------------------------------------------------------
nahoru:
ZNAK_XY h,l,MEZERA
ld a,l
cp YMIN
jr Z,na0
dec l
na0: ZNAK_XY h,l,ZNAK
ret
;-------------------------------------------------------------------------------
delay: push bc
push de
ld d,40
ld e,0
d01: dec e
jr nz, d01
dec d
jr nz, d01
pop de
pop bc
ret
end ENTRY_POINT
Ta keyb rutina jde napsat mnohem kratsi
;-------------------------------------------------------------------------------
keyb: push bc ;A - kod klavesy
push de
ld d,7 ;pocitadlo radku
ld bc,0xFEF0 ;adresa prvniho radku & adresa portu
keyb01: in a,(c) ;nacteni portu
cpl ;negace
add a,a ;posun o 3 bity
add a,a
add a,a
jp nz,keyb02 ;je stisknuta
dec d ;pocitadlo radku
rl b ; 0xFE->FD->FB->F7->EF->DF->BF->7F
jp c,keyb01 ;dalsi radek
ld d,a ;neni stisknuta zadna (D=A=00)
keyb02: add a,d ;pricteni cisla radku
pop de
pop bc
ret ;stisknuta klavesa (A=sssssrrr)
A u toho CASE vetveni ktera klavesa je stiskla muzes usetrit taky nejake bajty kdyz mas rozdily mezi vetvemi nekdy jedna.
Misto:
cp 0x26 call z,vpravo cp 0x16 call z,vlevo cp 0x25 call z,nahoru cp 0x27 call z,dolu
To napis:
sub 0x16 call z,vlevo ; 0x16 cp 0x25-0x16 call z,nahoru ; 0x25 dec a call z,vpravo ; 0x26 dec a call z,dolu ; 0x27
Ale mas tam chybu, ze v tech podrutinach prepisujes registr A. Takze ti to funguje jen nahodou.
Kdyz se nad tim zamyslis, tak ten zpusob cteni klavesnice neni idealni. Protoze najdes prvni stisklou klavesu a ukoncis cteni klavesnice, ale ty pro tu hru spis potrebujes zjistit i vic klaves, napriklad nahoru+doprava+fire. Takze bud nejake 40 bitove pole a nebo jeste lepe pro kazdou konkretni klavesu mit znovu cteni. Mas tam 4 az 5 klaves.
Nejak takto treba na 110 bajtu (z puvodnich 203):
ENTRY_POINT equ $8000
ROM_CLS equ $0DAF
AT equ $16
X0 equ 15
Y0 equ 10
XMIN equ 0
XMAX equ 31
YMIN equ 0
YMAX equ 21
ZNAK equ "#"
MEZERA equ " "
org ENTRY_POINT
start: call ROM_CLS
ld hl,256*X0+Y0 ;pocatecni nastaveni X,Y
ld de,ZNAK*257 ;D=E=znak
call print_yx_hl_e
main: halt
halt
ld bc,0xFB02 ; Q
call checkpress
call nz,nahoru
ld b,0xFD ; A, C=0x02
call checkpress
call nz,dolu
ld bc,0xDF04 ; O
call checkpress
call nz,vlevo
ld c,0x02 ; P, B=0xEF
call checkpress
call nz,vpravo
jr main
;-------------------------------------------------------------------------------
;in:
;B=0x7F C= ..BNMs_.
;B=0xBF C= ..HJKLe.
;B=0xDF C= ..YUIOP.
;B=0xEF C= ..67890.
;B=0xF7 C= ..54321.
;B=0xFB C= ..TREWQ.
;B=0xFD C= ..GFDSA.
;B=0xFE C= ..VCXZc.
;out: nz=press
checkpress:
in a,(c) ;nacteni portu
cpl ;negace
add a,a ;posun o 3 bity
and c
ret
;-------------------------------------------------------------------------------
vpravo:
ld a,h
cp XMAX
ret Z
call print_yx_hl_space
inc h
ld e,d
jr print_yx_hl_e
;-------------------------------------------------------------------------------
vlevo:
if (XMIN=0)
inc h
dec h
else
ld a,h
cp XMIN
endif
ret Z
call print_yx_hl_space
dec h
ld e,d
jr print_yx_hl_e
;-------------------------------------------------------------------------------
dolu:
ld a,l
cp YMAX
ret Z
call print_yx_hl_space
inc l
ld e,d
jr print_yx_hl_e
;-------------------------------------------------------------------------------
nahoru:
if (XMIN=0)
inc l
dec l
else
ld a,l
cp YMIN
endif
ret Z
call print_yx_hl_space
dec l
ld e,d
jr print_yx_hl_e
;-------------------------------------------------------------------------------
print_yx_hl_space:
ld E,' ' ; 1e20
print_yx_hl_e:
ld A, AT ; řídicí kód pro specifikaci pozice psaní
rst 0x10 ; zavolání rutiny v ROM
ld A, L ; y-ová souřadnice
rst 0x10 ; zavolání rutiny v ROM
ld A, H ; x-ová souřadnice
rst 0x10 ; zavolání rutiny v ROM
ld A, E ; kód znaku '*' pro tisk
rst 0x10 ; zavolání rutiny v ROM
ret
;-------------------------------------------------------------------------------
end ENTRY_POINT
PS: Ja zase pouzivam v C masku klavesy co chci tisknout vynasobenou dvema. V emulatorou to funguje, ale na realnem ZX netusim... Z toho obrazku obvodu a ze jsou pri nejakych kombinacich vyvolany duchove je uz mimo muj level. Proste HW...
Nemám s Z80 tolik zkušeností a ani neznám všechny instrukce, tak jsem si nebyl jistý jak to vlastně je. Jestli můžu instrukce rotací použít i na jiný instrukce než akumulátor. Je nějaká rozumná tabulka s instrukcemi? Když jsem to na internetu hledal, tak na mne vylezla buď moc podrobná (odstavec na každou instrukci) nebo zas jen tabulka kde zjistím maximálně hex kod. Používám to od p. Tišnovského co je v lekcích, ale je to po kouskách a myslím že ne kompletní.
To že přečtu jen jednu klávesu je mi jasný, neměl jsem v tuhle chvíli ambice to udělat líp. S tím že v podprogramech přepisuju A máš pravdu, nevšimnul jsem si toho. Funguje to jen proto, že kódy zvolených kláves jsou větší než souřadnice X.Y.
Ja osobne pouzivam 5. odkaz uvedeny v clanku v odkazech.
Asi pred pul rokem prosel web nejakym designovym updatem a uz to neni ono. Plus ma (nebo mel) tam asi nejake drobne chyby, ze ma spatne zacervenene instrukce.
Popisky jsou v nahledu. Nekdy je stejne potreba vetsi popisek, protoze nektere instrukce jsou matouci.
Nejdulezitejsi je prvni tabulka, protoze je bez prefixu, takze nejkratsi instrukce a nejrychlejsi.
Pak muze byt uzitecna CB a ED.
Zbytek v nejakych specialnich pripadech, bud slozity algoritmus, nebo mas vsechno obsazene, ale je otazka zda to nejde napsat jinak.
Nekdy instrukce, ktere vypadaji jako kopie z prvni tabulky se ale lisi priznaky (ruzne posuny a rotace A).
Ale treba LD (adr),HL ma dve varianty "22 lo hi" a "ED 63 lo hi". Uplne zbytecne, ale... nekdy by se to treba mohlo hodit kdyz provadis nektere triky, kdy se snazis eliminovat nejaky skok, nebo se snazis mit 2 kody v sobe, jen posunuty o bajt (Z80 ti to skoro okamzite ale slouci diky tomu jak ma udelane opcody).
PS: U keyb si pouzil 3x "rla" a fungovalo ti to protoze si pred prvni "rla" mel vynulovany carry po "and" a pri druhem a tretim, protoze jsi mel vynulovany carry, protoze si zaroven tim "and" odmazal 3 nejvyssi bity z A.
Pokud by si ale rovnou pouzil "add A,A", nemusel bys carry resit, kdyz tam chces jen nuly.
PPS: Dobra prace.
Protoze prvni je spatne a je to mysleno jako oprava. Narazil jsem na to v nejakem dalsim kodu.
Prvni dela:
rl b ; 0xFE->FC->F8->F0->E0->C0->80->00
"rl b" = "adc b,b", ale v kodu je vzdy vynulovany carry takze je to "add b,b" (Pak ze Z80 umi pocitat jen s A(HL)).
Premyslel jsem proc to fungovalo a dospel jsem k nazoru, ze...
Tomu prvne predhodite spravnou hodnotu a on otestuje stisk klaves na radku 0xFE a zjisti ze je nejaka stiskla a ukonci se a nebo neni zadna stiskla a pokracuje.
Dalsi hodnota je 0xFC a program provede znovu kontrolu jako v FE kde nic neni pokud to nahodou neni stiskle prave v ten moment (opakovane zmacknuti Z (3.bit 0xFE) by asi melo obcas vyvolat stisk S (3.bit 0xFD)) a provede kontrolu i pro 0xFD (dela asi proste zaroven kontrolu 0xFE a 0xFD kdyz ma dva bity nulove)
Atd pro zbytek.