Programování pro osmibitová Atari: blokové výplně a přesuny dat, grafický subsystém

Dnes
Doba čtení: 36 minut

Sdílet

Projekt Atari perex.
Autor: Michal Tauchman, podle licence: CC BY-SA 4.0
Projekt Atari perex.
Čtvrtý článek o vývoji pro počítače Atari je rozdělen na dvě části. V části první si ukážeme vyplňování paměti i přesunů bloků pouze s využitím osmibitových registrů. Pak se začneme zabývat grafickým subsystémem Atari.

Akumulátor a dva index registry – a není to málo, Antone Pavloviči?

Co se dozvíte v článku
  1. Akumulátor a dva index registry – a není to málo, Antone Pavloviči?
  2. Vyplnění bloku paměti bajtem se zvolenou hodnotou
  3. Řešení využívající jen jediný index registr
  4. Oprava klasické chyby ±1
  5. Vyplňování směrem k vyšším adresám
  6. Porovnání všech čtyř metod z pohledu velikosti kódu a výpočetní rychlosti
  7. Vyplnění bloku většího než 256 bajtů: koncept nepřímého adresování
  8. Realizace programu vyplňujícího více než 256 bajtů paměti
  9. Algoritmus pro blokové přesuny dat
  10. Grafické subsystémy osmibitových mikropočítačů
  11. Rozlišení a počet barev
  12. Grafické čipy v osmibitových počítačích Atari
  13. ANTIC – generování rastrové grafiky
  14. Grafické režimy čipu ANTIC
  15. GTIA – sprity a vícebarevná grafika
  16. Priorita spritů a detekce kolize spritů
  17. Příloha A: Seznam všech doposud popsaných instrukcí mikroprocesoru MOS 6502
  18. Příloha B: Makefile pro překlad všech demonstračních příkladů
  19. Repositář s demonstračními příklady
  20. Odkazy na Internetu

Osmibitový mikroprocesor MOS 6502, který je ústředním prvkem osmibitových domácích mikropočítačů Atari, obsahoval pouze minimální, ovšem ještě stále prakticky použitelný počet registrů. Všechny tyto registry jsou pro připomenutí vypsány v následující tabulce:

# Registr Šířka Význam
1 A 8 bitů akumulátor
2 X 8 bitů index registr (osmibitová adresa nebo offset adresy)
3 Y 8 bitů index registr (osmibitová adresa nebo offset adresy)
4 SP 8 bitů část ukazatele na vrchol zásobníku (+ $0100)
5 PC 16 bitů čítač instrukcí
6 P 7/8 bitů příznakový a stavový registr

Poslední tři registry se většinou používají nepřímo. Naopak přímo se pracuje s akumulátorem A a index registry X a Y. Kupodivu i přes tato omezení (připomeňme si, že index registry jsou jen osmibitové) je možné realizovat i sofistikované programy, a to díky pokročilým adresním režimům. Opět si pro připomenutí vypišme ty adresovací režimy, které se používají pro adresování operandů (načítání, ukládání, vyplňování bloků/polí, přenosy bloků atd.). Vynechám režimy používané pro realizaci skoků:

# Zápis Název Assembler Stručný popis
1 impl implied INS operand je odvozen přímo z instrukce, například INX
2 A accumulator INS A operandem je přímo akumulátor
3 # immediate INS #$BB za instrukcí následuje bajt s konstantou
4 zpg zeropage INS $LL operand je uložen na nulté stránce na adrese LL
5 zpg,X zeropage, X-indexed INS $LL,X operand je uložen na nulté stránce na adrese LL+X
6 zpg,Y zeropage, Y-indexed INS $LL,Y operand je uložen na nulté stránce na adrese LL+Y
7 abs absolute INS $LLHH za instrukcí následuje šestnáctibitová adresa, na níž je operand uložen
8 abs,X absolute, X-indexed INS $LLHH,X za instrukcí následuje šestnáctibitová adresa, která je přičtena k X
9 abs,Y absolute, Y-indexed INS $LLHH,Y za instrukcí následuje šestnáctibitová adresa, která je přičtena k Y
10 X,ind X-indexed, indirect INS ($LL,X) efektivní adresa je spočtena z hodnoty uložené na (LL+X)
11 ind,Y indirect, Y-indexed INS ($LL),Y efektivní adresa je spočtena z hodnoty uložené na LL, k výsledku se přičte Y
Poznámka: povšimněte si, že poslední dva adresovací režimy mají i přes podobné jméno zcela odlišnou funkci. Tudíž i index registry X a Y nejsou zcela zaměnitelné. V praxi se setkáme především s druhým režimem (indirect, Y-indexed popř. jen indirect indexed), a to při práci s bloky paměti, poli atd. Podle mého názoru se jedná o jednu z nejlepších vlastností MOS 6502.

Vyplnění bloku paměti bajtem se zvolenou hodnotou

Nejprve se pokusme vytvořit jednoduchý program, který po svém spuštění vyplní prvních pět textových řádků ve standardním textovém režimu. Každý řádek obsahuje čtyřicet znaků, takže je nutné vyplnit pouze 5×40=200 bajtů. A to je „pěkná“ hodnota, protože je menší než 256, což celý algoritmus zjednoduší.

První varianta programu pro vyplnění pěti textových řádků bude používat index registr Y ve funkci offsetu a index registr X bude počitadlo smyčky. Offset se zvyšuje instrukcí INY (INcrement Y) a adresa pro zápis je vypočtena na základě adresy uložené v buňkách 88 a 89, ke které je připočten právě obsah registru Y. Počitadlo budeme postupně snižovat instrukcí DEX (DEcrement X), která mj. nastavuje i příznakový bit zero. Ten je testován instrukcí BNE (Branch if Not Equal), čímž je realizována celá programová smyčka:

.include "atari.inc"
 
.CODE
 
 
.proc main
        lda #33                 ; kod znaku, ktery se bude tisknout
        ldx #40*5               ; počet zápisů
        ldy #0                  ; offset
clear:
        sta (88), y             ; tisk znaku "A" na zvolené místo na obrazovce
                                ; (adresa Video RAM je na adresách 88 a 89)
        iny                     ; zvýšit hodnotu offsetu
        dex                     ; zmenšit hodnotu počitadla
        bne clear               ; skok, pokud X>0
 
loop:   jmp loop
end:
.endproc
 
 
.segment "EXEHDR"
.word   $ffff                   ; uvodni sekvence bajtu v souboru XEX
.word   main                    ; zacatek kodoveho segmentu
.word   main::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

Po překladu a spuštění tohoto programu by měla mít obrazovka následující obsah:

Obrázek 1: Obrazovka ve standardním textovém režimu; prvních pět řádků je vyplněno jednoduchou programovou smyčkou.

Obrázek 1: Obrazovka ve standardním textovém režimu; prvních pět řádků je vyplněno jednoduchou programovou smyčkou. 

Autor: tisnik, podle licence: Rights Managed

Řešení využívající jen jediný index registr

