V předchozích dvou článcích o vývoji aplikací pro osmibitové domácí mikropočítače Atari [1] [2] jsme si (i když jen na poměrně malých programech) ukázali, jak probíhal vývoj a ladění aplikací s využitím původního Atari Assembler/Editoru či novějšího a mnohem rychlejšího assembleru MAC/65 doplněného o debugger nazvaný DDT (oba tyto nástroje dodávala společnost OSS).
Co se dozvíte v článku
- Kódy strojových instrukcí mikroprocesoru MOS 6502
- Jsou kódy instrukcí zvoleny podle nějakého schématu?
- Rozdělení instrukcí do tří hlavních skupin
- Tabulky instrukcí rozdělených do logických skupin
- Prostředí Turbo Monitoru
- Ovládání Turbo Monitoru
- Prohlížení obsahu paměti: monitor a disassembler
- Zápis strojového kódu do operační paměti v monitoru
- Spuštění první verze programu
- Úprava programu: přidání nekonečné programové smyčky
- Spuštění upraveného programu
- Výpočet adres pro podmíněné skoky
- Příklad s podmíněnými skoky
- Spuštění příkladu a ověření v emulátoru, že je příklad vykonáván korektně
- Disassembling subrutin uložených v paměti ROM
- Disassembling s výstupem na zvolené zařízení
- Závěr
- Repositář s demonstračními příklady
- Odkazy na Internetu
Z pohledu dnešního vývojáře sice mohou všechny tři zmíněné vývojové nástroje vypadat velmi spartánsky, ovšem pro situaci před cca 40–45 lety se ve skutečnosti jednalo o uživatelsky přívětivé nástroje (další revoluce nastala až s příchodem integrovaných vývojových prostředí Turbo Pascalu a dalších programovacích jazyků – to je však jiná oblast informatiky, která se už netýká osmibitových počítačů Atari).
Nicméně vyvíjet (a do jisté míry i ladit) aplikace bylo možné i s využitím ještě jednodušších prostředků, než jsou assemblery a debuggery. Příkladem může být tuzemský Turbo Monitor, což byl program používaný mj. pro úpravu a převod programů do formátu Turbo 2000 (jak již ostatně naznačuje jeho název). Ovšem díky tomu, že tento nástroj obsahuje kromě klasických operací, které si s monitory spojujeme, i disassembler a navíc i možnost spouštět strojový kód umístěný na libovolné adrese, lze ho použít i pro další činnosti – úpravu her pro nekonečný počet životů, ladění existujících programů, ale i (a to je již poměrně hardcode i na osmdesátá léta minulého století) pro vývoj nových programů „na zelené louce“.
Co je tedy pro takový vývoj zapotřebí? Ve skutečnosti jen zcela minimální prostředky, o to však větší znalosti (tedy naprostý opak k vibe codingu):
- Atari a magnetofon s úpravou na Turbo 2000 (v moderní době emulátor osmibitových mikropočítačů Atari)
- Turbo Monitor na kazetě nebo na cartridge
- Tabulka s kódy strojových instrukcí mikroprocesoru MOS 6502 (nebo několik dále popsaných menších tabulek, což je praktičtější)
- Mapa registrů čipů ANTIC, GTIA a POKEY (vývoj her) popř. výpis adres subrutin operačního systému a DOSu (vývoj uživatelského SW)
- Čtverečkovaný papír A4+tužka – ukazuje se, že právě toto jsou nejdůležitější dva nástroje!
Kódy strojových instrukcí mikroprocesoru MOS 6502
Vzhledem k tomu, že Turbo Monitor neobsahuje plnohodnotný assembler, který by dokázal převádět symbolická jména instrukcí a symbolické adresy (návěští) do strojového kódu, je nutné tuto činnosti provádět ručně a vrátit se tak vlastně na začátek padesátých let minulého století, kdy ještě plnohodnotné assemblery vlastně neexistovaly. U moderních architektur mikroprocesorů, které obsahují tisíce různých variant instrukcí a nepřeberné množství jejich kódování (adresovací režimy, prefixy atd.), by takový přístup byl velmi složitý, ovšem osmibitový mikroprocesor MOS 6502 je v tomto ohledu mnohem jednodušší. Dokáže totiž vykonat pouze 56 strojových instrukcí a i když mnohé instrukce podporují různé adresovací režimy, je jejich kódování do určité míry pravidelné, což zjednodušuje ruční zápis strojového kódu.
Nejprve si uveďme seznam všech podporovaných instrukcí:
| # | Instrukce | Plné jméno | Popis |
|---|---|---|---|
| 1 | ADC | add with carry | součet hodnoty s akumulátorem (včetně příznaku přetečení) |
| 2 | SBC | subtract with carry | odečtení hodnoty od akumulátoru (včetně příznaku výpůjčky) |
| 3 | AND | and with accumulator | logické AND s akumulátorem |
| 4 | ORA | or with accumulator | logické OR s akumulátorem |
| 5 | EOR | exclusive or with accumulator | logické XOR s akumulátorem |
| 6 | INC | increment | zvýšení hodnoty v paměti o 1 (kupodivu nelze s akumulátorem, ovšem s pamětí ano) |
| 7 | INX | increment X | zvýšení hodnoty index registru X o 1 |
| 8 | INY | increment Y | zvýšení hodnoty index registru Y o 1 |
| 9 | DEC | decrement | snížení hodnoty v paměti o 1 (opět nelze s akumulátorem) |
| 10 | DEX | decrement X | snížení hodnoty index registru X o 1 |
| 11 | DEY | decrement Y | snížení hodnoty index registru Y o 1 |
| 12 | CMP | compare with accumulator | odečtení hodnoty od akumulátoru bez zápisu výsledku (nastavují se však příznaky) |
| 13 | CPX | compare with X | odečtení hodnoty od index registru X bez zápisu výsledku (nastavují se však příznaky) |
| 14 | CPY | compare with Y | odečtení hodnoty od index registru Y bez zápisu výsledku (nastavují se však příznaky) |
| 15 | BIT | bit test | logické AND bez uložení výsledků (změní se jen příznakové bity) |
| 16 | ASL | arithmetic shift left | aritmetický posun doleva o jeden bit |
| 17 | LSR | logical shift right | logický posun doprava o jeden bit (instrukce pro aritmetický posun doprava neexistuje) |
| 18 | ROL | rotate left | rotace doleva o jeden bit |
| 19 | ROR | rotate right | rotace doprava o jeden bit |
| 20 | JMP | jump | skok (existuje několik adresovacích režimů) |
| 21 | JSR | jump to subroutine | skok do podprogramu s uložením návratové adresy na zásobník |
| 22 | RTS | return from subroutine | návrat z podprogramu |
| 23 | RTI | return from interrupt | návrat z prerušovací rutiny |
| 24 | BCC | branch on carry clear | rozvětvení za podmínky C==0 |
| 25 | BCS | branch on carry set | rozvětvení za podmínky C==1 |
| 26 | BEQ | branch on equal (zero set) | rozvětvení za podmínky Z==1 |
| 27 | BNE | branch on not equal (zero clear) | rozvětvení za podmínky Z==0 |
| 28 | BPL | branch on plus (negative clear) | rozvětvení za podmínky N==0 |
| 29 | BMI | branch on minus (negative set) | rozvětvení za podmínky N==1 |
| 30 | BVC | branch on overflow clear | rozvětvení za podmínky O==0 |
| 31 | BVS | branch on overflow set | rozvětvení za podmínky O==1 |
| 32 | PHA | push accumulator on stack | uložení obsahu akumulátoru na zásobník |
| 33 | PHP | push processor status on stack | uložení obsahu registru s příznaky na zásobník |
| 34 | PLA | pull accumulator from stack | obnovení obsahu akumulátoru ze zásobníku |
| 35 | PLP | pull processor status from stack | obnovení obsahu registru s příznaky ze zásobníku |
| 36 | LDA | load accumulator | načtení hodnoty z paměti do akumulátoru |
| 37 | LDX | load X register | načtení hodnoty paměti do index registru X |
| 38 | LDY | load Y register | načtení hodnoty paměti do index registru Y |
| 39 | STA | store accumulator | uložení akumulátoru do paměti |
| 40 | STX | store X register | uložení index registru X do paměti |
| 41 | STY | store Y register | uložení index registru Y do paměti |
| 42 | TAX | transfer accumulator to X | přesun hodnoty z akumulátoru do index registru X |
| 43 | TAY | transfer accumulator to Y | přesun hodnoty z akumulátoru do index registru Y |
| 44 | TXA | transfer X to accumulator | přesun hodnoty z index registru X do akumulátoru |
| 45 | TYA | transfer Y to accumulator | přesun hodnoty z index registru Y do akumulátoru |
| 46 | TSX | transfer stack pointer to X | přesun ukazatele vrcholu zásobníku do index registru X |
| 47 | TXS | transfer X to stack pointer | přesun hodnoty z index registru X do ukazatele vrcholu zásobníku |
| 48 | CLC | clear carry flag | vynulování příznaku přenosu |
| 49 | SEC | set carry flag | nastavení příznaku přenosu |
| 50 | CLD | clear decimal mode | vynulování příznaku dekadického režimu |
| 51 | SED | set decimal mode | nastavení příznaku dekadického režimu |
| 52 | CLI | clear interrupt disable bit | vynulování bitu pro zákaz přerušení |
| 53 | SEI | set interrupt disable bit | nastavení bitu pro zákaz přerušení |
| 54 | CLV | clear overflow flag | vynulování příznaku přetečení (neexistuje však instrukce pro jeho nastavení) |
| 55 | NOP | no-op | přesun na další instrukci |
| 56 | BRK | force break | vyvolání přerušení, uložení návratové adresy na zásobník, uložení příznaků na zásobník |
Následuje seznam všech podporovaných adresovacích režimů. Právě adresovací režimy odlišují mikroprocesor MOS 6502 od naprosté většiny ostatních mikroprocesorů a umožňují použít odlišný styl programování založený na efektivním použití nulté stránky paměti a jednoho z index registrů (každý z nich má nepatrně odlišné možnosti). Existuje celkem třináct (!) adresovacích režimů, ovšem žádná instrukce nevyužívá všechny dostupné režimy. Některé adresovací režimy jsou určeny pouze pro skoky, další pro implicitní operandy atd.:
| # | Zápis | Název | Assembler | Stručný popis |
|---|---|---|---|---|
| 1 | A | accumulator | INS A | operandem je přímo akumulátor |
| 2 | abs | absolute | INS $LLHH | za instrukcí následuje šestnáctibitová adresa, na níž je operand uložen |
| 3 | abs,X | absolute, X-indexed | INS $LLHH,X | za instrukcí následuje šestnáctibitová adresa, která je přičtena k X |
| 4 | abs,Y | absolute, Y-indexed | INS $LLHH,Y | za instrukcí následuje šestnáctibitová adresa, která je přičtena k Y |
| 5 | # | immediate | INS #$BB | za instrukcí následuje bajt s konstantou |
| 6 | impl | implied | INS | operand je odvozen přímo z instrukce, například INX |
| 7 | ind | indirect | INS ($LLHH) | nepřímá adresace přes adresu uloženou za instrukcí (ta je ukazatelem), nepřímý skok |
| 8 | X,ind | X-indexed, indirect | INS ($LL,X) | efektivní adresa je spočtena z hodnoty uložené na (LL+X) |
| 9 | ind,Y | indirect, Y-indexed | INS ($LL),Y | efektivní adresa je spočtena z hodnoty uložené na LL, k výsledku se přičte Y |
| 10 | rel | relative | INS $BB | použito u relativních skoků; za instrukcí je jeden bajt reprezentující offset se znaménkem |
| 11 | zpg | zeropage | INS $LL | operand je uložen na nulté stránce na adrese LL |
| 12 | zpg,X | zeropage, X-indexed | INS $LL,X | operand je uložen na nulté stránce na adrese LL+X |
| 13 | zpg,Y | zeropage, Y-indexed | INS $LL,Y | operand je uložen na nulté stránce na adrese LL+Y |
Jsou kódy instrukcí zvoleny podle nějakého schématu?
V případě, že všechny možné existující kombinace instrukcí a adresovacích režimů zapíšeme do často používané mřížky 16×16 buněk, budeme moci zjistit, jestli jsou instrukce stejného typu seskupeny podle vyššího nibblu a/nebo nibblu nižšího (nibblem se označuje čtveřice bitů). Získáme tedy tabulku se všemi instrukčními kódy, které jsou na mikroprocesoru MOS 6502 podporovány. Mnohé instrukce ovšem (zcela pochopitelně) za instrukčním kódem očekávají další jeden až dva bajty s konstantou či adresou:
| Spodní nibble | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Horní nibble | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
| 0 | BRK impl | ORA X,ind | × | × | × | ORA zpg | ASL zpg | × | PHP impl | ORA # | ASL A | × | × | ORA abs | ASL abs | × |
| 1 | BPL rel | ORA ind,Y | × | × | × | ORA zpg,X | ASL zpg,X | × | CLC impl | ORA abs,Y | × | × | × | ORA abs,X | ASL abs,X | × |
| 2 | JSR abs | AND X,ind | × | × | BIT zpg | AND zpg | ROL zpg | × | PLP impl | AND # | ROL A | × | BIT abs | AND abs | ROL abs | × |
| 3 | BMI rel | AND ind,Y | × | × | × | AND zpg,X | ROL zpg,X | × | SEC impl | AND abs,Y | × | × | × | AND abs,X | ROL abs,X | × |
| 4 | RTI impl | EOR X,ind | × | × | × | EOR zpg | LSR zpg | × | PHA impl | EOR # | LSR A | × | JMP abs | EOR abs | LSR abs | × |
| 5 | BVC rel | EOR ind,Y | × | × | × | EOR zpg,X | LSR zpg,X | × | CLI impl | EOR abs,Y | × | × | × | EOR abs,X | LSR abs,X | × |
| 6 | RTS impl | ADC X,ind | × | × | × | ADC zpg | ROR zpg | × | PLA impl | ADC # | ROR A | × | JMP ind | ADC abs | ROR abs | × |
| 7 | BVS rel | ADC ind,Y | × | × | × | ADC zpg,X | ROR zpg,X | × | SEI impl | ADC abs,Y | × | × | × | ADC abs,X | ROR abs,X | × |
| 8 | × | STA X,ind | × | × | STY zpg | STA zpg | STX zpg | × | DEY impl | × | TXA impl | × | STY abs | STA abs | STX abs | × |
| 9 | BCC rel | STA ind,Y | × | × | STY zpg,X | STA zpg,X | STX zpg,Y | × | TYA impl | STA abs,Y | TXS impl | × | × | STA abs,X | × | × |
| A | LDY # | LDA X,ind | LDX # | × | LDY zpg | LDA zpg | LDX zpg | × | TAY impl | LDA # | TAX impl | × | LDY abs | LDA abs | LDX abs | × |
| B | BCS rel | LDA ind,Y | × | × | LDY zpg,X | LDA zpg,X | LDX zpg,Y | × | CLV impl | LDA abs,Y | TSX impl | × | LDY abs,X | LDA abs,X | LDX abs,Y | × |
| C | CPY # | CMP X,ind | × | × | CPY zpg | CMP zpg | DEC zpg | × | INY impl | CMP # | DEX impl | × | CPY abs | CMP abs | DEC abs | × |
| D | BNE rel | CMP ind,Y | × | × | × | CMP zpg,X | DEC zpg,X | × | CLD impl | CMP abs,Y | × | × | × | CMP abs,X | DEC abs,X | × |
| E | CPX # | SBC X,ind | × | × | CPX zpg | SBC zpg | INC zpg | × | INX impl | SBC # | NOP impl | × | CPX abs | SBC abs | INC abs | × |
| F | BEQ rel | SBC ind,Y | × | × | × | SBC zpg,X | INC zpg,X | × | SED impl | SBC abs,Y | × | × | × | SBC abs,X | INC abs,X | × |
Rozdělení instrukcí do tří hlavních skupin
Výše uvedená tabulka naznačuje, že kódy nebyly instrukcím přiřazeny zcela náhodně, ale že zde skutečně existují nějaká pravidla. Je to ostatně logické, protože pravidelná a do určité míry ortogonální instrukční sada umožňuje zjednodušit dekodér instrukcí (ten na MOS 6502 zabírá přibližně jednu třetinu plochy čipu). Z výše uvedené tabulky lze tato pravidla odvodit. Informace uvedené ve zbytku této kapitoly byly získány především ze stránky The 6502/65C02/65C816 Instruction Set Decoded, která se zabývá (a to do všech detailů) právě tímto tématem.
Operační kód instrukce, což je vždy jen jeden bajt, lze reprezentovat třemi skupinami bitů aaa, bbb a cc, které tvoří výsledný bajt ve formátu aaabbbcc. Bity ze skupin aaa a cc určují typ instrukce (například LDA), zatímco bity ze skupiny bbb slouží ke specifikaci adresního režimu (například (ZP,X)).
Na základě hodnoty bitů ze skupiny cc je možné instrukce rozdělit do tří množin (teoretická čtvrtá množina je prázdná):
| cc | |
|---|---|
| 01 | operace s akumulátorem |
| 10 | operace s index registry, bitové posuny a rotace |
| 00 | operace s index registry, skoky |
| 11 | nepoužito |
Odlišné kódování mají pouze instrukce pro podmíněné skoky. Bity instrukčního kódu mají vzorek xxy10000, kde xx určuje příznakový bit a y určuje, zda se skok provede v případě, že je příznakový bit vynulován či naopak nastaven.
Tabulky instrukcí rozdělených do logických skupin
Pro programátora, který používá Turbo Monitor, nemusí být výše uvedená tabulka se všemi instrukcemi příliš přehledná. Praktičtější je instrukce rozdělit logicky, ideálně na základě bitů ze skupiny cc zmíněné v předchozím textu, do několika menších tabulek, které vlastně mají oproti předchozí tabulce „inverzní“ strukturu (v původní tabulce jsou uloženy kódy instrukcí, kdežto typ instrukce a adresovací režim je zapsán v hlavičkách sloupců a řádků). Výsledky mohou vypadat následovně:
Operace s akumulátorem, tedy instrukce s bity cc nastavenými na hodnotu 01, mají ortogonální strukturu a tabulka tak pokrývá největší skupinu instrukcí, které podporují shodné adresovací režimy:
| Adr/Instrukce | ORA | AND | EOR | ADC | STA | LDA | CMP | SBC |
|---|---|---|---|---|---|---|---|---|
| (zp,X) | 01 | 21 | 41 | 61 | 81 | A1 | C1 | E1 |
| zp | 05 | 25 | 45 | 65 | 85 | A5 | C5 | E5 |
| # | 09 | 29 | 49 | 69 | A9 | C9 | E9 | |
| abs | 0D | 2D | 4D | 6D | 8D | AD | CD | ED |
| (zp),Y | 11 | 31 | 51 | 71 | 91 | B1 | D1 | F1 |
| zp,X | 15 | 35 | 55 | 75 | 95 | B5 | D5 | F5 |
| abs,Y | 19 | 39 | 59 | 79 | 99 | B9 | D9 | F9 |
| abs,X | 1D | 3D | 5D | 7D | 9D | BD | DD | FD |
Operace s index registry, bitové posuny a rotace:
| Adr/Instrukce | ASL | ROL | LSR | ROR | STX | LDX | DEC | INC |
|---|---|---|---|---|---|---|---|---|
| zp | 06 | 26 | 46 | 66 | 86 | A6 | C6 | E6 |
| A | 0A | 2A | 4A | 6A | 8A* | AA** | ||
| abs | 0E | 2E | 4E | 6E | 8E | AE | CE | EE |
| zp,X/zp,Y | 16 | 36 | 56 | 76 | 96 | B6 | D6 | F6 |
| abs,X/abs,Y | 1E | 3E | 5E | 7E | BE | DE | FE |
Operace s index registry a skoky:
| Adr/Instrukce | BIT | JMP | JMP() | STY | LDY | CPY | CPX |
|---|---|---|---|---|---|---|---|
| zp | 24 | 84 | A4 | C4 | E4 | ||
| abs | 2C | 4C | 6C | 8C | AC | CC | EC |
| zp,X | 94 | B4 | |||||
| abs,X | BC |
Instrukce skoků s podmínkou:
| Instrukce | Kód |
|---|---|
| BPL | 10 |
| BMI | 30 |
| BVC | 50 |
| BVS | 60 |
| BCC | 90 |
| BCS | B0 |
| BNE | D0 |
| BEQ | F0 |
Zbylé instrukce, které nezapadají do žádného schématu:
| Instrukce | Kód |
|---|---|
| BRK | 00 |
| JSR | 20 |
| RTI | 40 |
| RTS | 60 |
| PHP | 08 |
| PLP | 28 |
| PHA | 48 |
| PLA | 68 |
| DEY | 88 |
| TAY | A8 |
| INY | C8 |
| INX | E8 |
| CLC | 18 |
| SEC | 38 |
| CLI | 58 |
| SEI | 78 |
| TYA | 98 |
| CLV | B8 |
| CLD | D8 |
| SED | F8 |
| TXA | 8A |
| TXS | 9A |
| TAX | AA |
| TSX | BA |
| DEX | CA |
| NOP | EA |
Prostředí Turbo Monitoru
Nyní již máme k dispozici tabulky instrukcí, takže je možné se začít zabývat samotným Turbo Monitorem. Po jeho spuštění by měla obrazovka tohoto nástroje vypadat následovně:
Obrázek 1: Úvodní obrazovka Turbo Monitoru.
Ovládání Turbo Monitoru
Ovládání Turbo Monitoru se provádí jednopísmennými příkazy popř. kombinací Ctrl+klávesa. Příkazy lze rozdělit na práci se zařízeními a práci s pamětí:
| Příkaz | Provedená operace |
|---|---|
| L | načtení z kazety standardní rychlostí |
| S | zápis na kazetu standardní rychlostí |
| A | opakování S (SAVE) |
| O | načtení programu (Turbo) |
| I | načtení bloku (Turbo) |
| T | uložení programu (Turbo) |
| R | opakování T (TSAVE) |
| Z | ovládání motoru magnetofonu |
| W | načtení ze sektorů diskety |
| Ctrl+W | uložení na disketu po sektorech |
| E | přepnutí pro práci s RAMdiskem a nikoli disketou |
| H | přepínání číselné soustavy HEX/DEC |
| U | spuštění programu od zadané adresy |
| G | spuštění programu od zadané adresy, RTS nebo BRK se vrátí řízení do monitoru |
| X | přesun bloku paměti |
| F | vyhledávání v paměti |
| D | disassembler |
| Ctrl+D | celoobrazovkový disassembler |
| M | monitor |
| Q | přesun programu s přepočtem adres |
| P | výpis obsahu paměti |
| Ctrl+P | disassembling na zvolené zařízení |
Prohlížení obsahu paměti: monitor a disassembler
Turbo monitor umožňuje prohlížení obsahu paměti, a to několika způsoby. Pro programátory a hackery (v původním smyslu tohoto slova) je nejužitečnější výpis paměti zkombinovaný se zpětným dekódováním instrukcí. Po zadání příkazu D (disassemble) se monitor zeptá na počáteční adresu (opět – pozor na rozdíl mezi decimálními a hexadecimálními adresami) a poté se po každém stisku Enteru vypíše další instrukce:
Obrázek 2: Prohlížení obsahu paměti se zpětným dekódováním instrukcí.
Snadno si ověříme, že šestá paměťová stránka je prázdná a můžeme do ní zapisovat naše demonstrační příklady:
Obrázek 3: Prohlížení obsahu paměti se zpětným dekódováním instrukcí.
Zápis strojového kódu do operační paměti v monitoru
Druhý způsob výpisu obsahu paměti je spuštěn příkazem M neboli monitor. Opět je nutné zadat počáteční adresu. Monitor postupně vypisuje obsah paměti a očekává buď potvrzení klávesou Enter nebo zadání nových hodnot, které se ihned do paměti zapíšou. Nejprve si ukažme pouhý výpis obsahu paměti:
Obrázek 4: Prohlížení obsahu paměti odlišným způsobem (hexa výpis) s možností úpravy hodnot.
Nyní se dostáváme k prvnímu demonstračnímu příkladu. V něm se pouze nastavuje barva pozadí textu na černou. V assembleru vypadá výpis takového kódu jednoduše:
*= $0600 LDA #0 STA 710 BRK
Ovšem my dnes nemáme k dispozici assembler, ale jen monitor. Musíme tedy s využitím tabulek zmíněných výše provést ruční převod symbolických jmen instrukcí do strojového kódu:
A9 00 LDA #0 8D C6 02 STA 710 00 BRK
Pouze strojový kód tedy vypadá takto:
A9 00 8D C6 02 00
A přesně tuto sekvenci bajtů (tak jak je uvedena, tedy ve stejném pořadí) zapíšeme v režimu monitoru na adresy $0600 až $0605:
Obrázek 5: Zápis strojového kódu v monitoru.
Spuštění první verze programu
Stiskneme Reset pro ukončení režimu monitoru a výběrem příkazu D (disassembler) a zadáním adresy 0600 si ověříme, že zapsaná sekvence šesti bajtů skutečně odpovídá požadovaným instrukcím:
Obrázek 6: Kontrolní výpis kódu, který jsme do paměti zapsali.
Výborně, vše se zdá být zcela korektní. Proto opustíme režim disassembleru (například opět Resetem) a příkazem G provedeme spuštění programu zapsaného v šesté stránce paměti. Program je sice ukončen (konkrétně v našem případě instrukcí BRK), ale obrazovka se zdánlivě vůbec nezměnila – pozadí je stále zelené a nikoli černé. A po dokončení programu se vypíše obsah všech registrů. Z tohoto výpisu lze ověřit, že program skončil na adrese $0605 (správně) a akumulátor A je vynulován (opět správně):
Obrázek 7: Po spuštění programu příkazem G se vypíšou obsahy registrů, ale zdánlivě se nic dalšího nestalo.
Úprava programu: přidání nekonečné programové smyčky
Program ve skutečnosti změnil barvu pozadí, ovšem po dokončení jeho běhu si Turbo monitor obnovil vlastní display list i obsah barvových registrů. To je rozumné, protože například při ladění hry je vhodné se vždy vrátit k čitelnému textovému režimu. Ovšem jak dosáhnout toho, aby byly změny provedené programem skutečně vizuálně viditelné? Je to snadné – program zacyklíme – nikdy se tedy nevykoná instrukce BRK. Pro jednoduchost použijeme instrukci nepodmíněného skoku JMP a (opět pro jednoduchost – nechce se mi provádět výpočty adres) budeme skákat na začátek programu, tedy na adresu $0600:
LDA #0 STA 710 JMP 0600
Ruční překlad do assembleru (pozor na zápis adresy dolní/horní bajt):
A9 00 LDA #0 8D C6 02 STA 710 4C 00 06 JMP 0600
Výsledný strojový kód:
A9 00 8D C6 02 4C 00 06
Opět se přepneme do režimu monitoru a program přepíšeme novými instrukcemi:
Obrázek 8: Takto vypadá náš kód s přidaným nepodmíněným skokem.
Disassemblerem si ověříme, že byly jak kódy instrukcí, tak i konstanta a dvě adresy zapsány korektně:
Obrázek 9: Kontrolní výpis upraveného programu.
Spuštění upraveného programu
Po spuštění programu příkazem G 0600 by se skutečně měla změnit barva pozadí obrazovky. Následně se program zacyklí a vzhledem k tomu, že nikdy neprovede instrukci BRK, zůstane obrazovka v tomto stavu:
Obrázek 10: Po spuštění upraveného programu se už "něco děje".
Výpočet adres pro podmíněné skoky
V předchozím demonstračním příkladu jsem schválně použil instrukci pro absolutní nepodmíněný skok JMP, protože bylo dopředu známé, na jakou adresu se skok provede. Ovšem všechny skoky s podmínkou (je jich celkem osm, vždy dvě instrukce pro každý příznak) používají relativní adresu:
| Instrukce | Kód |
|---|---|
| BPL | 10 |
| BMI | 30 |
| BVC | 50 |
| BVS | 60 |
| BCC | 90 |
| BCS | B0 |
| BNE | D0 |
| BEQ | F0 |
Tato adresa je uložena v jediném bajtu, který reprezentuje osmibitovou hodnotu se znaménkem, tj. v rozsahu –128 až 127. Ovšem otázkou zůstává, od jaké přesné adresy se tato relativní adresa počítá? Každá architektura mikroprocesorů k tomuto problému přistupuje různě – například se provádí výpočet od adresy instrukce skoku nebo dokonce od adresy za kódem této instrukce. V případě mikroprocesoru MOS 6502 je situace snadnější – adresa se počítá od instrukce, která následuje za instrukcí podmíněného skoku.
Co to znamená v praxi?:
- Bajty F0 00 představují instrukci BEQ, která v případě splnění podmínky skočí na instrukci zapsanou ihned za skokem (a jedná se tedy o poněkud divně zapsanou instrukci NOP).
- Bajty 90 01 představují instrukci BCC, která v případě splnění podmínky přeskočí bajt následující za touto instrukcí. Pokud podmínka není splněna, je tento bajt přečten a příslušná instrukce je vykonána.
- Bajty F0 FE představují instrukci BEQ, která v případě splnění podmínky zopakuje sebe samu – a vstoupí tak do nekonečné smyčky. FE totiž znamená offset –2 a instrukce BEQ má přesně délku dvou bajtů.
Příklad s podmíněnými skoky
Pokusme se nyní demonstrační příklad upravit do takové podoby, aby se v něm použila dvojice podmíněných skoků. První z těchto instrukcí provádí skok vpřed – přeskočí se instrukce BRK (ta by totiž program ihned ukončila). A druhá z těchto instrukcí realizuje nekonečnou smyčku, tj. skok se provede na tu samou instrukci (opět: pokud by tato část byla napsána špatně, provede se BRK). Vycházím se přitom z faktu, že do akumulátoru je vložena nulová hodnota a tím pádem instrukce LDA nastaví příznak zero na jedničku (pozor – to je specifikum čipu MOS 6502; další mikroprocesory se takto nechovají):
CLC
BCC NEXT
BRK
NEXT LDA #0
STA 710
REPT BEQ 60
BRK
Ruční překlad do strojového kódu:
18 CLC 90 01 BCC NEXT 00 BRK A9 00 NEXT LDA #0 8D C6 02 STA 710 F0 FE REPT BEQ 60 00 BRK
Pouze strojový kód:
18 90 01 00 A9 00 8D C6 02 F0 FE 00
Program ve strojovém kódu přepíšeme v monitoru do paměti:
Obrázek 11: Zápis strojových instrukcí nového programu s instrukcemi pro podmíněné skoky.
A disassemblerem zkontrolujeme výsledek, zejména to, zda skoky používají korektní cílové adresy (disassembler je dokáže spočítat):
Obrázek 12: Disassembling testovacího programu s instrukcemi pro podmíněné skoky.
Spuštění příkladu a ověření v emulátoru, že je příklad vykonáván korektně
Takto upravený demonstrační příklad spustíme běžným způsobem, tj. s využitím G 0600 (opět: pozor na hexadecimální zápis adresy). Program by nikdy neměl vykonat instrukci BRK, ale měl by skončit v nekonečné smyčce. Obrazovka by měla zčernat a vypadat následovně:
Obrázek 13: Po spuštění opět uvidíme tuto obrazovku. To je důkaz, že se neprovedla žádná z instrukcí BRK.
Jak si však u programu běžícího v nekonečné smyčce ověříme, co vlastně provádí? Pokud program spouštíme v emulátoru počítačů Atari, je to relativně snadné. Klávesou F8 přejdeme do monitoru, ovšem nikoli do Turbo Monitoru, ale do monitoru, který je zabudován přímo v emulátoru. Měla by se zobrazit informace o tom, že se provádí instrukce BEQ $0609 umístěná na stejné adrese:
0 2 A=00 X=FF Y=04 S=FB P=--*---Z- PC=0609: F0 FE BEQ $0609
Příkazem d 0600 (vidíte tu podobnost s Turbo Monitorem?) si necháme zobrazit instrukce uložené od adresy $0600:
> d 0600 0600: 18 CLC 0601: 90 01 BCC $0604 0603: 00 BRK 0604: A9 00 LDA #$00 0606: 8D C6 02 STA $02C6 ;COLOR2 0609: F0 FE BEQ $0609 060B: 00 BRK 060C: 00 BRK 060D: 00 BRK 060E: 00 BRK 060F: 00 BRK 0610: 00 BRK
Vše se tedy zdá být v naprostém pořádku.
Disassembling subrutin uložených v paměti ROM
Vzhledem k tomu, že při práci Turbo Monitoru je ROM „přiklopena“ (je namapována do adresového prostoru čipu MOS 6502), můžeme si prohlédnout různé subrutiny, které jsou zde uloženy. Příkladem je subrutina pro kreslení využívaná mj. i Atari BASICem:
Obrázek 14: Prohlížení části ROM, zde konkrétně subrutin pro kreslení,
Disassembling s výstupem na zvolené zařízení
Jen v krátkosti se podívejme na způsob disassemblingu části paměti s tím, že se výsledek nezobrazí přímo na obrazovce, ale pošle se do zvoleného zařízení:
Obrázek 15: Export (zápis, tisk) disassemblované části RAM či ROM na zvolené zařízení.
Výsledkem bude soubor s následujícím obsahem, který ukazuje limity tohoto přístupu (neznámé instrukce atd.):
$FCFC: STA $3F $85 $3F $FCFE: LDA #$01 $A9 $01 $FD00: JSR $FDFC $20 $FC $FD $FD03: BMI $FD2E $30 $29 $FD05: LDA #$34 $A9 $34 $FD07: STA $D302 $8D $02 $D3 $FD0A: LDX $62 $A6 $62 $FD0C: LDY $FE93,X $BC $93 $FE $FD0F: LDA $FE91,X $BD $91 $FE $FD12: TAX $AA $FD13: ??? $F2 $FD14: TAY $A8 $FD15: STA $022A $8D $2A $02 $FD18: JSR $E45C $20 $5C $E4 $FD1B: LDA $022A $AD $2A $02 $FD1E: BNE $FD1B $D0 $FB $FD20: LDA #$80 $A9 $80 $FD22: STA $3D $85 $3D $FD24: STA $028A $8D $8A $02 $FD27: JMP $FD77 $4C $77 $FD $FD2A: LDY #$80 $A0 $80 $FD2C: DEC $11 $C6 $11 $FD2E: LDA #$00 $A9 $00 $FD30: STA $0289 $8D $89 $02 $FD33: RTS $60 $FD34: LDA #$80 $A9 $80 $FD36: STA $0289 $8D $89 $02 $FD39: LDA #$02 $A9 $02 $FD3B: JSR $FDFC $20 $FC $FD $FD3E: BMI $FD2E $30 $EE $FD40: LDA #$CC $A9 $CC $FD42: STA $D204 $8D $04 $D2 $FD45: LDA #$05 $A9 $05 $FD47: STA $D206 $8D $06 $D2 $FD4A: LDA #$60 $A9 $60 $FD4C: STA $0300 $8D $00 $03 $FD4F: JSR $E468 $20 $68 $E4 $FD52: LDA #$34 $A9 $34 $FD54: STA $D302 $8D $02 $D3 $FD57: LDX $62 $A6 $62 $FD59: LDY $FE8F,X $BC $8F $FE $FD5C: LDA $FE8D,X $BD $8D $FE $FD5F: TAX $AA $FD60: ??? $F2 $FD61: LDA #$20 $A9 $20 $FD63: ??? $5C $FD64: CPX $A9 $E4 $A9 $FD66: ??? $FF $FD67: STA $022A $8D $2A $02 $FD6A: LDA $11 $A5 $11 $FD6C: BEQ $FD2A $F0 $BC $FD6E: LDA $022A $AD $2A $02 $FD71: BNE $FD6A $D0 $F7 $FD73: LDA #$00 $A9 $00 $FD75: STA $3D $85 $3D $FD77: LDY #$01 $A0 $01 $FD79: RTS $60 $FD7A: LDA $3F $A5 $3F $FD7C: BMI $FDB1 $30 $33 $FD7E: LDX $3D $A6 $3D $FD80: CPX $028A $EC $8A $02 $FD83: BEQ $FD8D $F0 $08 $FD85: LDA $0400,X $BD $00 $04 $FD88: INC $3D $E6 $3D $FD8A: LDY #$01 $A0 $01 $FD8C: RTS $60 $FD8D: LDA #$52 $A9 $52 $FD8F: JSR $FE3F $20 $3F $FE $FD92: TYA $98 $FD93: BMI $FD8C $30 $F7 $FD95: LDA #$00 $A9 $00 $FD97: STA $3D $85 $3D $FD99: LDX #$80 $A2 $80 $FD9B: LDA $03FF $AD $FF $03 $FD9E: CMP #$FE $C9 $FE $FDA0: BEQ $FDAF $F0 $0D $FDA2: CMP #$FA $C9 $FA $FDA4: BNE $FDA9 $D0 $03 $FDA6: LDX $047F $AE $7F $04 $FDA9: STX $028A $8E $8A $02 $FDAC: JMP $FD7A $4C $7A $FD $FDAF: DEC $3F $C6 $3F $FDB1: LDY #$88 $A0 $88 $FDB3: RTS $60 $FDB4: LDX $3D $A6 $3D $FDB6: STA $0400,X $9D $00 $04 $FDB9: INC $3D $E6 $3D $FDBB: LDY #$01 $A0 $01 $FDBD: CPX #$7F $E0 $7F $FDBF: BEQ $FDC2 $F0 $01 $FDC1: RTS $60 $FDC2: LDA #$FC $A9 $FC $FDC4: JSR $FE7C $20 $7C $FE $FDC7: LDA #$00 $A9 $00 $FDC9: STA $3D $85 $3D $FDCB: RTS $60 $FDCC: LDY #$01 $A0 $01 $FDCE: RTS $60 $FDCF: LDA $0289 $AD $89 $02 $FDD2: BMI $FDDC $30 $08 $FDD4: LDY #$01 $A0 $01 $FDD6: LDA #$3C $A9 $3C $FDD8: STA $D302 $8D $02 $D3 $FDDB: RTS $60 $FDDC: LDX $3D $A6 $3D $FDDE: BEQ $FDEA $F0 $0A $FDE0: STX $047F $8E $7F $04 $FDE3: LDA #$FA $A9 $FA $FDE5: JSR $FE7C $20 $7C $FE $FDE8: BMI $FDD6 $30 $EC $FDEA: LDX #$7F $A2 $7F $FDEC: LDA #$00 $A9 $00 $FDEE: STA $0400,X $9D $00 $04 $FDF1: DEX $CA $FDF2: BPL $FDEE $10 $FA $FDF4: LDA #$FE $A9 $FE $FDF6: JSR $FE7C $20 $7C $FE $FDF9: JMP $FDD6 $4C $D6 $FD $FDFC: STA $40 $85 $40 $FDFE: LDA $14 $A5 $14 $FE00: CLC $18
Závěr
Podle názoru autora článku není vývoj aplikací přímo v monitoru prakticky možný, alespoň ne pro běžného vývojáře (zcela jistě se ovšem najdou geniální výjimky). Jakýkoli program delší než cca padesát strojových instrukcí je tedy mnohem lepší nejdříve navrhnout na papíře, provést zde první ruční refaktoring, přepsat výsledek refaktoringu do monitoru (resp. do paměti), uložit na paměťové médium, spustit a ověřit funkcionalitu (samozřejmě lze vynechat fázi ukládání, ostatně co by se tak mohlo pokazit, že?). Výsledkem mnohdy bývá strojový kód s mnoha „poslepovanými“ částmi (vyplněné NOPy atd.), ten je ovšem většinou možné v poslední fázi (zcela mechanicky) přepsat do strukturovanější podoby. Samotné ladění spočívá ve vkládání instrukcí BRK na místa, ve kterých by se v debuggeru použily breakpointy. Taktéž se tedy jedná o poměrně hardcore přístup, ovšem stále může být (pro jednodušší programy i hry) udržitelný.
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/issue_02/page_10.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 - SIO2PC
http://raster.atariportal.cz/hw/sio2pc.htm - Using Serial Input/Output
https://www.atarimagazines.com/compute/issue64/insight_atari.php - SIO ROUTINES
https://www.atariarchives.org/mmm/sio.php - ATARI 8 bit Serial Input/Output (SIO)
https://allpinouts.org/pinouts/connectors/serial/atari-8-bit-serial-input-output-sio/ - SIOspecs
https://archive.org/details/SIOspecs - Interview with Joe Decuir
http://www.atarimuseum.com/articles/joedecuir.html - USB AND OS/2
http://www.os2voice.org/VNL/past_issues/VNL1199H/vnewsf5.htm - Standards and specs: The ins and outs of USB
http://www.ibm.com/developerworks/power/library/pa-spec7.html - USB Class Codes
http://www.usb.org/developers/defined_class - Universal Serial Bus
http://en.wikipedia.org/wiki/Usb - SMBus.org
http://www.smbus.org - SMBus FAQ
http://www.smbus.org/faq/faq.htm - System Management Bus
http://en.wikipedia.org/wiki/System_Management_Bus - SMBus Specifications
http://www.smbus.org/specs - SMBus Control Method Interface Specification Version 1.0
http://www.smbus.org/specs/smbus_cmi10.pdf - System Management Bus (SMBus) Specification Version 2.0
http://www.smbus.org/specs/smbus20.pdf - Atari Connection Machine
http://acm.atariportal.cz/s/con8bit/8bits - Atari Program Recorder
https://en.wikipedia.org/wiki/Atari_Program_Recorder - THE ATARI CASSETTE
https://www.atariarchives.org/dere/chaptC.php - Turbo-2000
https://www.atari-800.cz/turbo-2000 - Atari XC12 FSK Decoder
https://zeninstruments.blogspot.com/2024/08/atari-xc12-fsk-decoder.html - Clock recovery
https://en.wikipedia.org/wiki/Clock_recovery - Jiří Richter (programátor)
https://cs.wikipedia.org/wiki/Ji%C5%99%C3%AD_Richter_(program%C3%A1tor) - Spectrum tape interface
https://sinclair.wiki.zxnet.co.uk/wiki/Spectrum_tape_interface - How Tape Loading Works
https://lemmings.info/how-tape-loading-works/ - CIO Reference
https://atarifoundry.com/docs/forge/advanced-topics/cio-reference - The central input-output system in Atari computers
https://www.atariarchives.org/alp/chapter_9.php - ATR: chpt.1: Central Input/Output (CIO)
https://www.atariarchives.org/cfn/12/02/0010.php - NOTE and POINT
https://atarifoundry.com/docs/forge/advanced-topics/note-point - Customizing the Atari Operating System Device Handlers: Part 1
https://www.atarimagazines.com/v8n2/customhandlers.html - Customizing the Atari Operating System Device Handlers: Part II
https://www.atarimagazines.com/v8n3/OperatingSystemHandlers.html - Spectrum tape interface
https://sinclair.wiki.zxnet.co.uk/wiki/Spectrum_tape_interface - Turbo 2000
http://wiki.oldcomp.cz/index.php?title=Turbo_2000 - Původní manuál k systému Turbo 2000
http://wiki.oldcomp.cz/images/6/65/Zak_praha_1987_p2_turbo2000.pdf - Mark and space
https://en.wikipedia.org/wiki/Mark_and_space - Obdélníkový průběh
https://cs.wikipedia.org/wiki/Obd%C3%A9ln%C3%ADkov%C3%BD_pr%C5%AFb%C4%9Bh - All you ever wanted to know about: Atari Cartridges
https://atariwiki.org/wiki/Wiki.jsp?page=Cartridges - Manuál k MAC/65
https://data.atariwiki.org/DOC/MAC-65_and_BUG-65_Rev._1.1-OSS-OCR.pdf - Manuál k DDT
https://atariwiki.org/wiki/attach/DDT/DDT_1.0_Manual.pdf - MAC/65 na Atari wiki
https://www.atariwiki.org/wiki/Wiki.jsp?page=Mac65 - Madass – home page
https://mads.atari8.info/ - Mad-Assembler (GitHub)
https://github.com/tebe6502/Mad-Assembler - The 6502/65C02/65C816 Instruction Set Decoded
https://llx.com/Neil/a2/opcodes.html
