Vlákno názorů k článku Práce s klávesnicí na ZX Spectru od xbastaj - Zkusil jsem si napsat podprogram pro získání kodu...

  • Článek je starý, nové názory již nelze přidávat.
  • 21. 5. 2023 2:04

    xbastaj

    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
  • 23. 5. 2023 3:00

    _dw

    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+fi­re. 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...

  • 23. 5. 2023 7:57

    xbastaj

    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.

  • 23. 5. 2023 14:16

    _dw

    Ja osobne pouzivam 5. odkaz uvedeny v clanku v odkazech.

    https://clrhome.org/table/

    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.

  • 24. 5. 2023 15:11

    _dw

    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.