Program z předchozí kapitoly je sice plně funkční, ovšem je taktéž zbytečně dlouhý a využívá všechny tři pracovní registry. Ve skutečnosti je ovšem možné ho zkrátit a navíc využít jen jediný index registr. Musí se jednat o registr Y, protože jen pro něj existuje adresovací režim indirect, Y-indexed. Registr naplníme koncovým offsetem (hodnotou počitadla) a budeme ho postupně snižovat o jedničku. Smyčka bude dokončena ve chvíli, kdy registr dosáhne nulové hodnoty:

.include "atari.inc"
 
.CODE
 
 
.proc main
        lda #33                 ; kod znaku, ktery se bude tisknout
        ldy #40*5               ; počet zápisů
clear:
        sta (88), y             ; tisk znaku "A" na zvolené místo na obrazovce
                                ; (adresa Video RAM je na adresách 88 a 89)
        dey                     ; zmenšit hodnotu počitadla a offsetu
        bne clear               ; skok, pokud Y>0

loop:   jmp loop
end:
.endproc
 
 
.segment "EXEHDR"
.word   $ffff                   ; uvodni sekvence bajtu v souboru XEX
.word   main                    ; zacatek kodoveho segmentu
.word   main::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

Po překladu a spuštění tohoto programu ovšem zjistíme, že vyplňuje chybně – podařilo se nám udělat klasickou „off-by-one“ chybu:

Obrázek 2: Vyplnění s chybou typu off-by-one.

Obrázek 2: Vyplnění s chybou typu off-by-one. 

Autor: tisnik, podle licence: Rights Managed

Oprava klasické chyby ±1

Vyplnění s posunem o jeden znak je způsobeno tím, že sice programovou smyčku opakujeme správně 40×5=200krát, ovšem samotný zápis je proveden ještě před snížením hodnoty index registru Y. Náprava je ve skutečnosti snadná, protože můžeme zápis provést až po zmenšení počitadla Y. Vše bude funkční, protože instrukce STA (zápis do paměti) je jednou z mála instrukcí mikroprocesoru MOS 6502, která nemění příznakové bity. Korektní varianta programu tedy bude vypadat takto:

.include "atari.inc"
 
.CODE
 
 
.proc main
        lda #33                 ; kod znaku, ktery se bude tisknout
        ldy #40*5               ; počet zápisů
clear:
        dey                     ; zmenšit hodnotu počitadla a offsetu
        sta (88), y             ; tisk znaku "A" na zvolené místo na obrazovce
                                ; (adresa Video RAM je na adresách 88 a 89)
        bne clear               ; skok, pokud Y>0

loop:   jmp loop
end:
.endproc
 
 
.segment "EXEHDR"
.word   $ffff                   ; uvodni sekvence bajtu v souboru XEX
.word   main                    ; zacatek kodoveho segmentu
.word   main::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

Výsledek bude (opět) korektní:

Obrázek 3: Korektní vyplnění prvních pěti textových řádků.

Obrázek 3: Korektní vyplnění prvních pěti textových řádků. 

Autor: tisnik, podle licence: Rights Managed

Vyplňování směrem k vyšším adresám

Samozřejmě se můžeme pokusit o úpravu počítané programové smyčky takovým způsobem, že budeme provádět vyplňování od prvního znaku a nikoli od znaku posledního. Počitadlo a současně i offset reprezentovaný index registrem Y tedy bude začínat hodnotou 0 a bude postupně zvyšováno až do hodnoty 40×5=200. Po dosažení této hodnoty bude počítaná programová smyčka ukončena:

.include "atari.inc"
 
.CODE
 
 
.proc main
        lda #33                 ; kod znaku, ktery se bude tisknout
        ldy #0                  ; počitadlo zápisů
clear:
        sta (88), y             ; tisk znaku "A" na zvolené místo na obrazovce
                                ; (adresa Video RAM je na adresách 88 a 89)
        iny                     ; zvětšit hodnotu počitadla a offsetu
        cpy #40*5               ; test na koncovou hodnotu počitadla
        bne clear               ; skok, pokud Y<40*5
 
loop:   jmp loop
end:
.endproc
 
 
.segment "EXEHDR"
.word   $ffff                   ; uvodni sekvence bajtu v souboru XEX
.word   main                    ; zacatek kodoveho segmentu
.word   main::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

Opět se podívejme na výsledek:

Obrázek 4: Korektní vyplnění prvních pěti textových řádků.

Obrázek 4: Korektní vyplnění prvních pěti textových řádků. 

Autor: tisnik, podle licence: Rights Managed

Porovnání všech čtyř metod z pohledu velikosti kódu a výpočetní rychlosti

Všechny čtyři algoritmy, tedy i včetně algoritmu nekorektního, si pochopitelně můžeme porovnat jak s ohledem na velikost strojového kódu, tak i s ohledem na rychlost jedné iterace. Nejprve si uvedeme výpisy strojových kódů jednotlivých variant, ze kterých je možné zjistit velikosti kódů:

000000r 1  A9 21                lda #33                 ; kod znaku, ktery se bude tisknout
000002r 1  A2 C8                ldx #40*5               ; počet zápisů
000004r 1  A0 00                ldy #0                  ; offset
000006r 1               clear:
000006r 1  91 58                sta (88), y             ; tisk znaku "A" na zvolené místo na obrazovce
000008r 1                                               ; (adresa Video RAM je na adresách 88 a 89)
000008r 1  C8                   iny                     ; zvýšit hodnotu offsetu
000009r 1  CA                   dex                     ; zmenšit hodnotu počitadla
00000Ar 1  D0 FA                bne clear               ; skok, pokud X>0
000000r 1  A9 21                lda #33                 ; kod znaku, ktery se bude tisknout
000002r 1  A0 C8                ldy #40*5               ; počet zápisů
000004r 1               clear:
000004r 1  91 58                sta (88), y             ; tisk znaku "A" na zvolené místo na obrazovce
000006r 1                                               ; (adresa Video RAM je na adresách 88 a 89)
000006r 1  88                   dey                     ; zmenšit hodnotu počitadla a offsetu
000007r 1  D0 FB                bne clear               ; skok, pokud Y>0
000000r 1  A9 21                lda #33                 ; kod znaku, ktery se bude tisknout
000002r 1  A0 C8                ldy #40*5               ; počet zápisů
000004r 1               clear:
000004r 1  88                   dey                     ; zmenšit hodnotu počitadla a offsetu
000005r 1  91 58                sta (88), y             ; tisk znaku "A" na zvolené místo na obrazovce
000007r 1                                               ; (adresa Video RAM je na adresách 88 a 89)
000007r 1  D0 FB                bne clear               ; skok, pokud Y>0
000000r 1  A9 21                lda #33                 ; kod znaku, ktery se bude tisknout
000002r 1  A0 00                ldy #0                  ; počitadlo zápisů
000004r 1               clear:
000004r 1  91 58                sta (88), y             ; tisk znaku "A" na zvolené místo na obrazovce
000006r 1                                               ; (adresa Video RAM je na adresách 88 a 89)
000006r 1  C8                   iny                     ; zvětšit hodnotu počitadla a offsetu
000007r 1  C0 C8                cpy #40*5               ; test na koncovou hodnotu počitadla
000009r 1  D0 F9                bne clear               ; skok, pokud Y<40*5

