Praktické použití grafických režimů nabízených čipem ANTIC
Co se dozvíte v článku
- Praktické použití grafických režimů nabízených čipem ANTIC
- Všechny grafické (bitmapové) režimy podporované čipem ANTIC
- Omezení adresovatelné kapacity video RAM čipem ANTIC a trik, který s tímto omezením pracuje
- Testovací dvoubarevný obrázek a jeho oříznuté varianty
- Demonstrační příklad: zobrazení rastrového obrázku v dvoubarevném režimu 80×48 pixelů
- Zjednodušený zápis display listu s využitím klauzule .res
- Nepřekročili jsme limit 4kB bloku pro video RAM?
- Demonstrační příklad: zobrazení rastrového obrázku v dvoubarevném režimu 160×96 pixelů
- Na hranici 4kB bloku
- Demonstrační příklad: zobrazení rastrového obrázku v dvoubarevném režimu 160×192 pixelů
- Grafický režim číslo 8: 320×192 pixelů monochromaticky
- Obrázek uložený ve formě binárních dat
- Demonstrační příklad: první pokus o zobrazení rastrového obrázku v režimu GR.8
- Zarovnání video RAM do bloků 4096 bajtů
- Oprava display listu: definice dvou paměťových bloků pro video RAM
- Korektní demonstrační příklad: rozdělení obrázku do dvou bloků video RAM pro režim GR.8
- Čtyřbarevné grafické režimy
- Příloha: Makefile pro překlad všech demonstračních příkladů
- Repositář s demonstračními příklady
- Odkazy na Internetu
Na předchozí článek s popisem textových režimů podporovaných čipem ANTIC, který je nedílnou součástí osmibitových počítačů, dnes navážeme. Popíšeme si totiž všechny oficiálně podporované grafické (rastrové) režimy a taktéž si ukážeme, jakým způsobem se řeší jedno z omezení ANTICu: možnost adresovat pouze 4kB video RAM. Všechny popisované vlastnosti si pochopitelně ukážeme na demonstračních příkladech.
Obrázek 1: Již minule jsme si v krátkosti představili grafický režim s rozlišením 80×48 pixelů, navíc ještě ve dvoubarevném provedení. To samozřejmě není zdaleka vše, co Atari nabízí.
Všechny grafické (bitmapové) režimy podporované čipem ANTIC
Připomeňme si, že grafických režimů podporovaných čipem ANTIC je celkem osm (dalších šest režimů je textových, takže celkem je podporováno čtrnáct základních režimů, které se dále rozšiřují o GTIA režimy, kterými se dnes ovšem nebudeme zabývat). Různé varianty základních grafických režimů vznikly kombinací různého horizontálního rozlišení (40, 80, 160 nebo 320 pixelů), vertikálního rozlišení (24, 48, 96 nebo 192 obrazových řádků) a počtu barev (zde je výběr jednoduchý: 2 nebo 4). Teoreticky by tedy mělo existovat 4×4×2=32 grafických režimů, ovšem většina „divných“ kombinací není podporována a navíc jsme omezeni i prakticky: potřebnou velikostí video RAM i vlastnostmi TV.
Základní informace o všech podporovaných grafických režimech čipu ANTIC jsou uvedeny v následující tabulce:
| ANTIC | BASIC | Pixelů/řádek | Počet řádků | Počet barev | Spotřeba RAM |
|---|---|---|---|---|---|
| 8 | 3 | 40 | 24 | 4 | 240 |
| 9 | 4 | 80 | 48 | 2 | 480 |
| A | 5 | 80 | 48 | 4 | 960 |
| B | 6 | 160 | 96 | 2 | 1920 |
| D | 7 | 160 | 96 | 4 | 3840 |
| C | 14 | 160 | 192 | 2 | 3840 |
| E | 15 | 160 | 192 | 4 | 7680 |
| F | 8 | 320 | 192 | 2 | 7680 |
Grafické režimy lze podle jejich základních vlastností rozdělit do tří podskupin:
- V první podskupině se nachází režimy se čtyřmi barvami. Jedná se o režimy s rozlišeními 40×24, 80×48, 160×96 a 160×192 pixelů (ANTIC 8, A, D a E) neboli z pohledu Atari BASICu GR.3, GR.5, GR.7 a GR.15. Každý pixel je v těchto režimech uložen ve dvou bitech a kromě posledního režimu jsou pixely čtvercové (v posledním režimu mají poloviční výšku).
- Režimy s rozlišením 80×48, 160×96 a 160×192 pixelů mají i svoje dvojbarevné varianty, v nichž je každý pixel uložen v jediném bitu. Jedná se o režimy ANTIC 9, B a C neboli GR.4, GR.6 a GR.14 (kupodivu neexistuje režim 40×24 pixelů se dvěma barvami; to by byla hodně primitivní grafika i na počítače z konce sedmdesátých let).
- Režim s rozlišením 320×192 pixelů (ANTIC F neboli v BASICu GR.8) je monochromatický, což znamená, že barva pozadí i popředí má stejný odstín a pouze jinou intenzitu. Jedná se tedy o obdobu standardního textového režimu GR.0, který má (z pohledu barev) stejné vlastnosti. Tento režim nabízí nejvyšší možné (standardní) rozlišení, ovšem taktéž vyžaduje skoro 8 kB RAM (jinak se ovšem jedná o běžný režim s jedním bitem na pixel).
Výběr barev pixelů se v jednotlivých režimech provádí takto (povšimněte si, proč je GR. 8 ve své vlastní kategorii, i když by jinak mohl spadat do druhé kategorie):
| Režimy | Výběr barev pixelů(registry) | Barva okraje |
|---|---|---|
| GR.3, GR.5, GR.7 a GR.15 | COLOR4, COLOR0, COLOR1, COLOR2 | COLOR4 |
| GR.4, GR.6 a GR.14 | COLOR4, COLOR0 | COLOR4 |
| GR.8 | COLOR0/COLOR1 (odstín, intenzita) | COLOR4 |
Omezení adresovatelné kapacity video RAM čipem ANTIC a trik, který s tímto omezením pracuje
Připomeňme si, jakým způsobem jsme prozatím vytvářeli display list (a to pro libovolný textový nebo rastrový režim). Jednalo se o sekvenci instrukcí pro čip ANTIC v tomto pořadí:
- Několik prázdných řádků na začátku obrazovky
- Nastavení počátku video RAM + nastavení prvního řádku vybraného režimu (jedna instrukce)
- Dále jsme nastavili dalších x řádků vybraného režimu
- Instrukce pro skok na začátek display listu
Příkladem může být display list s definicí 24 řádků v textovém režimu 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 .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Tímto způsobem lze tvořit display list pro libovolné režimy, ovšem musíme si dát pozor na to, že velikost video RAM nesmí překročit blok 4kB. V prvním kroku se tedy budeme snažit o to, aby video RAM začínala na začátku takového bloku, ovšem pro režimy 320×192 (dvě barvy) a 160×192 (čtyři barvy) je potřebná kapacita RAM rovna 7680 bajtům, což zcela jistě čtyřkilobajtový blok překročí. V takovém případě je nutné už do display listu vložit další instrukci nebo instrukce LMS, například:
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 .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_LMS+DL_CHR40x8x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 2 (GR.0) .byte <screen2, >screen2 ; počáteční adresa druhého bloku obrazové paměti .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_CHR40x8x1 ; jeden řádek textového režimu 2 (GR.0) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Testovací dvoubarevný obrázek a jeho oříznuté varianty
Nejprve se budeme zabývat dvoubarevnými rastrovými grafickými režimy, ve kterých je pro každý pixel vyhrazen jen jeden bit. Barvy osmi sousedních pixelů jsou vždy uloženy v jednom bajtu, takže velikost video RAM se vypočítá snadno: šířka×výška/8. Ve všech příkladech pro dvoubarevné režimy se pokusíme vykreslit obrázek (nebo jeho část) získaný ze slavné hry Starquake. Originál tohoto obrázku vypadá následovně:
Obrázek 1: Původní obrázek ze hry Starquake, který použijeme v dalších příkladech.
Z tohoto obrázku v původním rozlišení 320×192 pixelů byly v grafickém editoru vytvořeny výřezy 80×48 pixelů, 160×96 pixelů a 160×192 pixelů. Následně byly binární data obrázku převedeny do textového formátu kompatibilního s assemblerem CA65. Příkladem je obrázek 80×48 pixelů, který lze reprezentovat následovně (soubor image_80×48.asm):
.byte $ff, $ff, $fb, $bb, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $7f, $ff, $ff .byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $7f, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff .byte $fe, $7f, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $fd, $7f, $ff, $ff, $ff, $ff .byte $ff, $ff, $ff, $f0, $0d, $b0, $00, $00, $00, $00, $00, $00, $ff, $e0, $1b, $d8 .byte $00, $00, $00, $00, $00, $00, $ff, $e0, $0f, $f0, $00, $00, $00, $00, $00, $00 .byte $ff, $c7, $ff, $3f, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $c7, $ff, $3f, $ff, $ff .byte $ff, $ff, $ff, $ff, $ff, $8f, $fe, $7f, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $8f .byte $fe, $0f, $f0, $00, $00, $00, $00, $00, $ff, $1f, $fc, $1f, $e0, $00, $00, $00 .byte $00, $00, $ff, $1f, $fc, $1f, $e7, $fe, $7f, $ff, $9f, $fc, $fe, $3f, $f8, $3f .byte $cf, $fe, $7f, $ff, $9f, $fc, $fe, $3f, $f8, $3f, $cf, $fe, $7f, $ff, $3f, $fc .byte $fc, $7f, $f0, $7f, $9f, $fe, $7f, $ff, $3f, $fc, $fc, $7f, $f8, $7f, $9f, $fe .byte $7f, $fe, $7f, $fc, $f8, $ff, $f8, $ff, $3f, $fe, $7f, $fe, $7f, $fc, $f8, $ff .byte $fc, $ff, $3f, $fe, $7f, $fc, $fe, $fc, $f1, $ff, $fc, $fe, $7e, $7e, $7e, $fc .byte $fc, $fc, $f1, $ff, $fe, $7e, $7e, $7e, $7c, $f9, $fc, $fc, $e0, $07, $fe, $7c .byte $fc, $7e, $7c, $f9, $f8, $fc, $e0, $03, $ff, $3c, $fe, $7e, $7f, $f3, $f8, $fc .byte $c7, $ff, $ff, $39, $fe, $7e, $7f, $f3, $fc, $fc, $c7, $ff, $ff, $99, $fe, $7e .byte $7f, $f9, $fe, $fc, $8f, $ff, $ff, $93, $fe, $7e, $7f, $fc, $ff, $fc, $8f, $ff .byte $ff, $c3, $e0, $7e, $7c, $fe, $7f, $fc, $1f, $ff, $ff, $c7, $e0, $7e, $7c, $ff .byte $3f, $fc, $1f, $ff, $ff, $e7, $c0, $7e, $7c, $ff, $9f, $fc, $8f, $ff, $ff, $e7 .byte $c0, $7e, $7c, $ff, $cf, $fc, $8f, $ff, $ff, $f3, $80, $7e, $7c, $ff, $e7, $fc .byte $c0, $0d, $f0, $00, $00, $7e, $00, $00, $03, $fc, $c0, $15, $f8, $00, $00, $7e .byte $00, $00, $01, $fc, $e3, $f9, $ef, $ff, $ff, $ff, $ff, $ff, $f8, $fc, $e3, $fd .byte $cf, $ff, $ff, $ff, $ff, $ff, $f8, $fd, $f1, $fd, $df, $ff, $ff, $ff, $ff, $ff .byte $f8, $ff, $f0, $0d, $d8, $00, $00, $00, $00, $00, $00, $ff, $f8, $10, $98, $00 .byte $00, $00, $00, $00, $00, $ff, $f8, $0e, $30, $08, $00, $04, $10, $00, $00, $fc .byte $ff, $fe, $3f, $f7, $ff, $fb, $ef, $ff, $f8, $fb, $ff, $ff, $3f, $f1, $ff, $e7 .byte $9f, $ff, $f8, $c9, $ff, $ff, $9f, $f7, $ff, $cc, $3f, $ff, $f8, $0d, $ff, $ff .byte $9f, $e7, $ff, $81, $ff, $ff, $f8, $0c, $ff, $ff, $3e, $00, $3e, $3f, $ff, $ff .byte $fc, $3e, $ff, $ff, $20, $7f, $00, $7f, $ff, $ff, $fe, $fe, $ff, $fe, $43, $ff .byte $ff, $ff, $ff, $7f, $ff, $ff, $ff, $fe, $0f, $ff, $ff, $ff, $ff, $ff, $ff, $ff
Demonstrační příklad: zobrazení rastrového obrázku v dvoubarevném režimu 80×48 pixelů
V dnešním prvním demonstračním příkladu je nastaven dvoubarevný režim s rozlišením 80×48 pixelů, což je ten nejjednodušší grafický režim vůbec. Zápisem do registrů COLOR0 a COLOR4 jsou zvoleny barvy pixelů i pozadí a následně je příkazem:
screen: .include "image_80x48.asm"
přímo do zdrojového kódu vložena sekvence bajtů představující rastrový obrázek. Ten se nikam nepřenáší, protože data obrázku jsou umístěna na adresu uvedenou návěštím screen, jehož adresa byla zapsána do display listu. Výsledek vypadá následovně:
Obrázek 2: Výřez z originálního obrázku v grafickém režimu 80x48 pixelů.
; ---------------------------------------------------------------------
; Grafický režim 80x48 se dvěma barvami.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #10 ; kod barvy
sta COLOR0 ; ulozit do registru COLOR2
lda #0 ; kod barvy
sta COLOR4 ; ulozit do registru COLOR4
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
loop: jmp loop
.endproc
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_MAP80x4x2 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 9 (GR.4)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
screen:
.include "image_80x48.asm"
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
Zjednodušený zápis display listu s využitím klauzule .res
V předchozím demonstračním příkladu jsme 47× opakovali stejný řádek, resp. přesněji řečeno stejnou instrukci display listu:
.byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4) .byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4) .byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4) .byte DL_MAP80x4x2 ; jeden řádek grafického režimu 9 (GR.4) ... ... ...
To je ovšem zbytečně pracné (ostatně dovedete si představit display list pro režimy se 192 řádky), takže namísto toho použijeme jiný příkaz assembleru CA65 – .res. Zadáme počet opakování a bajt, který se má opakovat:
.res 47, DL_MAP80x4x2 ; opakovat řádky grafického režimu 9 (GR.4)
Celý display list se tedy zkrátí na:
dlist: .byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků .byte DL_LMS+DL_MAP80x4x2 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 9 (GR.4) .byte <screen, >screen ; počáteční adresa obrazové paměti .res 47, DL_MAP80x4x2 ; opakovat řádky grafického režimu 9 (GR.4) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Výsledkem bude naprosto stejný binární soubor a tedy i stejný výsledný obrázek:
Obrázek 3: Výřez z originálního obrázku v grafickém režimu 80x48 pixelů.
Pro úplnost si uvedeme i úplný zdrojový kód tohoto demonstračního příkladu:
; ---------------------------------------------------------------------
; Grafický režim 80x48 se dvěma barvami.
; Použití klauzule .res
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #0 ; kod barvy
sta COLOR0 ; ulozit do registru COLOR2
lda #10 ; kod barvy
sta COLOR4 ; ulozit do registru COLOR4
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
loop: jmp loop
.endproc
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_MAP80x4x2 ; určení počáteční adresy obrazové paměti + jeden řádek režimu 9 (GR.4)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 47, DL_MAP80x4x2 ; opakovat řádky grafického režimu 9 (GR.4)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
screen:
.include "image_80x48.asm"
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
Nepřekročili jsme limit 4kB bloku pro video RAM?
V předchozím textu jsme si řekli, že video RAM byl neměla překročit hranici čtyřkilobajtového bloku. Ovšem v obou úvodních demonstračních příkladech jsme vlastně nijak tento problém neřešili – jak programový kód, tak i display list a dokonce i samotná obrazová paměť jsou totiž součástí jediného kódového segmentu. Pokud tento segment nepřekročí velikost čtyř kilobajtů, bude vše v pořádku.
Pokusme se tedy zjistit, jak je kódový segment rozsáhlý a na jaké adrese začíná. Všechny tyto informace jsou dostupné v souboru s koncovkou .map, který produkuje linker:
Modules list:
-------------
antic_bitmap_2.o:
CODE Offs=000000 Size=00022F Align=00001 Fill=0000
EXEHDR Offs=000000 Size=000006 Align=00001 Fill=0000
AUTOSTRT Offs=000000 Size=000006 Align=00001 Fill=0000
Segment list:
-------------
Name Start End Size Align
----------------------------------------------------
AUTOSTRT 000000 000005 000006 00001
EXEHDR 000000 000005 000006 00001
CODE 002000 00222E 00022F 00001
Exports list by name:
---------------------
Exports list by value:
----------------------
Imports list:
-------------
Důležitý je tento řádek v sekci Segment list:
CODE 002000 00222E 00022F 00001
Z obsahu tohoto řádku vyplývá, že kódový segment začíná na adrese 0×2000 (8192), tj. na ideální hranici čtyřkilobajtového bloku. První podmínku jsme splnili. Délka segmentu je 0×22F neboli 559 bajtů, což je hluboko pod kritickou hodnotou 4096. I druhou podmínku jsme splnili – vše je v pořádku.
Demonstrační příklad: zobrazení rastrového obrázku v dvoubarevném režimu 160×96 pixelů
Další demonstrační příklad se v mnoha ohledech podobá oběma příkladům předchozím, ovšem namísto režimu s rozlišením 80×48 pixelů (což je hodně málo) použijeme režim s rozlišením 160×96 pixelů. Jeho display list vypadá následovně:
dlist: .byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků .byte DL_LMS+DL_MAP160x2x2 ; určení počáteční adresy obrazové paměti + jeden řádek režimu B (GR.6) .byte <screen, >screen ; počáteční adresa obrazové paměti .res 95, DL_MAP160x2x2 ; opakovat řádky grafického režimu B (GR.6) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Budeme vkládat i odlišný soubor s definicí bitmapy, která se má zobrazit:
screen: .include "image_160x96.asm"
Výsledek by měl vypadat takto (to je již lepší, i když nikoli dokonalé):
Obrázek 4: Výřez z originálního obrázku v grafickém režimu 160x96 pixelů.
Výpis úplného zdrojového kódu tohoto příkladu:
; ---------------------------------------------------------------------
; Grafický režim 160x96 se dvěma barvami.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #0 ; kod barvy
sta COLOR0 ; ulozit do registru COLOR2
lda #10 ; kod barvy
sta COLOR4 ; ulozit do registru COLOR4
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
loop: jmp loop
.endproc
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_MAP160x2x2 ; určení počáteční adresy obrazové paměti + jeden řádek režimu B (GR.6)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 95, DL_MAP160x2x2 ; opakovat řádky grafického režimu B (GR.6)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
screen:
.include "image_160x96.asm"
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
Pro jistotu zkontrolujeme i velikost kódového segmentu ohlášenou linkerem:
Modules list:
-------------
antic_bitmap_3.o:
CODE Offs=000000 Size=0007FF Align=00001 Fill=0000
EXEHDR Offs=000000 Size=000006 Align=00001 Fill=0000
AUTOSTRT Offs=000000 Size=000006 Align=00001 Fill=0000
Segment list:
-------------
Name Start End Size Align
----------------------------------------------------
AUTOSTRT 000000 000005 000006 00001
EXEHDR 000000 000005 000006 00001
CODE 002000 0027FE 0007FF 00001
Exports list by name:
---------------------
Exports list by value:
----------------------
Imports list:
-------------
Velikost kódového segmentu je rovna 0×7ff neboli 2047 bajtů. Stále se tedy nacházíme hluboko pod hranicí čtyř kilobajtů.
Na hranici 4kB bloku
V další části dnešního článku si ukážeme použití grafického režimu s rozlišením 160×192 pixelů se dvěma barvami. Potřebná velikost video RAM je v tomto případě rovna: 160×192/8=3840 bajtů. To tedy znamená, že samotná video RAM se stále ještě vejde do bloku o velikosti 4096 bajtů a zbytek, tj. 4096–3840=256 bajtů zbývá na vlastní programový kód a na display list. Nejprve si ukažme display list:
dlist: .byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků .byte DL_LMS+DL_MAP160x1x2 ; určení počáteční adresy obrazové paměti + jeden řádek režimu C (GR.14) .byte <screen, >screen ; počáteční adresa obrazové paměti .res 191, DL_MAP160x1x2 ; opakovat řádky grafického režimu C (GR.14) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Což je 3+3+191+3=200 bajtů. Na programový kód tedy zbývá jen 56 bajtů. V éře několika megabajtových programů typu „Hello world!“ to není moc, že? Ale zkusme počítat:
000000r 1 A9 00 lda #0 ; kod barvy 000002r 1 8D C4 02 sta COLOR0 ; ulozit do registru COLOR2 000005r 1 000005r 1 A9 0A lda #10 ; kod barvy 000007r 1 8D C8 02 sta COLOR4 ; ulozit do registru COLOR4 00000Ar 1 00000Ar 1 A9 rr lda #<dlist ; nižší byte adresy display listu 00000Cr 1 8D 30 02 sta SDLSTL 00000Fr 1 A9 rr lda #>dlist ; vyšší byte adresy display listu 000011r 1 8D 31 02 sta SDLSTH 000014r 1 000014r 1 4C rr rr loop: jmp loop 000017r 1 dlist:
0×17 znamená 23 bajtů programového kódu, tj. bez problémů jsme se vešli do zbývajícího limitu.
Demonstrační příklad: zobrazení rastrového obrázku v dvoubarevném režimu 160×192 pixelů
Ukažme si nyní, jak bude vypadat testovací obrázek upravený pro režim 160×192×2 barvy:
Obrázek 5: Výřez z originálního obrázku v grafickém režimu 160x192 pixelů.
Tento obrázek je vykreslen následujícím příkladem:
; ---------------------------------------------------------------------
; Grafický režim 160x192 se dvěma barvami.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #0 ; kod barvy
sta COLOR0 ; ulozit do registru COLOR2
lda #10 ; kod barvy
sta COLOR4 ; ulozit do registru COLOR4
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
loop: jmp loop
.endproc
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_MAP160x1x2 ; určení počáteční adresy obrazové paměti + jeden řádek režimu C (GR.14)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 191, DL_MAP160x1x2 ; opakovat řádky grafického režimu C (GR.14)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
screen:
.include "image_160x192.asm"
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
Zkontrolujeme soubor .map vyprodukovaný linkerem. Zajímat nás opět bude velikost kódového segmentu:
Modules list:
-------------
antic_bitmap_4.o:
CODE Offs=000000 Size=000FDF Align=00001 Fill=0000
EXEHDR Offs=000000 Size=000006 Align=00001 Fill=0000
AUTOSTRT Offs=000000 Size=000006 Align=00001 Fill=0000
Segment list:
-------------
Name Start End Size Align
----------------------------------------------------
AUTOSTRT 000000 000005 000006 00001
EXEHDR 000000 000005 000006 00001
CODE 002000 002FDE 000FDF 00001
Exports list by name:
---------------------
Exports list by value:
----------------------
Imports list:
-------------
Hodnota 0×0fdf odpovídá 4063, což je stále pod limitem 4096 bajtů.
Grafický režim číslo 8: 320×192 pixelů monochromaticky
Nyní přejděme ke grafickému režimu s nejvyšším možným rozlišením 320×192 pixelů. Prozatím jsme nenarazili na limit čtyř kilobajtů, ovšem nyní tento případ nastane, protože potřebná velikost video RAM je rovna 320×192/8=7680 bajtů, což je téměř dvojnásobek limitní velikosti. Budeme tedy muset vyřešit dva problémy: rozdělení video RAM v display listu a poté zajištění načtení dvou datových bloků, přičemž každý z těchto bloků bude začínat na hranici čtyř kilobajtů. Ovšem nejdříve si pochopitelně ukážeme, co se stane v případě, kdy tyto dva problémy budeme ignorovat – veškeré chyby budou ihned patrné při pohledu na obrazovku (resp. na její screenshot).
Obrázek uložený ve formě binárních dat
V dalších příkladech provedeme ještě jednu změnu. Již nebudeme načítat data rastrového obrázku uloženého ve formě příkazů .byte. Namísto toho bude rastrový obrázek uložen ve své binární podobě (1:1, tedy 7680 bajtů) v souboru image_320×192.bin. Obsah tohoto souboru je možné vložit do překládaného kódu příkazem .incbin (nikoli .include):
screen: .incbin "image_320x192.bin"
Demonstrační příklad: první pokus o zobrazení rastrového obrázku v režimu GR.8
Nyní tedy máme k dispozici rastrový obrázek uložený jako binární blok o velikosti 7680 bajtů, takže se pokusíme o jeho zobrazení s ignorováním všech problémů, které mohou nastat. Display list bude obsahovat pouze definice 192 stejných řádků:
dlist: .byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků .byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8) .byte <screen, >screen ; počáteční adresa obrazové paměti .res 191, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Obsah obrázku přímo vložíme do kódového segmentu jako ucelený blok:
screen: .incbin "image_320x192.bin"
Ještě nastavíme vhodné barvy (odlišně od předchozích režimů):
lda #0 ; kod barvy
sta COLOR1 ; ulozit do registru COLOR4
lda #14 ; kod barvy
sta COLOR2 ; ulozit do registru COLOR2
Výsledek nebude (podle očekávání) ideální, ovšem umožní nám vizuálně odhalit všechny problémy:
Obrázek 6: V tomto případě se zobrazí první polovina obrázku následovaná programovým kódem, display listem a částí původního obrázku.
Můžeme zde vidět „roztržení“ video RAM na hranici 4kB bloku. ANTIC začne data načítat ze začátku stejného bloku, což je další problém, protože zde leží programový kód i vlastní display list. Mimochodem ony „čtverečky“ jsou právě opakující se instrukce display listu.
Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:
; ---------------------------------------------------------------------
; Grafický režim 320x192 se dvěma barvami.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #0 ; kod barvy
sta COLOR1 ; ulozit do registru COLOR4
lda #14 ; kod barvy
sta COLOR2 ; ulozit do registru COLOR2
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
loop: jmp loop
.endproc
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 191, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
screen:
.incbin "image_320x192.bin"
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
Zarovnání video RAM do bloků 4096 bajtů
Pokusme se vyřešit první z problémů, tj. uložení celé video RAM do segmentu, který bude začínat přesně na hranici 4kB bloku. To je snadné (i když je nutné hledat podrobnosti v dokumentaci LD65, která je poměrně kryptická). Upravíme konfigurační soubor pro linker přidání nového segmentu IMAGESEG:
SEGMENTS
{
EXEHDR: load = HEADER, type = ro;
STARTUP: load = RAM, type = ro, define = yes, optional = yes;
ZEROPAGE: load = ZP, type = zp;
CODE: load = RAM, type = ro, define = yes;
AUTOSTRT: load = TRAILER, type = ro;
FONT: load = RAM, type = ro, optional = yes, align=1024;
IMAGESEG: load = RAM, type = ro, optional = yes, start=$3000;>
BSS: load = RAM, type = bss, define = yes, align=1024;
}
Ve zdrojovém kódu nejdříve ukončíme kódový segment (řádek s end) a poté do nového segmentu načteme celý obrázek:
.segment "IMAGESEG" .org $3000 screen: .incbin "image_320x192.bin"
Po překladu se v souboru .map přesvědčíme, že výsledný spustitelný soubor obsahuje nový segment i jeho obsah:
Modules list:
-------------
antic_bitmap_6.o:
CODE Offs=000000 Size=0000DF Align=00001 Fill=0000
IMAGESEG Offs=000000 Size=001E00 Align=00001 Fill=0000
EXEHDR Offs=000000 Size=000006 Align=00001 Fill=0000
AUTOSTRT Offs=000000 Size=000006 Align=00001 Fill=0000
Segment list:
-------------
Name Start End Size Align
----------------------------------------------------
AUTOSTRT 000000 000005 000006 00001
EXEHDR 000000 000005 000006 00001
CODE 002000 0020DE 0000DF 00001
IMAGESEG 003000 004DFF 001E00 00001
Výsledek sice stále nebude ideální, ovšem minimálně je nyní patrné, že ANTIC skutečně po dosažení čtyřkilobajtového bloku začne zobrazovat data ze začátku tohoto bloku:
Obrázek 7: Nyní se zobrazí zhruba první polovina obrázku a posléze ta se začne zobrazovat stejná polovina znovu.
Opět si pro úplnost ukažme celý zdrojový kód demonstračního příkladu:
; ---------------------------------------------------------------------
; Grafický režim 320x192 se dvěma barvami.
; Zarovnání bitmapy do bloků 4096 bajtů.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #0 ; kod barvy
sta COLOR1 ; ulozit do registru COLOR4
lda #14 ; kod barvy
sta COLOR2 ; ulozit do registru COLOR4
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
loop: jmp loop
.endproc
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 191, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
.segment "IMAGESEG"
.org $3000
screen:
.incbin "image_320x192.bin"
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
Oprava display listu: definice dvou paměťových bloků pro video RAM
Aby se korektně zobrazil rastrový obrázek o velikosti 7680 bajtů (který evidentně překračuje velikost 4kB), musíme nejdříve opravit display list. Po 96 řádcích GR.8 vložíme novou instrukci LMS (Load Memory Scan), za kterou následuje adresa druhé části video RAM. Video RAM je tedy rozdělena na dvě oblasti označené screen1 a screen2. Každá z těchto oblastí bude obsahovat 96 řádků GR.8 a tedy bude mít velikost 320×96/8=3840 bajtů (pod hranicí 4kB):
dlist: .byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků .byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8) .byte <screen1, >screen1 ; počáteční adresa obrazové paměti (první blok) .res 95, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8) .byte <screen2, >screen2 ; počáteční adresa obrazové paměti (druhý blok) .res 95, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
I samotný rastrový obrázek budeme muset rozdělit na dva binární bloky, každý o velikosti oněch 3840 bajtů. A každý z bloků se načte do svého vlastního segmentu:
.segment "IMAGESEG1" .org $3000 screen1: .incbin "image_320x192_1.bin" .segment "IMAGESEG2" .org $4000 screen2: .incbin "image_320x192_2.bin"
A – jak již pravděpodobně tušíte – musíme upravit i konfigurační soubor linkeru. Konkrétně v něm definujeme dva nové segmenty IMAGESEG1 a IMAGESEG2 a pro jednoduchost jim nastavíme počáteční adresy na začátku 4kB bloků:
SEGMENTS
{
EXEHDR: load = HEADER, type = ro;
STARTUP: load = RAM, type = ro, define = yes, optional = yes;
ZEROPAGE: load = ZP, type = zp;
CODE: load = RAM, type = ro, define = yes;
AUTOSTRT: load = TRAILER, type = ro;
FONT: load = RAM, type = ro, optional = yes, align=1024;
IMAGESEG1: load = RAM, type = ro, optional = yes, start=$3000;
IMAGESEG2: load = RAM, type = ro, optional = yes, start=$4000;
BSS: load = RAM, type = bss, define = yes, align=1024;
}
Po překladu upraveného příkladu pohledem na soubor .map ověříme, že existují dva nové segmenty:
Modules list:
-------------
antic_bitmap_7.o:
CODE Offs=000000 Size=0000E1 Align=00001 Fill=0000
IMAGESEG1 Offs=000000 Size=000F00 Align=00001 Fill=0000
IMAGESEG2 Offs=000000 Size=000F00 Align=00001 Fill=0000
EXEHDR Offs=000000 Size=000006 Align=00001 Fill=0000
AUTOSTRT Offs=000000 Size=000006 Align=00001 Fill=0000
Segment list:
-------------
Name Start End Size Align
----------------------------------------------------
AUTOSTRT 000000 000005 000006 00001
EXEHDR 000000 000005 000006 00001
CODE 002000 0020E0 0000E1 00001
IMAGESEG1 003000 003EFF 000F00 00001
IMAGESEG2 004000 004EFF 000F00 00001
A samozřejmě nás bude zajímat výsledek:
Obrázek 8:Konečně je vše korektní.
uff…
Korektní demonstrační příklad: rozdělení obrázku do dvou bloků video RAM pro režim GR.8
Úplný zdrojový kód demonstračního příkladu, který (konečně!) dokáže zobrazit korektní rastrový obrázek v grafickém režimu GR.8 (320×192 pixelů) bude vypadat následovně:
; ---------------------------------------------------------------------
; Grafický režim 320x192 se dvěma barvami.
; Zarovnání bitmapy do bloků 4096 bajtů.
; Korektní display list
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #0 ; kod barvy
sta COLOR1 ; ulozit do registru COLOR4
lda #14 ; kod barvy
sta COLOR2 ; ulozit do registru COLOR4
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
loop: jmp loop
.endproc
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8)
.byte <screen1, >screen1 ; počáteční adresa obrazové paměti (první blok)
.res 95, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_LMS+DL_MAP320x1x1 ; určení počáteční adresy obrazové paměti + jeden řádek režimu F (GR.8)
.byte <screen2, >screen2 ; počáteční adresa obrazové paměti (druhý blok)
.res 95, DL_MAP320x1x1 ; opakovat řádky grafického režimu F (GR.8)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
.segment "IMAGESEG1"
.org $3000
screen1:
.incbin "image_320x192_1.bin"
.segment "IMAGESEG2"
.org $4000
screen2:
.incbin "image_320x192_2.bin"
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
Čtyřbarevné grafické režimy
Při popisu dvoubarevných grafických režimů jsme se dozvěděli všechny informace potřebné i pro používání registrů čtyřbarevných. Ty se odlišují pouze tím, že barvy pixelů jsou uloženy ve dvou sousedních bitech a pixel tedy může být vykreslen barvou získanou z registru COLOR4 (pozadí), COLOR0, COLOR1 nebo COLOR2. Kromě grafického režimu GR.15 (160×192×4 barvy) mají všechny ostatní čtyřbarevné režimy takové požadavky na video RAM, že se bez problémů vejdou do 4kB bloku. A v případě režimu GR.15 se použije stejný trik s instrukcí LMS, který jsme již viděli výše.
Podívejme se na způsob nastavení režimu 160×96 pixelů se čtyřmi barvami. Tento režim má čtvercové pixely a relativně rozumně malé požadavky na video RAM (160×96/4=3840 bajtů):
dlist: .byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků .byte DL_LMS+DL_MAP160x2x4 ; určení počáteční adresy obrazové paměti + jeden řádek režimu B (GR.6) .byte <screen, >screen ; počáteční adresa obrazové paměti .res 95, DL_MAP160x2x4 ; opakovat řádky grafického režimu B (GR.6) .byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
Díky tomu, že se vejdeme i s kódem a display listem do 4kB, můžeme načíst binární obrázek přímo do kódového segmentu:
screen: .incbin "image_160x96x2.bin"
Na závěr nastavíme vhodné barvy:
lda #$76 ; kod barvy sta COLOR0 ; ulozit do registru COLOR0 lda #$34 ; kod barvy sta COLOR1 ; ulozit do registru COLOR1 lda #$0c ; kod barvy sta COLOR2 ; ulozit do registru COLOR2 lda #$46 ; kod barvy sta COLOR3 ; ulozit do registru COLOR3
Výsledek je zcela korektní:
Obrázek 9: Rastrový obrázek s rozlišením 160x96 pixelů se čtyřmi barvami.
A pochopitelně si ukážeme úplný zdrojový kód takto upraveného demonstračního příkladu:
; ---------------------------------------------------------------------
; Grafický režim 160x96 se čtyřmi barvami.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
.proc main
lda #$76 ; kod barvy
sta COLOR0 ; ulozit do registru COLOR0
lda #$34 ; kod barvy
sta COLOR1 ; ulozit do registru COLOR1
lda #$0c ; kod barvy
sta COLOR2 ; ulozit do registru COLOR2
lda #$46 ; kod barvy
sta COLOR3 ; ulozit do registru COLOR3
lda #<dlist ; nižší byte adresy display listu
sta SDLSTL
lda #>dlist ; vyšší byte adresy display listu
sta SDLSTH
loop: jmp loop
.endproc
dlist:
.byte DL_BLK8, DL_BLK8, DL_BLK8 ; 3x8=24 prázdných obrazových řádků
.byte DL_LMS+DL_MAP160x2x4 ; určení počáteční adresy obrazové paměti + jeden řádek režimu B (GR.6)
.byte <screen, >screen ; počáteční adresa obrazové paměti
.res 95, DL_MAP160x2x4 ; opakovat řádky grafického režimu B (GR.6)
.byte DL_JVB, <dlist, >dlist ; skok na začátek display listu
screen:
.incbin "image_160x96x2.bin"
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
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
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
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
