Na předchozí dvojici článků o subsystému CIO implementovaného ve všech osmibitových mikropočítačích Atari dnes navážeme. Ukážeme si, jakým způsobem je možné realizovat vlastní sofistikované zařízení, zaregistrovat toto zařízení pod vhodným písmenem a volat jeho operace z Atari BASICu.
Na předchozí dvojici článků o subsystému CIO (Central Input/Output) implementovaného ve všech osmibitových domácích mikropočítačích Atari [1] [2] dnes navážeme a celé téma dokončíme. Ukážeme si totiž, jakým způsobem je možné realizovat vlastní zařízení, zaregistrovat toto zařízení pod vhodným písmenem a volat jednotlivé operace přes CIO (OPEN, CLOSE, PUT, GET, XIO). Začneme tím nejjednodušším možným zařízením pojmenovaným N:, které neprovádí žádné konkrétní operace – pouze pro všechny operace vrací stav „OK“. Variantou bude stejné „nulové“ zařízení, které ovšem bude vracet chyby pro vybrané operace. Třetí zařízení, které v rámci dnešního článku naimplementujeme, bude pouze vypisovat, které operace byly zavolány, takže si budeme moci ověřit, jak vlastně CIO (a IOCB) reálně pracuje.
Ovšem nejzajímavější a taktéž nejkomplexnější bude implementace čtvrtého typu zařízení, které je nazváno G: (od slov GTIA resp. graphics). Přes toto zařízení bude možné ovládat sprite zobrazený na obrazovce. Celkem bude možné vytvořit čtyři zařízení G:, každé pro jeden sprite (pro jednoduchost nejsou podporovány střely, to by celý program prodloužilo, ovšem nepřineslo žádné nové informace o CIO). Jakákoli operace se zařízením tedy bude ihned vizualizována. Toto zařízení bude otestováno programem psaným v Atari BASICu.
Základ pro další demonstrační příklady: provedení zápisu s kontrolou, zda byl zápis proveden korektněV dalších kapitolách bude ukázána implementace a registrace nových zařízení. Základní operace, které tato zařízení implementují (nebo naopak neimplementují) budou testovány jednoduchým programem napsaným v assembleru, který provede následující operace:
Pokud je operace zápisu provedena bez chyby, obrazovka zezelená:
Obrázek 1: Korektně provedená operace zápisu bajtu je signalizována zelenou obrazovkou.
Autor: tisnik, podle licence: Rights Managed
Ovšem v případě, že naopak dojde k chybě při zápisu, bude obrazovka tmavě červená:
Obrázek 2: Nekorektně provedená operace zápisu bajtu je signalizována červenou obrazovkou.
Autor: tisnik, podle licence: Rights Managed
Celý zdrojový kód takto navrženého příkladu vypadá následovně:
.include "atari.inc"
.CODE
IOCB4 = $40 ; offset odvozený od čísla IOCB kanálu (nutno násobit šestnácti)
; ---------------------------------------------------------------------
; hlavní program
; ---------------------------------------------------------------------
.proc main
jsr CLOSE_CHANNEL_4
jsr OPEN_CHANNEL_4_AS_H
jsr WRITE_BYTE_CHANNEL_4
jsr CHECK_ERROR
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; uzavření kanálu číslo 4
; ---------------------------------------------------------------------
.proc CLOSE_CHANNEL_4
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda #CLOSE ; prováděná operace
sta ICCOM, x ; zápis prováděné operace do bloku IOCB
jsr CIOV ; zavolání rutiny pro CIO
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; otevření kanálu číslo 4 v režimu zápisu na zařízení H:test.txt
; ---------------------------------------------------------------------
.proc OPEN_CHANNEL_4_AS_H
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda #OPEN ; prováděná operace
sta ICCOM, x ; zápis prováděné operace do bloku IOCB
; specifikace jména zařízení + jména souboru (16bitový ukazatel)
lda #<device_name
sta ICBAL, x
lda #>device_name
sta ICBAH, x
lda #OPNOT ; režim otevření: zápis (odpovídá druhému parametru příkazu OPEN v BASICu)
sta ICAX1, x
lda #0 ; odpovídá třetímu parametru příkazu OPEN v BASICu
sta ICAX2, x
jsr CIOV ; zavolání rutiny pro CIO
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; zápis jednoho znaku do kanálu číslo 4
; ---------------------------------------------------------------------
.proc WRITE_BYTE_CHANNEL_4
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda #PUTCHR ; prováděná operace
sta ICCOM, x ; zápis prováděné operace do bloku IOCB
; adresa zapisovaného bloku
lda #<char_to_put
sta ICBAL, x
lda #>char_to_put
sta ICBAH, x
; nastavení počtu zapisovaných bajtů
lda #1
sta ICBLL, x
lda #0
sta ICBLH, x
jsr CIOV ; zavolání rutiny pro CIO
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; kontrola chyby při zápisu
; ---------------------------------------------------------------------
.proc CHECK_ERROR
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda ICSTA, x ; status poslední operace
bpl ok ; test bitu 7
lda #18 ; kod barvy
sta COLOR2 ; ulozit do registru COLOR2
rts ; návrat ze subrutiny
ok:
lda #$c3 ; kod barvy
sta COLOR2 ; ulozit do registru COLOR2
rts ; návrat ze subrutiny
.endproc
; jméno zařízení (+ jméno souboru) ukončené nulou
device_name: .byte "H:TEST.TXT", 0
; zapisovaný bajt
char_to_put: .byte $41
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
Registrace a implementace vlastního zařízení s využitím subsystému CIO
Systém CIO nabízí poměrně jednoduchý způsob pro implementaci a registraci vlastního zařízení. Každé zařízení musí implementovat podprogramy (subrutiny) volané pro jednotlivé operace OPEN, CLOSE, PUT, GET, XIO a INIT. Adresy těchto subrutin jsou uloženy ve formě tabulky vektorů (je to ovšem nepatrně složitější kvůli způsobu volání těchto subrutin). A navíc je nutné adresu této tabulky vektorů uložit, společně s názvem zařízení, do HATABS (Handler Address Table). Jak již víme z předchozího článku, obsahuje tato tabulka pro každé zaregistrované zařízení tři bajty: jméno zařízení (jeden znak) a adresu tabulky vektorů. Jakmile je tabulka naplněna (resp. doplněna) o nové zařízení a do paměti jsou nahrány jeho ovladače (tj. ony handlery resp. subrutiny), může prakticky jakýkoli program začít toto zařízení používat.
Nejprve si ukážeme způsob implementace toho nejjednoduššího zařízení nazvaného „null device“ a označeného písmenem N:. Toto zařízení podporuje všechny operace, ovšem jejich implementace je „nulová“ – pouze se vrátí kód „provedeno bez chyby“.
Implementace „nulového zařízení“ v Atari BASICuVzhledem k tomu, že osmibitové domácí mikropočítače Atari jsou z pohledu IT dávnou minulostí, podívejme se na ukázkový dobový program s implementací nulového zařízení. Tento program je napsaný v Atari BASICu a po jeho spuštění se zaregistruje nové zařízení nazvané N:
100 REM NULL HANDLER INSTALLATION 110 REM BY BOB MARTIN 120 REM (c)1989,ANTIC PUBLISHING INC. 150 RESTORE :TRAP 170:ADDR=1536 160 READ A:POKE ADDR,A:ADDR=ADDR+1:GOTO 160 170 TRAP 180:XXX=USR(1536) 180 ? :? "N: HANDLER INSTALLED!":END 190 DATA 104,162,0,189,26 200 DATA 3,240,7,232,232,232,208,246,240,242 210 DATA 169,78,157,26,3,169,31,157,27,3 220 DATA 169,6,157,28,3,96,43,6,43,6 230 DATA 43,6,43,6,43,6,76,44,6,169 240 DATA 1,168,96,226,2,227,2,0,6
Zařízení je možné ihned poté začít používat, tj. například interaktivně otevřít kanál #4, provést zápis řetězce do kanálu, provést zápis bajtu do stejného kanálu a posléze kanál uzavřít. Tyto operace proběhnou bez chyby (ty totiž Atari BASIC kontroluje automaticky):
Obrázek 3: Práce se zařízením N: v Atari BASICu.
Autor: tisnik, podle licence: Rights Managed
Co se skrývá v řádcích 190 až 240?Celá implementace zařízení N:, a to včetně jeho registrace, je zakódována v programových řádcích 190 až 240. Jedná se o strojový kód, ke kterému ovšem existuje i příslušný zdrojový kód v assembleru. Ten vypadá následovně (opět zde zobrazuji původní výpis, který ovšem není kompatibilní s assemblerem CA65):
0100 ; ADD A NULL HANDLER 0110 ; BY BOB MARTIN 0120 ; (c)1989, ANTIC PUBLISHING INC. 0130 ; 0140 HATABS = $031A 0150 ; 0160 *= $0600 0170 ; 0180 ;INSERT THE N: DEVICE IN HATABS 0200 ; LOOK FOR EMPTY SPOT IN HATABS 0210 ; 0220 INSERT 0230 LDX #0 0240 NEXTPLACE 0250 LDA HATABS,X 0260 BEQ ESPOT 0270 INX 0280 INX 0290 INX 0300 BNE NEXTPLACE 0310 BEQ INSERT 0320 ; 0330 ; FOUND AN EMPYT SPOT 0340 ; 0350 ESPOT 0360 LDA #'N 0370 STA HATABS,X 0380 LDA #NULLT&$FF 0390 STA HATABS+1,X 0400 LDA #NULLT/$0100 0410 STA HATABS+2,X 0420 RTS 0430 ; 0440 ;HANDLER TABLE 0450 ; 0460 NULLT 0470 .WORD NULL-1 0480 .WORD NULL-1 0490 .WORD NULL-1 0500 .WORD NULL-1 0510 .WORD NULL-1 0520 JMP NULL 0530 ; 0540 ;NULL HANDLER 0550 ; 0560 NULL 0570 LDA #1 0580 TAY 0590 RTS 0600 ; 0610 ; RUN INITIALIZATION STEP 0620 ; 0630 *= $02E2 0640 .WORD INSERT 0650 .END
V tomto programu můžeme identifikovat několik důležitých částí. Zejména je zde implementována subrutina, která bude posléze volána pro operace (OPEN, CLOSE, PUT, GET, STATUS), které zařízení podporuje. Tato subrutina pouze naplní akumulátor A a index registr Y hodnotou 1 (operace proběhla bez chyby) a následně je subrutina ukončena:
0540 ;NULL HANDLER 0550 ; 0560 NULL 0570 LDA #1 0580 TAY 0590 RTS
Poznámka: POZOR v tomto programu je 37 let stará chyba (která mě mate dodneška), protože tabulka by měla obsahovat šest adres, nikoli pouze pět. Chybí zde vektor pro volání příkazu SPECIAL.
Dále můžeme v programu nalézt tabulku s vektory (adresou) výše uvedené subrutiny. Od skutečné adresy subrutiny je ovšem nutné odečíst jedničku, protože operační systém na subrutinu skáče nikoli instrukcí JSR ale RTS (ta očekává adresu modifikovanou právě o jedničku). Všechny operace, které může zařízení provádět, jsou realizovány stejnou subrutinou. Skok na konci se může provést pro inicializaci zařízení:
0440 ;HANDLER TABLE 0450 ; 0460 NULLT 0470 .WORD NULL-1 0480 .WORD NULL-1 0490 .WORD NULL-1 0500 .WORD NULL-1 0510 .WORD NULL-1 0520 JMP NULL
Dále se v kódu nachází smyčka, která hledá v tabulce HATABS první volné místo, tj. bajt, který namísto jména zařízení obsahuje nulu:
0180 ;INSERT THE N: DEVICE IN HATABS 0200 ; LOOK FOR EMPTY SPOT IN HATABS 0210 ; 0220 INSERT 0230 LDX #0 0240 NEXTPLACE 0250 LDA HATABS,X 0260 BEQ ESPOT 0270 INX 0280 INX 0290 INX 0300 BNE NEXTPLACE 0310 BEQ INSERT
Jakmile je volné místo nalezeno, je doplněna informace o novém zařízení. První bajt obsahuje jméno (N), další dva bajty pak adresu výše zmíněné tabulky vektorů:
0320 ; 0330 ; FOUND AN EMPYT SPOT 0340 ; 0350 ESPOT 0360 LDA #'N 0370 STA HATABS,X 0380 LDA #NULLT&$FF 0390 STA HATABS+1,X 0400 LDA #NULLT/$0100 0410 STA HATABS+2,X 0420 RTS
Poznámka: povšimněte si, že může teoreticky dojít k přepisu jiné části operační paměti v případě, že je tabulka HATABS zcela zaplněna. Tento stav není v kódu nijak řešen ani detekován.
Přepis implementace nulového zařízení do formátu kompatibilního s CA65V tomto seriálu primárně používáme assembler CA65, takže bude vhodné přepsat původní program právě do syntaxe, která bude rozpoznávána tímto assemblerem. Při té příležitosti opravíme chybu v tabulce s vektory subrutin.
Samotná subrutina, která realizuje všechny operace, je přepsána to této podoby:
; ---------------------------------------------------------------------
; všechny operace zařízení jsou definovány shodně
; ---------------------------------------------------------------------
.proc NULL_HANDLER
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
Dále je nutné přepsat tabulku s vektory (adres), kterou používá subsystém CIO při volání operací, které zařízení podporuje. Do tabulky byl připsán i chybějící vektor:
; tabulka s handlerem pro zařízení
null_table:
.word NULL_HANDLER-1 ; OPEN
.word NULL_HANDLER-1 ; CLOSE
.word NULL_HANDLER-1 ; GET
.word NULL_HANDLER-1 ; PUT
.word NULL_HANDLER-1 ; STATUS
.word NULL_HANDLER-1 ; SPECIAL
jmp NULL_HANDLER ; inicializace zařízení
end:
A na závěr následuje subrutina pro registraci nového zařízení (stále bez testu, jestli není náhodou tabulka HATABS již zaplněna):
; ---------------------------------------------------------------------
; registrace nového zařízení
; ---------------------------------------------------------------------
.proc REGISTER_NULL_DEVICE
insert:
ldx #0 ; offset v rámci tabulky HATABS
next_place:
lda HATABS, x ; načtení bajtu upraví i obsah příznaku Zero
beq empty_place ; načtená nula znamená, že jsme našli volné místo v tabulce
inx
inx ; zvýšení offsetu o 3 (jméno zařízení+adresa)
inx
bne next_place ; zkusme znova
beq insert
empty_place:
lda #'N' ; jméno nového zařízení
sta HATABS, x ; uložit do HATABS
lda #<null_table ; adresa tabulky s vektory obsluhy zařízení (nižší bajt)
sta HATABS+1, x ; uložit do HATABS
lda #>null_table ; adresa tabulky s vektory obsluhy zařízení (vyšší bajt)
sta HATABS+2, x ; uložit do HATABS
rts ; návrat ze subrutiny
.endproc
To je vše – výše uvedené části kódu zařadíme do našeho demonstračního příkladu. Na začátku je nutné zařízení zaregistrovat a poté ho již začít používat stejně snadno, jako například zařízení S: nebo C:
; ---------------------------------------------------------------------
; hlavní program
; ---------------------------------------------------------------------
.proc main
jsr REGISTER_NULL_DEVICE
jsr CLOSE_CHANNEL_4
jsr OPEN_CHANNEL_4_AS_N
jsr WRITE_BYTE_CHANNEL_4
jsr CHECK_ERROR
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
Nesmíme zapomenout změnit i jméno zařízení:
; jméno zařízení ukončené nulou device_name: .byte "N:", 0Demonstrační příklad: registrace nulového zařízení, provedení zápisu a kontrola, zda operace proběhla bez chyby
Implementaci a registraci nulového zařízení N: můžeme po přepsání do formátu kompatibilního s assemblerem CC65 zařadit do programu, který byl ukázán ve druhé kapitole. Výsledkem je demonstrační příklad, jehož zdrojový kód je uveden v této kapitole. Otevření kanálu s tímto zařízením a zápis bajtu dopadne korektně (tedy bez chyby), takže by obrazovka měla „zezelenat“:
Obrázek 4: Nulové zařízení vrací pro všechny operace stav "OK" signalizovaný zelenou obrazovkou.
Autor: tisnik, podle licence: Rights Managed
Úplný zdrojový kód takto upraveného demonstračního příkladu vypadá následovně:
.include "atari.inc"
.CODE
IOCB4 = $40 ; offset odvozený od čísla IOCB kanálu (nutno násobit šestnácti)
; ---------------------------------------------------------------------
; hlavní program
; ---------------------------------------------------------------------
.proc main
jsr REGISTER_NULL_DEVICE
jsr CLOSE_CHANNEL_4
jsr OPEN_CHANNEL_4_AS_N
jsr WRITE_BYTE_CHANNEL_4
jsr CHECK_ERROR
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; registrace nového zařízení
; ---------------------------------------------------------------------
.proc REGISTER_NULL_DEVICE
insert:
ldx #0
next_place:
lda HATABS, x
beq empty_place
inx
inx
inx
bne next_place
beq insert
empty_place:
lda #'N'
sta HATABS, x
lda #<null_table
sta HATABS+1, x
lda #>null_table
sta HATABS+2, x
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; všechny operace zařízení jsou definovány shodně
; ---------------------------------------------------------------------
.proc NULL_HANDLER
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
; ---------------------------------------------------------------------
; uzavření kanálu číslo 4
; ---------------------------------------------------------------------
.proc CLOSE_CHANNEL_4
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda #CLOSE ; prováděná operace
sta ICCOM, x ; zápis prováděné operace do bloku IOCB
jsr CIOV ; zavolání rutiny pro CIO
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; otevření kanálu číslo 4 v režimu zápisu na zařízení N:
; ---------------------------------------------------------------------
.proc OPEN_CHANNEL_4_AS_N
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda #OPEN ; prováděná operace
sta ICCOM, x ; zápis prováděné operace do bloku IOCB
; specifikace jména zařízení + jména souboru (16bitový ukazatel)
lda #<device_name
sta ICBAL, x
lda #>device_name
sta ICBAH, x
lda #OPNOT ; režim otevření: zápis (odpovídá druhému parametru příkazu OPEN v BASICu)
sta ICAX1, x
lda #0 ; odpovídá třetímu parametru příkazu OPEN v BASICu
sta ICAX2, x
jsr CIOV ; zavolání rutiny pro CIO
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; zápis jednoho znaku do kanálu číslo 4
; ---------------------------------------------------------------------
.proc WRITE_BYTE_CHANNEL_4
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda #PUTCHR ; prováděná operace
sta ICCOM, x ; zápis prováděné operace do bloku IOCB
; adresa zapisovaného bloku
lda #<char_to_put
sta ICBAL, x
lda #>char_to_put
sta ICBAH, x
; nastavení počtu zapisovaných bajtů
lda #1
sta ICBLL, x
lda #0
sta ICBLH, x
jsr CIOV ; zavolání rutiny pro CIO
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; kontrola chyby při zápisu
; ---------------------------------------------------------------------
.proc CHECK_ERROR
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda ICSTA, x ; status poslední operace
bpl ok ; test bitu 7
lda #18 ; kod barvy
sta COLOR2 ; ulozit do registru COLOR2
rts ; návrat ze subrutiny
ok:
lda #$c3 ; kod barvy
sta COLOR2 ; ulozit do registru COLOR2
rts ; návrat ze subrutiny
.endproc
; jméno zařízení ukončené nulou
device_name: .byte "N:", 0
; zapisovaný bajt
char_to_put: .byte $41
; tabulka s handlerem pro zařízení
null_table:
.word NULL_HANDLER-1 ; OPEN
.word NULL_HANDLER-1 ; CLOSE
.word NULL_HANDLER-1 ; GET
.word NULL_HANDLER-1 ; PUT
.word NULL_HANDLER-1 ; STATUS
.word NULL_HANDLER-1 ; SPECIAL
jmp NULL_HANDLER ; inicializace zařízení
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
Modifikace handleru pro všechny operace prováděné zařízením tak, aby se ohlásila chyba
Původní handler (pro všechny operace, které zařízení podporuje) nastavoval jak akumulátor A, tak i index registr Y na hodnotu 1, což znamená „operace provedena bez chyby“:
; ---------------------------------------------------------------------
; všechny operace zařízení jsou definovány shodně
; ---------------------------------------------------------------------
.proc NULL_HANDLER
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
Operační systém Atari, resp. jeho CIO subsystém totiž volá příslušný handler tak, že do registrů „přednastaví“ chybový kód 146 (NOT IMPLEMENTED) a je na vývojáři zařízení, aby tento kód explicitně změnil. Ostatně si můžeme snadno otestovat, co se stane v případě, že subrutina volaná po provedení všech operací, které má zařízení provést, obsahuje pouze instrukci pro návrat z podprogramu (tudíž se nemění obsah žádného registru):
; ---------------------------------------------------------------------
; všechny operace zařízení jsou definovány shodně
; ---------------------------------------------------------------------
.proc NULL_HANDLER
; nebudeme modifikovat obsah akumulátoru ani index registru Y
; tím se "propíše" chyba do IOCB
rts
.endproc
Demonstrační příklad: registrace a otestování nulového zařízení, které pro všechny operace ohlásí chybu
Výše uvedenou úpravu handleru pro zařízení N: pochopitelně můžeme velmi snadno přidat do demonstračního příkladu, který zařízení zaregistruje, otevře kanál, pošle do něj bajt a otestuje, jestli tato operace byla provedena bez chyby. Vzhledem k tomu, že v tomto případě chyba nastane při všech voláních (operacích), nebude žádným překvapením, že se zobrazí červená obrazovka smrti:
Obrázek 5: Upravené nulové zařízení vrací pro všechny operace příznak chyby.
Autor: tisnik, podle licence: Rights Managed
Úplný zdrojový kód takto upraveného demonstračního příkladu vypadá následovně:
.include "atari.inc"
.CODE
IOCB4 = $40 ; offset odvozený od čísla IOCB kanálu (nutno násobit šestnácti)
; ---------------------------------------------------------------------
; hlavní program
; ---------------------------------------------------------------------
.proc main
jsr REGISTER_NULL_DEVICE
jsr CLOSE_CHANNEL_4
jsr OPEN_CHANNEL_4_AS_N
jsr WRITE_BYTE_CHANNEL_4
jsr CHECK_ERROR
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; registrace nového zařízení
; ---------------------------------------------------------------------
.proc REGISTER_NULL_DEVICE
insert:
ldx #0
next_place:
lda HATABS, x
beq empty_place
inx
inx
inx
bne next_place
beq insert
empty_place:
lda #'N'
sta HATABS, x
lda #<null_table
sta HATABS+1, x
lda #>null_table
sta HATABS+2, x
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; všechny operace zařízení jsou definovány shodně
; ---------------------------------------------------------------------
.proc NULL_HANDLER
; nebudeme modifikovat obsah akumulátoru ani index registru Y
; tím se "propíše" chyba do IOCB
rts
.endproc
; ---------------------------------------------------------------------
; uzavření kanálu číslo 4
; ---------------------------------------------------------------------
.proc CLOSE_CHANNEL_4
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda #CLOSE ; prováděná operace
sta ICCOM, x ; zápis prováděné operace do bloku IOCB
jsr CIOV ; zavolání rutiny pro CIO
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; otevření kanálu číslo 4 v režimu zápisu na zařízení H:test.txt
; ---------------------------------------------------------------------
.proc OPEN_CHANNEL_4_AS_N
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda #OPEN ; prováděná operace
sta ICCOM, x ; zápis prováděné operace do bloku IOCB
; specifikace jména zařízení + jména souboru (16bitový ukazatel)
lda #<device_name
sta ICBAL, x
lda #>device_name
sta ICBAH, x
lda #OPNOT ; režim otevření: zápis (odpovídá druhému parametru příkazu OPEN v BASICu)
sta ICAX1, x
lda #0 ; odpovídá třetímu parametru příkazu OPEN v BASICu
sta ICAX2, x
jsr CIOV ; zavolání rutiny pro CIO
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; zápis jednoho znaku do kanálu číslo 4
; ---------------------------------------------------------------------
.proc WRITE_BYTE_CHANNEL_4
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda #PUTCHR ; prováděná operace
sta ICCOM, x ; zápis prováděné operace do bloku IOCB
; adresa zapisovaného bloku
lda #<char_to_put
sta ICBAL, x
lda #>char_to_put
sta ICBAH, x
; nastavení počtu zapisovaných bajtů
lda #1
sta ICBLL, x
lda #0
sta ICBLH, x
jsr CIOV ; zavolání rutiny pro CIO
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; kontrola chyby při zápisu
; ---------------------------------------------------------------------
.proc CHECK_ERROR
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda ICSTA, x ; status poslední operace
bpl ok ; test bitu 7
lda #18 ; kod barvy
sta COLOR2 ; ulozit do registru COLOR2
rts ; návrat ze subrutiny
ok:
lda #$c3 ; kod barvy
sta COLOR2 ; ulozit do registru COLOR2
rts ; návrat ze subrutiny
.endproc
; jméno zařízení ukončené nulou
device_name: .byte "N:", 0
; zapisovaný bajt
char_to_put: .byte $41
; tabulka s handlerem pro zařízení
null_table:
.word NULL_HANDLER-1 ; OPEN
.word NULL_HANDLER-1 ; CLOSE
.word NULL_HANDLER-1 ; GET
.word NULL_HANDLER-1 ; PUT
.word NULL_HANDLER-1 ; STATUS
.word NULL_HANDLER-1 ; SPECIAL
jmp NULL_HANDLER ; inicializace zařízení
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
Implementace zařízení, které bude vypisovat volané operace na obrazovku
Nulové zařízení, které jsme doposud používali, vlastně žádným zásadním způsobem nedávalo najevo, jaké operace provádí. To ovšem můžeme relativně snadno změnit. Pokusíme se implementovat všechny základní operace, tedy OPEN, CLOSE, GET, PUT a STATUS, jako samostatné subrutiny. Každá z těchto subrutin vypíše na obrazovku jeden znak (OPEN vypíše „o“, CLOSE vypíše „c“ atd.), nastaví stav operace na jedničku (tedy: operace proběhla bez chyby) a následně se subrutina ukončí. Všechny subrutiny se budou lišit jen ve vypisovaném znaku a taktéž místa na obrazovce, do které se znak vypíše:
.proc OPEN_OPERATION
lda #'o' ; ATASCII hodnota znaku
ldy #1 ; nastavit registr Y
sta (88),y ; tisk znaku na obrazovku
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
.proc CLOSE_OPERATION
lda #'c' ; ATASCII hodnota znaku
ldy #41 ; nastavit registr Y
sta (88),y ; tisk znaku na obrazovku
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
.proc GET_OPERATION
lda #'g' ; ATASCII hodnota znaku
ldy #81 ; nastavit registr Y
sta (88),y ; tisk znaku na obrazovku
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
.proc PUT_OPERATION
lda #'p' ; ATASCII hodnota znaku
ldy #121 ; nastavit registr Y
sta (88),y ; tisk znaku na obrazovku
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
.proc STATUS_OPERATION
lda #'s' ; ATASCII hodnota znaku
ldy #161 ; nastavit registr Y
sta (88),y ; tisk znaku na obrazovku
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
.proc SPECIAL_OPERATION
lda #'*' ; ATASCII hodnota znaku
ldy #201 ; nastavit registr Y
sta (88),y ; tisk znaku na obrazovku
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
Pochopitelně musíme modifikovat i tabulku s handlerem zařízení do následující podoby:
; tabulka s handlerem pro zařízení
null_table:
.word OPEN_OPERATION-1 ; OPEN
.word CLOSE_OPERATION-1 ; CLOSE
.word GET_OPERATION-1 ; GET
.word PUT_OPERATION-1 ; PUT
.word STATUS_OPERATION-1 ; STATUS
.word SPECIAL_OPERATION-1 ; SPECIAL
jmp NULL_HANDLER ; inicializace zařízení
Demonstrační příklad: vizualizace operací, které zařízení provádí
Přidáním samostatných subrutin pro každou operaci je možné snadno vizualizovat a částečně odhalit, jaké operace zařízení skutečně provedlo. V našem konkrétním případě by měla obrazovka (na konci programu) vypadat následovně, protože byly provedeny jen tři operace:
Obrázek 6: Byly provedeny operace OPEN, CLOSE a PUT(CHAR).
Autor: tisnik, podle licence: Rights Managed
Následuje výpis celého zdrojového kódu tohoto demonstračního příkladu:
.include "atari.inc"
.CODE
IOCB4 = $40 ; offset odvozený od čísla IOCB kanálu (nutno násobit šestnácti)
; ---------------------------------------------------------------------
; hlavní program
; ---------------------------------------------------------------------
.proc main
jsr REGISTER_NULL_DEVICE
jsr CLOSE_CHANNEL_4
jsr OPEN_CHANNEL_4_AS_N
jsr WRITE_BYTE_CHANNEL_4
jsr CHECK_ERROR
jsr CLOSE_CHANNEL_4
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; registrace nového zařízení
; ---------------------------------------------------------------------
.proc REGISTER_NULL_DEVICE
insert:
ldx #0
next_place:
lda HATABS, x
beq empty_place
inx
inx
inx
bne next_place
beq insert
empty_place:
lda #'N'
sta HATABS, x
lda #<null_table
sta HATABS+1, x
lda #>null_table
sta HATABS+2, x
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; použito jen pro INIT
; ---------------------------------------------------------------------
.proc NULL_HANDLER
lda #1
tay
rts
.endproc
; ---------------------------------------------------------------------
; uzavření kanálu číslo 4
; ---------------------------------------------------------------------
.proc CLOSE_CHANNEL_4
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda #CLOSE ; prováděná operace
sta ICCOM, x ; zápis prováděné operace do bloku IOCB
jsr CIOV ; zavolání rutiny pro CIO
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; otevření kanálu číslo 4 v režimu zápisu na zařízení N:
; ---------------------------------------------------------------------
.proc OPEN_CHANNEL_4_AS_N
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda #OPEN ; prováděná operace
sta ICCOM, x ; zápis prováděné operace do bloku IOCB
; specifikace jména zařízení + jména souboru (16bitový ukazatel)
lda #<device_name
sta ICBAL, x
lda #>device_name
sta ICBAH, x
lda #OPNOT ; režim otevření: zápis (odpovídá druhému parametru příkazu OPEN v BASICu)
sta ICAX1, x
lda #0 ; odpovídá třetímu parametru příkazu OPEN v BASICu
sta ICAX2, x
jsr CIOV ; zavolání rutiny pro CIO
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; zápis jednoho znaku do kanálu číslo 4
; ---------------------------------------------------------------------
.proc WRITE_BYTE_CHANNEL_4
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda #PUTCHR ; prováděná operace
sta ICCOM, x ; zápis prováděné operace do bloku IOCB
; adresa zapisovaného bloku
lda #<char_to_put
sta ICBAL, x
lda #>char_to_put
sta ICBAH, x
; nastavení počtu zapisovaných bajtů
lda #1
sta ICBLL, x
lda #0
sta ICBLH, x
jsr CIOV ; zavolání rutiny pro CIO
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; kontrola chyby při zápisu
; ---------------------------------------------------------------------
.proc CHECK_ERROR
ldx #IOCB4 ; offset odvozený od čísla IOCB kanálu
lda ICSTA, x ; status poslední operace
bpl ok ; test bitu 7
lda #18 ; kod barvy
sta COLOR2 ; ulozit do registru COLOR2
rts ; návrat ze subrutiny
ok:
lda #$c3 ; kod barvy
sta COLOR2 ; ulozit do registru COLOR2
rts ; návrat ze subrutiny
.endproc
; ---------------------------------------------------------------------
; subrutiny pro realizaci operací zařízení
; ---------------------------------------------------------------------
.proc OPEN_OPERATION
lda #'o' ; ATASCII hodnota znaku
ldy #1 ; nastavit registr Y
sta (88),y ; tisk znaku na obrazovku
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
.proc CLOSE_OPERATION
lda #'c' ; ATASCII hodnota znaku
ldy #41 ; nastavit registr Y
sta (88),y ; tisk znaku na obrazovku
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
.proc GET_OPERATION
lda #'g' ; ATASCII hodnota znaku
ldy #81 ; nastavit registr Y
sta (88),y ; tisk znaku na obrazovku
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
.proc PUT_OPERATION
lda #'p' ; ATASCII hodnota znaku
ldy #121 ; nastavit registr Y
sta (88),y ; tisk znaku na obrazovku
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
.proc STATUS_OPERATION
lda #'s' ; ATASCII hodnota znaku
ldy #161 ; nastavit registr Y
sta (88),y ; tisk znaku na obrazovku
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
.proc SPECIAL_OPERATION
lda #'*' ; ATASCII hodnota znaku
ldy #201 ; nastavit registr Y
sta (88),y ; tisk znaku na obrazovku
lda #1 ; chybový kód
tay ; musí být uložen jak v A, tak i v Y
rts
.endproc
; jméno zařízení ukončené nulou
device_name: .byte "N:", 0
; zapisovaný bajt
char_to_put: .byte $41
; tabulka s handlerem pro zařízení
null_table:
.word OPEN_OPERATION-1 ; OPEN
.word CLOSE_OPERATION-1 ; CLOSE
.word GET_OPERATION-1 ; GET
.word PUT_OPERATION-1 ; PUT
.word STATUS_OPERATION-1 ; STATUS
.word SPECIAL_OPERATION-1 ; SPECIAL
jmp NULL_HANDLER ; inicializace zařízení
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
Implementace zařízení, které dokáže provádět manipulace se sprity na obrazovce
Skutečná zařízení by ovšem měla implementovat složitější operace, nikoli pouze triviální výpis znaku na obrazovku. Proto si ukážeme příklad komplexnějšího zařízení (resp. dokonce čtyř zařízení), která budou mít označení G1: až G4:. Každé z těchto zařízení slouží pro ovládání jednoho spritu–hráče (player). Jakákoli operace se zařízením bude ihned vizualizována – například se změní bitmapa spritu atd. Takto implementovaná zařízení budou otestována programem psaným v Atari BASICu, ovšem pochopitelně je možné i jejich interaktivní využití (přímé zápisy příkazů BASICu bez uvedení čísel řádků).
Poznámka: autorem zařízení G: i demo programu v Atari BASICu je Milan Vančura, kterému tímto moc děkuji jak za zdrojové kódy, tak i za skvělou spolupráci!
Základní schopnosti nabízené zařízeními G1 až G4Nejprve se podívejme na některé základní vlastnosti, které zařízení G1 až G4 uživatelům nabízí. Při otevření zařízení se zobrazí část spritu (horní a dolní šipka) na obou okrajích herního pole. Uzavření zařízení znamená, že tyto šipky zmizí. Ukažme si to pro jedno zařízení:
10 DIM A$(100) 20 REM INIT ADDRESS OF G_DEVICE V.1.1 30 INIT=39176 40 INPUT A$ 50 A=USR(INIT) 60 OPEN #2,8,0,"G1:" 70 INPUT A$ 80 CLOSE #2 90 END
Obrázek 7: Otevření zařízení G1.
Autor: tisnik, podle licence: Rights Managed
Zařízení pracují nezávisle na sobě, takže je možné jich postupně otevřít (a poté postupně uzavřít) větší množství:
10 DIM A$(100) 20 REM INIT ADDRESS OF G_DEVICE V.1.1 30 INIT=39176 40 INPUT A$ 41 A=USR(INIT) 50 OPEN #2,8,0,"G1:" 51 INPUT A$ 60 OPEN #3,8,0,"G2:" 61 INPUT A$ 70 OPEN #4,8,0,"G3:" 71 INPUT A$ 80 OPEN #5,8,0,"G4:" 90 INPUT A$ 91 CLOSE #2 92 INPUT A$ 93 CLOSE #3 94 INPUT A$ 95 CLOSE #4 96 INPUT A$ 97 CLOSE #5 100 END
Obrázek 8: Otevření všech čtyř zařízení G1:, G2:, G3 a G4.
Autor: tisnik, podle licence: Rights Managed
Zápis do zařízení s využitím příkazů PUT nebo PRINT modifikuje bitmapu spritu, což se pochopitelně ihned vizuálně projeví změnou jeho tvaru (zařízení se chová běžným způsobem, tj. pamatuje si, na které místo byl proveden poslední zápis). Ovšem „kapacita“ zařízení je omezena výškou spritu, od které je nutné odečíst i části používané oběma šipkami (posléze tyto „zarážky“ uvidíme v implementační části). Opět si tuto vlastnost ukažme na prvním zařízení:
10 DIM A$(100) 20 REM INIT ADDRESS OF G_DEVICE V.1.1 30 INIT=39176 40 INPUT A$ 50 A=USR(INIT) 60 DATA FJBBB,DJBDN,LBDBL,FJNBB 70 DATA NHLBL,FHLJF 80 OPEN #2,8,0,"G1:" 100 FOR I=1 TO 4:READ A$ 110 PRINT #2;" ";A$;" " 120 NEXT I 130 INPUT A$ 140 CLOSE #2 150 END
Obrázek 9: Zápis dat do zařízení G1:.
Autor: tisnik, podle licence: Rights Managed
Podle očekávání mohou být data zapisována do všech čtyřech zařízení (nezávisle na sobě):
10 DIM A$(100) 20 REM INIT ADDRESS OF G_DEVICE V.1.1 30 INIT=39176 40 INPUT A$ 50 A=USR(INIT) 60 DATA FJBBB,DJBDN,LBDBL,FJNBB 70 DATA NHLBL,FHLJF 80 OPEN #2,8,0,"G1:" 81 OPEN #3,8,0,"G2:" 82 OPEN #4,8,0,"G3:" 83 OPEN #5,8,0,"G4:" 100 FOR I=1 TO 4:READ A$ 110 PRINT #2;" ";A$;" " 111 PRINT #3;" ";A$;" " 112 PRINT #4;" ";A$;" " 113 PRINT #5;" ";A$;" " 120 NEXT I 130 INPUT A$ 140 CLOSE #2 141 CLOSE #3 142 CLOSE #4 143 CLOSE #5 150 END
Obrázek 10: Zápis dat do všech čtyř zařízení G1:, G2:, G3 a G4.
Autor: tisnik, podle licence: Rights Managed
Kopie dat z jednoho zařízení G do jiného zařízení GTaktéž je umožněna kopie dat z jednoho (libovolného) zařízení G do dalšího zařízení G. K tomuto účelu lze využít příkazy INPUT #kanál a PRINT #kanál. Povšimněte si, že i když je zařízení uzavřeno (v tomto případě G1:), neztratí zapsaná data. To znamená, že zařízení nejdříve otevřeme v režimu zápisu, pozměníme bitmapu spritu, následně zařízení uzavřeme a ihned poté otevřeme, ovšem v režimu čtení:
10 DIM A$(100) 20 REM INIT ADDRESS OF G_DEVICE V.1.1 30 INIT=39176 40 INPUT A$ 50 A=USR(INIT) 60 DATA FJBBB,DJBDN,LBDBL,FJNBB 70 DATA NHLBL,FHLJF 80 OPEN #2,8,0,"G1:" 90 FOR I=1 TO 4:READ A$ 100 PRINT #2;" ";A$;" " 110 NEXT I 130 INPUT A$ 140 CLOSE #2 150 OPEN #2,4,0,"G1:" 151 OPEN #3,8,0,"G2:" 152 FOR I=1 TO 4 153 INPUT #2,A$ 154 PRINT #3;A$ 155 NEXT I 156 INPUT A$ 160 CLOSE #2 171 CLOSE #3 160 END
Obrázek 11: Zápis dat do zařízení G1:.
Autor: tisnik, podle licence: Rights Managed
Obrázek 12: Kopie dat ze zařízení G1: do zařízení G2:.
Autor: tisnik, podle licence: Rights Managed
Realizace jednotlivých CIO operací v assembleruJak je pravděpodobně z předchozích ukázek patrné, není realizace zařízení G: triviální, protože zařízení skutečně podporují čtení a zápis dat (tato data definují bitmapu spritů), zařízení korektně reagují na své otevření a uzavření atd. Základem je (opět) tabulka obsahující adresy handlerů pro jednotlivé CIO operace. Ve skutečnosti jsou implementovány operace OPEN, CLOSE, GET a PUT, nikoli však STATUS nebo SPECIAL. V tabulce je taktéž korektně uvedena instrukce pro skok na inicializační část, ovšem v praxi není zřejmé, že by se někdy reálně tato instrukce použila. Závěrečný nulový bajt je jen výplní, protože standardní ovladače zařízení ji mají taktéž (ovšem pravděpodobně není nutná – program bude pracovat i bez této výplně):
; table containing vectors to subroutines implementing CIO operations
; addresses need to be decreased by one - subroutines are called via RTS instruction!
hatabs_handler:
.word g_open-1
.word g_close-1
.word g_get-1
.word g_put-1
.word g_nondef-1
.word g_nondef-1
JMP g_init
.byte $00
Všechna čtyři zařízení G1 až G4 podporují operace GET a PUT, takže si musí pamatovat offset do bufferu (který je sdílen se sprity). Vzhledem k tomu, že se při otevření zařízení vykreslí šipky, je zapisovatelná část omezena na offsety od $10 do $70:
cursor_low_limit = $10
cursor_top_limit = $70
; read cursors for all four devices
read_cursors:
.byte cursor_low_limit, cursor_low_limit, cursor_low_limit, cursor_low_limit
; write cursors for all four devices
write_cursors:
.byte cursor_low_limit, cursor_low_limit, cursor_low_limit, cursor_low_limit
Sprity používané jednotlivými zařízeními mají nastaveny tyto barvy a jsou umístěny na horizontální pozice uložené v sekci dev_hpos:
; colors assigned to all four sprites (one per device)
dev_colors:
.byte $1C, $2C, $6C, $AC
; horizontal positions for all four sprites (one per device)
dev_hpos:
.byte $90, $A4, $B8, $CC
A konečně bitmapy šipek jsou definovány zde:
visual_markers:
.byte $08, $08, $08, $49, $2A, $1C, $08, $FF
Poslední „datová část“ obsahuje definici dvojice bajtů z nulté stránky, které použijeme pro nepřímé adresování:
; address of two bytes allocated on zero page: ; $CE, $CF ; cur_base_l :L,:Ht cur_base_l = $ce cur_base_h = cur_base_l + 1 ; the following address will be used in the code cur_base = cur_base_l
Samotný kód ovladače zařízení je rozdělen do několika subrutin. První subrutina (zdaleka nejdelší) je volána v případě, že se má zařízení otevřít. Na základě zvoleného režimu (čtení/zápis) jsou provedeny různé operace s bufferem a taktéž se provede vykreslení šipek (sprite) na obrazovku. Povšimněte si, jak se zde pracuje s číslem zařízení, které je uloženo na adrese ICDNOZ ($21, tedy na nulté stránce). Taktéž se pracuje adresou ICAX1Z, na kterou je uloženo první číslo použité v příkazu OPEN:
; ============
; HATABS_OPEN
; ============
; IN: ---
; OUT: Y: 01 = success
; >80 = error
; check device number, we support G1 - G4 only
g_open:
ldx ICDNOZ
dex ; save devnum; normalize 1-4 to 0-3
txa
and #$FC
beq devnum_ok
ldy #NONDEV ; error_code "no such device"
rts
; init the device in the command code, we support only (4,8,9,12,13)
; Binary Flags:
; 1 append (together with 8 only) => skip buffer cleanup and skip W cursor reset
; 4 reading => set read cursor, show device
; 8 writing => buffer cleanup, W cursor reset, show device
; any some operation => success (LDY #SUCCES)
devnum_ok:
ldy #NVALID ; error_code "invalid command"
lda #$F2
bit ICAX1Z
bne open_exit ; throw the error
lda #$08
bit ICAX1Z
beq test_read
lda #$01 ; test for append mode
bit ICAX1Z
bne open_appnd
jsr get_dev_base
jsr init_dev_data
lda #cursor_low_limit
sta write_cursors, x ; reset write_cursor
open_appnd:
jsr show_dev
ldy #SUCCES ; writing => set success
test_read:
lda #$04
bit ICAX1Z
beq open_exit
lda #cursor_low_limit
sta read_cursors, x ; reset read_cursor
jsr show_dev
g_nondef:
ldy #SUCCES
open_exit:
rts
Realizace operace CLOSE je mnohem jednodušší, protože postačuje posunout odpovídající sprite mimo obrazovku (před její levý okraj):
; ============
; HATABS_CLOSE
; ============
; IN: ---
; OUT: Y: 01 = success
; >80 = error (see the CIO err_code list)
g_close:
ldx ICDNOZ
dex
lda #$00
sta HPOSP0, x ; move sprite off the screen
ldy #SUCCES
rts
Následuje realizace operace čtení, která dokáže vrátit data zapsaná do bufferu zařízení. Samozřejmě se testuje, zda se program nesnaží číst neexistující (nezapsaná) data, zda se nenacházíme na konci bufferu atd. A navíc by měl korektní ovladač vrátit chybu v případě, kdy se pokoušíme číst data ze zařízení určeného pouze pro zápis (reálně tiskárna, v našem případě zařízení otevřené v režimu 8):
; ============
; HATABS_GET_CHAR
; ============
; IN: X - channel number * $10
; OUT: Y: 01 = success
; >80 = error (see the CIO err_code list)
; A: the byte read
g_get:
lda ICAX1, x
cmp #$09
bne no_wr_append
ldy #WRONLY ; error_code "attempted to read a write-only device"
rts
no_wr_append:
lda ICDNO, x
tax
dex ; dev number 0-3 to X
jsr get_dev_base
lda read_cursors, x ; read_cursor
cmp write_cursors, x ; is lower then write_cursor?
bcc read_ok
ldy #EOFERR ; error_code "end of file"
rts
read_ok:
tay
lda (cur_base), y ; read byte from the device
inc read_cursors, x
ldy #SUCCES
rts
Podobným způsobem je realizována i subrutina pro zápis dat na zařízení. Opět je nutné testovat, zda je zařízení otevřeno v režimu zápisu či připojení dat, jestli je v bufferu dostatek místa pro zápis atd. A úspěšný zápis pochopitelně znamená, že se změní tvar příslušného spritu na obrazovce:
; ============
; HATABS_PUT_CHAR
; ============
; IN: A: the byte to write
; OUT: Y: 01 = success
; >80 = error (see the CIO err_code list)
g_put:
pha
ldy #FNCNOT ; error_code "function not implemented in handler"
lda #$08
bit ICAX1Z ; throw the error
beq exit_w_error
lda ICDNO, x
tax
dex ; dev number 0-3 to X
jsr get_dev_base
lda write_cursors, x
cmp #cursor_top_limit ; still a space in the buff?
bcc write_ok
ldy #EOFERR ; error_code "end of file"
exit_w_error:
pla
rts
write_ok:
tay
pla
sta (cur_base),y ; store the byte in the device
inc write_cursors, x
ldy #SUCCES
rts
Počáteční inicializace zařízení implementovaná v subrutině init_dev_data sice vypadá komplikovaně, ovšem provádí se zde pouze vykreslení šipek (horní a dolní marker):
; ============
; Init Device Data
; ============
; IN: cur_base must be set
; OUT: device data cleared + markers drawn
init_dev_data:
ldy #$80
lda #$00
null_loop:
dey
sta (cur_base),y
bne null_loop
txa
pha
ldx #$07 ; draw visual markers - top
ldy #$0F
marker_bot:
lda visual_markers, x
sta (cur_base),y
dey
dex
bne marker_bot
ldx #$07 ; draw visual markers - bottom
ldy #$70
marker_top:
lda visual_markers, x
sta (cur_base),y
iny
dex
bne marker_top
pla
tax
rts
Pomocná subrutina show_dev nastaví barvu a pozici spritu (hráče), jehož index odpovídá číslu zařízení:
; ============
; Show Device on the Screen
; ============
; IN: X: device num (0-3)
; OUT: ---
; Show Device on the Screen
show_dev:
lda dev_colors, x
sta PCOLR0, x
lda dev_hpos, x
sta HPOSP0, x
rts
A konečně inicializační rutina g_init, která je volaná z BASICu příkazem PRINT USR, zaregistruje nová zařízení do tabulky HATABS (voláním rutiny operačního systému – to je novinka), povolí zobrazení hráčů (PMG), nastaví priority spritů, adresy, na kterých jsou sprity (a buffery zařízení) uloženy, jejich horizontální pozice mimo obrazovku atd.:
; ============
; main()
; ============
; !!! contains extra PLA at the end
; to be called from BASIC using USR()
g_init:
pha
txa
pha
tya
pha
; Add device using OS method
ldx #'G' ; device name
ldy #<hatabs_handler
lda #>hatabs_handler
jsr PHENTV ; ADD_NEWDEV
bcs exit_init
; set space in RAM and init GTIA
lda #<begin
sta MEMTOP
lda #>begin
sta MEMTOP+1
sta PMBASE
ldx #$03 ; init all devices data
loop_init:
jsr get_dev_base
jsr init_dev_data
dex
bpl loop_init
lda #$00
sta HPOSP0
sta HPOSP1
sta HPOSP2
sta HPOSP3
lda #$01
sta GPRIOR
lda #$03 ; enable sprites
sta GRACTL
lda #$2E ; enable DMA for display list and sprites, set normal playfield width
sta SDMCTL
exit_init:
pla
tay
pla
tax
pla
pla
rts
end:
Úplný zdrojový kód demonstračního příkladu s realizací ovladače zařízení G
Samozřejmě si musíme ukázat i úplný zdrojový kód příkladu napsaného pro assembler CA65. V příkladu je realizován celý ovladač zařízeních G1 až G4:
; %%%%%%%%%%%%%%%
; ; G device
; %%%%%%%%%%%%%%%
; Autor: (c) Milan Vančura, milan@caputu.cz
; License: CC-BY-SA
; This program is distributed with no warranty. Use on your risk.
; Note: This is a study material so, anyway, it is not intended to be used in any kind of production.
; -- show data in PMG --
; ;
.include "atari.inc"
.CODE
; memory segment starts here
begin = $9800
.org begin
; table containing vectors to subroutines implementing CIO operations
; addresses need to be decreased by one - subroutines are called via RTS instruction!
hatabs_handler:
.word g_open-1
.word g_close-1
.word g_get-1
.word g_put-1
.word g_nondef-1
.word g_nondef-1
JMP g_init
.byte $00
cursor_low_limit = $10
cursor_top_limit = $70
; offsets that are updated during operations with devices
; read cursors for all four devices
read_cursors:
.byte cursor_low_limit, cursor_low_limit, cursor_low_limit, cursor_low_limit
; write cursors for all four devices
write_cursors:
.byte cursor_low_limit, cursor_low_limit, cursor_low_limit, cursor_low_limit
; colors assigned to all four sprites (one per device)
dev_colors:
.byte $1C, $2C, $6C, $AC
; horizontal positions for all four sprites (one per device)
dev_hpos:
.byte $90, $A4, $B8, $CC
visual_markers:
.byte $08, $08, $08, $49, $2A, $1C, $08, $FF
; address of two bytes allocated on zero page:
; $CE, $CF ; cur_base_l :L,:H
cur_base_l = $ce
cur_base_h = cur_base_l + 1
; the following address will be used in the code
cur_base = cur_base_l
; ============
; HATABS_OPEN
; ============
; IN: ---
; OUT: Y: 01 = success
; >80 = error
; check device number, we support G1 - G4 only
g_open:
ldx ICDNOZ
dex ; save devnum; normalize 1-4 to 0-3
txa
and #$FC
beq devnum_ok
ldy #NONDEV ; error_code "no such device"
rts
; init the device in the command code, we support only (4,8,9,12,13)
; Binary Flags:
; 1 append (together with 8 only) => skip buffer cleanup and skip W cursor reset
; 4 reading => set read cursor, show device
; 8 writing => buffer cleanup, W cursor reset, show device
; any some operation => success (LDY #SUCCES)
devnum_ok:
ldy #NVALID ; error_code "invalid command"
lda #$F2
bit ICAX1Z
bne open_exit ; throw the error
lda #$08
bit ICAX1Z
beq test_read
lda #$01 ; test for append mode
bit ICAX1Z
bne open_appnd
jsr get_dev_base
jsr init_dev_data
lda #cursor_low_limit
sta write_cursors, x ; reset write_cursor
open_appnd:
jsr show_dev
ldy #SUCCES ; writing => set success
test_read:
lda #$04
bit ICAX1Z
beq open_exit
lda #cursor_low_limit
sta read_cursors, x ; reset read_cursor
jsr show_dev
g_nondef:
ldy #SUCCES
open_exit:
rts
; ============
; HATABS_CLOSE
; ============
; IN: ---
; OUT: Y: 01 = success
; >80 = error (see the CIO err_code list)
g_close:
ldx ICDNOZ
dex
lda #$00
sta HPOSP0, x ; move sprite off the screen
ldy #SUCCES
rts
; ============
; HATABS_GET_CHAR
; ============
; IN: X - channel number * $10
; OUT: Y: 01 = success
; >80 = error (see the CIO err_code list)
; A: the byte read
g_get:
lda ICAX1, x
cmp #$09
bne no_wr_append
ldy #WRONLY ; error_code "attempted to read a write-only device"
rts
no_wr_append:
lda ICDNO, x
tax
dex ; dev number 0-3 to X
jsr get_dev_base
lda read_cursors, x ; read_cursor
cmp write_cursors, x ; is lower then write_cursor?
bcc read_ok
ldy #EOFERR ; error_code "end of file"
rts
read_ok:
tay
lda (cur_base), y ; read byte from the device
inc read_cursors, x
ldy #SUCCES
rts
; ============
; HATABS_PUT_CHAR
; ============
; IN: A: the byte to write
; OUT: Y: 01 = success
; >80 = error (see the CIO err_code list)
g_put:
pha
ldy #FNCNOT ; error_code "function not implemented in handler"
lda #$08
bit ICAX1Z ; throw the error
beq exit_w_error
lda ICDNO, x
tax
dex ; dev number 0-3 to X
jsr get_dev_base
lda write_cursors, x
cmp #cursor_top_limit ; still a space in the buff?
bcc write_ok
ldy #EOFERR ; error_code "end of file"
exit_w_error:
pla
rts
write_ok:
tay
pla
sta (cur_base),y ; store the byte in the device
inc write_cursors, x
ldy #SUCCES
rts
; ============
; Get Device Base Address
; ============
; IN: X: device num (0-3)
; OUT: $CE, $CF: current device base :L,:H
get_dev_base:
lda #>begin+2 ; PMBASE + 2
sta cur_base_h
ldy #$00
txa
lsr
bcc dev02
ldy #$80
dev02:
sty cur_base_l
lsr
bcc dev01
inc cur_base_h
dev01:
rts
; ============
; Init Device Data
; ============
; IN: cur_base must be set
; OUT: device data cleared + markers drawn
init_dev_data:
ldy #$80
lda #$00
null_loop:
dey
sta (cur_base),y
bne null_loop
txa
pha
ldx #$07 ; draw visual markers - top
ldy #$0F
marker_bot:
lda visual_markers, x
sta (cur_base),y
dey
dex
bne marker_bot
ldx #$07 ; draw visual markers - bottom
ldy #$70
marker_top:
lda visual_markers, x
sta (cur_base),y
iny
dex
bne marker_top
pla
tax
rts
; ============
; Show Device on the Screen
; ============
; IN: X: device num (0-3)
; OUT: ---
; Show Device on the Screen
show_dev:
lda dev_colors, x
sta PCOLR0, x
lda dev_hpos, x
sta HPOSP0, x
rts
; ============
; main()
; ============
; !!! contains extra PLA at the end
; to be called from BASIC using USR()
g_init:
pha
txa
pha
tya
pha
; Add device using OS method
ldx #'G' ; device name
ldy #<hatabs_handler
lda #>hatabs_handler
jsr PHENTV ; ADD_NEWDEV
bcs exit_init
; set space in RAM and init GTIA
lda #<begin
sta MEMTOP
lda #>begin
sta MEMTOP+1
sta PMBASE
ldx #$03 ; init all devices data
loop_init:
jsr get_dev_base
jsr init_dev_data
dex
bpl loop_init
lda #$00
sta HPOSP0
sta HPOSP1
sta HPOSP2
sta HPOSP3
lda #$01
sta GPRIOR
lda #$03 ; enable sprites
sta GRACTL
lda #$2E ; enable DMA for display list and sprites, set normal playfield width
sta SDMCTL
exit_init:
pla
tay
pla
tax
pla
pla
rts
end:
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word begin ; zacatek kodoveho segmentu
.word end-1 ; konec kodoveho segmentu
; finito
Komplexní demo ukazující všechny možnosti zařízení G
Na závěr si ukažme ještě jedno rozsáhlejší demo, ve kterém jsou volány (prakticky) všechny operace, které jsou zařízeními G: podporovány. Toto demo je naprogramované v Atari BASICu a po spuštění se pokusí nalézt soubor g_device.xex, což je výsledek překladu demonstračního příkladu s ovladačem zařízeních G (viz předchozí kapitolu). V emulátoru osmibitových Atari je ovšem nutné vhodným způsobem nakonfigurovat zařízení H:, tj. povolit ho a vybrat adresář, ve kterém je soubor g_device.xex uložen. Samotné demo po načtení ovladače bude automaticky pokračovat – každá prováděná operace je okomentována a mezi jednotlivými operacemi je vložena časová prodleva (takže se vlastně jedná o skutečné demo).
Obrázek 13: Načtení ovladače zařízení G.
Autor: tisnik, podle licence: Rights Managed
Obrázek 14: Zápis do zařízení G3.
Autor: tisnik, podle licence: Rights Managed
Obrázek 15: Kopie dat mezi zařízením G3: a G4:.
Autor: tisnik, podle licence: Rights Managed
Obrázek 16: Čtení a kopie dat mezi zařízeními G.
Autor: tisnik, podle licence: Rights Managed
Obrázek 17: Ukončení dema.
Autor: tisnik, podle licence: Rights Managed
Zdrojový kód tohoto dema vytvořil opět Milan Vančura:
5 DIM A$(100):WAIT=3000:SEC=1:GDEVCLOSE=3120:GDEVCLEAR=3100:GDEVCHECK=1100:GDEVINIT=1400 6 DIM XEX$(50):XEX$="H:G_DEVICE.XEX":GDEVFORCEINIT=0 7 GOSUB GDEVCHECK 9 GOSUB GDEVINIT 11 DATA FJBBB,DJBDN,LBDBL,FJNBB 12 DATA NHLBL,FHLJF 13 ? "}WRITING RECORDS:" 14 OPEN #2,8,0,"G3:":? "REC "; 15 FOR I=1 TO 4:READ A$ 16 ? I;" ";:? #2;" ";A$;" " 17 GOSUB WAIT:NEXT I 20 ? :? "CLOSING DEVICE" 30 CLOSE #2:GOSUB WAIT 31 ? :? :? "OPENING FOR READING" 35 ? " AND COPY DATA TO ANOTHER ONE":? "COPY "; 40 OPEN #2,4,0,"G3:":OPEN #3,8,0,"G4:" 50 FOR I=1 TO 4 52 ? I;" ";:INPUT #2,A$:? #3;A$ 54 GOSUB WAIT:NEXT I 56 CLOSE #2:CLOSE #3 60 GOSUB WAIT 61 ? :? "OPENING FOR READING BACK" 64 OPEN #2,4,0,"G4:" 70 FOR I=1 TO 4 72 INPUT #2,A$:? I;": '";A$;"'" 74 GOSUB WAIT:NEXT I 80 CLOSE #2:GOSUB WAIT 81 ? :? "OPENING TO APPEND DATA" 84 OPEN #2,9,0,"G3:" 90 FOR I=5 TO 6 92 READ A$:? I;" ";:? #2;" ";A$;" " 94 GOSUB WAIT:NEXT I 96 CLOSE #2:GOSUB WAIT 100 ? "}OPENING G4 FOR BOTH READING AND APPEND" 102 OPEN #2,13,0,"G4:" 104 TRAP 110:? "READING ÆÉÖÅ ITEMS" 106 FOR I=1 TO 5:INPUT #2,A$:? I;" "; 108 GOSUB WAIT:NEXT I 110 ? :? "ERROR: ";PEEK(195) 112 ? :? "DEVICE STILL WORKS, ADDING DATA..." 114 RESTORE 12 120 FOR I=5 TO 6:READ A$:? I;" ";:? #2;" ";A$;" " 122 GOSUB WAIT:NEXT I 130 ? :? "WE CAN READ MORE ÎÏ×" 132 FOR I=5 TO 6:INPUT #2,A$:? I;" "; 134 GOSUB WAIT:NEXT I 140 CLOSE #2:? "CLOSING DEVICE" 142 GOSUB WAIT 150 ? "...":GOSUB WAIT 152 ? "AS NEITHER DEVICE WAS REOPEN" 154 ? "FOR NEW WRITE," 156 ? "DATA IS STILL THERE, IN BOTH ONES" 160 OPEN #2,4,0,"G3:":OPEN #3,4,0,"G4:" 162 SEC=2:GOSUB WAIT:GOSUB GDEVCLOSE 999 END 1100 REM CHECK G DEVICE IS PRESENT 1105 ? :? "CHECKING G DEVICE..."; 1110 HATABS=794 1115 GDEVLOAD=1200:GDEVFOUND=1150:GDEVNOTFOUND=1190 1120 FOR X=0 TO 37 STEP 3 1122 D=PEEK(HATABS+X) 1125 IF D=0 THEN GOTO GDEVNOTFOUND 1130 H=PEEK(HATABS+1+X)+256*PEEK(HATABS+2+X) 1132 REM ? CHR$(D);": ";H 1135 IF CHR$(D)="G" THEN GDEV=X:GOTO GDEVFOUND 1140 NEXT X:GOTO GDEVNOTFOUND 1150 FOR A=2 TO 4 STEP 2 1160 AC=PEEK(H+A)+256*PEEK(H+A+1) 1170 IF PEEK(AC)<>96 THEN GOTO GDEVNOTFOUND 1180 NEXT A 1185 ? "OK":RETURN 1190 ? "NOT FOUND":GDEVFORCEINIT=1 1195 GOSUB GDEVLOAD:RETURN 1200 REM G DEVICE NOT FOUND - LOADING 1210 ? :? "LOADING G DRIVER FROM XEX FILE":? XEX$,"..."; 1220 TRAP 1330:OPEN #2,4,0,XEX$ 1230 GET #2,X:GET #2,X 1240 GET #2,L:GET #2,H:P=L+256*H 1250 GET #2,X:GET #2,X 1260 TRAP 1300 1270 FOR R=P TO 65535 1280 GET #2,V:POKE R,V 1290 NEXT R 1300 CLOSE #2 1310 ? "OK":GOSUB WAIT:? "}" 1320 RETURN 1330 ? "CANNOT LOAD XEX FILE ";XEX$ 1340 ? "ABORTING" 1350 STOP 1400 REM ASK FOR G DRIVER INTIALIZATION 1410 REM INIT ADDRESS OF G_DEVICE V.1.1 1420 INIT=39176 1430 IF GDEVFORCEINIT THEN A=USR(INIT):RETURN 1440 ? "INIT G DEVICE";:INPUT A$:A$(LEN(A$)+1)=" " 1450 IF A$(1,1)="Y" THEN A=USR(INIT) 1460 RETURN 3000 REM WAIT FOR <SEC> SECONDS 3010 FOR W=1 TO SEC*700:NEXT W:RETURN 3100 REM RE-INIT G DEVICES G3, G4 3110 CLOSE #2:CLOSE #3:OPEN #2,8,0,"G3:":OPEN #3,8,0,"G4:" 3120 REM CLOSE G DEVICES G3, G4 3130 CLOSE #2:CLOSE #3 3140 RETURNPří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ž aktuální (stále rozšiřovaný) 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 \
cio_put_char.xex cio_put_block.xex \
cio_get_block.xex cio_modify_block.xex \
cio_print_1.xex cio_print_2.xex \
cio_print_3.xex cio_print_4.xex \
cio_read_file.xex cio_read_file_2.xex \
cio_write_byte_1.xex cio_write_byte_2.xex \
cio_write_byte_3.xex cio_custom_device.xex \
cio_null_device.xex cio_null_device_err \
g_device.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
g_device.xex: g_device.o
ld65 -C g_device.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
cio: \
cio_put_char.xex \
cio_put_block.xex \
cio_get_block.xex \
cio_modify_block.xex \
cio_print_1.xex \
cio_print_2.xex \
cio_print_3.xex \
cio_print_4.xex \
cio_read_file.xex \
cio_read_file_2.xex \
cio_write_byte_1.xex \
cio_write_byte_2.xex \
cio_write_byte_3.xex \
cio_custom_device.xex \
cio_null_device.xex \
cio_null_device_err
Výsledkem překladu jsou ve všech případech 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říkladyVš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. Doplněny jsou též odkazy na pomocné skripty naprogramované v Atari BASICu:
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | Podpora unifikované práce se zařízeními v operačním systému Atari: CIO (2.část) | 0 | 7.46 | 05-08-2026 |
| 2 | Klíčová slova „auto“ a „typeof“ v C23 | 0 | 6.85 | 03-08-2026 |
| 3 | «СберЗдоровье» и АТИМО создали единую систему медицинских и технических осмотров для бизнеса, связанного с логистикой | 0 | 6.85 | 13-08-2026 |
| 4 | «СберЗдоровье» и АТИМО создали единую систему медицинских и технических осмотров для бизнеса, связанного с логистикой | 0 | 6.85 | 13-08-2026 |
| 5 | Работа на опережение: операционная эффективность предприятия | 0 | 0 | 18-01-2019 |
| 6 | 10 вопросов поставщику ITAM-системы перед пилотом | 0 | 25.47 | 07-08-2026 |
| 7 | Минпромторг призвал включить в реестр промпродукции разработки вузов в сфере ИИ | 0 | 0 | 13-05-2025 |
| 8 | Минцифры предложило обязать создателей ПО обеспечивать его работу на отечественных ОС | 0 | 0 | 22-11-2022 |
| 9 | Реестр отечественного ПО без юридического квеста: инструкция для авторов решений 1С | 0 | 6.17 | 24-07-2026 |
| 10 | Платформа Foxconn D51S и мирный 2-ядерный Atom | 0 | 8.93 | 27-04-2010 |