Můžeme tedy snadno porovnat velikosti jednotlivých implementací:

Implementace Velikost (bajtů)
dva index registry 12
výplň směrem dolů, jeden registr 9
oprava předchozího řešení 9
výplň směrem nahoru 11

Rychlost jedné iterace vypočteme z tabulky na adrese https://www.nesdev.org/wi­ki/6502_cycle_times pouhým součtem cyklů jednotlivých instrukcí (instrukce se nepřekrývají):

Implementace Jedna iterace (cyklů)
dva index registry 6+2+2+3=13
výplň směrem dolů, jeden registr 6+2+3=11
oprava předchozího řešení 2+6+3=11
výplň směrem nahoru 6+2+2+3=13

Porovnáním obou tabulek tedy zjistíme, že realizace třetím algoritmem jasně vyhrává:

000004r 1               clear:
000004r 1  88                   dey                     ; zmenšit hodnotu počitadla a offsetu
000005r 1  91 58                sta (88), y             ; tisk znaku "A" na zvolené místo na obrazovce
000007r 1                                               ; (adresa Video RAM je na adresách 88 a 89)
000007r 1  D0 FB                bne clear               ; skok, pokud Y>0

Vyplnění bloku většího než 256 bajtů: koncept nepřímého adresování

Komplikovanější situace nastane tehdy, pokud velikost vyplňovaného bloku přesáhne 256 bajtů. V tomto případě již není možné ve funkci offsetu použít index registr Y, ale je nutné nějakým způsobem pracovat se šestnáctibitovou adresou. Ta nemůže být uložena v registru (žádný tak široký registr nemáme!), ale přímo v paměti. Využijeme tedy nepřímé adresování (což již do jisté míry známe) a budeme muset vyřešit i zvyšování této adresy: buď jen spodního bajtu, nebo i bajtu vyššího. Adresu pro zápis uložíme někam do nulté stránky paměti, ke které je rychlejší přístup:

to = $80                        ; dva bajty z nulté stránky

Taktéž je ovšem nutné pracovat i se šestnáctibitovým počitadlem, takže se opět použije dvojice bajtů uložená v operační paměti:

sizeh:    .byte  >800           ; vyšší bajt velikosti
sizel:    .byte  <800           ; nižší bajt velikosti
Poznámka: povšimněte si způsobu výpočtu hodnoty spodního a horního bajtu – tento výpočet pochopitelně provádí assembler v době překladu.

Zápis je proveden ve třech počítaných smyčkách. Nejprve se ve dvojici vnořených smyček vyplní celé 256bajtové bloky a ve druhé smyčce případný kratší blok na konci. Uveďme si nejprve první blok s vnořenými smyčkami. Vnější smyčka má počitadlo X, původně obsahuje počet vyplňovaných 256bajtových bloků. Vnitřní smyčka má počitadlo Y a provádí výplň celého bloku (což znamená, že se Y automaticky nuluje – jedná se totiž o čistě osmibitový registr):

        ldy #0                  ; offset pro zápis
        ldx sizeh               ; vyšší bajt velikosti (počet bloků)
        beq fill2               ; je nulový? -> přeneseme jen zbylé bajty
fill1:  sta (to), y             ; zápis do bloku
        iny                     ; zvýšit offset
        bne  fill1              ; počítáme až do 256
        inc to+1                ; zvýšit adresu (!!!)
        dex                     ; zmenšit počitadlo bloků
        bne fill1               ; pokračovat dalším blokem

Přenos zbylých bajtů je realizováno takto (X obsahuje hodnotu získanou sizel, Y byl naopak vynulován, takže nemusíme provádět inicializaci):

fill2:  ldx sizel               ; nižší bajt velikosti (počet zbývajících bajtů)
        beq fill4               ; je nulový? -> vše hotovo!
fill3:  sta (to), y             ; přenést zbylé bajty (méně než 256)
        iny                     ; zvýšit offset
        dex                     ; zmenšit počitadlo smyčky
        bne fill3               ; opakujeme dokud se X nevynuluje
fill4:

Realizace programu vyplňujícího více než 256 bajtů paměti

Podívejme se nyní na praktickou realizaci algoritmu pro vyplnění 800 bajtů textové paměti, tedy celých dvaceti textových řádků:

.include "atari.inc"
 
.CODE
 
to = $80                        ; dva bajty z nulté stránky
 
.proc main
        lda 88                  ; načíst část adresy počátku video RAM
        sta to                  ; uložit do "proměnné"
        lda 89                  ; načíst část adresy počátku video RAM
        sta to+1                ; uložit do "proměnné"
 
        lda #33                 ; kod znaku, ktery se bude tisknout
 
        ldy #0                  ; offset pro zápis
        ldx sizeh               ; vyšší bajt velikosti (počet bloků)
        beq fill2               ; je nulový? -> přeneseme jen zbylé bajty
fill1:  sta (to), y             ; zápis do bloku
        iny                     ; zvýšit offset
        bne  fill1              ; počítáme až do 256
        inc to+1                ; zvýšit adresu (!!!)
        dex                     ; zmenšit počitadlo bloků
        bne fill1               ; pokračovat dalším blokem
fill2:  ldx sizel               ; nižší bajt velikosti (počet zbývajících bajtů)
        beq fill4               ; je nulový? -> vše hotovo!
fill3:  sta (to), y             ; přenést zbylé bajty (méně než 256)
        iny                     ; zvýšit offset
        dex                     ; zmenšit počitadlo smyčky
        bne fill3               ; opakujeme dokud se X nevynuluje
fill4:
 
loop:   jmp loop
 
 ; data
sizeh:    .byte  >800           ; vyšší bajt velikosti
sizel:    .byte  <800           ; nižší bajt velikosti
 
end:
.endproc
 
 
.segment "EXEHDR"
.word   $ffff                   ; uvodni sekvence bajtu v souboru XEX
.word   main                    ; zacatek kodoveho segmentu
.word   main::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

O tom, že se vyplní více než 256 bajtů (znaků) se můžeme velmi snadno přesvědčit:

Obrázek 5: Obrazovka, ve které je vyplněno více než 256 bajtů (znaků).

Obrázek 5: Obrazovka, ve které je vyplněno více než 256 bajtů (znaků). 

Autor: tisnik, podle licence: Rights Managed

Obrazovka, ve které je vyplněno více než 256 bajtů (znaků).

Pro úplnost se ještě podívejme na výpis vygenerovaného strojového kódu:

