V dnešním článku se budeme věnovat zdánlivě jednoduchému tématu, kterým je práce s klávesnicí osmibitových domácích mikropočítačů Atari. Ve skutečnosti se však jedná o dosti komplikované téma, protože některé klávesy jsou zapojeny popř. zpracovávány specifickým způsobem.
Co se dozvíte v článku
- Rozdělení kláves do skupin na základě jejich funkce a zapojení
- Čtení stavu kláves START, SELECT a OPTION
- První demonstrační příklad: zobrazení stavu kláves START, SELECT a OPTION
- Druhý demonstrační příklad: řízení barvy hráče (spritu) klávesami START, SELECT a OPTION
- Matice klávesnice a čtení stavu kláves čipem POKEY
- Realizace čtení kláves umístěných v matici
- Klávesy Shift, Control a Break
- Registr KBCODE čipu POKEY
- Kódy stisknutých kláves a neexistující kombinace kláves
- Třetí demonstrační příklad: přečtení kódu naposledy stisknuté klávesy
- Systémová subrutina pro čekání na stisk klávesy
- Čtvrtý demonstrační příklad: čtení klávesy s využitím systémové subrutiny
- Kódy, které jsou vracené systémovou subrutinou
- Detekce stisku klávesy Break
- Subrutina volaná při stisku klávesy Break
- Pátý demonstrační příklad: detekce stisku klávesy Break
- Příloha: Makefile pro překlad všech demonstračních příkladů
- Repositář s demonstračními příklady
- Odkazy na Internetu
V praxi se standardní klávesy používaly při programování, v textových editorech, v tabulkovém procesoru VisiCalc atd., zatímco ve hrách se setkáme s ovládáním pomocí joysticku a tří kláves Start, Select a Option (Atari totiž bylo od začátku navrženo tak, aby se k němu daly připojit standardní joysticky, což je poměrně velký rozdíl oproti například ZX Spectru). Samozřejmě existují i výjimky z tohoto pravidla, protože některé hry standardní klávesnici používaly. Ovšem skutečně se spíše jedná o výjimky (příkladem je Karateka, kterou lze ovládat jak joystickem, tak i klávesnicí, další hrou je Air Support od společnosti Synapse Software).
Obrázek 1: Hru Karateka lze ovládat jak joystickem, tak i klávesnicí.
Obrázek 2: Hra Air Support používá více klávesových zkratek a tak vyžaduje manuál.
Rozdělení kláves do skupin na základě jejich funkce a zapojení
Pod tímto odstavcem je zobrazena klávesnice počítačů Atari 800 XL, což byl mimochodem v Evropě nejprodávanější model. Tato klávesnice není zapojena do jediné matice, tedy tak, jak by to bylo „logické“, ale klávesy jsou rozděleny do několika skupin s odlišným chováním i odlišným způsobem zpracování operačním systémem. Samostatnou skupinu pochopitelně tvoří Reset.
V další skupině jsou tři klávesy Start, Select a Option (pravý pruh pod Resetem), které jsou interně připojeny k integrovanému obvodu GTIA a je možné číst jejich aktuální stav (stisk se nikde nezaznamenává). Největší skupina kláves je tvořena levým blokem prakticky všech kláves (včetně Esc, Returnu, ale i Inverse Video), ovšem s výjimkou Control, Shift a Break. Do této skupiny ovšem patří i Help a na některých modelech Atari i klávesy F1 až F4. Čtvrtou skupinu tvoří klávesy Control a Shift a pátá (poslední) skupina je tvořena klávesou Break.
Obrázek 3: Klávesnice Atari 800XL.
Čtení stavu kláves START, SELECT a OPTION
Nejjednodušší je čtení stavu kláves START, SELECT a OPTION, které jsou velmi často používány ve hrách, například v hlavním menu atd. Tyto klávesy jsou přímo připojeny na čip GTIA na piny S0 až S2. To se sice může zdát zvláštní, protože GTIA je určen pro práci se sprity a taktéž pro speciální grafické režimy GTIA, ovšem jedná se o integrovaný obvod se čtyřiceti piny, z nichž mnohé by zůstaly „volné“.
Proto jsou na tři piny připojeny výše zmíněné klávesy a na další čtyři piny tlačítka všech čtyř připojitelných joysticků (typicky ovšem jen dvou). Čipy pro osmibitová Atari byly navrhovány společně – není zde tedy taková redundance, jakou nalezneme například na PC.
Obrázek 4: Ve hrách se typicky používají klávesy Start, Select a Option. Někdy to však nestačí...
Pro přečtení stavu kláves START, SELECT a OPTION postačuje přečíst obsah GTIA registru nazvaného CONSOL a získat hodnotu spodních tří bitů (mají inverzní význam, tj. 1 značí „nestlačeno“):
GTIA = $D000 ;CTIA/GTIA area ;------------------------------------------------------------------------- ; CTIA/GTIA Address Equates ;------------------------------------------------------------------------- ; Read/Write Addresses CONSOL = GTIA + $1F ;console switches and speaker control
První demonstrační příklad: zobrazení stavu kláves START, SELECT a OPTION
V dnešním prvním demonstračním příkladu se čte stav všech tří kláves START, SELECT a OPTION a zobrazuje se formou trojice znaků v levém horním rohu obrazovky. Znak „0“ znamená „stisknuto“, znak „1“ opačný stav klávesy. Pro zobrazení tohoto příznaku se používá pomocné makro print_digit, kterému se předává logická 0/1 v akumulátoru A a offset znaku v index registru Y:
; ---------------------------------------------------------------------
; tisk číslice uložené v akumulátoru na specifikovaný offset
; ---------------------------------------------------------------------
.macro print_digit offset
clc
adc #16 ; převod na interní kód znaku
ldy #offset ; nastavit registr Y
sta (88),y ; tisk znaku "0" nebo "1" na specifikované místo na obrazovce
; (adresa Video RAM je na adresách 88 a 89)
.endmacro
V programu před každým zobrazením snímku (50× či 60× za sekundu) přečteme obsah registru CONSOL a vhodnou bitovou operací z něj získáme vždy jediný bit, napříkad:
lda CONSOL ; načtení stavu kláves START, SELECT, OPTION
and #%00000001 ; jen klávesa START
print_digit 2
či:
lda CONSOL ; načtení stavu kláves START, SELECT, OPTION
lsr
lsr
and #%00000001 ; jen klávesa OPTION
print_digit 6
Obrázek 5: Výchozí stav - žádná klávesa nestlačena.
Obrázek 6: Stisknuta je jen klávesa START.
Obrázek 7: Všechny tři klávesy START, SELECT i OPTION jsou stlačeny.
Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:
.include "atari.inc"
.CODE
.proc main
; nastavit vektor pro odloženou VBI
lda #>vbi_handler
sta VVBLKD+1
lda #<vbi_handler
sta VVBLKD
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; tisk číslice uložené v akumulátoru na specifikovaný offset
; ---------------------------------------------------------------------
.macro print_digit offset
clc
adc #16 ; převod na interní kód znaku
ldy #offset ; nastavit registr Y
sta (88),y ; tisk znaku "0" nebo "1" na specifikované místo na obrazovce
; (adresa Video RAM je na adresách 88 a 89)
.endmacro
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc vbi_handler
lda CONSOL ; načtení stavu kláves START, SELECT, OPTION
and #%00000001 ; jen klávesa START
print_digit 2
lda CONSOL ; načtení stavu kláves START, SELECT, OPTION
lsr
and #%00000001 ; jen klávesa SELECT
print_digit 4
lda CONSOL ; načtení stavu kláves START, SELECT, OPTION
lsr
lsr
and #%00000001 ; jen klávesa OPTION
print_digit 6
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
end:
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Druhý demonstrační příklad: řízení barvy hráče (spritu) klávesami START, SELECT a OPTION
Ve druhém demonstračním příkladu, který si dnes ukážeme, je realizována modifikace barvy hráče (spritu, konkrétně spritu číslo 0), opět s využitím kláves START, SELECT a OPTION. Hráčem lze pohybovat joystickem a klávesami se vybírá odstín barvy. Ovšem musíme si uvědomit, že odstín je uložen v horních čtyřech bitech registrů PCOLRx, zatímco ve spodních čtyřech bitech je uložena intenzita (světlost). Z tohoto důvodu je změna barvy spritu provedena následujícím způsobem:
lda CONSOL ; načtení stavu kláves START, SELECT, OPTION
asl ; bitový posun doleva o pět bitů
asl
asl
asl
asl
clc
adc #12 ; přidat požadovanou intenzitu
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
Obrázek 8: Ovládání barvy hráče klávesami START, SELECT a OPTION (výchozí stav).
Obrázek 9: Ovládání barvy hráče klávesami START, SELECT a OPTION.
Obrázek 10: Ovládání barvy hráče klávesami START, SELECT a OPTION.
Obrázek 11: Ovládání barvy hráče klávesami START, SELECT a OPTION.
Úplný zdrojový kód dnešního druhého demonstračního příkladu vypadá následovně:
.include "atari.inc"
.CODE
.proc main
lda #128 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #$ff ; bitová maska prvního i druhého hráče
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+512+64, x ; uložit byte
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
; nastavit vektor pro odloženou VBI
lda #>horizontal_movement
sta VVBLKD+1
lda #<horizontal_movement
sta VVBLKD
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
ldx x_pos ; původní pozice prvního hráče
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos ; zapamatovat si pozici prvního hráče
lda CONSOL ; načtení stavu kláves START, SELECT, OPTION
asl ; bitový posun doleva o pět bitů
asl
asl
asl
asl
clc
adc #12 ; přidat požadovanou intenzitu
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
; pozice hráče
x_pos: .byte 128
end:
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
Matice klávesnice a čtení stavu kláves čipem POKEY
Podívejme se nyní na způsob zapojení matice klávesnice (s výjimkou kláves popsaných v předchozím textu). Tato matice obsahuje (teoreticky) až 8×8=64 kláves a navíc jsou k ní přidány i tři klávesy zpracovávané samostatně (pravý sloupec):
Obrázek 11: Matice klávesnice počítačů Atari (je nutné zobrazit originál obrázku).
Nás nyní bude zajímat standardní matice 8×8. Tato matice obsahuje osm vodorovných a osm svislých vodičů, přičemž každá klávesa (při stisku) spojí unikátní kombinaci vodorovného a svislého vodiče. Takové zapojení nalezneme i u mnoha dalších počítačů (v některých případech se ještě zapojují diody do série s klávesou, aby nedocházelo k situaci, kdy stisk tří sousedních kláves bude detekován jako stisk kláves čtyř – ghosting). Ovšem vraťme se k počítačům Atari. Osm svislých vodičů je připojeno do integrovaného obvodu CD4051, což je multiplexer (tříbitová hodnota je převedena na jeden bit z osmi). A i vodorovné vodiče jsou zapojeny do svého multiplexeru CD4051. Tyto multiplexery jsou připojeny k čipu POKEY, který realizuje čtení stavu matice kláves.
Realizace čtení kláves umístěných v matici
Ze schématu zapojení je navíc patrné, že výstup z druhého multiplexeru pro vodorovné vodiče je (přes pull up rezistor) připojen ke vstupnímu pinu čipu POKEY. Čtení stavu kláves v matici je prováděno následovně – POKEY generuje všechny možné kombinace signálů K0 až K5. Tím dochází k postupnému výběru sloupců a řádků (vybere se sloupec a z osmi možných kláves se přečte stav jediné klávesy na zvoleném řádku). Stav konkrétní klávesy je poté čipem POKEY čten a ten si tento stav zaznamená a po dalších úpravách uloží do svého registru KBCODE.
Obrázek 12: Připojení matice klávesnice k čipu POKEY.
Samozřejmě je vhodné zjistit, jak se vlastně generují signály K0 až K5. Jak je tomu u osmibitových Atari zvykem, je řešení minimalistické. POKEY obsahuje šestibitový čítač, který je nastartován zápisem do registru SKCTL. S frekvencí odpovídající řádkovému kmitočtu (ano – vše je navázáno buď na frekvenci CPU, nebo na řádkovém kmitočtu) se hodnota čítače zvyšuje a je přiváděna na piny K0 až K5. Čtení celé klávesnice je tak možné provést cca 200× za sekundu:
Obrázek 13: Časování signálů K0 až K5.
Klávesy Shift, Control a Break
Ze zobrazené matice klávesnice, která byla uvedena v šesté kapitole, je zřejmé, že klávesy Shift, Control a Break nejsou do hlavní matice zařazeny, i když by tam pro ně bylo místo. Důvody pro toto oddělení jsou dva. Klávesy Shift a Control jsou použity pro modifikaci kódu stisknuté klávesy – typicky provádí přepnutí těch kláves, které slouží pro zápis dvou či tří různých znaků. U písmen je to verzálka+minuska a navíc i grafický znak s ATASCII hodnotou 0 až 31. U numerických kláves se klávesou Shift přepíná na „vyšší znak“ zobrazený na klávesnici (namísto jedničky vykřičník, namísto dvojky uvozovky atd.), stejně jako u kláves napravo od běžných znaků. A navíc Control slouží i pro posuny kurzoru atd.
A klávesa Break je zvláštní hned několikrát, i když se nejedná o modifikační klávesu. V operačním systému je pro její obsluhu vyhrazena zvláštní subrutina; klávesa sama o sobě není použita pro tisk znaku a většina subrutin pro práci s klávesnicí Break zcela ignoruje. To si ostatně můžeme otestovat v dalším demonstračním příkladu, který zobrazuje kódy stisknutých kláves – klávesa Break skutečně bude tímto příkladem zcela ignorována. A naopak – existuje speciální typ HW přerušení navázaného přímo na stisk Break.
Registr KBCODE čipu POKEY
Integrovaný obvod POKEY zjišťuje jak stav kláves umístěných do hlavní matice, tak i stav přeřaďovačů Shift a Control i klávesy Break. Následně jsou takto získaná data čipem POKEY zpracována následujícím způsobem:
- Kód stisknuté klávesy je zkombinován s informací o stisku přeřaďovačů Shift a Control. Výsledek je uložen do registru KBCODE, odkud je možné tuto informaci přečíst (což typicky provádí operační systém, ovšem nic nám nebrání v tom, abychom obsah tohoto registru přečetli sami v assemblerovském programu).
- Pokud došlo ke stisku nějaké běžné klávesy, je vygenerováno přerušení K (Keyboard Interrupt).
- Pokud došlo ke stisku klávesy Break, je vygenerováno přerušení BREAK (Break Key Interrupt). Reakci na stisk této klávesy lze tedy umístit do obslužné rutiny tohoto přerušení.
Kódy stisknutých kláves a neexistující kombinace kláves
Z registru KBCODE lze vyčíst bajt s informací o stisku klávesy, a to včetně přeřaďovačů. Současný stisk Shift zvyšuje kód klávesy o 64, stisk Control zvyšuje kód klávesy o 128 a kombinace Shift+Control+klávesa zvyšuje kód o 192. Ovšem ne všechny klávesy podporují kombinaci obou přeřaďovačů. Co je ale zajímavé – Shift a Control lze použít se všemi klávesami, včetně Esc, Return, Caps (!) i Inverse Video (částečně i HELP):
| Klávesa | Kód | SHIFT+ | CONTROL+ | SHIFT+CONTROL+ |
|---|---|---|---|---|
| L | 0 | 64 | 128 | |
| J | 1 | 65 | 129 | |
| ; | 2 | 66 | 130 | |
| K | 5 | 69 | 133 | |
| + | 6 | 70 | 134 | |
| * | 7 | 71 | 135 | |
| O | 8 | 72 | 136 | 200 |
| P | 10 | 74 | 138 | 202 |
| U | 11 | 75 | 139 | 203 |
| RET | 12 | 76 | 140 | 204 |
| I | 13 | 77 | 141 | 205 |
| – | 14 | 78 | 142 | 206 |
| = | 15 | 79 | 143 | 207 |
| V | 16 | 80 | 144 | |
| HELP | 17 | 81 | 145 | |
| C | 18 | 82 | 146 | |
| B | 21 | 85 | 149 | |
| X | 22 | 86 | 150 | |
| Z | 23 | 87 | 151 | |
| 4 | 24 | 88 | 152 | 216 |
| 3 | 26 | 90 | 154 | 218 |
| 6 | 27 | 91 | 155 | 219 |
| ESC | 28 | 92 | 156 | 220 |
| 5 | 29 | 93 | 157 | 221 |
| 2 | 30 | 94 | 158 | 222 |
| 1 | 31 | 95 | 159 | 223 |
| , | 32 | 96 | 160 | 224 |
| SPC | 33 | 97 | 161 | 225 |
| . | 31 | 98 | 162 | 226 |
| N | 35 | 99 | 163 | 227 |
| M | 37 | 101 | 165 | 229 |
| / | 38 | 102 | 166 | 230 |
| INV | 39 | 103 | 167 | 231 |
| R | 40 | 104 | 168 | 232 |
| E | 42 | 106 | 170 | 234 |
| Y | 43 | 107 | 171 | 235 |
| TAB | 44 | 108 | 172 | 236 |
| T | 45 | 109 | 173 | 237 |
| W | 46 | 110 | 174 | 238 |
| Q | 47 | 111 | 175 | 239 |
| 9 | 48 | 112 | 176 | 240 |
| 0 | 50 | 114 | 178 | 242 |
| 7 | 51 | 115 | 179 | 243 |
| DEL | 52 | 116 | 180 | 244 |
| 8 | 53 | 117 | 181 | 245 |
| < | 54 | 118 | 182 | 246 |
| > | 55 | 119 | 183 | 247 |
| F | 56 | 120 | 184 | 248 |
| H | 57 | 121 | 185 | 249 |
| D | 58 | 122 | 186 | 250 |
| CAP | 60 | 124 | 188 | 252 |
| G | 61 | 125 | 189 | 253 |
| S | 62 | 126 | 190 | 254 |
| A | 63 | 127 | 191 | 255 |
Třetí demonstrační příklad: přečtení kódu naposledy stisknuté klávesy
Ve třetím demonstračním příkladu je periodicky čten registr KBCODE čipu POKEY. Přečtená hodnota je převedena na dvojici hexadecimálních číslic a výsledek je vytištěn na obrazovku. Můžete si tak sami otestovat kombinace kláves, fakt, že klávesa Break je ignorována atd.:
.include "atari.inc"
; ---------------------------------------------------------------------
; Definice maker
; ---------------------------------------------------------------------
.macro lsr_ shift_count
.repeat shift_count
lsr
.endrep
.endmacro
.CODE
; ---------------------------------------------------------------------
; Hlavní program
; ---------------------------------------------------------------------
.proc main
; nastavit vektor pro odloženou VBI
lda #>vbi_handler
sta VVBLKD+1
lda #<vbi_handler
sta VVBLKD
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; Tisk hodnoty 0..255 formou dvou hexadecimálních číslic
; ---------------------------------------------------------------------
.proc print_2_hex_digits
pha ; ulozit akumulator na zasobnik
lsr_ 4 ; posun 4 nejvyssich bitu do bitu nejnizsich
jsr nibble_to_hex_char ; prevod na interni kod cislice
ldy #0 ; pozice na obrazovce
jsr print_char_at_y ; tisk cislice/znaku
pla
and #$0f ; ziskat jen nizsi nibble
jsr nibble_to_hex_char ; prevod na interni kod cislice
ldy #1 ; pozice na obrazovce
jsr print_char_at_y ; tisk cislice/znaku
.endproc
; ---------------------------------------------------------------------
; Převod hodnoty 0..15 na jeden hexadecimální znak
; ---------------------------------------------------------------------
.proc nibble_to_hex_char
cmp #$0a ; test na hodnotu 0-9 nebo 10-15
bcc skip_add ; je to hodnota 0-9
adc #6 ; pricist sedmicku (6+carry)
skip_add:
adc #16 ; prevod hodnoty na interni kod (ne ATASCII!)
rts ; navrat z podprogramu
.endproc
; ---------------------------------------------------------------------
; Tisk znaku (v akumulátoru) na offset definovaný v index registru Y
; ---------------------------------------------------------------------
.proc print_char_at_y
sta (88), y ; tisk znaku na první místo na obrazovce
; (adresa Video RAM je na adresách 88 a 89)
rts
.endproc
; ---------------------------------------------------------------------
; Subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc vbi_handler
lda KBCODE ; načtení kódu stlačených kláves
jsr print_2_hex_digits
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
end: ; potrebujeme znat adresu konce kodoveho segmentu
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Systémová subrutina pro čekání na stisk klávesy
Práce s klávesnicí stylem, jaký jsme si až doposud ukázali, je poměrně nízkoúrovňová a využít je ji možné především ve hrách. Ovšem pro programy typu textový editor by bylo výhodnější mít k dispozici mechanismus pro čekání na stisk klávesy a ideálně by se měl vracet ATASCII kód stisknuté klávesy, pokud je to pochopitelně možné (některé kombinace kláves nemají odpovídající ATASCII kód). Takový mechanismus existuje, dokonce ve dvou variantách. Pravděpodobně nejjednodušší je využití systémové subrutiny nazvané KBHANDLER. Při jejím zavolání se používá známý trik – adresa subrutiny se uloží na zásobník a instrukcí RTS se provede skok na tuto adresu:
; ---------------------------------------------------------------------
; Přečtení kódu naposledy stisknuté klávesy, kód se vrátí v registru A
; ---------------------------------------------------------------------
.proc get_key
lda KBHANDLER+1 ; cteni horni casti adresy ulozene v ROM
pha ; ulozeni na zasobnik
lda KBHANDLER ; cteni dolni casti adresy ulozene v ROM
pha ; ulozeni na zasobnik
rts ; vyber adresy ze zasobniku + skok
; zde neni nutne mit RTS
.endproc
end: ; potrebujeme znat adresu konce kodoveho segmentu
Čtvrtý demonstrační příklad: čtení klávesy s využitím systémové subrutiny
Výše uvedená systémová subrutina je použita v dnešním čtvrtém demonstračním příkladu. V něm se čeká na stisk klávesy a poté je kód této klávesy zobrazen v hexadecimální číselné soustavě (do levého horního rohu obrazovky):
.include "atari.inc"
KBHANDLER = $e424 ; rutina pro cteni klavesy
; ---------------------------------------------------------------------
; Definice maker
; ---------------------------------------------------------------------
.macro lsr_ shift_count
.repeat shift_count
lsr
.endrep
.endmacro
.CODE
; ---------------------------------------------------------------------
; Hlavní program
; ---------------------------------------------------------------------
.proc main
loop:
jsr get_key ; načíst kód stisknuté klávesy
jsr print_2_hex_digits ; tisk kódu stiknuté klávesy
jmp loop
.endproc
; ---------------------------------------------------------------------
; Tisk hodnoty 0..255 formou dvou hexadecimálních číslic
; ---------------------------------------------------------------------
.proc print_2_hex_digits
pha ; ulozit akumulator na zasobnik
lsr_ 4 ; posun 4 nejvyssich bitu do bitu nejnizsich
jsr nibble_to_hex_char ; prevod na interni kod cislice
ldy #0 ; pozice na obrazovce
jsr print_char_at_y ; tisk cislice/znaku
pla
and #$0f ; ziskat jen nizsi nibble
jsr nibble_to_hex_char ; prevod na interni kod cislice
ldy #1 ; pozice na obrazovce
jsr print_char_at_y ; tisk cislice/znaku
.endproc
; ---------------------------------------------------------------------
; Převod hodnoty 0..15 na jeden hexadecimální znak
; ---------------------------------------------------------------------
.proc nibble_to_hex_char
cmp #$0a ; test na hodnotu 0-9 nebo 10-15
bcc skip_add ; je to hodnota 0-9
adc #6 ; pricist sedmicku (6+carry)
skip_add:
adc #16 ; prevod hodnoty na interni kod (ne ATASCII!)
rts ; navrat z podprogramu
.endproc
; ---------------------------------------------------------------------
; Tisk znaku (v akumulátoru) na offset definovaný v index registru Y
; ---------------------------------------------------------------------
.proc print_char_at_y
sta (88), y ; tisk znaku na první místo na obrazovce
; (adresa Video RAM je na adresách 88 a 89)
rts
.endproc
; ---------------------------------------------------------------------
; Přečtení kódu naposledy stisknuté klávesy, kód se vrátí v registru A
; ---------------------------------------------------------------------
.proc get_key
lda KBHANDLER+1 ; cteni horni casti adresy ulozene v ROM
pha ; ulozeni na zasobnik
lda KBHANDLER ; cteni dolni casti adresy ulozene v ROM
pha ; ulozeni na zasobnik
rts ; vyber adresy ze zasobniku + skok
; zde neni nutne mit RTS
.endproc
end: ; potrebujeme znat adresu konce kodoveho segmentu
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Kódy, které jsou vracené systémovou subrutinou
Zajímavé bude zjistit, jaké kódy jsou vlastně subrutinou get_key vráceny. V následující tabulce jsou některé kódy vypsány i s komentářem:
| Klávesa | Kód | Shift+klávesa | Control+klávesa | Stručný popis |
|---|---|---|---|---|
| A | 41 | 41 | 01 | ATASCII kód znaku „A“, Shift nepřepíná na malá písmena |
| 0 | 30 | 29 | 29 | ATASCII kód znaku „0“, se Shiftem pak pravá kulatá závorka, Control nemá vliv |
| . | 2E | 3E | 60 | ATASCII kód znaku „.“, se Shiftem pak kód znaku „>“ |
| Space | 20 | 20 | 20 | ATASCII kód mezery, Shift ani Control nemají vliv |
| Enter | 9B | 9B | 9B | ATASCII kód Enteru, Shift ani Control nemají vliv |
| Tab | 7F | 9F | 9E | různé řídicí kódy pro skok, vytvoření a smazání zarážky |
| Esc | 1B | 1B | ? | ATASCII kód Esc, Shift se ignoruje |
Z tabulky je patrné, že pro základní klávesy, popř. i Shift+klávesa, se vrací ATASCII kódy. Control u písmen přepíná na nejnižších 32 znaků ATASCII a u mnoha dalších kláves se Control ignoruje.
Detekce stisku klávesy Break
Jak jsme si již naznačili v předchozím textu, chová se klávesa Break odlišně od většiny ostatních kláves. Stisk Break je totiž čipem POKEY zpracován takovým způsobem, že se kód této klávesy sice neuloží do registru KBCODE, ovšem současně se vyvolá speciální IRQ nazvané taktéž Break. Operační systém má nastavenou subrutinu použitou pro obsluhu tohoto přerušení. U programů psaných v BASICu (ale i TurboBASICu) vede stisk Break k vyvolání této subrutiny, která ukončí běh programu a vypíše právě zpracovávaný řádek:
Obrázek 14: Zastavení BASICového programu klávesou BREAK.
Subrutina volaná při stisku klávesy Break
Pro aplikace psané v assembleru nemá (alespoň většinou) význam na stisk Break reagovat ukončením programu. Ovšem i přesto si ukažme, jak stisk této speciální klávesy zachytit. Postačuje přesměrovat vektor nazvaný BRKKY na náš vlastní podprogram. Samotné přesměrování vektoru je snadné:
; nastavit vektor pro BREAK
lda #>break_handler
sta BRKKY+1
lda #<break_handler
sta BRKKY
Samotná obsluha může (například) na obrazovku vypsat znak „B“:
; ---------------------------------------------------------------------
; Subrutina pro obsluhu stisku klavesy BREAK
; ---------------------------------------------------------------------
.proc break_handler
lda #34 ; kod znaku "B"
ldy #1
sta (88), y ; tisk znaku na první místo na obrazovce
; (adresa Video RAM je na adresách 88 a 89)
rti
.endproc
Pátý demonstrační příklad: detekce stisku klávesy Break
V pátém a dnes již posledním demonstračním příkladu se provádí nekonečná programová smyčka, takže program „nic nedělá“. Ovšem ještě předtím byla modifikována subrutina pro detekci stisku klávesy Break tak, aby se vypsal znak „B“.
Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:
.include "atari.inc"
KBHANDLER = $e424 ; rutina pro cteni klavesy
; ---------------------------------------------------------------------
; Definice maker
; ---------------------------------------------------------------------
.macro lsr_ shift_count
.repeat shift_count
lsr
.endrep
.endmacro
.CODE
; ---------------------------------------------------------------------
; Hlavní program
; ---------------------------------------------------------------------
.proc main
; nastavit vektor pro BREAK
lda #>break_handler
sta BRKKY+1
lda #<break_handler
sta BRKKY
loop:
jmp loop
.endproc
; ---------------------------------------------------------------------
; Subrutina pro obsluhu stisku klavesy BREAK
; ---------------------------------------------------------------------
.proc break_handler
lda #34 ; kod znaku "B"
ldy #1
sta (88), y ; tisk znaku na první místo na obrazovce
; (adresa Video RAM je na adresách 88 a 89)
rti
.endproc
end: ; potrebujeme znat adresu konce kodoveho segmentu
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Příloha: Makefile pro překlad všech demonstračních příkladů
Všechny minule i dnes popsané demonstrační příklady, pro jejichž překlad je zapotřebí použít assembler ca65 a linker ld65, je možné přeložit s využitím souboru Makefile, jehož obsah je vypsán pod tímto odstavcem:
execs := dummy.xex print_a.xex \
background_color_1.xex background_color_2.xex \
color_computation_1.xex color_computation_2.xex \
subroutine_1.xex subroutine_2.xex \
hex_number_1.xex hex_number_2.xex \
hex_number_3.xex hex_number_4.xex \
hex_number_5.xex hex_number_6.xex \
hex_number_7.xex hex_number_8.xex \
hex_number_9.xex \
fill_block_1.xex fill_block_2.xex \
fill_block_3.xex fill_block_4.xex \
fill_block_5.xex \
pmg_01.xex pmg_02.xex \
pmg_03.xex pmg_04.xex \
pmg_05.xex pmg_06.xex \
pmg_07.xex pmg_08.xex \
pmg_09.xex pmg_10.xex \
pmg_11.xex pmg_12.xex \
pmg_13.xex pmg_14.xex \
pmg_15.xex pong.xex \
pmg_stick_1.xex pmg_stick_2.xex \
pmg_stick_3.xex pmg_stick_4.xex \
pmg_stick_5.xex pmg_stick_6.xex \
pmg_collisions_1.xex pmg_collisions_2.xex \
pmg_collisions_3.xex pmg_collisions_4.xex \
antic_1.xex antic_2.xex \
antic_3.xex antic_4.xex \
antic_5.xex antic_6.xex \
antic_7.xex antic_8.xex \
antic_9.xex antic_A.xex \
antic_B.xex \
antic_bitmap_1.xex antic_bitmap_2.xex \
antic_bitmap_3.xex antic_bitmap_4.xex \
antic_bitmap_5.xex antic_bitmap_6.xex \
antic_bitmap_7.xex antic_bitmap_8.xex \
antic_dli_1.asm antic_dli_2.asm \
antic_dli_3.asm antic_dli_4.asm \
antic_dli_5.asm antic_dli_pmg.asm \
sound_1.xex sound_2.xex \
sound_3.xex sound_4.xex \
sound_5.xex sound_6.xex \
sound_7.xex sound_8.xex \
sound_9.xex sound_A.xex \
sound_B.xex sound_C.xex \
sound_D.xex sound_E.xex \
sound_F.xex \
rnd_1.xex rnd_2.xex \
rnd_3.xex rnd_4.xex \
paddle_1.xex paddle_2.xex \
key_01.xex key_02.xex \
key_03.xex key_04.xex \
key_05.xex
all: $(execs)
clean:
rm -f *.o
rm -f *.xex
.PHONY: all clean
%.o: %.asm
ca65 $< -t atari -o $@ -l $(basename $<)_list.asm --list-bytes 100
%.xex: %.o
ld65 -C linker.cfg $< -o $@ -m $(basename $<).map
antic_bitmap_6.xex: antic_bitmap_6.o
ld65 -C linker_image.cfg $< -o $@ -m $(basename $<).map
antic_bitmap_7.xex: antic_bitmap_7.o
ld65 -C linker_image_2.cfg $< -o $@ -m $(basename $<).map
antic: \
antic_1.xex \
antic_2.xex \
antic_3.xex \
antic_4.xex \
antic_5.xex \
antic_6.xex \
antic_7.xex \
antic_8.xex \
antic_9.xex \
antic_A.xex \
antic_B.xex
antic_bitmap: \
antic_bitmap_1.xex \
antic_bitmap_2.xex \
antic_bitmap_3.xex \
antic_bitmap_4.xex \
antic_bitmap_5.xex \
antic_bitmap_6.xex \
antic_bitmap_7.xex \
antic_bitmap_8.xex
antic_dli: \
antic_dli_1.xex \
antic_dli_2.xex \
antic_dli_3.xex \
antic_dli_4.xex \
antic_dli_5.xex \
antic_dli_pmg.xex
antic_vbi: \
antic_vbi_1.xex \
antic_vbi_2.xex \
antic_vbi_3.xex \
antic_vbi_dli_1.xex \
antic_vbi_dli_2.xex \
antic_vbi_dli_3.xex \
antic_vbi_dli_4.xex
antic_scrolling: \
antic_scrolling_1.xex \
antic_scrolling_2.xex \
antic_scrolling_3.xex \
antic_scrolling_4.xex
gtia: \
gtia_mode_1.xex \
gtia_mode_2.xex \
gtia_mode_3.xex \
gtia_mode_4.xex \
gtia_mode_5.xex \
gtia_mode_6.xex \
gtia_mode_7.xex \
gtia_mode_8.xex \
gtia_mode_9.xex \
gtia_mode_A.xex \
gtia_mode_B.xex
sound: \
sound_1.xex \
sound_2.xex \
sound_3.xex \
sound_4.xex \
sound_5.xex \
sound_6.xex \
sound_7.xex \
sound_8.xex \
sound_9.xex \
sound_A.xex \
sound_B.xex \
sound_C.xex \
sound_D.xex \
sound_E.xex \
sound_F.xex
rnd: \
rnd_1.xex \
rnd_2.xex \
rnd_3.xex \
rnd_4.xex
paddle: \
paddle_1.xex \
paddle_2.xex
key: \
key_01.xex \
key_02.xex \
key_03.xex \
key_04.xex \
key_05.xex
Výsledkem překladu jsou soubory s koncovkou .xex, které je možné přímo spustit v emulátoru osmibitových počítačů Atari.
Repositář s demonstračními příklady
Všechny demonstrační příklady, s nimiž jsme se v předchozích článcích i v článku dnešním seznámili a které jsou určeny pro překlad s využitím assembleru ca65, jsou dostupné, jak je zvykem, na GitHubu. V tabulce níže jsou uvedeny odkazy na jednotlivé zdrojové kódy příkladů psané v assembleru i „listingy“ vygenerované samotným assemblerem, ze kterých je patrné, jakým způsobem se jednotlivé příklady přeložily do výsledného XEX souboru:
Odkazy na Internetu
- MOS 6502 instruction set
http://www.6502.org/users/obelisk/6502/instructions.html - EXE File Format Description
https://gury.atari8.info/refs/file_formats_exe.php - XEX Filter – A toolkit to analyze and manipulate Atari binary files
https://www.vitoco.cl/atari/xex-filter/index.html - chkxex.py
https://raw.githubusercontent.com/seban-slt/tcx_tools/refs/heads/master/chkxex.py - ca65 Users Guide
https://cc65.github.io/doc/ca65.html - cc65 Users Guide
https://cc65.github.io/doc/cc65.html - ld65 Users Guide
https://cc65.github.io/doc/ld65.html - da65 Users Guide
https://cc65.github.io/doc/da65.html - Překladače jazyka C pro historické osmibitové mikroprocesory
https://www.root.cz/clanky/prekladace-jazyka-c-pro-historicke-osmibitove-mikroprocesory/ - Překladače programovacího jazyka C pro historické osmibitové mikroprocesory (2)
https://www.root.cz/clanky/prekladace-programovaciho-jazyka-c-pro-historicke-osmibitove-mikroprocesory-2/ - Getting Started Programming in C: Coding a Retro Game with C Part 2
https://retrogamecoders.com/getting-started-with-c-cc65/ - NES game development in 6502 assembly – Part 1
https://kibrit.tech/en/blog/nes-game-development-part-1 - NES 6502 Programming Tutorial – Part 1: Getting Started
https://dev.xenforo.relay.cool/index.php?threads/nes-6502-programming-tutorial-part-1-getting-started.858389/ - Minimal NES example using ca65
https://github.com/bbbradsmith/NES-ca65-example - List of 6502-based Computers and Consoles
https://www.retrocompute.co.uk/list-of-6502-based-computers-and-consoles/ - 6502 – the first RISC µP
http://ericclever.com/6500/ - 3 Generations of Game Machine Architecture
http://www.atariarchives.org/dev/CGEXPO99.html - “Hello, world” from scratch on a 6502 — Part 1
https://www.youtube.com/watch?v=LnzuMJLZRdU - A Tour of 6502 Cross-Assemblers
https://bumbershootsoft.wordpress.com/2016/01/31/a-tour-of-6502-cross-assemblers/ - Adventures with ca65
https://atariage.com/forums/topic/312451-adventures-with-ca65/ - example ca65 startup code
https://atariage.com/forums/topic/209776-example-ca65-startup-code/ - 6502 PRIMER: Building your own 6502 computer
http://wilsonminesco.com/6502primer/ - 6502 Instruction Set
https://www.masswerk.at/6502/6502_instruction_set.html - Chip Hall of Fame: MOS Technology 6502 Microprocessor
https://spectrum.ieee.org/tech-history/silicon-revolution/chip-hall-of-fame-mos-technology-6502-microprocessor - Single-board computer
https://en.wikipedia.org/wiki/Single-board_computer - www.6502.org
http://www.6502.org/ - 6502 PRIMER: Building your own 6502 computer – clock generator
http://wilsonminesco.com/6502primer/ClkGen.html - Great Microprocessors of the Past and Present (V 13.4.0)
http://www.cpushack.com/CPU/cpu.html - Jak se zrodil procesor?
https://www.root.cz/clanky/jak-se-zrodil-procesor/ - Osmibitové mikroprocesory a mikrořadiče firmy Motorola (1)
https://www.root.cz/clanky/osmibitove-mikroprocesory-a-mikroradice-firmy-motorola-1/ - Mikrořadiče a jejich použití v jednoduchých mikropočítačích
https://www.root.cz/clanky/mikroradice-a-jejich-pouziti-v-jednoduchych-mikropocitacich/ - Mikrořadiče a jejich aplikace v jednoduchých mikropočítačích (2)
https://www.root.cz/clanky/mikroradice-a-jejich-aplikace-v-jednoduchych-mikropocitacich-2/ - 25 Microchips That Shook the World
https://spectrum.ieee.org/tech-history/silicon-revolution/25-microchips-that-shook-the-world - Comparison of instruction set architectures
https://en.wikipedia.org/wiki/Comparison_of_instruction_set_architectures - How To Start Learning Atari 8 Bit Assembly For Free
https://forums.atariage.com/topic/300732-how-to-start-learning-atari-8-bit-assembly-for-free/ - WUDSN (Demo Group)
https://www.wudsn.com/ - Machine Language For Beginners
https://www.atariarchives.org/mlb/ - Assembly language: all about I/O
https://www.atarimagazines.com/v3n8/AllAbout_IO.html - Sedmdesátiny assemblerů: lidsky čitelný strojový kód
https://www.root.cz/clanky/sedmdesatiny-assembleru-lidsky-citelny-strojovy-kod/ - Color names
https://atariwiki.org/wiki/Wiki.jsp?page=Color%20names - ATASCII
https://en.wikipedia.org/wiki/ATASCII - Put characters in display ram isn't ATASCII?
https://forums.atariage.com/topic/359973-put-characters-in-display-ram-isnt-atascii/ - ATASCII And Internal Character Code Values
https://www.atariarchives.org/mapping/appendix10.php - Reading ATASCII from the keyboard in assembly
https://forums.atariage.com/topic/361733-reading-atascii-from-the-keyboard-in-assembly/ - Why does the 6502 JSR instruction only increment the return address by 2 bytes?
https://retrocomputing.stackexchange.com/questions/19543/why-does-the-6502-jsr-instruction-only-increment-the-return-address-by-2-bytes - Pushing return address to stack off by 1 byte
https://forums.atariage.com/topic/378206-pushing-return-address-to-stack-off-by-1-byte/ - Intel x86 documentation has more pages than the 6502 has transistors
https://www.righto.com/2013/09/intel-x86-documentation-has-more-pages.html - Clearing a Section of Memory
http://www.6502.org/source/general/clearmem.htm - Practical Memory Move Routines by Bruce Clark
http://www.6502.org/source/general/memory_move.html - 6502 Assembly Programming Guide
https://neumont-gamedev.github.io/posts/retrogamedev-6502-guide/ - Off-by-one error
https://en.wikipedia.org/wiki/Off-by-one_error - 6502 cycle times
https://www.nesdev.org/wiki/6502_cycle_times - Atari TIA
http://www.atarihq.com/danb/tia.shtml - TIA Playfield
http://www.atarihq.com/danb/TIA/Playfield.shtml - Atari Inc.:
ANTIC C012296 (NTSC) Revision D
Atari Incorporated, Sunnyvale CA, 1982 - Atari Inc.:
GTIA C014805 (NTSC) Revision A
Atari Incorporated, Sunnyvale CA, 1982 - Atari 5200
http://www.atariage.com/software_search.html?SystemID=5200 - Atari 5200 Hardware and Accessories
http://www.atariage.com/5200/archives/hardware.html - Atari 5200 Screenshots
http://www.atariage.com/system_items.html?SystemID=5200&ItemTypeID=SCREENSHOT - History of video game consoles (second generation): Wikipedia
http://en.wikipedia.org/wiki/History_of_video_game_consoles_(second_generation) - Atari 5200: Wikipedia
http://en.wikipedia.org/wiki/Atari_5200 - Player-Missile Graphics
https://www.atariarchives.org/agagd/chapter5.php - Sprite (computer graphics)
https://en.wikipedia.org/wiki/Sprite_(computer_graphics) - Atari Graphics Demonstrations by Underground Software, 1985 | Atari 8 bit Demo
https://www.youtube.com/watch?v=h7N9EYSyCkw - Atari 8-bit Display List Interrupts: A Complete(ish) Tutorial
https://playermissile.com/dli_tutorial/ - Atari Assembler Editor manual
https://atariwiki.org/wiki/attach/Atari%20Assembler%20Editor/ATARI%20Assembler%20Editor%20User-s%20Manual-OCR.pdf - Atari 8-bit Fine Scrolling: A Complete(ish) Tutorial
https://playermissile.com/scrolling_tutorial/index.html - Atari Fine Scrolling
https://www.atarimagazines.com/compute/issue67/338_1_Atari_Fine_Scrolling.php - CTIA / GTIA Pinout Diagram
https://user.xmission.com/~trevin/atari/gtia_pinout.html - GTIA Modes
https://page6.org/archive/issue02/page10.htm - 56 graphic modes
https://www.atari800×l.eu/docs/kb/kb-hardware-0005-atari-8bit-56-graphic-modes.html - UNLOCKING THE 56 GRAPHIC MODES
https://www.atarimagazines.com/v3n5/allmodes.html - GTIA Modes 9, 10 & 11 + ANTIC data and color clocks
https://forums.atariage.com/topic/366256-gtia-modes-9–10–11-antic-data-and-color-clocks/ - ANTIC, GTIA and timing info
https://atarimax.com/jindroush.atari.org/atanttim.html - 6.10 Cycle timing
https://github.com/AnimaInCorpore/A8E/blob/main/AHRM/6.%20CTIA-GTIA/10.%20Cycle%20timing.md - Doug Neubauer
https://en.wikipedia.org/wiki/Doug_Neubauer - POKEY
https://en.wikipedia.org/wiki/POKEY - Inside the Atari 800XL
https://www.goto10retro.com/p/800×l-inside - Grayscale Project – „Jam Session“ – Chiptune Visualization / Atari SAP
https://www.youtube.com/watch?v=Qx-AHgvwrHo&list=PL92E73FD91764173B - POKEY MUSIC ( ATARI XL / XE ) =+ MUSIC FROM POLAND += DEMO
https://www.youtube.com/watch?v=5PswfMjMop4&list=RD5PswfMjMop4&start_radio=1 - Atari.org
http://www.atari.org/ - Atari POKEY
http://www.absoluteastronomy.com/topics/Atari_POKEY - Chiptune
http://www.absoluteastronomy.com/topics/Chiptune - ASAP – Another Slight Atari Player
http://asap.sourceforge.net/ - Atari SAP music archive
http://asma.atari.org/ - RASTER Music Tracker (RMT)
http://raster.infos.cz/atari/rmt/rmt.htm - POKEY explorer
https://github.com/ivop/pokey-explorer - POKEY C012294 Documentation
https://7800.8bitdev.org/index.php/POKEY_C012294_Documentation - Pokey Registers
https://user.xmission.com/~trevin/atari/pokey_regs.html - POKEY CO12294
http://krap.pl/mirrorz/atari/homepage.ntlworld.com/kryten_droid/Atari/800XL/atari_hw/pokey.htm - De Re Atari: SOUND
https://www.atariarchives.org/dere/chapt07.php#H7_3_5 - Paddle (game controller)
https://en.wikipedia.org/wiki/Paddle_(game_controller) - Interfacing Your Atari
https://www.atariarchives.org/creativeatari/Interfacing_Your_Atari.php - Atari SIO
https://en.wikipedia.org/wiki/Atari_SIO - Atari Chips
https://atarimax.com/jindroush.atari.org/achip.html - Table 4: Keyboard Code Index, KEYCODE: ATASCII
https://www.atariarchives.org/c3ba/kcindex.php
