Jemný scrolling na osmibitových mikropočítačích Atari
Co se dozvíte v článku
- Jemný scrolling na osmibitových mikropočítačích Atari
- Jemný scrolling ovládaný řídicími registry čipu ANTIC a instrukcemi display listu
- Kostra programu s realizací jemného horizontálního i vertikálního posunu řízeného joystickem
- Úplný zdrojový kód prvního demonstračního příkladu
- Tisk hodnoty horizontálního a vertikálního offsetu řízeného joystickem
- Úplný zdrojový kód druhého demonstračního příkladu
- Nastavení řádků v display listu pro povolení vertikálního scrollingu
- Jemný vertikální scrolling řízený joystickem
- Úplný zdrojový kód třetího demonstračního příkladu
- Podrobnější pohled na stav obsah obrazovky při scrollingu
- Modifikace display listu: současný jemný horizontální i vertikální scrolling
- Řízení horizontálního i vertikálního scrollingu joystickem
- Úplný zdrojový kód čtvrtého demonstračního příkladu
- Specifikace šířky herního pole
- Úplný zdrojový kód pátého demonstračního příkladu
- Ukázky display listů použitých v reálných hrách
- Komplikovanější display listy áčkových her
- Příloha: Makefile pro překlad všech demonstračních příkladů
- Repositář s demonstračními příklady
- Odkazy na Internetu
Grafický řadič ANTIC tvoří nedílnou součást osmibitových mikropočítačů Atari. V předchozích dílech jsme si ukázali základní způsoby využití display listu, DLI (přerušení vyvolaná na zvolených řádcích) a VBI (přerušení vyvolaná na konci vykreslování celého snímku). Již tyto techniky dokážou i na relativně slabých osmibitových Atari „vyčarovat“ zajímavé grafické scény. Ovšem ještě nám zbývá popis dvou vlastností ANTICu.
První vlastností je podpora pro takzvaný jemný scrolling (fine scrolling), který umožňuje posuny obrazu ve vertikálním směru s přesností na obrazový řádek a ve směru horizontálním s přesností na jeden či dva pixely (podle použitého režimu). Důležité je, že tento scrolling lze provádět bez nutnosti přesunu obrazových dat, což je v přímém kontrastu se scrollingem na PC (před zavedením karty VGA) či například se ZX Spectrem. A poslední vlastností ANTICu, s níž se dnes seznámíme, je možnost zúžit či naopak rozšířit herní scénu v horizontálním směru. Podporovány jsou šířky 128/160/192 pixelů v režimech s barvami resp. 256/320/384 pixelů v režimech monochromatických.
Jemný scrolling ovládaný řídicími registry čipu ANTIC a instrukcemi display listu
Již v úvodním článku o čipu ANTIC jsme si řekli, že se tento integrovaný obvod konfiguruje přes patnáct řídicích registrů. Těchto registrů je tedy mnohem méně, než je tomu v případě čipu GTIA (ten obsahuje registry pro ovládání PMG, kolizní registry, barvové registry atd.), ovšem na druhou stranu ANTIC využívá několik datových struktur uložených v paměti ROM nebo v operační paměti. Mezi tyto struktury patří především takzvaný display list a taktéž znaková sada (1024 bajtů neboli čtyři stránky paměti). Vraťme se však k řídicím registrům čipu ANTIC. Ty jsou vypsány v následující tabulce:
| Jméno | Význam | Čtení/zápis | Stínový registr |
|---|---|---|---|
| DMACTL | Direct Memory Access Control | W | SDMCTL |
| CHACTL | Character Control | W | CHART |
| DLISTL | Display List Pointer (low byte) | W | SDLSTL |
| DLISTH | Display List Pointer (high byte) | W | SDLSTH |
| HSCROL | Horizontal Fine Scroll | W | |
| VSCROL | Vertical Fine Scroll | W | |
| PMBASE | Player/Missile Base Address | W | |
| CHBASE | Character Set Base Address | W | CHBAS |
| WSYNC | Wait for Horizontal Sync | W | |
| VCOUNT | Vertical Line Counter | R | |
| PENH | Light Pen Horizontal Position | R | LPENH |
| PENV | Light Pen Vertical Position | R | LPENV |
| NMIEN | Non-Maskable Interrupt (NMI) Enable | W | |
| NMIRES | Non-Maskable Interrupt (NMI) Reset | W | |
| NMIST | Non-Maskable Interrupt (NMI) Status | R |
V dnešním článku nás budou zajímat především řídicí registry nazvané HSCROL a VSCROL. Jedná se o registry určené jen pro zápis, což je poněkud nepříjemné, protože není možné přečíst jejich obsah a modifikovat ho. Pro nás to bude znamenat jediné – údaj o horizontální a vertikálním posunu bude uložen v operační paměti a do registrů HSCROL a VSCROL bude zapisována jen pozměněná hodnota. Oba registry jsou sice osmibitové, ovšem význam mají pouze spodní čtyři bity, takže posun je možný o 0 až 15 jednotek v horizontálním a/nebo vertikálním směru.
Tyto dva řídicí registry jsou dostupné (pro zápis) na adresách 0×d4004 a 0×d4005. Obě adresy jsou dostupné i v assembleru CA65 ve formě symbolů HSCROL a VSCROL, ovšem samozřejmě pouze za předpokladu, že načteme soubor atari.inc:
ANTIC = $D400 ;ANTIC area HSCROL = ANTIC + $04 ;horizontal scroll VSCROL = ANTIC + $05 ;vertical scroll
Kostra programu s realizací jemného horizontálního i vertikálního posunu řízeného joystickem
Všechny dnešní demonstrační příklady jsou založeny na stejném základě, ve kterém jsou využity jak makra assembleru, tak i několik subrutin. Kostra programu je tvořena subrutinou main, ve které se nachází jen volání čtyř maker (první tři z nich mají parametry):
; ---------------------------------------------------------------------
; vstupní bod do programu
; ---------------------------------------------------------------------
.proc main
set_display_list dlist ; nastavení display listu
fill_screen screen ; výpis znaků na obrazovku
set_vbi_handler scroll ; nastavení obsluhy VBI
game_loop ; hlavní herní smyčka
.endproc
Makro pro nastavení display listu jsme si již ukazovali minule, takže jen krátce: tomuto makru se předává návěští, na kterém začíná display list. Adresa display listu je assemblerem rozdělena na vyšší a nižší bajt. Tyto dvě hodnoty jsou uloženy do řídicích registrů SDLSTH a SDLSTH čipu ANTIC:
; ---------------------------------------------------------------------
; 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
Samotný display list zobrazí 24 řádků standardního textového režimu ANTIC 2 (GR.0):
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 23, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Makro pro vyplnění (části) obrazovky je jednoduché. Předává se mu adresa prvního bajtu ve video RAM a makro provádí zápis do tří oblastí na obrazovce. V textovém režimu se zobrazí všech 128 znaků ATASCII ve třech opakujících se blocích:
; ---------------------------------------------------------------------
; 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
Následuje další makro, ve kterém se adresa subrutiny pro obsluhu VBI rozdělí na vyšší a nižší bajt. Následně se zavolá služba SETVBV, která handler (obslužnou subrutinu) zaregistruje:
; ---------------------------------------------------------------------
; 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 s implementací herní smyčky pravděpodobně není nutné podrobně vysvětlovat, protože se jedná o prázdnou nekonečnou smyčku:
; ---------------------------------------------------------------------
; 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
Poslední část zdrojového kódu příkladu obsahuje definici subrutiny (podprogramu), který je volán v každém VBI. V kódu načítáme náklon joysticku a na základě toho, zda je joystick nakloněn doleva nebo doprava měníme obsah paměti na adrese x_scroll. Na konci VBI handleru se volá služba systému XITVBV:
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc scroll
ldx x_scroll ; 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 x_scroll ; zapamatovat si pozici prvního hráče
; scrolling
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
.endproc
Výsledkem by měla být obrazovka s následujícím obsahem:
Obrázek 1: Obrazovka dnešního prvního demonstračního příkladu.
Úplný zdrojový kód prvního demonstračního příkladu
Ukažme si nyní pro úplnost celý zdrojový kód dnešního prvního demonstračního příkladu (ve kterém se ještě scrolling neprovádí):
.include "atari.inc"
.CODE
; ---------------------------------------------------------------------
; 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í 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 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
; ---------------------------------------------------------------------
; 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
set_vbi_handler scroll ; nastavení obsluhy VBI
game_loop ; hlavní herní smyčka
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc scroll
ldx x_scroll ; 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 x_scroll ; zapamatovat si pozici prvního hráče
; scrolling
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
.endproc
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 23, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
; horizontální scrolling
x_scroll: .byte 0
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
Tisk hodnoty horizontálního a vertikálního offsetu řízeného joystickem
Abychom správně a do všech detailů pochopili, jak je vlastně jemný scrolling čipem ANTIC realizován, necháme si na obrazovce zobrazit stav proměnných (bajtů uložených v operační paměti), které obsahují jemný horizontální i vertikální posun. Důležité je, že rozsah hodnot posunu je omezen na 0 až 15, takže načtenou hodnotu instrukcí AND omezíme právě na tento rozsah. Navíc ještě předtím hodnotu vydělíme čtyřmi dvojicí posunů doprava (LSR). Proč tuto operaci provádíme? VBI je voláno 50× nebo 60× za sekundu (v závislosti na použité TV normě), což vlastně znamená, že po náklonu joysticku by se počitadla měnila velmi rychle. Vydělením čtyřmi vlastně frekvenci změn snížíme na úroveň, která je dobře zvládnutelná (člověk s průměrnou reakční dobou dokáže nastavit hodnotu přesně):
; ---------------------------------------------------------------------
; makro pro tisk jedné hexa číslice na určené místo na obrazovce
; ---------------------------------------------------------------------
.macro print_hex address, screen, offset
.local skip_add ; definice lokálního symbolu
lda address ; načíst hodnotu, která se má vypsat
lsr A ; 4x "zpomalení"
lsr A
and #15 ; scrolling je jen v rozsahu 0-15
cmp #$0a ; test na hodnotu 0-9 nebo 10-15
bcc skip_add ; je to hodnota 0-9?
adc #6 ; pricist sedmicku (6+carry)
skip_add:
adc #16 ; prevod hodnoty na interni kod (ne ATASCII!)
sta screen+offset
.endmacro
V subrutině volané při obsluze DLI budou měněny obě proměnné x_scroll i y_scroll:
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc scroll
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dec x_scroll ; změnit počitadlo horizontálního scrollingu
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inc x_scroll ; změnit počitadlo horizontálního scrollingu
not_right:
cmp #14 ; je nakloněn nahoru?
bne not_up
dec y_scroll ; změnit počitadlo vertikálního scrollingu
not_up:
cmp #13 ; je nakloněn dolů?
bne not_down
inc y_scroll ; změnit počitadlo vertikálního scrollingu
not_down:
print_hex x_scroll, screen, 40*23
print_hex y_scroll, screen, 40*23+2
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
.endproc
Jak je ze subrutiny patrné, provádí se i výpis obou proměnných, a to do levého spodního rohu obrazovky:
Obrázek 2: Výchozí stav příkladu: obě počitadla scrollingu jsou nulová.
Obrázek 3: Změna stavu počitadel scrollingu pomocí joysticku.
Obrázek 4: Maximální hodnoty počitadel mohou dosáhnout čísla 15.
Úplný zdrojový kód druhého demonstračního příkladu
Úplný zdrojový kód dnešního druhého demonstračního příkladu vypadá následovně:
.include "atari.inc"
.CODE
; ---------------------------------------------------------------------
; 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í 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 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 tisk jedné hexa číslice na určené místo na obrazovce
; ---------------------------------------------------------------------
.macro print_hex address, screen, offset
.local skip_add ; definice lokálního symbolu
lda address ; načíst hodnotu, která se má vypsat
lsr A ; 4x "zpomalení"
lsr A
and #15 ; scrolling je jen v rozsahu 0-15
cmp #$0a ; test na hodnotu 0-9 nebo 10-15
bcc skip_add ; je to hodnota 0-9?
adc #6 ; pricist sedmicku (6+carry)
skip_add:
adc #16 ; prevod hodnoty na interni kod (ne ATASCII!)
sta screen+offset
.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
set_vbi_handler scroll ; nastavení obsluhy VBI
game_loop ; hlavní herní smyčka
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc scroll
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dec x_scroll ; změnit počitadlo horizontálního scrollingu
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inc x_scroll ; změnit počitadlo horizontálního scrollingu
not_right:
cmp #14 ; je nakloněn nahoru?
bne not_up
dec y_scroll ; změnit počitadlo vertikálního scrollingu
not_up:
cmp #13 ; je nakloněn dolů?
bne not_down
inc y_scroll ; změnit počitadlo vertikálního scrollingu
not_down:
print_hex x_scroll, screen, 40*23
print_hex y_scroll, screen, 40*23+2
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
.endproc
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 23, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
; horizontální scrolling
x_scroll: .byte 0
; vertikální scrolling
y_scroll: .byte 0
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
Nastavení řádků v display listu pro povolení vertikálního scrollingu
Vertikální scrolling není obecně prováděn s celou obrazovkou, ale pouze s těmi řádky, u nichž je v display listu nastaven příznak DL_VSCROL. To tedy znamená, že můžeme na běžné textové obrazovce v režimu ANTIC 2 (GR.0) vybrat například jen sedm řádků, které budou scrollovat. Takový display list může vypadat například následovně:
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 11, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .res 7, DL_VSCROL+DL_CHR40x8x1 ; zde bude povolen scrolling .res 5, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Jemný vertikální scrolling řízený joystickem
V dalším kroku upravíme subrutinu volanou pro obsluhu VBI takovým způsobem, že se na základě hodnoty uložené do proměnné y_scroll provede jemný vertikální scrolling. Hodnotu z proměnné musíme vydělit čtyřmi (aby to odpovídalo hodnotě vytištěné na obrazovce) a výsledek uložíme do řídicího registru VSCROL čipu ANTIC. To je vše – jediná instrukce zápisu způsobí scrolling, a to bez nutnosti „pálit“ další strojový čas:
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc scroll
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dec x_scroll ; změnit počitadlo horizontálního scrollingu
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inc x_scroll ; změnit počitadlo horizontálního scrollingu
not_right:
cmp #14 ; je nakloněn nahoru?
bne not_up
dec y_scroll ; změnit počitadlo vertikálního scrollingu
not_up:
cmp #13 ; je nakloněn dolů?
bne not_down
inc y_scroll ; změnit počitadlo vertikálního scrollingu
not_down:
lda y_scroll ; změna vertikálního posunu
lsr A ; 4x "zpomalení"
lsr A
sta VSCROL ; zápis do řídicího registru ANTICu
print_hex x_scroll, screen, 40*23
print_hex y_scroll, screen, 40*23+2
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
.endproc
Výsledek (zpomalená animace):
Úplný zdrojový kód třetího demonstračního příkladu
Opět si ukažme úplný zdrojový kód příkladu, který byl popsán v předchozím textu:
.include "atari.inc"
.CODE
; ---------------------------------------------------------------------
; 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í 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 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 tisk jedné hexa číslice na určené místo na obrazovce
; ---------------------------------------------------------------------
.macro print_hex address, screen, offset
.local skip_add ; definice lokálního symbolu
lda address ; načíst hodnotu, která se má vypsat
lsr A ; 4x "zpomalení"
lsr A
and #15 ; scrolling je jen v rozsahu 0-15
cmp #$0a ; test na hodnotu 0-9 nebo 10-15
bcc skip_add ; je to hodnota 0-9?
adc #6 ; pricist sedmicku (6+carry)
skip_add:
adc #16 ; prevod hodnoty na interni kod (ne ATASCII!)
sta screen+offset
.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
set_vbi_handler scroll ; nastavení obsluhy VBI
game_loop ; hlavní herní smyčka
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc scroll
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dec x_scroll ; změnit počitadlo horizontálního scrollingu
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inc x_scroll ; změnit počitadlo horizontálního scrollingu
not_right:
cmp #14 ; je nakloněn nahoru?
bne not_up
dec y_scroll ; změnit počitadlo vertikálního scrollingu
not_up:
cmp #13 ; je nakloněn dolů?
bne not_down
inc y_scroll ; změnit počitadlo vertikálního scrollingu
not_down:
lda y_scroll ; změna vertikálního posunu
lsr A ; 4x "zpomalení"
lsr A
sta VSCROL ; zápis do řídicího registru ANTICu
print_hex x_scroll, screen, 40*23
print_hex y_scroll, screen, 40*23+2
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
.endproc
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 11, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.res 7, DL_VSCROL+DL_CHR40x8x1 ; zde bude povolen scrolling
.res 5, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
; horizontální scrolling
x_scroll: .byte 0
; vertikální scrolling
y_scroll: .byte 0
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
Podrobnější pohled na stav obsah obrazovky při scrollingu
Důležité je pochopit, jak vlastně vertikální scrolling pracuje. Čip ANTIC pracuje tak, že u všech logických řádků display listu s nastaveným bitem DL_VSCROL změní načasování zobrazení obrazových řádků – zobrazení je posunuto právě o hodnotu uloženou v registru VSCROL. Příkladem je standardní textový režim ANTIC 2 (GR.0), ve kterém se každý logický řádek zobrazí na osmi obrazových řádcích – jinými slovy má každý znak výšku osmi obrazových řádků. A hodnota VSCROL (ideálně v rozsahu 0–7) dokáže u všech textových řádků s nastaveným DL_VSCROL provést posun těchto textových řádků nahoru o 0 až 7 obrazových řádků. U prvního scrollovaného řádku tedy může dojít k vynechání několika televizních řádků (písmenům budou chybět vršky). Celá oblast řádků s nastaveným DL_VSCROL se bude chovat jako jediný blok.
Obrázek 5: Vertikální posun o 0 obrazových řádků.
Ovšem jak se chová obrazovka pod touto oblastí, tedy na dalším textovém/grafickém řádku bez nastaveného příznaku DL_VSCROL? Tento řádek je využit jako buffer, ve kterém ANTIC sesynchronizuje další řádky, které se již posouvat nebudou. To je patrné na obrázcích před a za tímto odstavcem: počitadla posunu jsou stále zobrazena na stejném místě, i když oblast nad nimi byla posunuta.
Obrázek 6: Vertikální posun o 7 obrazových řádků.
Standardní textový režim má znaky vysoké osm obrazových řádků, ovšem hodnota VSCROL může nabývat hodnot 0 až 15. V tomto případě hodnoty větší než 7 „donutí“ ANTIC zobrazit další textový řádek a scrolling se pokazí. To znamená, že v tomto režimu má smysl omezit scrolling jen na hodnoty 0–7:
Obrázek 7: Vertikální posun o 8 obrazových řádků.
Obrázek 8: Vertikální posun o 15 obrazových řádků.
Modifikace display listu: současný jemný horizontální i vertikální scrolling
V dalším kroku provedeme úpravu display listu, a to konkrétně takovým způsobem, že pro šest textových řádků v dolní polovině obrazovky povolíme jak horizontální, tak i vertikální scrolling. Je to ve skutečnosti snadné, jak je ostatně patrné ze zdrojového kódu s definicí display listu:
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 11, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .res 6, DL_HSCROL+DL_VSCROL+DL_CHR40x8x1 ; zde bude povolen scrolling .res 6, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Řízení horizontálního i vertikálního scrollingu joystickem
Pochopitelně budeme muset pozměnit i subrutinu volanou pro obsluhu VBI. V této subrutině budeme nastavovat jak řídicí registr VSCROL, tak nově i HSCROL:
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc scroll
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dec x_scroll ; změnit počitadlo horizontálního scrollingu
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inc x_scroll ; změnit počitadlo horizontálního scrollingu
not_right:
cmp #14 ; je nakloněn nahoru?
bne not_up
dec y_scroll ; změnit počitadlo vertikálního scrollingu
not_up:
cmp #13 ; je nakloněn dolů?
bne not_down
inc y_scroll ; změnit počitadlo vertikálního scrollingu
not_down:
lda x_scroll ; změna horizontálního posunu
lsr A ; 4x "zpomalení"
lsr A
sta HSCROL ; zápis do řídicího registru ANTICu
lda y_scroll ; změna vertikálního posunu
lsr A ; 4x "zpomalení"
lsr A
sta VSCROL ; zápis do řídicího registru ANTICu
print_hex x_scroll, screen, 40*23
print_hex y_scroll, screen, 40*23+2
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
.endproc
Samozřejmě nás bude zajímat, jak budou vypadat výsledné obrazovky pro různé horizontální posuny:
Obrázek 9: Horizontální posun o 0 barvových cyklů.
Obrázek 10: Horizontální posun o 2 barvové cykly.
Obrázek 11: Horizontální posun o 10 barvových cyklů.
Obrázek 12: Horizontální posun o 15 barvových cyklů.
Tyto obrazovky mohou být to značné míry matoucí, protože například počitadla scrollingu se již nenachází v levém horním rohu, ale na zcela jiném místě. Je tomu tak z toho důvodu, že v případě, že je povolený horizontální scrolling, přepne čip ANTIC šířku řádků tak, jakoby se jednalo o široké herní pole a s horizontálním rozlišením 384 pixelů (48 znaků), což pozměňuje význam ukazatele použitého při čtení dat z video RAM. To je téma, kterému se budeme věnovat v navazujících kapitolách.
Horizontální posun je prováděn zpožděním vykreslení pixelů o 0 až 15 barvových cyklů, což v monochromatických režimech znamená o 0 až 30 pixelů (násobí se dvěma, každý pixel je široký 1/2 obrazového cyklu). Prakticky jsme v jednotlivých režimech (teoreticky) omezeni takto:
| ANTIC | GR.x | HSCROL | Poznámka |
|---|---|---|---|
| 2 | 0 | 0–3 | výchozí textový režim |
| 3 | × | 0–3 | textový režim s vyššími znaky |
| 4 | 12 | 0–3 | pětibarevný textový režim, znaky jsou široké 4 pixely |
| 5 | 13 | 0–3 | pětibarevný textový režim, znaky jsou široké 4 pixely |
| 6 | 1 | 0–7 | znaky s dvojnásobnou šířkou |
| 7 | 2 | 0–7 | znaky s dvojnásobnou šířkou |
Úplný zdrojový kód čtvrtého demonstračního příkladu
Celý zdrojový kód dnešního čtvrtého (předposledního) demonstračního příkladu vypadá takto:
.include "atari.inc"
.CODE
; ---------------------------------------------------------------------
; 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í 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 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 tisk jedné hexa číslice na určené místo na obrazovce
; ---------------------------------------------------------------------
.macro print_hex address, screen, offset
.local skip_add ; definice lokálního symbolu
lda address ; načíst hodnotu, která se má vypsat
lsr A ; 4x "zpomalení"
lsr A
and #15 ; scrolling je jen v rozsahu 0-15
cmp #$0a ; test na hodnotu 0-9 nebo 10-15
bcc skip_add ; je to hodnota 0-9?
adc #6 ; pricist sedmicku (6+carry)
skip_add:
adc #16 ; prevod hodnoty na interni kod (ne ATASCII!)
sta screen+offset
.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
set_vbi_handler scroll ; nastavení obsluhy VBI
game_loop ; hlavní herní smyčka
.endproc
; ---------------------------------------------------------------------
; subrutina pro obsluhu VBI
; ---------------------------------------------------------------------
.proc scroll
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dec x_scroll ; změnit počitadlo horizontálního scrollingu
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inc x_scroll ; změnit počitadlo horizontálního scrollingu
not_right:
cmp #14 ; je nakloněn nahoru?
bne not_up
dec y_scroll ; změnit počitadlo vertikálního scrollingu
not_up:
cmp #13 ; je nakloněn dolů?
bne not_down
inc y_scroll ; změnit počitadlo vertikálního scrollingu
not_down:
lda x_scroll ; změna horizontálního posunu
lsr A ; 4x "zpomalení"
lsr A
sta HSCROL ; zápis do řídicího registru ANTICu
lda y_scroll ; změna vertikálního posunu
lsr A ; 4x "zpomalení"
lsr A
sta VSCROL ; zápis do řídicího registru ANTICu
print_hex x_scroll, screen, 40*23
print_hex y_scroll, screen, 40*23+2
jmp XITVBV ; zpracovat zbytek odloženého VBLANKu
.endproc
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 11, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.res 6, DL_HSCROL+DL_VSCROL+DL_CHR40x8x1 ; zde bude povolen scrolling
.res 6, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
; horizontální scrolling
x_scroll: .byte 0
; vertikální scrolling
y_scroll: .byte 0
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
Specifikace šířky herního pole
Už v předchozím demonstračním příkladu jsme mohli vidět jednu zajímavou vlastnost čipu ANTIC. Víme již, že výška zobrazené scény je řízena display listem a tak je možné namísto de facto standardních 192 obrazových řádků zobrazit až přibližně 224 řádků (v závislosti na televizoru atd.). Šířka scény odpovídá podle použitého režimu 40 znakům resp. 160 popř. 320 pixelům (což ve všech případech odpovídá 160 barvovým cyklům – color cycles). Ovšem i tuto šířku je možné modifikovat, a to konkrétně modifikací spodních dvou bitů řídicího registru DMACTL:
| Bity v DMACTL | Šířka v barevných režimech | Šířka v mono režimech | Znaků/řádek (GR.0) |
|---|---|---|---|
| 00 | vypnuto | vypnuto | vypnuto |
| 01 | 128 | 256 | 32 |
| 10 | 160 | 320 | 40 |
| 11 | 192 | 384 | 48 |
A jak vypadá obrazovka v praxi? Ukážeme si to na trojici screenshotů:
Obrázek 13: Úzké herní pole 32 znaků.
Obrázek 14: Normální herní pole 40 znaků.
Obrázek 15: Široké herní pole 48 znaků.
Úplný zdrojový kód pátého demonstračního příkladu
Pátý a dnes již poslední demonstrační příklad vypadá následovně:
.include "atari.inc"
.CODE
; ---------------------------------------------------------------------
; 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í 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
; ---------------------------------------------------------------------
; nastavení spodních dvou bitů stínového registru SDMCTL
; ---------------------------------------------------------------------
.macro set_playfield_width bitfield
lda SDMCTL ; načíst stínový registr
and #%11111100 ; vynulovat spodní dva bity
ora bitfield ; nastavit spodní dva bity na zvolenou masku
sta SDMCTL ; a uložit zpět
.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
jsr get_key ; čekání na stisk klávesy
set_playfield_width #%00
jsr get_key ; čekání na stisk klávesy
set_playfield_width #%01
jsr get_key ; čekání na stisk klávesy
set_playfield_width #%10
jsr get_key ; čekání na stisk klávesy
set_playfield_width #%11
game_loop ; hlavní herní smyčka
.endproc
; ---------------------------------------------------------------------
; čekání na stisk klávesy
; ---------------------------------------------------------------------
.proc get_key
KBHANDLER = $e424 ; rutina pro cteni klavesy
lda KBHANDLER+1 ; cteni horni casti adresy ulozene v ROM
pha ; ulozeni na zasobnik
lda KBHANDLER ; cteni dolni casti adresy ulozene v ROM
pha ; ulozeni na zasobnik
rts ; vyber adresy ze zasobniku + skok
; zde neni nutne mit RTS
.endproc
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 23, DL_CHR40x8x1 ; opakovat řádky textového režimu 2 (GR.0)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
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
Ukázky display listů použitých v reálných hrách
Nyní již máme k dispozici dostatek informací k tomu, abychom pochopili display listy použité u mnoha klasických ataráckých her. V této kapitole i v kapitole navazující si vždycky ukážeme výpis display listu a následně i screenshot ze hry, která tento display list používá.
Slavná hra Preliminary Monty pracuje celá v textovém režimu ANTIC 4 (GR.12) s 40×24 znaky a pěti barvami. Každý textový řádek vyvolává DLI, scrolling není použit:
1F00: 3x 8 BLANK 1F03: LMS 3800 MODE 4 1F06: 23x DLI MODE 4 1F1D: MODE 4 1F1E: JVB 1F00
Obrázek 16: Druhá nejvyšší úroveň pyramidy ve hře Preliminary Monty.
Ve hře Nekonečný příběh se kombinuje grafický režim s režimem textovým. Konkrétně můžeme vidět osmdesát řádků v grafickém režimu ANTIC E (GR.15) se čtyřmi barvami a horizontálním rozlišením 160 pixelů (grafické řádky jsou vysoké jeden pixel, tj. vertikálně se jedná o nejjemnější grafiku). Poslední grafický řádek vyvolá DLI. Následuje patnáct řádků ve standardním textovém režimu ANTIC 2 (GR.0). Ani zde se nepoužívá scrolling:
0300: 2x 8 BLANK 0302: 4 BLANK 0303: LMS F000 MODE E 0306: 78x MODE E 0354: DLI MODE E 0355: LMS FC80 MODE 2 0358: 14x MODE 2 0366: JVB 0300
Obrázek 17: Nekonečný příběh.
Display list úvodní obrazovky hry Collosus Chess obsahuje zajímavé instrukce. Nejprve je použitý textový režim s pěti barvami (ANTIC 4, GR.12) pro zobrazení jména hry. Následují prázdné řádky uprostřed obrazovky a poté dalších několik textových řádků. Pro zobrazení jmen autorů je použit textový režim ANTIC 6 (GR.1) se znaky s dvojitou šířkou. Ze statického obrázku to sice není patrné, ale poslední (textový) řádek scrolluje – obsahuje kdysi tak obvyklý „nekonečný řádek“ A jak je z display listu patrné, využívá tento scrollující řádek standardní textový režim ANTIC 2 (GR.0) doplněný o příznak HSCROLL:
17A3: 4x 8 BLANK 17A7: DLI 8 BLANK 17A8: LMS 17C8 MODE 4 17AB: 7x MODE 4 17B2: 2x 8 BLANK 17B4: 5x MODE 4 17B9: 4x 8 BLANK 17BD: LMS 19D0 MODE 6 17C0: 2x 8 BLANK 17C2: LMS 1A10 HSCROL MODE 2 17C5: JVB 17A3
Obrázek 18: Úvodní obrazovka hry Colossus Chess.
Ve hře Mr.Robot dochází ke kombinaci textového režimu ANTIC 6 (GR.1) se širokými znaky (řádek s nadpisem a skóre) a režimu ANTIC 4 (GR.12) s pětibarevnými znaky. I zde se používá DLI, dokonce na každém textovém řádku herní plochy. V DLI se nastavují barvy plošinek atd.:
9E3F: 3x 8 BLANK 9E42: LMS 9D69 MODE 6 9E45: 3 BLANK 9E46: MODE 6 9E47: 3 BLANK 9E48: 8 BLANK 9E49: DLI 8 BLANK 9E4A: DLI LMS 4804 MODE 4 9E4D: 19x DLI MODE 4 9E60: JVB 9E3F
Obrázek 19: Mr. Robot and his Robot Factory.
V další známé hře Alley Cat (konkrétně v úvodní obrazovce) se střídají režimy ANTIC 6 (GR.1) se čtyřbarevnými širokými znaky a režim ANTIC 4 (GR.12) s pětibarevnou grafikou. A zcela dole je neomylně vidět standardní textový režim ANTIC 2 (GR.0) obsazující dva řádky. I zde se často používá DLI:
5AEF: 2x 8 BLANK 5AF1: 4 BLANK 5AF2: LMS 4600 MODE 6 5AF5: 4x MODE 6 5AF9: DLI MODE 6 5AFA: 8 BLANK 5AFB: DLI MODE 6 5AFC: 3x MODE 6 5AFF: DLI 4 BLANK 5B00: 8 BLANK 5B01: LMS 5238 MODE 4 5B04: 9x MODE 4 5B0D: DLI 2 BLANK 5B0E: MODE 2 5B0F: 4 BLANK 5B10: LMS 5C16 MODE 2 5B13: JVB 5AEF
Obrázek 20: Úvodní obrazovka hry Alley Cat.
Vrtulník či letadlo byly sice velmi oblíbenými dopravními a bojovými prostředky v mnoha střílečkách (viz výše zmíněná hra Fort Apocalypse, Protector a série Blue Max), ovšem nesmíme zapomenout ani na jiné typy zbraní. Příkladem velmi dobře promyšlené střílečky kombinované s bludišťovkou je hra Zeppelin pocházející z roku 1983, jejímž autorem je William Mataga. Jak již název napovídá, je hlavním „hrdinou“ v této hře řiditelná vzducholoď vybavená kanónem střílejícím do osmi směrů i bombami. Tato střílečka má poměrně rozsáhlý dvourozměrný herní svět; jakousi jeskyni, v níž se nachází nepřátelské balóny, vzducholodě i létající miny. Jedná se o střílečku s horizontálním i vertikálním scrollingem, která je zvláštní jak po grafické stránce, tak i po stránce ovládání – vzducholoď je sice možné v určité míře ovládat (v prostoru jedné obrazovky), ovšem hlavní směr pohybu je předem daný (naskriptovaný) a lze ho ovlivnit jen nepřímo volbou letové trasy. Úkolem hráče je postupně otevřít průchody do dalších levelů, k čemuž slouží klíče, nálože s TNT atd.
Celá hra plynule scrolluje jak horizontálně, tak i vertikálně, což je ostatně patrné i při pohledu do jejího display listu. Šířka řádků je nastavena na 48 znaků a používá se asi nejlepší herní režim osmibitových Atari: ANTIC 4 (GR.12). A co je ještě zajímavé – na začátku nejsou zobrazeny prázdné řádky, herní scéna začíná co nejvýše je to fyzicky možné:
25B5: LMS 7950 VSCROL HSCROL MODE 4 25B8: LMS 7980 VSCROL HSCROL MODE 4 25BB: LMS 79B0 VSCROL HSCROL MODE 4 25BE: LMS 79E0 VSCROL HSCROL MODE 4 25C1: LMS 7A10 VSCROL HSCROL MODE 4 25C4: LMS 7A40 VSCROL HSCROL MODE 4 25C7: LMS 7A70 VSCROL HSCROL MODE 4 25CA: LMS 7AA0 VSCROL HSCROL MODE 4 25CD: LMS 7AD0 VSCROL HSCROL MODE 4 25D0: LMS 7620 VSCROL HSCROL MODE 4 25D3: LMS 7650 VSCROL HSCROL MODE 4 25D6: LMS 7680 VSCROL HSCROL MODE 4 25D9: LMS 76B0 VSCROL HSCROL MODE 4 25DC: LMS 76E0 VSCROL HSCROL MODE 4 25DF: LMS 7710 VSCROL HSCROL MODE 4 25E2: LMS 7740 VSCROL HSCROL MODE 4 25E5: LMS 7770 VSCROL HSCROL MODE 4 25E8: LMS 77A0 VSCROL HSCROL MODE 4 25EB: LMS 77D0 VSCROL HSCROL MODE 4 25EE: LMS 7800 VSCROL HSCROL MODE 4 25F1: LMS 7830 VSCROL HSCROL MODE 4 25F4: LMS 7860 VSCROL HSCROL MODE 4 25F7: LMS 7890 VSCROL HSCROL MODE 4 25FA: LMS 78C0 VSCROL HSCROL MODE 4 25FD: LMS 78F0 HSCROL MODE 4 2600: DLI 2 BLANK 2601: LMS 24E5 MODE 4 2604: 2x MODE 4 2606: JVB 25B5
Obrázek 21: Šestá úroveň hry Zeppelin (tato hra ovšem čísluje úrovně od 7 do 1).
Komplikovanější display listy áčkových her
Zajímavé bude zjistit i to, jak vypadají display listy u těch her, které mají či měly vysoké hodnocení.
Slavnou hru Bruce Lee pravděpodobně není zapotřebí čtenářům představovat. Display list této hry je zajímavý, protože se zde kombinuje standardní textový režim ANTIC 2 (GR.0) použitý pro zobrazení skóre a počtu životů s režimy vícebarevnými. První textový řádek je oddělen od herního pole prázdnými řádky. A samotná herní scéna využívá (překvapivě!) pětibarevný textový režim ANTIC 5 (GR.13) s dvojnásobně vysokými řádky. To znamená, že herní scéna má relativně malé rozlišení, což je patrné na lampionech a později na „žebřících“. Dále si povšimněte poměrně velkého množství řádků, na kterých je vyvoláno DLI (první DLI dokážou vykreslit hory odlišnou barvou, než jaká je použita ve zbytku scény atd.):
0EF1: 2x 8 BLANK 0EF3: 5 BLANK 0EF4: 2x 3 BLANK 0EF6: 2 BLANK 0EF7: 1 BLANK 0EF8: DLI LMS 78D5 MODE 2 0EFB: DLI 2 BLANK 0EFC: 2x MODE 5 0EFE: DLI MODE 5 0EFF: MODE 5 0F00: DLI MODE 5 0F01: 4x MODE 5 0F05: DLI MODE 5 0F06: MODE 5 0F07: JVB 0EF1
Obrázek 22: První obrazovka slavné hry Bruce Lee.
Velmi známou hrou je i Dropzone. Tato hra využívá jemný horizontální scrolling (možná vůbec nejlepší, co je na osmibitových Atari vidět). A vzhledem k tomu, že celá herní scéna je široká „nudle“, pravděpodobně nebude překvapením, že prakticky na každém řádku musel být upraven začátek obrazové paměti. Hra poměrně unikátním způsobem kombinuje režimy ANTIC 4 (pětibarevný textový režim s herní scénou) a ANTIC E (čtyřbarevný grafický režim pro zobrazení „radaru“). Většina řádků má povolený scrolling:
84D0: 2x 8 BLANK 84D2: DLI 1 BLANK 84D3: 2 BLANK 84D4: LMS 0824 HSCROL MODE 4 84D7: LMS 0924 HSCROL MODE 4 84DA: LMS 0A24 HSCROL MODE 4 84DD: LMS 0B24 HSCROL MODE 4 84E0: DLI LMS 0C24 HSCROL MODE 4 84E3: LMS 0D24 HSCROL MODE 4 84E6: LMS 0E24 HSCROL MODE 4 84E9: LMS 0F24 HSCROL MODE 4 84EC: LMS 1024 HSCROL MODE 4 84EF: DLI LMS 1124 HSCROL MODE 4 84F2: LMS 1224 HSCROL MODE 4 84F5: LMS 1324 HSCROL MODE 4 84F8: LMS 1424 HSCROL MODE 4 84FB: LMS 1524 HSCROL MODE 4 84FE: LMS 1624 HSCROL MODE 4 8501: DLI LMS 1724 HSCROL MODE 4 8504: LMS 9424 HSCROL MODE E 8507: LMS 9524 HSCROL MODE E 850A: LMS 9624 HSCROL MODE E 850D: LMS 9724 HSCROL MODE E 8510: LMS 9824 HSCROL MODE E 8513: LMS 9924 HSCROL MODE E 8516: LMS 9A24 HSCROL MODE E 8519: LMS 9B24 HSCROL MODE E 851C: LMS 9C24 HSCROL MODE E 851F: DLI LMS 9D24 HSCROL MODE E 8522: LMS 9E24 HSCROL MODE E 8525: LMS 9F24 HSCROL MODE E 8528: LMS A024 HSCROL MODE E 852B: LMS A124 HSCROL MODE E 852E: LMS A224 HSCROL MODE E 8531: LMS A324 HSCROL MODE E 8534: LMS A424 HSCROL MODE E 8537: LMS A524 HSCROL MODE E 853A: DLI LMS A624 HSCROL MODE E 853D: LMS A724 HSCROL MODE E 8540: LMS A824 HSCROL MODE E 8543: LMS A924 HSCROL MODE E 8546: LMS AA24 HSCROL MODE E 8549: LMS AB24 HSCROL MODE E 854C: LMS AC24 HSCROL MODE E 854F: LMS AD24 HSCROL MODE E 8552: LMS AE24 HSCROL MODE E 8555: LMS AF24 HSCROL MODE E 8558: LMS B024 HSCROL MODE E 855B: LMS B124 HSCROL MODE E 855E: LMS B224 HSCROL MODE E 8561: 1 BLANK 8562: DLI 1 BLANK 8563: 2 BLANK 8564: DLI LMS 0700 MODE 4 8567: 1 BLANK 8568: LMS 1800 MODE E 856B: 1 BLANK 856C: LMS 1898 HSCROL MODE 4 856F: DLI LMS 1918 HSCROL MODE 4 8572: DLI LMS 1998 HSCROL MODE 4 8575: 1 BLANK 8576: LMS 1800 MODE E 8579: JVB 84D0
Obrázek 23: Dropzone.
Dále se podíváme na další slavnou hru, konkrétně na International Karate. V této hře je zkombinován grafický režim ANTIC E (čtyři barvy, horizontální rozlišení 160 pixelů) v horní části obrazovky se statickým obrázkem. Samotná hra se odehrává v režimu ANTIC 4 (GR.12) a zcela nahoře se nachází dva pětibarevné textové řádky (skóre atd.). Velké množství instrukcí s DLI naznačuje změny barev ve statickém obrázku (zde je skutečně více než čtyři barvy). A největší magie se odehrává při animaci obou hráčů; kombinují se zde možnosti textového režimu se sprity:
6260: 8 BLANK 6261: 5 BLANK 6262: DLI LMS 61C0 MODE 4 6265: DLI MODE 4 6266: DLI 1 BLANK 6267: 1 BLANK 6268: LMS B000 MODE E 626B: 62x MODE E 62A9: 2x DLI MODE E 62AB: 8x MODE E 62B3: 2x DLI MODE E 62B5: 10x MODE E 62BF: 2x DLI MODE E 62C1: 8x MODE E 62C9: 2x DLI MODE E 62CB: MODE E 62CC: LMS 0800 MODE 4 62CF: 10x MODE 4 62D9: DLI MODE 4 62DA: DLI LMS 0E27 MODE E 62DD: DLI LMS 0A40 MODE 4 62E0: DLI LMS 0E27 MODE E 62E3: LMS 0E27 MODE E 62E6: JVB 6260
Obrázek 24: International Karate.
A konečně nesmíme zapomenout ani na slavný Boulder Dash, který na osmibitových domácích počítačích Atari využívá jemný scrolling do všech stran. Textové řádky v pětibarevném režimu jsou široké 80 znaků, tj. jedná se o dvojnásobek šířky obrazovky, což přesně odpovídá velikosti herní scény (alespoň v první úrovni Boulder Dashe II). Zajímavé je, že řádek se skóre, životy atd. taktéž využívá režim ANTIC 4 (vždycky jsem si z nějakého důvodu myslel, že je to ANTIC 6):
911D: 3x 8 BLANK 9120: DLI LMS 257C MODE 4 9123: LMS 1028 VSCROL HSCROL MODE 4 9126: LMS 1078 VSCROL HSCROL MODE 4 9129: LMS 10C8 VSCROL HSCROL MODE 4 912C: LMS 1118 VSCROL HSCROL MODE 4 912F: LMS 1168 VSCROL HSCROL MODE 4 9132: LMS 11B8 VSCROL HSCROL MODE 4 9135: LMS 1208 VSCROL HSCROL MODE 4 9138: LMS 1258 VSCROL HSCROL MODE 4 913B: LMS 12A8 VSCROL HSCROL MODE 4 913E: LMS 12F8 VSCROL HSCROL MODE 4 9141: LMS 1348 VSCROL HSCROL MODE 4 9144: LMS 1398 VSCROL HSCROL MODE 4 9147: LMS 13E8 VSCROL HSCROL MODE 4 914A: LMS 1438 VSCROL HSCROL MODE 4 914D: LMS 1488 VSCROL HSCROL MODE 4 9150: LMS 14D8 VSCROL HSCROL MODE 4 9153: LMS 1528 VSCROL HSCROL MODE 4 9156: LMS 1578 VSCROL HSCROL MODE 4 9159: LMS 15C8 VSCROL HSCROL MODE 4 915C: LMS 1618 VSCROL HSCROL MODE 4 915F: LMS 1668 VSCROL HSCROL MODE 4 9162: LMS 16B8 VSCROL HSCROL MODE 4 9165: LMS 1708 VSCROL HSCROL MODE 4 9168: LMS 1758 HSCROL MODE 4 916B: JVB 911D
Obrázek 25: První úroveň hry Boulder Dash II.
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: \
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
Výsledkem překladu jsou soubory s koncovkou .xex, které je možné přímo spustit v emulátoru osmibitových počítačů Atari.
Repositář s demonstračními příklady
Všechny demonstrační příklady, s nimiž jsme se v předchozích článcích i v článku dnešním seznámili a které jsou určeny pro překlad s využitím assembleru ca65, jsou dostupné, jak je zvykem, na GitHubu. V tabulce níže jsou uvedeny odkazy na jednotlivé zdrojové kódy příkladů psané v assembleru i „listingy“ vygenerované samotným assemblerem, ze kterých je patrné, jakým způsobem se jednotlivé příklady přeložily do výsledného XEX souboru:
Odkazy na Internetu
- MOS 6502 instruction set
http://www.6502.org/users/obelisk/6502/instructions.html - EXE File Format Description
https://gury.atari8.info/refs/file_formats_exe.php - XEX Filter – A toolkit to analyze and manipulate Atari binary files
https://www.vitoco.cl/atari/xex-filter/index.html - chkxex.py
https://raw.githubusercontent.com/seban-slt/tcx_tools/refs/heads/master/chkxex.py - ca65 Users Guide
https://cc65.github.io/doc/ca65.html - cc65 Users Guide
https://cc65.github.io/doc/cc65.html - ld65 Users Guide
https://cc65.github.io/doc/ld65.html - da65 Users Guide
https://cc65.github.io/doc/da65.html - Překladače jazyka C pro historické osmibitové mikroprocesory
https://www.root.cz/clanky/prekladace-jazyka-c-pro-historicke-osmibitove-mikroprocesory/ - Překladače programovacího jazyka C pro historické osmibitové mikroprocesory (2)
https://www.root.cz/clanky/prekladace-programovaciho-jazyka-c-pro-historicke-osmibitove-mikroprocesory-2/ - Getting Started Programming in C: Coding a Retro Game with C Part 2
https://retrogamecoders.com/getting-started-with-c-cc65/ - NES game development in 6502 assembly – Part 1
https://kibrit.tech/en/blog/nes-game-development-part-1 - NES 6502 Programming Tutorial – Part 1: Getting Started
https://dev.xenforo.relay.cool/index.php?threads/nes-6502-programming-tutorial-part-1-getting-started.858389/ - Minimal NES example using ca65
https://github.com/bbbradsmith/NES-ca65-example - List of 6502-based Computers and Consoles
https://www.retrocompute.co.uk/list-of-6502-based-computers-and-consoles/ - 6502 – the first RISC µP
http://ericclever.com/6500/ - 3 Generations of Game Machine Architecture
http://www.atariarchives.org/dev/CGEXPO99.html - “Hello, world” from scratch on a 6502 — Part 1
https://www.youtube.com/watch?v=LnzuMJLZRdU - A Tour of 6502 Cross-Assemblers
https://bumbershootsoft.wordpress.com/2016/01/31/a-tour-of-6502-cross-assemblers/ - Adventures with ca65
https://atariage.com/forums/topic/312451-adventures-with-ca65/ - example ca65 startup code
https://atariage.com/forums/topic/209776-example-ca65-startup-code/ - 6502 PRIMER: Building your own 6502 computer
http://wilsonminesco.com/6502primer/ - 6502 Instruction Set
https://www.masswerk.at/6502/6502_instruction_set.html - Chip Hall of Fame: MOS Technology 6502 Microprocessor
https://spectrum.ieee.org/tech-history/silicon-revolution/chip-hall-of-fame-mos-technology-6502-microprocessor - Single-board computer
https://en.wikipedia.org/wiki/Single-board_computer - www.6502.org
http://www.6502.org/ - 6502 PRIMER: Building your own 6502 computer – clock generator
http://wilsonminesco.com/6502primer/ClkGen.html - Great Microprocessors of the Past and Present (V 13.4.0)
http://www.cpushack.com/CPU/cpu.html - Jak se zrodil procesor?
https://www.root.cz/clanky/jak-se-zrodil-procesor/ - Osmibitové mikroprocesory a mikrořadiče firmy Motorola (1)
https://www.root.cz/clanky/osmibitove-mikroprocesory-a-mikroradice-firmy-motorola-1/ - Mikrořadiče a jejich použití v jednoduchých mikropočítačích
https://www.root.cz/clanky/mikroradice-a-jejich-pouziti-v-jednoduchych-mikropocitacich/ - Mikrořadiče a jejich aplikace v jednoduchých mikropočítačích (2)
https://www.root.cz/clanky/mikroradice-a-jejich-aplikace-v-jednoduchych-mikropocitacich-2/ - 25 Microchips That Shook the World
https://spectrum.ieee.org/tech-history/silicon-revolution/25-microchips-that-shook-the-world - Comparison of instruction set architectures
https://en.wikipedia.org/wiki/Comparison_of_instruction_set_architectures - How To Start Learning Atari 8 Bit Assembly For Free
https://forums.atariage.com/topic/300732-how-to-start-learning-atari-8-bit-assembly-for-free/ - WUDSN (Demo Group)
https://www.wudsn.com/ - Machine Language For Beginners
https://www.atariarchives.org/mlb/ - Assembly language: all about I/O
https://www.atarimagazines.com/v3n8/AllAbout_IO.html - Sedmdesátiny assemblerů: lidsky čitelný strojový kód
https://www.root.cz/clanky/sedmdesatiny-assembleru-lidsky-citelny-strojovy-kod/ - Color names
https://atariwiki.org/wiki/Wiki.jsp?page=Color%20names - ATASCII
https://en.wikipedia.org/wiki/ATASCII - Put characters in display ram isn't ATASCII?
https://forums.atariage.com/topic/359973-put-characters-in-display-ram-isnt-atascii/ - ATASCII And Internal Character Code Values
https://www.atariarchives.org/mapping/appendix10.php - Reading ATASCII from the keyboard in assembly
https://forums.atariage.com/topic/361733-reading-atascii-from-the-keyboard-in-assembly/ - Why does the 6502 JSR instruction only increment the return address by 2 bytes?
https://retrocomputing.stackexchange.com/questions/19543/why-does-the-6502-jsr-instruction-only-increment-the-return-address-by-2-bytes - Pushing return address to stack off by 1 byte
https://forums.atariage.com/topic/378206-pushing-return-address-to-stack-off-by-1-byte/ - Intel x86 documentation has more pages than the 6502 has transistors
https://www.righto.com/2013/09/intel-x86-documentation-has-more-pages.html - Clearing a Section of Memory
http://www.6502.org/source/general/clearmem.htm - Practical Memory Move Routines by Bruce Clark
http://www.6502.org/source/general/memory_move.html - 6502 Assembly Programming Guide
https://neumont-gamedev.github.io/posts/retrogamedev-6502-guide/ - Off-by-one error
https://en.wikipedia.org/wiki/Off-by-one_error - 6502 cycle times
https://www.nesdev.org/wiki/6502_cycle_times - Atari TIA
http://www.atarihq.com/danb/tia.shtml - TIA Playfield
http://www.atarihq.com/danb/TIA/Playfield.shtml - Atari Inc.:
ANTIC C012296 (NTSC) Revision D
Atari Incorporated, Sunnyvale CA, 1982 - Atari Inc.:
GTIA C014805 (NTSC) Revision A
Atari Incorporated, Sunnyvale CA, 1982 - Atari 5200
http://www.atariage.com/software_search.html?SystemID=5200 - Atari 5200 Hardware and Accessories
http://www.atariage.com/5200/archives/hardware.html - Atari 5200 Screenshots
http://www.atariage.com/system_items.html?SystemID=5200&ItemTypeID=SCREENSHOT - History of video game consoles (second generation): Wikipedia
http://en.wikipedia.org/wiki/History_of_video_game_consoles_(second_generation) - Atari 5200: Wikipedia
http://en.wikipedia.org/wiki/Atari_5200 - Player-Missile Graphics
https://www.atariarchives.org/agagd/chapter5.php - Sprite (computer graphics)
https://en.wikipedia.org/wiki/Sprite_(computer_graphics) - Atari Graphics Demonstrations by Underground Software, 1985 | Atari 8 bit Demo
https://www.youtube.com/watch?v=h7N9EYSyCkw - Atari 8-bit Display List Interrupts: A Complete(ish) Tutorial
https://playermissile.com/dli_tutorial/ - Atari Assembler Editor manual
https://atariwiki.org/wiki/attach/Atari%20Assembler%20Editor/ATARI%20Assembler%20Editor%20User-s%20Manual-OCR.pdf - Atari 8-bit Fine Scrolling: A Complete(ish) Tutorial
https://playermissile.com/scrolling_tutorial/index.html - Atari Fine Scrolling
https://www.atarimagazines.com/compute/issue67/338_1_Atari_Fine_Scrolling.php