000000r 1               .proc main
000000r 1  A5 58                lda 88                  ; načíst část adresy počátku video RAM
000002r 1  85 80                sta to                  ; uložit do "proměnné"
000004r 1  A5 59                lda 89                  ; načíst část adresy počátku video RAM
000006r 1  85 81                sta to+1                ; uložit do "proměnné"
000008r 1
000008r 1  A9 21                lda #33                 ; kod znaku, ktery se bude tisknout
00000Ar 1
00000Ar 1  A0 00                ldy #0                  ; offset pro zápis
00000Cr 1  AE rr rr             ldx sizeh               ; vyšší bajt velikosti (počet bloků)
00000Fr 1  F0 0A                beq fill2               ; je nulový? -> přeneseme jen zbylé bajty
000011r 1  91 80        fill1:  sta (to), y             ; zápis do bloku
000013r 1  C8                   iny                     ; zvýšit offset
000014r 1  D0 FB                bne  fill1              ; počítáme až do 256
000016r 1  E6 81                inc to+1                ; zvýšit adresu (!!!)
000018r 1  CA                   dex                     ; zmenšit počitadlo bloků
000019r 1  D0 F6                bne fill1               ; pokračovat dalším blokem
00001Br 1  AE rr rr     fill2:  ldx sizel               ; nižší bajt velikosti (počet zbývajících bajtů)
00001Er 1  F0 06                beq fill4               ; je nulový? -> vše hotovo!
000020r 1  91 80        fill3:  sta (to), y             ; přenést zbylé bajty (méně než 256)
000022r 1  C8                   iny                     ; zvýšit offset
000023r 1  CA                   dex                     ; zmenšit počitadlo smyčky
000024r 1  D0 FA                bne fill3               ; opakujeme dokud se X nevynuluje
000026r 1               fill4:
000026r 1
000026r 1  4C rr rr     loop:   jmp loop

Celková délka programu nyní dosahuje úctyhodných 41 bajtů!

Algoritmus pro blokové přesuny dat

Pro úplnost si ještě představme algoritmus pro blokové přesuny dat, konkrétně přesuny bloků, které mohou přesahovat velikost 256 bajtů (pokud jsou bloky menší, jedná se o relativně jednoduchou záležitost). Při blokových přesunech je pochopitelně nutné zjistit, jestli se zdrojový a cílový blok nepřekrývá a pokud tomu tam je, který z bloků začíná na nižší adrese, protože výběr konkrétního způsobu přenosu na těchto okolnostech závisí. Různé realizace přesunu bloků uvedené pod tímto odstavcem byly získány ze stránky http://www.6502.org/source/ge­neral/memory_move.html a dokonce nejsou psány v syntaxi CA65 (to je však triviálně řešitelné):

 ; Move memory down
 ;
 ; FROM = source start address
 ;   TO = destination start address
 ; SIZE = number of bytes to move
 ;
MOVEDOWN LDY #0
         LDX SIZEH
         BEQ MD2
MD1      LDA (FROM),Y ; move a page at a time
         STA (TO),Y
         INY
         BNE MD1
         INC FROM+1
         INC TO+1
         DEX
         BNE MD1
MD2      LDX SIZEL
         BEQ MD4
MD3      LDA (FROM),Y ; move the remaining bytes
         STA (TO),Y
         INY
         DEX
         BNE MD3
MD4      RTS

Přesun opačným směrem:

 ; Move memory up
 ;
 ; FROM = source start address
 ;   TO = destination start address
 ; SIZE = number of bytes to move
 ;
MOVEUP   LDX SIZEH    ; the last byte must be moved first
         CLC          ; start at the final pages of FROM and TO
         TXA
         ADC FROM+1
         STA FROM+1
         CLC
         TXA
         ADC TO+1
         STA TO+1
         INX          ; allows the use of BNE after the DEX below
         LDY SIZEL
         BEQ MU3
         DEY          ; move bytes on the last page first
         BEQ MU2
MU1      LDA (FROM),Y
         STA (TO),Y
         DEY
         BNE MU1
MU2      LDA (FROM),Y ; handle Y = 0 separately
         STA (TO),Y
MU3      DEY
         DEC FROM+1   ; move the next page (if any)
         DEC TO+1
         DEX
         BNE MU1
         RTS

Jiná alternativa:

 ; Move memory up
 ;
 ; FROM = 1 + source end address
 ; TO   = 1 + destination end address
 ; SIZE = number of bytes to move
 ;
MOVEUP   LDY #$FF
         LDX SIZEH
         BEQ MU3
MU1      DEC FROM+1
         DEC TO+1
MU2      LDA (FROM),Y ; move a page at a time
         STA (TO),Y
         DEY
         BNE MU2
         LDA (FROM),Y ; handle Y = 0 separately
         STA (TO),Y
         DEY
         DEX
         BNE MU1
MU3      LDX SIZEL
         BEQ MU5
         DEC FROM+1
         DEC TO+1
MU4      LDA (FROM),Y ; move the remaining bytes
         STA (TO),Y
         DEY
         DEX
         BNE MU4
MU5      RTS

Ještě jedna odlišná alternativa:

 ; Move memory up
 ;
 ; FROM = source end address
 ; TO   = destination end address
 ; SIZE = number of bytes to move
 ;
MOVEUP   LDY #0
         LDX SIZEH
         BEQ MU3
MU1      LDA (FROM),Y ; handle Y = 0 separately
         STA (TO),Y
         DEY
         DEC FROM+1
         DEC TO+1
MU2      LDA (FROM),Y ; move a page at a time
         STA (TO),Y
         DEY
         BNE MU2
         DEX
         BNE MU1
MU3      LDX SIZEL
         BEQ MU5
         LDA (FROM),Y ; handle Y = 0 separately
         STA (TO),Y
         DEY
         DEX
         BEQ MU5
         DEC FROM+1
         DEC TO+1
MU4      LDA (FROM),Y ; move the remaining bytes
         STA (TO),Y
         DEY
         DEX
         BNE MU4
MU5      RTS
Poznámka: na tomto místě je vhodné upozornit na to, že u některých jiných mikroprocesorů, včetně mikroprocesorů osmibitových, lze blokový přenos realizovat jedinou instrukcí popř. několika málo instrukcemi (LDIR a LLDR u Z80):
        ld   hl, SOURCE_ADDRESS     ; adresa zdrojového bloku
        ld   de, TARGET_ADDRESS     ; adresa cílového bloku
        ld   bc, BLOCK_SIZE         ; velikost přenášených dat
        ldir       ; provést blokový přenos

Grafické subsystémy osmibitových mikropočítačů

Ve druhé části dnešního článku se, prozatím alespoň ve stručnosti, seznámíme s grafickým subsystémem osmibitových mikropočítačů Atari. Ten je do značné míry unikátní a do jisté míry vychází z čipů, které obsahovala osmibitová herní konzole Atari 2600.

S nástupem osmibitových domácích počítačů je spojen i prudký rozvoj počítačové grafiky, který byl způsobem zejména vývojem počítačových her. Později byly pro tyto počítače vytvořeny i „seriózní“ programy vyžívající grafické možnosti těchto počítačů, například jednoduché CAD systémy, matematické programy, které dokázaly vykreslovat grafy apod.

Podívejme se však nejprve, jaké vlastnosti měl typický osmibitový počítač. Vzhledem k tomu, že mikroprocesory, kterými byly tyto počítače osazeny (většinou MOS 6502 a jeho varianty či Zilog Z80), měly osmibitovou datovou sběrnici a šestnáctibitovou sběrnici adresovou. Bylo tak možné adresovat až 216B=64KB paměti.

