VBI – Vertical Blank Interrupt
Co se dozvíte v článku
- VBI – Vertical Blank Interrupt
- Krátké připomenutí: příklad s aktivním čekáním na dokončení překreslení snímku
- Podprogram pro pohyb hráče joystickem realizovaný ve VBI
- První způsob nastavení vektoru pro VBI
- Úplný zdrojový kód druhého demonstračního příkladu
- Druhý způsob nastavení vektoru pro VBI
- Úplný zdrojový kód třetího demonstračního příkladu
- Kombinace DLI a VBI v jednom programu
- Úplný zdrojový kód čtvrtého demonstračního příkladu
- Odstranění nadbytečné instrukce RTS na konci obsluhy VBI
- Úplný zdrojový kód pátého demonstračního příkladu
- Refaktoring kódu v assembleru: subrutiny a makra
- Přepis všech částí programu do formy podprogramů (subrutin)
- Úplný zdrojový kód šestého demonstračního příkladu
- Přepis všech částí programu do formy maker
- Úplný zdrojový kód sedmého demonstračního příkladu
- Obsah navazujícího článku
- Příloha: Makefile pro překlad všech demonstračních příkladů
- Repositář s demonstračními příklady
- Odkazy na Internetu
V předchozím článku o vývoji her a dalších aplikací pro osmibitové domácí mikropočítače Atari jsme si představili základní koncepty DLI neboli Display List Interruptu. Připomeňme si, že přímo v display listu, který popisuje strukturu obrazové paměti i způsob jejího vykreslení, je možné u libovolných řádků povolit tzv. DLI. Čip ANTIC u takových řádků (nepřímo) zavolá podprogram (subrutinu), která DLI obslouží. Tyto podprogramy musí být poměrně rychlé, protože pro obsluhu DLI je k dispozici přibližně 114 cyklů, přičemž jedna strojová instrukce trvá 2 až 8 cyklů. V DLI se tedy provádí jen základní operace, které souvisí s jednotlivými částmi obrazovky (zobrazení více spritů, vícebarevné pozadí, přepnutí znakové sady atd.).
Ovšem navíc a prakticky zcela nezávisle na DLI je možné naprogramovat subrutinu či subrutiny, které se spouští po vykreslení celého snímku, tedy ještě před začátkem dalšího snímku. Popř. je možné sice začít s výpočtem před začátkem dalšího snímku, ale výpočet může pokračovat i při jeho vykreslování (více než 20000 strojových cyklů). K tomuto účelu slouží VBI neboli Vertical Blank Interrupt. Výhodou VBI je, že programátor má k dispozici mnohem větší počet strojových cyklů, takže se mnohdy celá herní logika vejde právě do VBI. Obsluha VBI je nepatrně složitější, než v případě DLI. Celé zpracování je totiž rozděleno do čtyř částí (což jsou jednotlivé podprogramy, které se vzájemně volají):
| Rutina | Poznámka |
|---|---|
| VBLANK | lze naprogramovat, přibližně 2500 strojových cyklů (3800 bez systémové části) |
| SYSVBV | systémový podprogram |
| Odložený VBLANK | cca 20000 cyklů (cca 4500 instrukcí), z toho přibližně 2778 při kreslení prázdných řádků na začátku obrazovky |
| XITVBV | zavoláno při opouštění odloženého VBLANKu |
Programátor tedy může vytvořit dva podprogramy pro obsluhu VBI. První podprogram by měl mít délku do cca 2500 strojových cyklů a jeho předností je, že je skutečně vykonáván tehdy, pokud se neprovádí vykreslování (posílají se SYNCy do televizoru a paprsek se vrací na začátek obrazovky). Druhý podprogram pro „odložený“ VBLANK může být mnohem delší, protože se provádí během vykreslování snímku. Ovšem na začátku obrazovky se typicky nachází 24 prázdných obrazových řádků, takže několik set cyklů (uvádí se 2778, ovšem podle mě je to méně, 2778 cyklů odpovídá 24×63,5µs) lze využít pro provádění grafických operací, které nezpůsobí problikávání obrazovky atd. (například pohyby spritů).
Krátké připomenutí: příklad s aktivním čekáním na dokončení překreslení snímku
V předchozích částech tohoto seriálu jsme si mj. ukázali způsob vykreslování PMG (Player Missile Graphics), tj. pohyblivých objektů na obrazovce (neboli spritů). Dokonce jsme tyto sprity ovládali s využitím joysticku. Ovšem právě při ovládání spritů, což je vlastně jedna z forem primitivní animace, bylo nutné zajistit, aby se rychlost pohybu sesynchronizovala s generováním snímků na obrazovce. Tím se zajistí, že rychlost výsledné hry bude konstantní a nebude zapotřebí hlídat časovače atd.
Připomeňme si, že čekání na dokončení snímku jsme (alespoň prozatím) prováděli aktivním čekáním na změnu hodnoty časovače RTCLOK, přesněji řečeno na změnu jeho nejnižšího bajtu. Využívalo se přitom toho faktu, že časovač je modifikován právě po dokreslení celého snímku:
; ---------------------------------------------------------------------
; subrutina pro čekání na konec snímku
; ---------------------------------------------------------------------
.proc _wait_vsync
ldx RTCLOK+2 ; čekání na konec snímku
@wt: cpx RTCLOK+2
beq @wt
rts
.endproc
Úplný zdrojový kód příkladu, ve kterém je realizován pohyb prvním hráčem s využitím joysticku, vypadal následovně:
; ---------------------------------------------------------------------
; Ovládání PMG joystickem v horizontálním směru.
; Aktivní čekání na dokončení vykreslování snímku.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #100 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #120 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #0 ; priorita hráčů a pozadí
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+50, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+50, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+50, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+50, x ; uložit byte - čtvrtý hráč
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
loop:
jsr horizontal_movement
jmp loop
.endproc
; ---------------------------------------------------------------------
; subrutina pro horizontální posun prvního hráče
; ---------------------------------------------------------------------
.proc horizontal_movement
ldx x_pos ; původní pozice prvního hráče
txa ; uložení obsahu X do akumulátoru
jsr _wait_vsync ; čekání na další snímek
jsr _wait_vsync ; čekání na další snímek
tax ; obnovení obsahu X z akumulátoru
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos ; zapamatovat si pozici prvního hráče
rts
.endproc
; ---------------------------------------------------------------------
; subrutina pro čekání na konec snímku
; ---------------------------------------------------------------------
.proc _wait_vsync
ldx RTCLOK+2 ; čekání na konec snímku
@wt: cpx RTCLOK+2
beq @wt
rts
.endproc
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
; horizontální pozice hráče
x_pos: .byte 80
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
Podprogram pro pohyb hráče joystickem realizovaný ve VBI
Předchozí demonstrační příklad ovšem není v žádném případě dokonalý. Jedním z problémů je fakt, že je změna pozice hráče realizována přímo v hlavní programové smyčce a druhý problém spočívá v nutnosti aktivního čekání na vykreslení snímku. Tím se ovšem zbytečně „pálí“ strojové cykly, kterých pochopitelně mikroprocesor MOS 6502 nemá nekonečné množství. Jedno z možných řešení obou problémů (současně) je přesun kódu pro posun hráče s využitím joysticku přímo do obsluhy VBI. Samotný přesun kódu je snadný; výsledná subrutina vypadá následovně (již neobsahuje volání subrutiny _wait_vsync, tu totiž vůbec nepotřebujeme):
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
ldx x_pos ; původní pozice prvního hráče
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos ; zapamatovat si pozici prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
Obrázek 1: Posun spritů je řešen v rámci VBI.
Obrázek 2: Posun spritů je řešen v rámci VBI (první sprite byl posunut joystickem).
Obrázek 3: Posun spritů je řešen v rámci VBI (první sprite lze posunout až přes okraj obrazovky).
První způsob nastavení vektoru pro VBI
Prozatím jsme vytvořili nový podprogram (subrutinu) nazvanou horizontal_movement. Assembler pochopitelně v pozdější fázi překladu zná počáteční adresu tohoto podprogramu. Jedná se o šestnáctibitovou adresu, kterou je možné s využitím operátorů > a < rozdělit na vyšší a nižší bajt adresy. Tato vlastnost assembleru se nám bude hodit, protože musíme adresu subrutiny horizontal_movement uložit do vektoru VVBLKD, jenž je umístěný na adresách 0×224 a 0×225:
VVBLKD = $0224 ;DEFERRED VERTICAL BLANK NMI VECTOR
Poněkud naivní (a ne zcela bezpečné) zaregistrování subrutiny horizontal_movement může vypadat takto:
; nastavit vektor pro odloženou VBI
lda #>horizontal_movement
sta VVBLKD+1
lda #<horizontal_movement
sta VVBLKD
Hlavní herní smyčka se nyní zredukuje do následující triviální podoby, protože ovládání hráče je provedeno v obslužné subrutině VBI:
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
Úplný zdrojový kód druhého demonstračního příkladu
Dnešní druhý demonstrační příklad, ve kterém je pohyb hráče řešen v obsluze VBI, vypadá následovně:
; ---------------------------------------------------------------------
; Ovládání PMG joystickem v horizontálním směru.
; Pohyb PMG realizovaný ve VBI.
;
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #100 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #120 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #0 ; priorita hráčů a pozadí
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+50, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+50, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+50, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+50, x ; uložit byte - čtvrtý hráč
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
; nastavit vektor pro odloženou VBI
lda #>horizontal_movement
sta VVBLKD+1
lda #<horizontal_movement
sta VVBLKD
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
ldx x_pos ; původní pozice prvního hráče
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos ; zapamatovat si pozici prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
; horizontální pozice hráče
x_pos: .byte 80
end:
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Druhý způsob nastavení vektoru pro VBI
Jak jsme si řekli v předchozím textu, nemusí být přímá modifikace adresy uložené do vektoru VVBLKD zcela bezpečná. Vhodnější je využít systémový podprogram (nebo, chcete-li, službu operačního systému) nazvanou SETVBV. Tato služba se zavolá skokem do subrutiny se stejným jménem, ovšem navíc je zapotřebí nastavit pracovní registry mikroprocesoru MOS 6502 následujícím způsobem:
- Do akumulátoru A se uloží konstanta 6, pokud měníme subrutinu zavolanou přímo při vzniku VBI (zde je ovšem počet cyklů omezen) nebo hodnotu 7, pokud měníme „odloženou“ obsluhu VBI.
- Do index registru X je uložen vyšší bajt adresy obslužné subrutiny.
- Do index registru Y je uložen nižší bajt adresy obslužné subrutiny.
To tedy znamená, že nastavení adresy „odložené“ obsluhy VBI může vypadat následovně:
; nastavit vektor pro odloženou VBI
lda #7 ; změna vektoru pro odložené VBI
ldx #>horizontal_movement
ldy #<horizontal_movement
jsr SETVBV ; zavolat službu systému pro nastavení vektoru
Hlavní herní smyčka zůstává stále stejná, tj.:
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
Úplný zdrojový kód třetího demonstračního příkladu
Úplný zdrojový kód dnešního třetího demonstračního příkladu vypadá následovně:
; ---------------------------------------------------------------------
; Ovládání PMG joystickem v horizontálním směru.
; Pohyb PMG realizovaný ve VBI.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #100 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #120 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #0 ; priorita hráčů a pozadí
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+50, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+50, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+50, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+50, x ; uložit byte - čtvrtý hráč
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
; nastavit vektor pro odloženou VBI
lda #7 ; změna vektoru pro odložené VBI
ldx #>horizontal_movement
ldy #<horizontal_movement
jsr SETVBV ; zavolat službu systému pro nastavení vektoru
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
ldx x_pos ; původní pozice prvního hráče
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos ; zapamatovat si pozici prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
; horizontální pozice hráče
x_pos: .byte 80
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
Kombinace DLI a VBI v jednom programu
V předchozím článku jsme si popsali způsob nastavení DLI v display listu i to, jak může vypadat subrutina, která DLI obslouží. A dnes jsme se zabývali naprogramováním subrutiny zavolané při VBI, tj. při dokončení vykreslení snímku. Nabízí se tedy pochopitelná otázka – je možné DLI a VBI zkombinovat v jednom programu? Samozřejmě to možné je a právě tento způsob je využit v mnoha hrách pro osmibitové mikropočítače Atari. Pouze si musíme uvědomit, že subrutiny pro obsluhu DLI (pochopitelně) vyžadují určitý počet strojových cyklů MOS 6502. A tyto cykly potom budou chybět pro odložené VBI.
Připomeňme si, že zatímco VBI je po resetu Atari povoleno (resp. se o to postará operační systém), je nutné DLI povolit explicitně, například takto:
; povolení DLI
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
Poté již můžeme nastavit oba vektory obsahující adresy pro obsluhu DLI a VBI:
; vektor DLI
lda #<dli ; nastavení DLI
sta VDSLST
lda #>dli
sta VDSLST+1
; -------------------------------------------------
; nastavit vektor pro odloženou VBI
lda #7 ; změna vektoru pro odložené VBI
ldx #>horizontal_movement
ldy #<horizontal_movement
jsr SETVBV ; zavolat službu systému pro nastavení vektoru
Obslužná subrutina pro DLI končí takto:
rti ; návrat z DLI
Zatímco obslužná rutina pro odložené VBI bude končit:
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
Obrázek 4: Posun spritů je řešen v rámci VBI, změna barev pozadí pomocí DLI.
Obrázek 5: Posun spritů je řešen v rámci VBI, změna barev pozadí pomocí DLI (posun prvního spritu joystickem).
Obrázek 6: Posun spritů je řešen v rámci VBI, změna barev pozadí pomocí DLI (posun prvního spritu joystickem až na okraj obrazovky).
Úplný zdrojový kód čtvrtého demonstračního příkladu
Oba podprogramy, z nichž jeden je vyvolán při každém DLI a druhý při VBI, byly zakomponovány do jediného programu, jehož úplný zdrojový kód vypadá následovně:
; ---------------------------------------------------------------------
; Ovládání PMG joystickem v horizontálním směru.
; Změna pozice PMG realizovaná ve VBI.
; Změna barvy herního pole realizovaná v DLI.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
ldy #0 ; počitadlo zápisů
clear:
tya ; kód vypisovaného znaku
sta screen, y ; tisk znaku na zvolené místo na obrazovce
sta screen+40*8, y
sta screen+40*16, y
iny ; zvětšit hodnotu počitadla a offsetu
cpy #$80
bne clear ; skok
; vektor DLI
lda #<dli ; nastavení DLI
sta VDSLST
lda #>dli
sta VDSLST+1
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #100 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #120 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #1 ; priorita hráčů a pozadí
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+100, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+100, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+100, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+100, x ; uložit byte - čtvrtý hráč
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
; nastavit vektor pro odloženou VBI
lda #7 ; změna vektoru pro odložené VBI
ldx #>horizontal_movement
ldy #<horizontal_movement
jsr SETVBV ; zavolat službu systému pro nastavení vektoru
; povolení DLI
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
ldx x_pos ; původní pozice prvního hráče
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos ; zapamatovat si pozici prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda color ; barva pozadí
sta COLPF2 ; přímo nastavit zápisem do HW registru
eor #%10000000 ; negovat nejvyšší bit
sta color
pla ; obnovit akumulátor
rti ; návrat z DLI
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_CHR40x8x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 2 (GR.0)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 6, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 7, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 8, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
color:
.byte $c4 ; původní barva
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
; horizontální pozice hráče
x_pos: .byte 80
end:
.BSS
screen: .res 40*24
.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
Odstranění nadbytečné instrukce RTS na konci obsluhy VBI
Ještě jednou se vraťme k podprogramu horizontal_movement, který se volá v rámci obsluhy VBI:
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
ldx x_pos ; původní pozice prvního hráče
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos ; zapamatovat si pozici prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
Zajímavé jsou poslední dvě instrukce:
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
První instrukce provede přímý skok na adresu XITVBV, druhá provádí návrat z podprogramu. Ovšem to je zajímavé – jak se vlastně procesor dozví, že se má nejdříve vrátit za volání XITVBV, aby mohl rts vykonat? Odpovědí je, že se do tohoto místa programu řízení nevrátí, protože XITVBV samo končí instrukcí rts nebo rti. Tudíž je „naše“ rts nadbytečná a můžeme ji odstranit:
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
ldx x_pos ; původní pozice prvního hráče
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos ; zapamatovat si pozici prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
.endproc
Úplný zdrojový kód pátého demonstračního příkladu
Dnešní pátý demonstrační příklad se příliš neliší od příkladu čtvrtého, pouze v něm došlo k odstranění nadbytečné instrukce rts. Pro úplnost si ale ukážeme i takto upravený zdrojový kód:
; ---------------------------------------------------------------------
; Ovládání PMG joystickem v horizontálním směru.
; Změna pozice PMG realizovaná ve VBI.
; Změna barvy herního pole realizovaná v DLI.
; Odstranění nadbytečné instrukce RTS ve VBI.
;
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
ldy #0 ; počitadlo zápisů
clear:
tya ; kód vypisovaného znaku
sta screen, y ; tisk znaku na zvolené místo na obrazovce
sta screen+40*8, y
sta screen+40*16, y
iny ; zvětšit hodnotu počitadla a offsetu
cpy #$80
bne clear ; skok
; vektor DLI
lda #<dli ; nastavení DLI
sta VDSLST
lda #>dli
sta VDSLST+1
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #100 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #120 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #1 ; priorita hráčů a pozadí
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+100, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+100, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+100, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+100, x ; uložit byte - čtvrtý hráč
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
; nastavit vektor pro odloženou VBI
lda #7 ; změna vektoru pro odložené VBI
ldx #>horizontal_movement
ldy #<horizontal_movement
jsr SETVBV ; zavolat službu systému pro nastavení vektoru
; povolení DLI
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
ldx x_pos ; původní pozice prvního hráče
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos ; zapamatovat si pozici prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
.endproc
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda color ; barva pozadí
sta COLPF2 ; přímo nastavit zápisem do HW registru
eor #%10000000 ; negovat nejvyšší bit
sta color
pla ; obnovit akumulátor
rti ; návrat z DLI
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_CHR40x8x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 2 (GR.0)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 6, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 7, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 8, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
color:
.byte $c4 ; původní barva
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
; horizontální pozice hráče
x_pos: .byte 80
end:
.BSS
screen: .res 40*24
.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
Refaktoring kódu v assembleru: subrutiny a makra
Již ve druhém článku o programování pro osmibitová Atari jsme si řekli, že programy psané v assembleru jsou psány na relativně nízké úrovni abstrakce. Proto je prakticky nutné, alespoň u rozsáhlejších aplikací, nějakým způsobem neustále provádět jejich refaktoring, protože v opačném případě bychom získali „špagetový kód“ skládající se z nic neříkajících instrukcí. Assemblery a do jisté míry i samotné mikroprocesory nám nabízí dvě základní techniky, které je možné využít a různým způsobem je kombinovat. Jedná se o rozdělení úlohy na jednotlivé podprogramy (subrutiny), ideálně s rozumně nastaveným rozhraním, které specifikuje, jakým způsobem se mají do subrutin předávat parametry. A druhou technologií jsou makra, kterými jsme se zabývali ve třetím článku. Ovšem již samotné rozdělení programu na podprogramy může zvětšit čitelnost a vlastně se dostat na vyšší úroveň abstrakce (o programu budeme uvažovat jako o vzájemně se volajících blocích, nikoli jako o sekvenci instrukcí).
Program, který využívá jak DL a PMG, tak i DLI a VBI (to je zkratek), je již relativně komplikovaný, takže se může vyplatit jeho refaktoring. Oba způsoby, tedy rozdělení na podprogramy popř. do maker, si ukážeme v navazujících kapitolách.
Přepis všech částí programu do formy podprogramů (subrutin)
Přepis programu do formy s podprogramy může vypadat jednoduše. Hlavní část je dobře čitelná, ovšem jak uvidíme dále, ve skutečnosti není příliš obecná. Nalezneme zde pouze volání podprogramů realizované instrukcí JSR (Jump to SubRoutine):
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
jsr set_display_list ; nastavení display listu
jsr fill_screen ; výpis znaků na obrazovku
jsr setup_sprites
jsr set_dli_handler ; nastavení obsluhy DLI
jsr set_vbi_handler ; nastavení obsluhy VBI
jsr enable_interrupts
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
Další části programu jsou realizovány subrutinami a tedy končí instrukcí RTS (ReTurn from Subroutine). Nevýhodou je, že se sice jedná o subrutiny, ale ty pracují s (de facto) globálními symboly screen, dlist a x_pos (a také s adresami obslužných programů pro DLI a VBI):
; ---------------------------------------------------------------------
; subrutina pro vyplnění obrazovky znaky
; ---------------------------------------------------------------------
.proc fill_screen
ldy #0 ; počitadlo zápisů
clear:
tya ; kód vypisovaného znaku
sta screen, y ; tisk znaku na zvolené místo na obrazovce
sta screen+40*8, y
sta screen+40*16, y
iny ; zvětšit hodnotu počitadla a offsetu
cpy #$80
bne clear ; skok
rts
.endproc
; ---------------------------------------------------------------------
; subrutina pro nastavení display listu
; ---------------------------------------------------------------------
.proc set_display_list
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
rts
.endproc
; ---------------------------------------------------------------------
; subrutina pro nastavení všech spritů
; ---------------------------------------------------------------------
.proc setup_sprites
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #100 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #120 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #1 ; priorita hráčů a pozadí
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+100, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+100, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+100, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+100, x ; uložit byte - čtvrtý hráč
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
rts
.endproc
; ---------------------------------------------------------------------
; subrutina pro povolení DLI i VBI
; ---------------------------------------------------------------------
.proc enable_interrupts
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
rts
.endproc
; ---------------------------------------------------------------------
; subrutina pro nastavení obsluhy VBI
; ---------------------------------------------------------------------
.proc set_vbi_handler
; nastavit vektor pro odloženou VBI
lda #7 ; změna vektoru pro odložené VBI
ldx #>horizontal_movement
ldy #<horizontal_movement
jsr SETVBV ; zavolat službu systému pro nastavení vektoru
rts
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
ldx x_pos ; původní pozice prvního hráče
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos ; zapamatovat si pozici prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda color ; barva pozadí
sta COLPF2 ; přímo nastavit zápisem do HW registru
eor #%10000000 ; negovat nejvyšší bit
sta color
pla ; obnovit akumulátor
rti ; návrat z DLI
Úplný zdrojový kód šestého demonstračního příkladu
Po překladu došlo k nárůstu délky programu, protože bylo nutné zakódovat všechny nové instrukce JSR a RTS. Původní délka:
Segment list: ------------- Name Start End Size Align ---------------------------------------------------- AUTOSTRT 000000 000005 000006 00001 EXEHDR 000000 000005 000006 00001 CODE 002000 0020E4 0000E5 00001 BSS 002400 0027BF 0003C0 00001
Nová délka téměř dosahuje velikosti jedné stránky paměti, tj. celých 1/256 adresovací kapacity mikroprocesoru:
Segment list: ------------- Name Start End Size Align ---------------------------------------------------- AUTOSTRT 000000 000005 000006 00001 EXEHDR 000000 000005 000006 00001 CODE 002000 0020FD 0000FE 00001 BSS 002400 0027BF 0003C0 00001
A výsledný zdrojový kód? Ten vypadá takto:
; ---------------------------------------------------------------------
; Ovládání PMG joystickem v horizontálním směru.
;
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
jsr set_display_list ; nastavení display listu
jsr fill_screen ; výpis znaků na obrazovku
jsr setup_sprites
jsr set_dli_handler ; nastavení obsluhy DLI
jsr set_vbi_handler ; nastavení obsluhy VBI
jsr enable_interrupts
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endproc
; ---------------------------------------------------------------------
; subrutina pro vyplnění obrazovky znaky
; ---------------------------------------------------------------------
.proc fill_screen
ldy #0 ; počitadlo zápisů
clear:
tya ; kód vypisovaného znaku
sta screen, y ; tisk znaku na zvolené místo na obrazovce
sta screen+40*8, y
sta screen+40*16, y
iny ; zvětšit hodnotu počitadla a offsetu
cpy #$80
bne clear ; skok
rts
.endproc
; ---------------------------------------------------------------------
; subrutina pro nastavení display listu
; ---------------------------------------------------------------------
.proc set_display_list
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
rts
.endproc
; ---------------------------------------------------------------------
; subrutina pro nastavení všech spritů
; ---------------------------------------------------------------------
.proc setup_sprites
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #100 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #120 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #1 ; priorita hráčů a pozadí
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+100, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+100, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+100, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+100, x ; uložit byte - čtvrtý hráč
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
rts
.endproc
; ---------------------------------------------------------------------
; subrutina pro nastavení DLI i VBI
; ---------------------------------------------------------------------
.proc enable_interrupts
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
rts
.endproc
; ---------------------------------------------------------------------
; subrutina pro povolení VBI
; ---------------------------------------------------------------------
.proc set_vbi_handler
; nastavit vektor pro odloženou VBI
lda #7 ; změna vektoru pro odložené VBI
ldx #>horizontal_movement
ldy #<horizontal_movement
jsr SETVBV ; zavolat službu systému pro nastavení vektoru
rts
.endproc
; ---------------------------------------------------------------------
; subrutina pro nastavení obsluhy DLI
; ---------------------------------------------------------------------
.proc set_dli_handler
; vektor DLI
lda #<dli ; nastavení DLI
sta VDSLST
lda #>dli
sta VDSLST+1
rts
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc horizontal_movement
ldx x_pos ; původní pozice prvního hráče
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos ; zapamatovat si pozici prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
rts
.endproc
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda color ; barva pozadí
sta COLPF2 ; přímo nastavit zápisem do HW registru
eor #%10000000 ; negovat nejvyšší bit
sta color
pla ; obnovit akumulátor
rti ; návrat z DLI
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_CHR40x8x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 2 (GR.0)
.byte @lt;screen, >screen ; počáteční adresa obrazové paměti
.res 6, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 7, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 8, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
color:
.byte $c4 ; původní barva
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
; horizontální pozice hráče
x_pos: .byte 80
end:
.BSS
screen: .res 40*24
.segment "EXEHDR"
.word $ffff ; uvodni sekvence bajtu v souboru XEX
.word main ; zacatek kodoveho segmentu
.word end - 1 ; konec kodoveho segmentu
.segment "AUTOSTRT" ; segment s pocatecni adresou
.word RUNAD ; naplni se pouze adresy RUNAD a RUNAD+1
.word RUNAD+1
.word main ; adresa vstupniho bodu do programu
; finito
Přepis všech částí programu do formy maker
Zajímavější a potenciálně užitečnější může být přepis částí programu do formy maker. Předností tohoto řešení je snadné předávání parametrů do makra, protože to se provádí v době překladu a nejsme tedy omezeni možnostmi čipu MOS 6502, relativně malým zásobníkem atd. atd. Druhou předností je fakt, že program jako celek bude menší a nepatrně rychlejší – ušetří se za instrukce JSR a RTS, ovšem pouze za předpokladu, že každé makro bude použito (expandováno) jen jedenkrát. A je zde ještě jeden malý problém – všechny symboly definované v makru by měly být lokální, aby bylo možné makro v případě potřeby skutečně expandovat vícekrát.
Hlavní část programu v případě, že jsou další jeho části napsané s využitím maker, vypadá poměrně vysokoúrovňově, protože do maker můžeme předávat parametry a vyhnout se tak přímému použití globálních symbolů:
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
set_display_list dlist ; nastavení display listu
fill_screen screen ; výpis znaků na obrazovku
setup_sprites sprite ; nastavení spritů
set_dli_handler dli ; nastavení obsluhy DLI
set_vbi_handler vbi ; nastavení obsluhy VBI
enable_interrupts ; povolení přerušení
game_loop ; hlavní herní smyčka
.endproc
Samotná makra obsahují i definici parametrů:
; ---------------------------------------------------------------------
; makro pro nastavení display listu
; ---------------------------------------------------------------------
.macro set_display_list label
lda #<label ; nižší byte adresy display listu
sta SDLSTL
lda #>label ; vyšší byte adresy display listu
sta SDLSTH
.endmacro
Další makro obsahuje návěští, ovšem to je definováno jako lokální symbol:
; ---------------------------------------------------------------------
; makro pro vyplnění obrazovky znaky
; ---------------------------------------------------------------------
.macro fill_screen screen
.local clear ; definice lokálního symbolu
ldy #0 ; počitadlo zápisů
clear:
tya ; kód vypisovaného znaku
sta screen, y ; tisk znaku na zvolené místo na obrazovce
sta screen+40*8, y
sta screen+40*16, y
iny ; zvětšit hodnotu počitadla a offsetu
cpy #$80
bne clear ; skok
.endmacro
Další makro s lokálními symboly (a bez parametrů):
; ---------------------------------------------------------------------
; makro pro nastavení DLI i VBI
; ---------------------------------------------------------------------
.macro enable_interrupts
.local NMIEN_DLI ; definice lokálního symbolu
.local NMIEN_VBI ; definice lokálního symbolu
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
.endmacro
A konečně si ukažme makra, do kterých se předávají jména subrutin, které mají být zaregistrovány pro obsluhu DLI nebo VBI:
; ---------------------------------------------------------------------
; makro pro nastavení DLI
; ---------------------------------------------------------------------
.macro set_dli_handler handler
; vektor DLI
lda #<handler ; nastavení DLI
sta VDSLST
lda #>handler
sta VDSLST+1
.endmacro
a:
; ---------------------------------------------------------------------
; makro pro nastavení VBI
; ---------------------------------------------------------------------
.macro set_vbi_handler handler
; nastavit vektor pro odloženou VBI
lda #7 ; změna vektoru pro odložené VBI
ldx #>handler
ldy #<handler
jsr SETVBV ; zavolat službu systému pro nastavení vektoru
.endmacro
Úplný zdrojový kód sedmého demonstračního příkladu
Závěrem praktické části dnešního článku si ukážeme úplný zdrojový kód příkladu s DLI i VBI, který je celý realizován s využitím maker:
; ---------------------------------------------------------------------
; Ovládání PMG joystickem v horizontálním směru.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
; ---------------------------------------------------------------------
; definice maker
; ---------------------------------------------------------------------
; ---------------------------------------------------------------------
; makro pro nastavení display listu
; ---------------------------------------------------------------------
.macro set_display_list label
lda #<label ; nižší byte adresy display listu
sta SDLSTL
lda #>label ; vyšší byte adresy display listu
sta SDLSTH
.endmacro
; ---------------------------------------------------------------------
; makro pro vyplnění obrazovky znaky
; ---------------------------------------------------------------------
.macro fill_screen screen
.local clear ; definice lokálního symbolu
ldy #0 ; počitadlo zápisů
clear:
tya ; kód vypisovaného znaku
sta screen, y ; tisk znaku na zvolené místo na obrazovce
sta screen+40*8, y
sta screen+40*16, y
iny ; zvětšit hodnotu počitadla a offsetu
cpy #$80
bne clear ; skok
.endmacro
; ---------------------------------------------------------------------
; makro pro nastavení všech spritů
; ---------------------------------------------------------------------
.macro setup_sprites sprite_address
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #100 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #120 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #1 ; priorita hráčů a pozadí
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
.local addr ; definice lokálního symbolu
addr = 152*256
.local next_line ; definice lokálního symbolu
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite_address-1, x ; načíst
sta addr+PLAYER_0_OFFSET+100, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+100, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+100, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+100, x ; uložit byte - čtvrtý hráč
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
.endmacro
; ---------------------------------------------------------------------
; makro pro nastavení DLI i VBI
; ---------------------------------------------------------------------
.macro enable_interrupts
.local NMIEN_DLI ; definice lokálního symbolu
.local NMIEN_VBI ; definice lokálního symbolu
NMIEN_DLI=$80 ; maska povolení DLI
NMIEN_VBI=$40 ; maska povolení VBI
lda #NMIEN_DLI | NMIEN_VBI ; povolení DLI
sta NMIEN
.endmacro
; ---------------------------------------------------------------------
; makro pro nastavení VBI
; ---------------------------------------------------------------------
.macro set_vbi_handler handler
; nastavit vektor pro odloženou VBI
lda #7 ; změna vektoru pro odložené VBI
ldx #>handler
ldy #<handler
jsr SETVBV ; zavolat službu systému pro nastavení vektoru
.endmacro
; ---------------------------------------------------------------------
; makro pro nastavení DLI
; ---------------------------------------------------------------------
.macro set_dli_handler handler
; vektor DLI
lda #<handler ; nastavení DLI
sta VDSLST
lda #>handler
sta VDSLST+1
.endmacro
; ---------------------------------------------------------------------
; hlavní herní smyčka
; ---------------------------------------------------------------------
.macro game_loop
.local loop ; definice lokálního symbolu
loop:
jmp loop ; program vlastně nice nedělá - jen cyklí!
.endmacro
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
set_display_list dlist ; nastavení display listu
fill_screen screen ; výpis znaků na obrazovku
setup_sprites sprite ; nastavení spritů
set_dli_handler dli ; nastavení obsluhy DLI
set_vbi_handler vbi ; nastavení obsluhy VBI
enable_interrupts ; povolení přerušení
game_loop ; hlavní herní smyčka
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc vbi
ldx x_pos ; původní pozice prvního hráče
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
stx x_pos ; zapamatovat si pozici prvního hráče
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
.endproc
; ---------------------------------------------------------------------
; obsluha DLI
; ---------------------------------------------------------------------
dli:
pha ; uschovat akumulátor
lda color ; barva pozadí
sta COLPF2 ; přímo nastavit zápisem do HW registru
eor #%10000000 ; negovat nejvyšší bit
sta color
pla ; obnovit akumulátor
rti ; návrat z DLI
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_CHR40x8x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 2 (GR.0)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 6, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 7, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_DLI+DL_CHR40x8x1 ; GR.0 ovšem navíc s povolením DLI
.res 8, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, >dlist, >dlist ; skok na začátek display listu
color:
.byte $c4 ; původní barva
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
; horizontální pozice hráče
x_pos: .byte 80
end:
.BSS
screen: .res 40*24
.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
Obsah navazujícího článku
Popisem čipu ANTIC jsme se zabývali již ve čtyřech článcích, a to z toho důvodu, že se jedná o integrovaný obvod nabízející programátorům poměrně velké možnosti. Příště jeho popis dokončíme. Zabývat se budeme především realizací takzvaného jemného scrollingu. Čip ANTIC totiž umožňuje provádění horizontálního a/nebo vertikálního scrollingu s přesností na jednotlivé pixely, a to bez nutnosti přenosu dat ve video paměti. Scrolling je možné realizovat ve všech režimech, ovšem význam má především v režimech textových a pseudotextových (GR.13, GR.14). Ovšem to není vše. Navíc je ještě možné nastavit šířku herního pole (playfiend). K dispozici jsou tři šířky, které se uvádí v počtu takzvaných color cyklů (odpovídá frekvenci změny barvové složky TV signálu): 128 cyklů, 160 cyklů a 192 cyklů. Počet color cyklů odpovídá horizontálnímu rozlišení v grafických režimech B, C, D a E. Naproti tomu v režimu F (GR.8) se jedná o 256, 320 a 384 pixelů (maximální dosažitelné rozlišení je tak přibližně rovno 384×224 pixelů).
Příloha: Makefile pro překlad všech demonstračních příkladů
Všechny minule i dnes popsané demonstrační příklady, pro jejichž překlad je zapotřebí použít assembler ca65 a linker ld65, je možné přeložit s využitím souboru Makefile, jehož obsah je vypsán pod tímto odstavcem:
execs := dummy.xex print_a.xex \
background_color_1.xex background_color_2.xex \
color_computation_1.xex color_computation_2.xex \
subroutine_1.xex subroutine_2.xex \
hex_number_1.xex hex_number_2.xex \
hex_number_3.xex hex_number_4.xex \
hex_number_5.xex hex_number_6.xex \
hex_number_7.xex hex_number_8.xex \
hex_number_9.xex \
fill_block_1.xex fill_block_2.xex \
fill_block_3.xex fill_block_4.xex \
fill_block_5.xex \
pmg_01.xex pmg_02.xex \
pmg_03.xex pmg_04.xex \
pmg_05.xex pmg_06.xex \
pmg_07.xex pmg_08.xex \
pmg_09.xex pmg_10.xex \
pmg_11.xex pmg_12.xex \
pmg_13.xex pmg_14.xex \
pmg_15.xex pong.xex \
pmg_stick_1.xex pmg_stick_2.xex \
pmg_stick_3.xex pmg_stick_4.xex \
pmg_stick_5.xex pmg_stick_6.xex \
pmg_collisions_1.xex pmg_collisions_2.xex \
pmg_collisions_3.xex pmg_collisions_4.xex \
antic_1.xex antic_2.xex \
antic_3.xex antic_4.xex \
antic_5.xex antic_6.xex \
antic_7.xex antic_8.xex \
antic_9.xex antic_A.xex \
antic_B.xex \
antic_bitmap_1.xex antic_bitmap_2.xex \
antic_bitmap_3.xex antic_bitmap_4.xex \
antic_bitmap_5.xex antic_bitmap_6.xex \
antic_bitmap_7.xex antic_bitmap_8.xex \
antic_dli_1.asm antic_dli_2.asm \
antic_dli_3.asm antic_dli_4.asm \
antic_dli_5.asm antic_dli_pmg.asm
all: $(execs)
clean:
rm -f *.o
rm -f *.xex
.PHONY: all clean
%.o: %.asm
ca65 $< -t atari -o $@ -l $(basename $<)_list.asm --list-bytes 100
%.xex: %.o
ld65 -C linker.cfg $< -o $@ -m $(basename $<).map
antic_bitmap_6.xex: antic_bitmap_6.o
ld65 -C linker_image.cfg $< -o $@ -m $(basename $<).map
antic_bitmap_7.xex: antic_bitmap_7.o
ld65 -C linker_image_2.cfg $< -o $@ -m $(basename $<).map
antic_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
Výsledkem překladu jsou soubory s koncovkou .xex, které je možné přímo spustit v emulátoru osmibitových počítačů Atari.
Repositář s demonstračními příklady
Všechny demonstrační příklady, s nimiž jsme se v předchozích článcích i v článku dnešním seznámili a které jsou určeny pro překlad s využitím assembleru ca65, jsou dostupné, jak je zvykem, na GitHubu. V tabulce níže jsou uvedeny odkazy na jednotlivé zdrojové kódy příkladů psané v assembleru i „listingy“ vygenerované samotným assemblerem, ze kterých je patrné, jakým způsobem se jednotlivé příklady přeložily do výsledného XEX souboru:
Odkazy na Internetu
- MOS 6502 instruction set
http://www.6502.org/users/obelisk/6502/instructions.html - EXE File Format Description
https://gury.atari8.info/refs/file_formats_exe.php - XEX Filter – A toolkit to analyze and manipulate Atari binary files
https://www.vitoco.cl/atari/xex-filter/index.html - chkxex.py
https://raw.githubusercontent.com/seban-slt/tcx_tools/refs/heads/master/chkxex.py - ca65 Users Guide
https://cc65.github.io/doc/ca65.html - cc65 Users Guide
https://cc65.github.io/doc/cc65.html - ld65 Users Guide
https://cc65.github.io/doc/ld65.html - da65 Users Guide
https://cc65.github.io/doc/da65.html - Překladače jazyka C pro historické osmibitové mikroprocesory
https://www.root.cz/clanky/prekladace-jazyka-c-pro-historicke-osmibitove-mikroprocesory/ - Překladače programovacího jazyka C pro historické osmibitové mikroprocesory (2)
https://www.root.cz/clanky/prekladace-programovaciho-jazyka-c-pro-historicke-osmibitove-mikroprocesory-2/ - Getting Started Programming in C: Coding a Retro Game with C Part 2
https://retrogamecoders.com/getting-started-with-c-cc65/ - NES game development in 6502 assembly – Part 1
https://kibrit.tech/en/blog/nes-game-development-part-1 - NES 6502 Programming Tutorial – Part 1: Getting Started
https://dev.xenforo.relay.cool/index.php?threads/nes-6502-programming-tutorial-part-1-getting-started.858389/ - Minimal NES example using ca65
https://github.com/bbbradsmith/NES-ca65-example - List of 6502-based Computers and Consoles
https://www.retrocompute.co.uk/list-of-6502-based-computers-and-consoles/ - 6502 – the first RISC µP
http://ericclever.com/6500/ - 3 Generations of Game Machine Architecture
http://www.atariarchives.org/dev/CGEXPO99.html - “Hello, world” from scratch on a 6502 — Part 1
https://www.youtube.com/watch?v=LnzuMJLZRdU - A Tour of 6502 Cross-Assemblers
https://bumbershootsoft.wordpress.com/2016/01/31/a-tour-of-6502-cross-assemblers/ - Adventures with ca65
https://atariage.com/forums/topic/312451-adventures-with-ca65/ - example ca65 startup code
https://atariage.com/forums/topic/209776-example-ca65-startup-code/ - 6502 PRIMER: Building your own 6502 computer
http://wilsonminesco.com/6502primer/ - 6502 Instruction Set
https://www.masswerk.at/6502/6502_instruction_set.html - Chip Hall of Fame: MOS Technology 6502 Microprocessor
https://spectrum.ieee.org/tech-history/silicon-revolution/chip-hall-of-fame-mos-technology-6502-microprocessor - Single-board computer
https://en.wikipedia.org/wiki/Single-board_computer - www.6502.org
http://www.6502.org/ - 6502 PRIMER: Building your own 6502 computer – clock generator
http://wilsonminesco.com/6502primer/ClkGen.html - Great Microprocessors of the Past and Present (V 13.4.0)
http://www.cpushack.com/CPU/cpu.html - Jak se zrodil procesor?
https://www.root.cz/clanky/jak-se-zrodil-procesor/ - Osmibitové mikroprocesory a mikrořadiče firmy Motorola (1)
https://www.root.cz/clanky/osmibitove-mikroprocesory-a-mikroradice-firmy-motorola-1/ - Mikrořadiče a jejich použití v jednoduchých mikropočítačích
https://www.root.cz/clanky/mikroradice-a-jejich-pouziti-v-jednoduchych-mikropocitacich/ - Mikrořadiče a jejich aplikace v jednoduchých mikropočítačích (2)
https://www.root.cz/clanky/mikroradice-a-jejich-aplikace-v-jednoduchych-mikropocitacich-2/ - 25 Microchips That Shook the World
https://spectrum.ieee.org/tech-history/silicon-revolution/25-microchips-that-shook-the-world - Comparison of instruction set architectures
https://en.wikipedia.org/wiki/Comparison_of_instruction_set_architectures - How To Start Learning Atari 8 Bit Assembly For Free
https://forums.atariage.com/topic/300732-how-to-start-learning-atari-8-bit-assembly-for-free/ - WUDSN (Demo Group)
https://www.wudsn.com/ - Machine Language For Beginners
https://www.atariarchives.org/mlb/ - Assembly language: all about I/O
https://www.atarimagazines.com/v3n8/AllAbout_IO.html - Sedmdesátiny assemblerů: lidsky čitelný strojový kód
https://www.root.cz/clanky/sedmdesatiny-assembleru-lidsky-citelny-strojovy-kod/ - Color names
https://atariwiki.org/wiki/Wiki.jsp?page=Color%20names - ATASCII
https://en.wikipedia.org/wiki/ATASCII - Put characters in display ram isn't ATASCII?
https://forums.atariage.com/topic/359973-put-characters-in-display-ram-isnt-atascii/ - ATASCII And Internal Character Code Values
https://www.atariarchives.org/mapping/appendix10.php - Reading ATASCII from the keyboard in assembly
https://forums.atariage.com/topic/361733-reading-atascii-from-the-keyboard-in-assembly/ - Why does the 6502 JSR instruction only increment the return address by 2 bytes?
https://retrocomputing.stackexchange.com/questions/19543/why-does-the-6502-jsr-instruction-only-increment-the-return-address-by-2-bytes - Pushing return address to stack off by 1 byte
https://forums.atariage.com/topic/378206-pushing-return-address-to-stack-off-by-1-byte/ - Intel x86 documentation has more pages than the 6502 has transistors
https://www.righto.com/2013/09/intel-x86-documentation-has-more-pages.html - Clearing a Section of Memory
http://www.6502.org/source/general/clearmem.htm - Practical Memory Move Routines by Bruce Clark
http://www.6502.org/source/general/memory_move.html - 6502 Assembly Programming Guide
https://neumont-gamedev.github.io/posts/retrogamedev-6502-guide/ - Off-by-one error
https://en.wikipedia.org/wiki/Off-by-one_error - 6502 cycle times
https://www.nesdev.org/wiki/6502_cycle_times - Atari TIA
http://www.atarihq.com/danb/tia.shtml - TIA Playfield
http://www.atarihq.com/danb/TIA/Playfield.shtml - Atari Inc.:
ANTIC C012296 (NTSC) Revision D
Atari Incorporated, Sunnyvale CA, 1982 - Atari Inc.:
GTIA C014805 (NTSC) Revision A
Atari Incorporated, Sunnyvale CA, 1982 - Atari 5200
http://www.atariage.com/software_search.html?SystemID=5200 - Atari 5200 Hardware and Accessories
http://www.atariage.com/5200/archives/hardware.html - Atari 5200 Screenshots
http://www.atariage.com/system_items.html?SystemID=5200&ItemTypeID=SCREENSHOT - History of video game consoles (second generation): Wikipedia
http://en.wikipedia.org/wiki/History_of_video_game_consoles_(second_generation) - Atari 5200: Wikipedia
http://en.wikipedia.org/wiki/Atari_5200 - Player-Missile Graphics
https://www.atariarchives.org/agagd/chapter5.php - Sprite (computer graphics)
https://en.wikipedia.org/wiki/Sprite_(computer_graphics) - Atari Graphics Demonstrations by Underground Software, 1985 | Atari 8 bit Demo
https://www.youtube.com/watch?v=h7N9EYSyCkw - Atari 8-bit Display List Interrupts: A Complete(ish) Tutorial
https://playermissile.com/dli_tutorial/ - Atari Assembler Editor manual
https://atariwiki.org/wiki/attach/Atari%20Assembler%20Editor/ATARI%20Assembler%20Editor%20User-s%20Manual-OCR.pdf