Do tohoto adresového prostoru byla namapována jak klasická operační paměť typu RAM/RWM, tak i paměť ROM a obrazová paměť, která se většinou nacházela přímo v operační paměti, což komplikovalo přístup procesoru a grafického čipu na sběrnici (oba čipy se musely nějakým způsobem o sběrnici dělit, z čehož například plyne rozdíl mezi „pomalým“ a „rychlým“ regionem RAM v ZX Spectru).

Z výše uvedeného také vyplývá, že kapacita obrazové paměti musela být co nejmenší, aby její adresový rozsah neubíral příliš mnoho z již tak omezeného adresového prostoru mikroprocesoru, nehledě na to, že by bylo pomalejší i překreslování snímků ve hrách atd.. Maximální praktická velikost obrazové paměti se z tohoto důvodu pohybovala na hranici osmi kilobytů (výjimkou jsou některé modely BBC Micro).

I na takto malém prostoru však bylo možné vytvořit překvapivě působivé a k tomu i dynamické efekty. Návrháři většinou použili obvody, které mohly pracovat ve více grafických režimech, někdy byly využity i barvové atributy platné pro větší blok pixelů, typicky 8×8. Řešení je v této oblasti mnoho, každé má své výhody a nevýhody.

Dalším požadavkem kladeným na domácí počítače byla samozřejmě i jejich cena. Z důvodu co nejnižšího zatížení peněženky případného kupce se proto tyto počítače konstruovaly tak, že je bylo možné zapojit na běžný televizor a jako externí paměťové médium se používaly kompaktní magnetofonové kazety nebo i klasické magnetofonové cívky (snad s výjimkou českého počítače Maťo, který používal gramofonové desky). Televizní přijímač vykazuje díky šířce pásma a modulaci i demodulaci při zobrazování jemné rastrové grafiky velké množství chyb, které bylo zapotřebí eliminovat (nebo naopak i využít) vhodným návrhem grafického výstupu.

Taktovací frekvence použitých mikroprocesorů se pohybovala v řádu jednotek MHz a proto nebylo vhodné, aby samotný mikroprocesor řešil tvorbu všech grafických efektů, neboť to vyžaduje poměrně rozsáhlé, a tím pádem i pomalé přesuny dat v paměti. Z tohoto důvodu měly některé počítače na grafickém čipu implementovánu podporu pro sprity, pomocí nichž bylo možné zobrazovat pohybující se předměty nezávisle na okolním obraze (sem pochopitelně spadají i osmibitová Atari).

Rozlišení a počet barev

S velikostí obrazové paměti úzce souvisí i rozlišovací schopnost a počet současně zobrazitelných barev. Podle rozlišovací schopnosti můžeme osmibitové počítače zhruba rozdělit do dvou skupin:

  1. V první skupině se nachází počítače, které zobrazovaly obraz v rozlišení 256×192 pixelů, což v textovém režimu odpovídá textovému oknu 32×24 znaků. Předností tohoto rozlišení je jednoduché adresování (délka řádku je mocninou dvou) a také fakt, že se všechny pixely zobrazily přesně i na běžném televizním přijímači. Nevýhodný je především horizontální počet pixelů při práci v textových režimech, kdy je počet 32 znaků pro mnoho účelů nedostatečný, což se obcházelo zmenšením rastru, ve kterých se znaky zobrazují. Typickým zástupcem těchto počítačů je slavné ZX Spectrum a prakticky všechny jeho klony a nástupci.
  2. Ve druhé skupině se nachází počítače, jejichž maximální podporované rozlišení bylo 320×192 resp. 320×200 pixelů nebo vyšší. Toto rozlišení se nazývalo „profesionální“, protože bylo možné zobrazit znaky o rozměrech 8×8 pixelů v textovém režimu 40×24 resp. 40×25 znaků podobně jako na prvních PC s grafickou kartou CGA – ostatně násobky 320 najdeme i dnes u mnoha standardizovaných rozlišení. Nevýhodou těchto rozlišení je složitější výpočet pozice pixelu či znaku na obrazovce a také problematické zobrazení na televizním monitoru, kdy při poměrně velkém horizontálním rozlišení docházelo ke vzniku barevných artefaktů u sousedních pixelů, které byly navzájem kontrastní (zejména v NTSC, zatímco norma PAL byla použitelná i pro takto relativně vysoké horizontální rozlišení). Představitelem této skupiny počítačů jsou osmibitová Atari a Commodore C64.
Poznámka: i vertikální rozlišení je omezeno možnostmi televizorů (a jejich norem). K podrobnostem se ještě vrátíme příště.

Grafické čipy v osmibitových počítačích Atari

V osmibitových domácích počítačích firmy Atari (například se jedná o modely Atari 800XL, Atari 800XE, Atari 130XE, Atari 1200XL a Atari 65XE) byla pro vytváření grafického výstupu použita dvojice poměrně komplikovaných číslicových obvodů nazvaných ANTIC a GTIA. Tyto dva obvody se navzájem doplňovaly ve svých funkcích a umožňovaly tak precizní nastavení grafického či textového režimu, horizontální i vertikální posun obrazu a na zobrazeném pozadí nezávislé vykreslování spritů.

Na čipech ANTIC a GTIA je nejzajímavější začlenění do systému počítače. Obvod ANTIC byl totiž ve skutečnosti samostatným grafickým procesorem (GPU), jelikož obsahoval vlastní sadu příkazů a dokázal obsluhovat přístup do paměti bez zásahu procesoru (tím byl MOS 6502A). Dnes si popíšeme základní možnosti těchto čipů, příště je už vyžijeme v programech.

ANTIC – generování rastrové grafiky

Na osmibitových počítačích Atari se grafika skládala ze dvou částí: pozadí (hrací pole, playfield) a pohyblivých rastrových obrázků (spritů). Pro nastavení grafického režimu pozadí (playfield) byl určen obvod ANTIC (což je zkratka ze sousloví Alpha-Numeric Television Interface Circuit), který umožnil pro každý vykreslovaný řádek (scanline) programově nastavit jeden ze dvanácti grafických režimů, které se lišily svým rozlišením, počtem současně zobrazitelných barev a velikostí alokované paměti.

Některé z podporovaných režimů zobrazovaly pouze znaky, jiné byly čistě bitmapové a další byly sice textové, ovšem se speciální interpretací rastrů znaků (tyto režimy jsou nejužitečnější při tvorbě her). U každého zobrazovaného řádku bylo možné povolit přerušení DLI (Display-List Interrupt), které nastalo při horizontálním zpětném běhu elektronového paprsku na televizní obrazovce. Pro každý zobrazovaný řádek bylo také možné povolit jemný posuv o maximálně osm pixelů, čímž bylo možné realizovat jednoduchý scrolling obrazovky nebo její části (tj. pouze několika vybraných řádků).

Vzhledem k tomu, že se čip ANTIC řídil vlastní sadou příkazů nazývanou Display List a při vykreslování přebíral od hlavního procesoru řízení sběrnice, se v podstatě jednalo o jeden z prvních široce používaných grafických procesorů (Graphics Processing Unit – GPU). Operační paměť byla mezi hlavním procesorem počítače MOS 6502 a čipem ANTIC sdílená, což vedlo k nutnosti postupného přebírání řízení jedním z procesorů za pomoci operací přerušení.

Díky frekvenci hlavního procesoru a potřebě synchronizace elektronového paprsku bylo maximální podporované rozlišení rovno 352×240 pixelům. Toto rozlišení, které se v literatuře nazývá celkem trefně overscan, se kvůli kompatibilitě s kompozitními monitory a televizními obrazovkami snižovalo na standardních 320×200 pixelů resp. 320×192 pixelů. V režimu overscan totiž paprsek okrajové části obrazu kreslil „za roh“ obrazovky, což bylo krásně viditelné například na elektronkových televizorech Salermo, které měly přední část obrazovky o cca 2cm vysunutou z šasi přístroje.

Grafické režimy čipu ANTIC

V následující tabulce jsou pro ilustraci některých možností čipu ANTIC uvedeny vybrané základní režimy, které je možné nastavit pro každý obrazový řádek zvlášť. V prvním sloupci je číslo (kód) instrukce pro čip ANTIC, ve sloupci druhém pak číslo, které se předává příkazu GRAPHICSBasicu při nastavování textového či grafického režimu.

Instrukce ANTIC GRAPHICS x Režim Rozlišení Počet barev Poznámka
2 0 textový 40 znaků na řádek 2 standardní textový režim
3 × textový 40 znaků na řádek 2 textový režim s vyššími znaky
4 12 textový 40 znaků na řádek 5 výška znaku 8 pixelů, 4 barvy/znak
5 13 textový 40 znaků na řádek 5 výška znaku 16 pixelů, 4 barvy/znak
6 1 textový 20 znaků na řádek 5 výška znaku 8 pixelů
7 2 textový 20 znaků na řádek 5 výška znaku 16 pixelů
8 3 grafický 40 pixelů na řádek 4 neznám žádnou aplikaci, která by tento režim používala
9 4 grafický 80 pixelů na řádek 2
A 5 grafický 80 pixelů na řádek 4
B 6 grafický 160 pixelů na řádek 2 výška pixelu dva obrazové řádky, vertikální rozlišení cca 96 pixelů
C 14 grafický 160 pixelů na řádek 2 výška pixelu jeden obrazový řádek, vertikální rozlišení cca 192 pixelů
D 7 grafický 160 pixelů na řádek 4 výška pixelu dva obrazové řádky, vertikální rozlišení cca 96 pixelů
E 15 grafický 160 pixelů na řádek 4 výška pixelu jeden obrazový řádek, vertikální rozlišení cca 192 pixelů
F 8 grafický 320 pixelů na řádek 2 nejvyšší možné standardní rozlišení

Povšimněte si, že textový režim číslo 3 není přímo z Basicu přístupný. Jedná se o režim s vyššími znaky, který lze s výhodou využít například v textových editorech používajících font s „nabodeníčky“. Pokud je tedy požadováno, aby se zobrazil textový režim se 40 znaky na řádek a 24 textovými řádky, musí display-list řídicí čip ANTIC obsahovat mj. i 24 instrukcí s kódem 2 (podrobnosti si řekneme příště). Ve skutečnosti je však display-list poněkud složitější, protože obsahuje i instrukci pro vykreslení horního jednobarevného okraje, instrukci pro nastavení začátku obrazové paměti (adresu prvního znaku na obrazovce) a na samotném konci display-listu taktéž instrukci pro skok na jeho začátek.

Obrázek 6: Hra Amaroute běžící v monochromatickém režimu s nejvyšším rozlišením 320x192 pixelů. Povšimněte si korektního skrývání vzdálených objektů; pravděpodobně se používá optimalizovaná podoba malířova algoritmu.

Obrázek 6: Hra Amaroute běžící v monochromatickém režimu s nejvyšším rozlišením 320x192 pixelů. Povšimněte si korektního skrývání vzdálených objektů; pravděpodobně se používá optimalizovaná podoba malířova algoritmu. 

Autor: tisnik, podle licence: Rights Managed

GTIA – sprity a vícebarevná grafika

S obvodem ANTIC úzce spolupracoval číslicový obvod GTIA (Graphics Television Interface Adapter nebo také George's Television Interface Adapter), který zaváděl podporu pro další tři grafické režimy s horizontálním rozlišením 80 pixelů na řádek a především umožňoval vykreslování spritů (ovšem mluvit o nových grafických režimech je poněkud zavádějící; podrobnější vysvětlení bude opět uvedeno jindy). Současně bylo možné vykreslit čtyři sprity s rozlišením maximálně 8×256 pixelů a další čtyři sprity s rozlišením 2×256 pixelů, které bylo možno spojit do jednoho většího spritu s rozlišením 8×256 pixelů.

Sprity široké 8 pixelů se v literatuře nazývají hráči (players), úzké dvoupixelové sprity se jmenují střely (missiles). Proto se ve světě osmibitových Atari celý koncept spritů nazývá PMG neboli player-missile graphics.

Sprity byly jednobarevné. Více barev bylo možno dosáhnout logickými operacemi nad překrývajícími se sprity (počítače Commodore C64 ovšem nabízely i sprity v režimu multicolor). Každý sprite mohl pomocí jedné instrukce měnit svoji horizontální velikost i horizontální pozici, přičemž polohy spritů byly navzájem nezávislé.

Vertikální pozice spritů se měnila přesunem bitmapy spritu v operační paměti. Bylo také možné definovat priority vykreslování spritů vůči sobě navzájem i vůči pozadí.

Priorita spritů a detekce kolize spritů

Čip GTIA podporoval i změnu priority jednotlivých spritů (jejich překrývání) a bylo také možné detekovat kolizi spritu s jiným spritem popř. s nějakou barvou hracího pole. To stejné samozřejmě platí i pro střely, u nichž byla možná detekce kolize s hráčem či kolize s hracím polem. Při kolizi (do úvahy se samozřejmě braly pouze viditelné pixely spritu, tj. pixely nastavené na logickou jedničku) se nastavil příslušný bit ve stavových registrech, odkud bylo možné kdykoli poté zjistit, zda ke kolizi došlo či nikoli. Díky této funkcionalitě bylo možné velmi snadno otestovat například náraz hráče do stěny, zásah hráče střelou atd.

Obrázek 7: Hra Adventure 2 pro počítače Atari 5200. Jedná se o hry z 21. století vytvořenou v domácích podmínkách, která se snaží zachovat prvky z původní hry Adventure pro Atari 2600 (viz tvar hráče - čtverečku).

Obrázek 7: Hra Adventure 2 pro počítače Atari 5200. Jedná se o hry z 21. století vytvořenou v domácích podmínkách, která se snaží zachovat prvky z původní hry Adventure pro Atari 2600 (viz tvar hráče - čtverečku). 

Autor: tisnik, podle licence: Rights Managed

Vzhledem k tomu, že sprity byly pouze jednobarevné, museli se vícebarevní hráči sestavovat z několika spritů. Omezení počtu spritů naproti tomu nebylo kritické, neboť jeden sprite mohl být ve skutečnosti použitý pro zobrazení většího množství objektů ve scéně – jediným omezením bylo to, že tyto objekty nesměly ležet na stejném obrazovém řádku.

Obrázek 8: Další screenshot ze hry Adventure 2.

Obrázek 8: Další screenshot ze hry Adventure 2. 

Autor: tisnik, podle licence: Rights Managed

Příloha A: Seznam všech doposud popsaných instrukcí mikroprocesoru MOS 6502

Všechny doposud popsané instrukce mikroprocesoru MOS 6502 jsou vypsány v následující tabulce:

# Instrukce Plné jméno Popis
1 ADC add with carry součet hodnoty s akumulátorem (včetně přetečení)
2 SBC subtract with carry odečtení hodnoty od akumulátoru (včetně výpůjčky)
       
3 AND and with accumulator logické AND s akumulátorem
4 ORA or with accumulator logické OR s akumulátorem
5 EOR exclusive or with accumulator logické XOR s akumulátorem
       
6 INC increment zvýšení hodnoty o 1 (kupodivu nelze s akumulátorem, ovšem s pamětí ano)
7 INX increment X zvýšení hodnoty index registru X o 1
8 INY increment Y zvýšení hodnoty index registru Y o 1
9 DEC decrement snížení hodnoty o 1 (opět nelze s akumulátorem)
10 DEX decrement X snížení hodnoty index registru X o 1
11 DEY decrement Y snížení hodnoty index registru Y o 1
       
12 CMP compare with accumulator odečtení hodnoty od akumulátoru bez zápisu výsledku
13 CPX compare with X odečtení hodnoty od index registru X bez zápisu výsledku
14 CPY compare with Y odečtení hodnoty od index registru Y bez zápisu výsledku
15 BIT bit test logické AND bez uložení výsledků (změní se jen příznakové bity)
       
16 ASL arithmetic shift left aritmetický posun doleva o jeden bit
17 LSR logical shift right logický posun doprava o jeden bit
18 ROL rotate left rotace doleva o jeden bit
19 ROR rotate right rotace doprava o jeden bit
       
20 JMP jump skok (existuje několik adresovacích režimů)
21 JSR jump to subroutine skok do podprogramu s uložením návratové adresy na zásobník
22 RTS return from subroutine návrat z podprogramu
23 RTI return from interrupt návrat z prerušovací rutiny
       
24 BCC branch on carry clear rozvětvení za podmínky C==0
25 BCS branch on carry set rozvětvení za podmínky C==1
26 BEQ branch on equal (zero set) rozvětvení za podmínky Z==1
27 BMI branch on minus (negative set) rozvětvení za podmínky N==1
28 BNE branch on not equal (zero clear) rozvětvení za podmínky Z==0
29 BPL branch on plus (negative clear) rozvětvení za podmínky N==0
30 BVC branch on overflow clear rozvětvení za podmínky O==0
31 BVS branch on overflow set rozvětvení za podmínky O==1
       
32 PHA push accumulator on stack uložení obsahu akumulátoru na zásobník
33 PLA pull accumulator from stack obnovení obsahu akumulátoru ze zásobníku

Příloha B: 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:

Školení Zabbix

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
 
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

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:

# Příklad Stručný popis příkladu Adresa
1 Makefile definice cílů pro překlad všech demonstračních příkladů z této tabulky https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/Makefile
2 linker.cfg konfigurační soubor pro linker https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/linker.cfg
       
3 dummy.asm pouze nekonečná smyčka https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/dummy.asm
4 dummy_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/dummy_list.asm
5 dummy_list.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/dummy.map
       
6 background_color1.asm změna barvy pozadí – základní varianta příkladu https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/background_color1.asm
7 background_color1_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/background_color1_list.asm
8 background_color1.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/background_color1.map
       
9 background_color2.asm změna barvy pozadí – využití předdefinovaných konstant https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/background_color2.asm
10 background_color2_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/background_color2_list.asm
11 background_color2.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/background_color2.map
       
12 print_a.asm tisk znaku přímo do obrazové paměti https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/print_a.asm
13 print_a_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/print_a_list.asm
14 print_a.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/print_a.map
       
15 color_computation1.asm výpočet barvy, varianta bez bitových posunů https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/color_computation1.asm
16 color_computation1_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/color_computation1_list.asm
17 color_computation1.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/color_computation1.map
       
18 color_computation2.asm výpočet barvy, varianta s bitovými posuny https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/color_computation2.asm
19 color_computation2_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/color_computation2_list.asm
20 color_computation2.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/color_computation2.map
       
21 subroutine1.asm skok do podprogramu bez předávání parametrů https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/subroutine1.asm
22 subroutine1_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/subroutine1_list.asm
23 subroutine1.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/subroutine1.map
       
24 subroutine2.asm skok do podprogramu s předáním parametru https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/subroutine2.asm
25 subroutine2_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/subroutine2_list.asm
26 subroutine2.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/subroutine2.map
       
27 hex_number1.asm tisk jedné hexadecimální číslice (nekorektní varianta) https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number1.asm
28 hex_number1_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number1_list.asm
29 hex_number1.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number1.map
       
30 hex_number2.asm tisk jedné hexadecimální číslice (korektní varianta) https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number2.asm
31 hex_number2_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number2_list.asm
32 hex_number2.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number2.map
       
33 hex_number3.asm tisk jedné hexadecimální číslice (korektní varianta po refaktoringu) https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number3.asm
34 hex_number3_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number3_list.asm
35 hex_number3.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number3.map
       
36 hex_number4.asm tisk jedné hexadecimální číslice, varianta postavená na makrech https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number4.asm
37 hex_number4_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number4_list.asm
38 hex_number4.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number4.map
       
39 hex_number5.asm tisk jedné hexadecimální číslice, varianta postavená na makrech, lokální automaticky generovaná návěští https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number5.asm
40 hex_number5_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number5_list.asm
41 hex_number5.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number5.map
       
42 hex_number6.asm příprava pro tisk dvouciferné hexadecimální hodnoty https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number6.asm
43 hex_number6_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number6_list.asm
44 hex_number6.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number6.map
       
45 hex_number7.asm realizace tisku dvouciferné hexadecimální hodnoty https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number7.asm
46 hex_number7_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number7_list.asm
47 hex_number7.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number7.map
       
48 hex_number8.asm makro pro logický posun doprava o zadaný počet bitů https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number8.asm
49 hex_number8_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number8_list.asm
50 hex_number8.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number8.map
       
51 hex_number9.asm čekání na stisk klávesy https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number8.asm
52 hex_number9_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number8_list.asm
53 hex_number9.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/hex_number8.map
       
54 fill_block1.asm vyplnění bloku, varianta se dvěma index registry https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block1.asm
55 fill_block1_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block1_list.asm
56 fill_block1.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block1.map
       
57 fill_block2.asm vyplnění bloku, nekorektní varianta využívající jeden index registr https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block2.asm
58 fill_block2_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block2_list.asm
59 fill_block2.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block2.map
       
60 fill_block3.asm vyplnění bloku, korektní varianta využívající jeden index registr https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block3.asm
61 fill_block3_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block3_list.asm
62 fill_block3.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block3.map
       
63 fill_block4.asm vyplnění bloku delšího než 256 bajtů https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block4.asm
64 fill_block4_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block4_list.asm
65 fill_block4.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block4.map
       
66 fill_block5.asm vyplnění bloku, čítání směrem nahoru https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block5.asm
67 fill_block5_list.asm „listing“ vygenerovaný assemblerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block5_list.asm
68 fill_block5.map mapa paměti; soubor vytvořený linkerem https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/fill_block5.map
       
68 chars.bas tisk všech znaků na obrazovku (varianta naprogramovaná v BASICu) https://github.com/tisnik/8bit-fame/blob/master/Atari800-ca65/chars.bas

Odkazy na Internetu

  1. MOS 6502 instruction set
    http://www.6502.org/users/o­belisk/6502/instructions.html
  2. EXE File Format Description
    https://gury.atari8.info/ref­s/file_formats_exe.php
  3. XEX Filter – A toolkit to analyze and manipulate Atari binary files
    https://www.vitoco.cl/atari/xex-filter/index.html
  4. chkxex.py
    https://raw.githubusercon­tent.com/seban-slt/tcx_tools/refs/heads/mas­ter/chkxex.py
  5. ca65 Users Guide
    https://cc65.github.io/doc/ca65.html
  6. cc65 Users Guide
    https://cc65.github.io/doc/cc65.html
  7. ld65 Users Guide
    https://cc65.github.io/doc/ld65.html
  8. da65 Users Guide
    https://cc65.github.io/doc/da65.html
  9. Překladače jazyka C pro historické osmibitové mikroprocesory
    https://www.root.cz/clanky/prekladace-jazyka-c-pro-historicke-osmibitove-mikroprocesory/
  10. 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/
  11. Getting Started Programming in C: Coding a Retro Game with C Part 2
    https://retrogamecoders.com/getting-started-with-c-cc65/
  12. NES game development in 6502 assembly – Part 1
    https://kibrit.tech/en/blog/nes-game-development-part-1
  13. 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/
  14. Minimal NES example using ca65
    https://github.com/bbbradsmith/NES-ca65-example
  15. List of 6502-based Computers and Consoles
    https://www.retrocompute.co.uk/list-of-6502-based-computers-and-consoles/
  16. 6502 – the first RISC µP
    http://ericclever.com/6500/
  17. 3 Generations of Game Machine Architecture
    http://www.atariarchives.or­g/dev/CGEXPO99.html
  18. “Hello, world” from scratch on a 6502 — Part 1
    https://www.youtube.com/wat­ch?v=LnzuMJLZRdU
  19. A Tour of 6502 Cross-Assemblers
    https://bumbershootsoft.wor­dpress.com/2016/01/31/a-tour-of-6502-cross-assemblers/
  20. Adventures with ca65
    https://atariage.com/forum­s/topic/312451-adventures-with-ca65/
  21. example ca65 startup code
    https://atariage.com/forum­s/topic/209776-example-ca65-startup-code/
  22. 6502 PRIMER: Building your own 6502 computer
    http://wilsonminesco.com/6502primer/
  23. 6502 Instruction Set
    https://www.masswerk.at/6502/6502_in­struction_set.html
  24. Chip Hall of Fame: MOS Technology 6502 Microprocessor
    https://spectrum.ieee.org/tech-history/silicon-revolution/chip-hall-of-fame-mos-technology-6502-microprocessor
  25. Single-board computer
    https://en.wikipedia.org/wiki/Single-board_computer
  26. www.6502.org
    http://www.6502.org/
  27. 6502 PRIMER: Building your own 6502 computer – clock generator
    http://wilsonminesco.com/6502pri­mer/ClkGen.html
  28. Great Microprocessors of the Past and Present (V 13.4.0)
    http://www.cpushack.com/CPU/cpu.html
  29. Jak se zrodil procesor?
    https://www.root.cz/clanky/jak-se-zrodil-procesor/
  30. Osmibitové mikroprocesory a mikrořadiče firmy Motorola (1)
    https://www.root.cz/clanky/osmibitove-mikroprocesory-a-mikroradice-firmy-motorola-1/
  31. 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/
  32. 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/
  33. 25 Microchips That Shook the World
    https://spectrum.ieee.org/tech-history/silicon-revolution/25-microchips-that-shook-the-world
  34. Comparison of instruction set architectures
    https://en.wikipedia.org/wi­ki/Comparison_of_instructi­on_set_architectures
  35. How To Start Learning Atari 8 Bit Assembly For Free
    https://forums.atariage.com/to­pic/300732-how-to-start-learning-atari-8-bit-assembly-for-free/
  36. WUDSN (Demo Group)
    https://www.wudsn.com/
  37. Machine Language For Beginners
    https://www.atariarchives.org/mlb/
  38. Assembly language: all about I/O
    https://www.atarimagazines­.com/v3n8/AllAbout_IO.html
  39. Sedmdesátiny assemblerů: lidsky čitelný strojový kód
    https://www.root.cz/clanky/sed­mdesatiny-assembleru-lidsky-citelny-strojovy-kod/
  40. Color names
    https://atariwiki.org/wiki/Wi­ki.jsp?page=Color%20names
  41. ATASCII
    https://en.wikipedia.org/wiki/ATASCII
  42. Put characters in display ram isn't ATASCII?
    https://forums.atariage.com/to­pic/359973-put-characters-in-display-ram-isnt-atascii/
  43. ATASCII And Internal Character Code Values
    https://www.atariarchives­.org/mapping/appendix10.php
  44. Reading ATASCII from the keyboard in assembly
    https://forums.atariage.com/to­pic/361733-reading-atascii-from-the-keyboard-in-assembly/
  45. Why does the 6502 JSR instruction only increment the return address by 2 bytes?
    https://retrocomputing.stac­kexchange.com/questions/19543/why-does-the-6502-jsr-instruction-only-increment-the-return-address-by-2-bytes
  46. Pushing return address to stack off by 1 byte
    https://forums.atariage.com/to­pic/378206-pushing-return-address-to-stack-off-by-1-byte/
  47. Intel x86 documentation has more pages than the 6502 has transistors
    https://www.righto.com/2013/09/intel-x86-documentation-has-more-pages.html
  48. Clearing a Section of Memory
    http://www.6502.org/source/ge­neral/clearmem.htm
  49. Practical Memory Move Routines by Bruce Clark
    http://www.6502.org/source/ge­neral/memory_move.html
  50. 6502 Assembly Programming Guide
    https://neumont-gamedev.github.io/posts/retrogamedev-6502-guide/
  51. Off-by-one error
    https://en.wikipedia.org/wiki/Off-by-one_error
  52. 6502 cycle times
    https://www.nesdev.org/wi­ki/6502_cycle_times

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.



Nejnovější články