Horizontální posun spritů po ploše obrazovky
Co se dozvíte v článku
- Horizontální posun spritů po ploše obrazovky
- Automatický pohyb prvního hráče, koncept čekací smyčky
- Úplný zdrojový kód dnešního prvního demonstračního příkladu
- Čekání na konec vykreslování snímku
- Úplný zdrojový kód dnešního druhého demonstračního příkladu
- Posun hráče pomocí joysticku
- Nové užitečné instrukce: přesuny dat mezi registry
- Korektní realizace posunu hráče pomocí joysticku
- Úplný zdrojový kód dnešního třetího demonstračního příkladu
- Zobrazení všech čtyř střel společně s hráči
- Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu
- Čtyři střely nebo pátý hráč?
- Úplný zdrojový kód dnešního pátého demonstračního příkladu
- Povolení pátého hráče
- Úplný zdrojový kód dnešního šestého demonstračního příkladu
- Detekce stisku tlačítka joysticku, reakce na stisk tlačítka
- Příloha A: Seznam všech doposud popsaných instrukcí mikroprocesoru MOS 6502
- Příloha B: Makefile pro překlad všech demonstračních příkladů
- Repositář s demonstračními příklady
- Odkazy na Internetu
Připomeňme si, že čip GTIA umožňuje horizontální posun spritů po ploše obrazovky. Pro tento účel se používá osm registrů vypsaných v následující tabulce:
| Registr | Režim | Význam |
|---|---|---|
| HPOSP0 | W | horizontální pozice hráče číslo 0 |
| HPOSP1 | W | horizontální pozice hráče číslo 1 |
| HPOSP2 | W | horizontální pozice hráče číslo 2 |
| HPOSP3 | W | horizontální pozice hráče číslo 3 |
| HPOSM0 | W | horizontální pozice střely číslo 0 |
| HPOSM1 | W | horizontální pozice střely číslo 1 |
| HPOSM2 | W | horizontální pozice střely číslo 2 |
| HPOSM3 | W | horizontální pozice střely číslo 3 |
Každý z těchto registrů obsahuje osmibitovou hodnotu, což znamená rozsah 0 až 255. Ovšem okolo herního pole jsou okraje a samotné herní pole má šířku 320 pixelů (v nejvyšším rozlišení). Jak se tedy mapuje horizontální pozice spritu a pozice pixelu herního pole? Používá se tento vzoreček:
x_screen = (x_sprite - 48) * 2
Pro grafické režimy se 160 pixely na řádku je to ještě jednodušší:
x_screen = x_sprite - 48
Automatický pohyb prvního hráče, koncept čekací smyčky
V dnešním prvním demonstračním příkladu je realizován horizontální posun prvního hráče. Hráč se neustále pohybuje doprava (s přetečením na levý okraj), což lze programově realizovat velmi snadno pomocí nekonečné smyčky, ve které je horizontální pozice hráče uložena v akumulátoru:
lda #80 ; výchozí pozice prvního hráče
loop:
jsr wait ; počkáme určitou dobu
sec
adc #0
sta HPOSP0 ; změna pozice prvního hráče
jmp loop
Zajímavá je realizace podprogramu pro pozastavení výpočtů, který je nazvaný wait. Ten je naprogramován formou vnořené čekací smyčky, protože osmibitové pracovní registry mají nedostatečný rozsah pro čekání s využitím smyčky jediné (doba čekání je totiž velmi krátká). Proto je použita vnější smyčka s počitadlem Y a vnitřní smyčka, která je zopakována 256× (začínáme nulou, ta se ovšem ihned zmenšuje na 255):
.proc wait
ldy #50
wait1: ; vnější čekací smyčka
ldx #0
wait2: ; vnitřní čekací smyčka
dex ; počitadlo vnitřní smyčky
bne wait2
dey ; počitadlo vnější smyčky
bne wait1
rts ; návrat ze subrutiny
.endproc
Úplný zdrojový kód dnešního prvního demonstračního příkladu
Obě techniky popsané v předchozí kapitole byly použity v dnešním prvním demonstračním příkladu. Příklad vykreslí všechny čtyři hráče a poté začne postupně měnit horizontální pozici hráče prvního:
Obrázek 1: Automatický horizontální posun prvního hráče.
Obrázek 2: Automatický horizontální posun prvního hráče: překryv prvního a druhého hráče.
Obrázek 3: Automatický horizontální posun prvního hráče: překryv prvního a čtvrtého hráče.
Úplný zdrojový kód tohoto příkladu vypadá následovně:
; ---------------------------------------------------------------------
; Initializace PMG, povolení PMG, nastavení pozice, bitmapy a barvy všech
; objektů.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
.proc main
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #100 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #120 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #0 ; priorita hráčů a pozadí
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+50, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+50, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+50, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+50, x ; uložit byte - čtvrtý hráč
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
lda #80 ; výchozí pozice prvního hráče
loop:
jsr wait ; počkáme určitou dobu
sec
adc #0
sta HPOSP0 ; změna pozice prvního hráče
jmp loop
.endproc
.proc wait
ldy #50
wait1: ; vnější čekací smyčka
ldx #0
wait2: ; vnitřní čekací smyčka
dex ; počitadlo vnitřní smyčky
bne wait2
dey ; počitadlo vnější smyčky
bne wait1
rts ; návrat ze subrutiny
.endproc
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
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
Čekání na konec vykreslování snímku
Zpomalení posunu hráče lze ovšem realizovat i jinak. Můžeme například čekat na dokončení vykreslování snímku. To lze realizovat buď přímo (ukážeme si to při programování čipu ANTIC), nebo můžeme čekat na jeden „tik“ vnitřních hodin. Jedná se o bajt uložený v nulté stránce paměti, který je automaticky zvyšován každých 1/60 nebo 1/50 sekundy operačním systémem (podle použité televizní normy). Samotná realizace takto pojaté čekací smyčky je vlastně jednoduchá – čekáme na změnu hodnoty přečtené z adresy RTCLOK+2:
.proc _wait_vsync
ldx RTCLOK+2 ; čekání na konec snímku
@wt: cpx RTCLOK+2
beq @wt
rts
.endproc
Obrázek 4: Automatický horizontální posun prvního hráče, čekání na dokončení vykreslení obrazovky.
Obrázek 5: Automatický horizontální posun prvního hráče, čekání na dokončení vykreslení obrazovky.
Obrázek 6: Automatický horizontální posun prvního hráče, čekání na dokončení vykreslení obrazovky.
Úplný zdrojový kód dnešního druhého demonstračního příkladu
Nová podoba demonstračního příkladu, který posune prvního hráče každé dva snímky obrazovky (rychlost 25 nebo 30 pixelů za sekundu), vypadá takto:
; ---------------------------------------------------------------------
; Initializace PMG, povolení PMG, nastavení pozice, bitmapy a barvy všech
; objektů. Posun prvního hráče s čekáním na další snímek.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
.proc main
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #100 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #120 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #0 ; priorita hráčů a pozadí
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+50, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+50, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+50, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+50, x ; uložit byte - čtvrtý hráč
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
lda #80 ; výchozí pozice prvního hráče
loop:
jsr _wait_vsync
jsr _wait_vsync
jsr _wait_vsync
sec
adc #0
sta HPOSP0 ; změna pozice prvního hráče
jmp loop
.endproc
.proc _wait_vsync
ldx RTCLOK+2 ; čekání na konec snímku
@wt: cpx RTCLOK+2
beq @wt
rts
.endproc
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
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
Posun hráče pomocí joysticku
V další fázi vylepšování příkladu s PMG budeme prvního hráče ovládat pomocí joysticku. Prozatím dokážeme hráčem pohybovat doleva a doprava, takže se omezíme pouze na tyto dva směry. Můžeme se pokusit naprogramovat první variantu programové smyčky, ve které na základě náklonu joysticku zvýšíme nebo naopak snížíme hodnotu index registru X, který obsahuje horizontální pozici hráče. Vysokoúrovňová varianta vypadá takto:
ldx #80 ; výchozí pozice prvního hráče
loop:
if joystick vlevo -> dex
if joystick vpravo -> inx
stx HPOSP0 ; změna pozice prvního hráče
jmp loop
Nyní musíme dopsat realizaci podmínek. Stav prvního joysticku je uložen v hardwarovém registru STICK0, přičemž náklon joysticku je zakódován následovně (binární zápis ukazuje, že je v kódech určitá logika – každý směr je zakódován jedním znegovaným bitem):
Decimálně Binárně
14 1110
| |
10 | 6 1010 | 0110
\ |/ \ |/
11-- 15 ---7 1011-- 1111 --0111
/ |\ / |\
9 | 5 1001 | 0101
| |
13 1101
My tedy budeme rozpoznávat pouze kódy 11 a 7. Realizace posunu hráče je vlastně velmi jednoduchá a omezuje se na dvojici instrukcí CMP+BNE (test na hodnotu a skok, pokud je hodnota odlišná od hodnoty porovnávané):
ldx #80 ; výchozí pozice prvního hráče
loop:
jsr _wait_vsync
jsr _wait_vsync
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
jmp loop
Nové užitečné instrukce: přesuny dat mezi registry
V souvislosti s rostoucí komplexností programů se začínají využívat všechny pracovní registry mikroprocesoru MOS 6502. Musíme se tedy seznámit s instrukcemi určenými pro přenos hodnot mezi těmito registry. Tyto instrukce (je jich oficiálně pouze šest) začínají znakem T a obsahují svůj význam přímo v mnemotechnické zkratce instrukce (nemají tedy žádné parametry – zapomeňte na univerzální MOV, u kterého není zřejmé, co je zdrojem a co cílem):
| # | Instrukce | Plné jméno | Popis |
|---|---|---|---|
| 40 | TAX | transfer accumulator to X | přesun hodnoty z akumulátoru do index registru X |
| 41 | TAY | transfer accumulator to Y | přesun hodnoty z akumulátoru do index registru Y |
| 42 | TXA | transfer X to accumulator | přesun hodnoty z index registru X do akumulátoru |
| 43 | TYA | transfer Y to accumulator | přesun hodnoty z index registru Y do akumulátoru |
| 44 | TSX | transfer stack pointer to X | přesun ukazatele vrcholu zásobníku do index registru X |
| 45 | TXS | transfer X to stack pointer | přesun hodnoty z index registru X do ukazatele vrcholu zásobníku |
Korektní realizace posunu hráče pomocí joysticku
Se znalostí instrukcí pro přesuny dat můžeme celou smyčku realizující posun hráče s využitím joysticku upravit. Před voláním subrutiny _wait_vsync uložíme obsah index registru X do akumulátoru a poté ho opět obnovíme (TXA+TAX). Nemusíme se tedy starat o zásobník ani o uložení do operační paměti:
ldx #80 ; výchozí pozice prvního hráče
loop:
txa ; uložení obsahu X do akumulátoru
jsr _wait_vsync
jsr _wait_vsync
tax ; obnovení obsahu X z akumulátoru
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
jmp loop
Úplný zdrojový kód dnešního třetího demonstračního příkladu
Výše uvedenou programovou smyčku můžeme bez dalších úprav přidat do nové verze demonstračního příkladu, jehož úplný zdrojový kód vypadá následovně:
; ---------------------------------------------------------------------
; Ovládání PMG joystickem.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
.proc main
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #100 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #120 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #0 ; priorita hráčů a pozadí
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+50, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+50, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+50, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+50, x ; uložit byte - čtvrtý hráč
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
ldx #80 ; výchozí pozice prvního hráče
loop:
txa ; uložení obsahu X do akumulátoru
jsr _wait_vsync
jsr _wait_vsync
tax ; obnovení obsahu X z akumulátoru
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
jmp loop
.endproc
.proc _wait_vsync
ldx RTCLOK+2 ; čekání na konec snímku
@wt: cpx RTCLOK+2
beq @wt
rts
.endproc
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
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
Zobrazení všech čtyř střel společně s hráči
Připomeňme si, že kromě čtyř hráčů o šířce osmi pixelů a výšce 128 nebo 256 pixelů je možné čipem GTIA ovládat i takzvané střely (missiles), které mají šířku pouhé dva pixely. Tvary všech čtyř střel jsou uloženy v jediném bloku (128 nebo 256 bajtů), a to konkrétně od offsetu 384 v rámci adresového rozsahu 0..1024, který vyhradíme pro sprity:
PLAYER_0_OFFSET = 512 PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128 PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128 PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128 MISSILES_OFFSET = 384
Řídit je možné horizontální pozice všech čtyř střel, a to nezávisle na hráčích a nezávisle na ostatních střelách:
| Registr | Režim | Význam |
|---|---|---|
| HPOSM0 | W | horizontální pozice střely číslo 0 |
| HPOSM1 | W | horizontální pozice střely číslo 1 |
| HPOSM2 | W | horizontální pozice střely číslo 2 |
| HPOSM3 | W | horizontální pozice střely číslo 3 |
Ovšem barva střel je závislá na barvě hráčů (nebo naopak), což je patrné i z následujícího screenshotu:
Obrázek 7: Čtyři hráči a čtyři střely.
Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu
Další úprava demonstračního příkladu spočívá v přidání všech čtyř střel na obrazovku. Povšimněte si, že každá střela je skutečně široká pouze dva pixely a tudíž každé střele odpovídají jen dva bity z bitmapy 8×128 nebo 8×256 pixelů. Navíc je patrné, že střely mají stejnou barvu jako odpovídající hráči:
; ---------------------------------------------------------------------
; Ovládání PMG joystickem.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
MISSILES_OFFSET = 384
.proc main
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #95 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #110 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #125 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #140 ; horizontální pozice první střely
sta HPOSM0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #150 ; horizontální pozice druhé střely
sta HPOSM1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #160 ; horizontální pozice třetí střely
sta HPOSM2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #170 ; horizontální pozice čtvrté střely
sta HPOSM3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #0 ; priorita hráčů a pozadí
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+50, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+50, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+50, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+50, x ; uložit byte - čtvrtý hráč
sta addr+MISSILES_OFFSET+50, x ; uložit byte - střely
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
ldx #80 ; výchozí pozice prvního hráče
loop:
txa ; uložení obsahu X do akumulátoru
jsr _wait_vsync
jsr _wait_vsync
tax ; obnovení obsahu X z akumulátoru
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
jmp loop
.endproc
.proc _wait_vsync
ldx RTCLOK+2 ; čekání na konec snímku
@wt: cpx RTCLOK+2
beq @wt
rts
.endproc
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
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ři střely nebo pátý hráč?
Vzhledem k tomu, že střely mají šířku dvou pixelů a celkem je možné zobrazit čtyři střely, nabízí se otázka, jestli z nich nelze vytvořit „pátého hráče“. Prvním krokem k pátému hráči je horizontální posun střel takovým způsobem, aby od sebe byly posunuty přesně o dva pixely a současně byly uspořádány zleva doprava v pořadí M3+M2+M1+M0:
lda #146 ; horizontální pozice první střely
sta HPOSM0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #144 ; horizontální pozice druhé střely
sta HPOSM1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #142 ; horizontální pozice třetí střely
sta HPOSM2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrté střely
sta HPOSM3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
Výsledkem je „něco“, co se podobá duhovému pátému hráči:
Obrázek 8: Čtyři střely umístěné tak, aby simulovaly pátého hráče.
Úplný zdrojový kód dnešního pátého demonstračního příkladu
Opět si pro úplnost ukažme úplný zdrojový kód dnešního pátého demonstračního příkladu, jenž vypadá následovně:
; ---------------------------------------------------------------------
; Ovládání PMG joystickem.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
MISSILES_OFFSET = 384
.proc main
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #95 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #110 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #125 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #146 ; horizontální pozice první střely
sta HPOSM0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #144 ; horizontální pozice druhé střely
sta HPOSM1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #142 ; horizontální pozice třetí střely
sta HPOSM2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrté střely
sta HPOSM3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #0 ; priorita hráčů a pozadí
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+50, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+50, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+50, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+50, x ; uložit byte - čtvrtý hráč
sta addr+MISSILES_OFFSET+50, x ; uložit byte - střely
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
ldx #80 ; výchozí pozice prvního hráče
loop:
txa ; uložení obsahu X do akumulátoru
jsr _wait_vsync
jsr _wait_vsync
tax ; obnovení obsahu X z akumulátoru
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
jmp loop
.endproc
.proc _wait_vsync
ldx RTCLOK+2 ; čekání na konec snímku
@wt: cpx RTCLOK+2
beq @wt
rts
.endproc
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
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
Povolení pátého hráče
V tomto seriálu jsme se již několikrát setkali s řídicím registrem GPRIOR. Podle názvu se jedná o registr řídicí prioritu spritů a herního pole, ovšem k tomuto účelu slouží jen spodní čtyři bity. Šestý bit již známe – zapíná operaci OR barev spritů, pokud se překrývají. Ovšem zajímavý je i pátý bit, který zapíná „pátého hráče“:
lda #16 ; priorita hráčů a pozadí -> povolení pátého hráče
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
Pátý hráč se sice skládá ze čtyř střel, ovšem bude mít jednotnou barvu zapisovanou do registru COLOR3 (POZOR: to je změna, toto je registr s více významy):
lda #HUE_YELLOWRED<<4 + 8 ; barva pátého hráče (odstín+intenzita)
sta COLOR3 ; uložit do řídicího registru COLPF3 na čipu GTIA
Výsledek bude vypadat takto:
Obrázek 9: Čtyři střely se shodnou barvou jsou považovány za pátého hráče.
Úplný zdrojový kód dnešního šestého demonstračního příkladu
Dnešní předposlední demonstrační příklad vypadá takto:
; ---------------------------------------------------------------------
; Ovládání PMG joystickem.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
MISSILES_OFFSET = 384
.proc main
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #95 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #110 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #125 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #146 ; horizontální pozice první střely
sta HPOSM0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #144 ; horizontální pozice druhé střely
sta HPOSM1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #142 ; horizontální pozice třetí střely
sta HPOSM2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrté střely
sta HPOSM3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #HUE_YELLOWRED<<4 + 8 ; barva pátého hráče (odstín+intenzita)
sta COLOR3 ; uložit do řídicího registru COLPF3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #16 ; priorita hráčů a pozadí -> povolení pátého hráče
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+50, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+50, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+50, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+50, x ; uložit byte - čtvrtý hráč
sta addr+MISSILES_OFFSET+50, x ; uložit byte - střely
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
ldx #80 ; výchozí pozice prvního hráče
loop:
txa ; uložení obsahu X do akumulátoru
jsr _wait_vsync
jsr _wait_vsync
tax ; obnovení obsahu X z akumulátoru
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
jmp loop
.endproc
.proc _wait_vsync
ldx RTCLOK+2 ; čekání na konec snímku
@wt: cpx RTCLOK+2
beq @wt
rts
.endproc
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
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
Detekce stisku tlačítka joysticku, reakce na stisk tlačítka
V předchozím textu jsme si ukázali, jak lze zjistit náklon joysticku. Ještě nám zbývá detekce stisku tlačítka joysticku. To je snadné, protože stačí přečíst obsah registru STRIG0. Obsah tohoto registru je ovšem poněkud matoucí: jednička znamená, že tlačítko není stisknutu a nula naopak znamená detekci stisku. Snadno tedy můžeme realizovat například změnu barvy hráče stiskem tlačítka:
lda STRIG0 ; 0=stisknuto (Z je nastaven)
bne not_pressed ; NE==NZ
tya
clc
adc #16 ; změna odstínu barvy, ne intenzity
tay
sty PCOLR0 ; modifikovat barvový registr
not_pressed:
Obrázek 10: Změna barvy prvního hráče s využitím tlačítka joysticku.
Obrázek 11: Změna barvy prvního hráče s využitím tlačítka joysticku.
Již naposledy si ukažme zdrojový kód:
; ---------------------------------------------------------------------
; Ovládání PMG joystickem.
; ---------------------------------------------------------------------
.include "atari.inc"
.CODE
PLAYER_0_OFFSET = 512
PLAYER_1_OFFSET = PLAYER_0_OFFSET + 128
PLAYER_2_OFFSET = PLAYER_1_OFFSET + 128
PLAYER_3_OFFSET = PLAYER_2_OFFSET + 128
MISSILES_OFFSET = 384
.proc main
lda #80 ; horizontální pozice prvního hráče
sta HPOSP0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #95 ; horizontální pozice druhého hráče
sta HPOSP1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #110 ; horizontální pozice třetího hráče
sta HPOSP2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #125 ; horizontální pozice čtvrtého hráče
sta HPOSP3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #146 ; horizontální pozice první střely
sta HPOSM0 ; uložit do řídicího registru HPOSP0 na čipu GTIA
lda #144 ; horizontální pozice druhé střely
sta HPOSM1 ; uložit do řídicího registru HPOSP1 na čipu GTIA
lda #142 ; horizontální pozice třetí střely
sta HPOSM2 ; uložit do řídicího registru HPOSP2 na čipu GTIA
lda #140 ; horizontální pozice čtvrté střely
sta HPOSM3 ; uložit do řídicího registru HPOSP3 na čipu GTIA
lda #HUE_GREEN<<4 + 12 ; barva prvního hráče (odstín+intenzita)
sta PCOLR0 ; uložit do řídicího registru PCOLR0 na čipu GTIA
lda #HUE_YELLOW<<4 + 12 ; barva druhého hráče (odstín+intenzita)
sta PCOLR1 ; uložit do řídicího registru PCOLR1 na čipu GTIA
lda #HUE_MAGENTA<<4 + 12 ; barva třetího hráče (odstín+intenzita)
sta PCOLR2 ; uložit do řídicího registru PCOLR2 na čipu GTIA
lda #HUE_CYAN<<4 + 12 ; barva čtvrtého hráče (odstín+intenzita)
sta PCOLR3 ; uložit do řídicího registru PCOLR3 na čipu GTIA
lda #HUE_YELLOWRED<<4 + 8 ; barva pátého hráče (odstín+intenzita)
sta COLOR3 ; uložit do řídicího registru COLPF3 na čipu GTIA
lda #$ff ; bitová maska všech hráčů
sta GRAFP0 ; uložit do řídicího registru GRAFP0 na čipu GTIA
sta GRAFP1 ; uložit do řídicího registru GRAFP1 na čipu GTIA
sta GRAFP2 ; uložit do řídicího registru GRAFP2 na čipu GTIA
sta GRAFP3 ; uložit do řídicího registru GRAFP3 na čipu GTIA
lda #3 ; bitové pole: povolení hráčů i střel
sta GRACTL ; uložit do řídicího registru GRACTL na čipu GTIA
lda #16 ; priorita hráčů a pozadí -> povolení pátého hráče
sta GPRIOR ; uložit do řídicího registru GPRIOR na čipu GTIA
lda #152 ; paměťová stránka číslo 152
sta PMBASE
addr = 152*256
ldx #8 ; začneme na hodnotě o 1 vyšší
next_line:
lda sprite-1, x ; načíst
sta addr+PLAYER_0_OFFSET+50, x ; uložit byte - první hráč
sta addr+PLAYER_1_OFFSET+50, x ; uložit byte - druhý hráč
sta addr+PLAYER_2_OFFSET+50, x ; uložit byte - třetí hráč
sta addr+PLAYER_3_OFFSET+50, x ; uložit byte - čtvrtý hráč
sta addr+MISSILES_OFFSET+50, x ; uložit byte - střely
dex ; snížit offset + nastavit příznaky
bne next_line ; další byte spritu
lda #46 ; povolení PMG DMA
sta SDMCTL
ldy #14 ; barva prvního hráče
ldx #80 ; výchozí pozice prvního hráče
loop:
txa ; uložení obsahu X do akumulátoru
jsr _wait_vsync
jsr _wait_vsync
tax ; obnovení obsahu X z akumulátoru
lda STICK0 ; čtení joysticku
cmp #11 ; je nakloněn doleva?
bne not_left
dex ; posun hráče doleva
not_left:
cmp #7 ; je nakloněn doprava?
bne not_right
inx ; posun hráče doprava
not_right:
stx HPOSP0 ; změna pozice prvního hráče
lda STRIG0 ; 0=stisknuto (Z je nastaven)
bne not_pressed ; NE==NZ
tya
clc
adc #16 ; změna odstínu barvy, ne intenzity
tay
sty PCOLR0 ; modifikovat barvový registr
not_pressed:
jmp loop
.endproc
.proc _wait_vsync
ldx RTCLOK+2 ; čekání na konec snímku
@wt: cpx RTCLOK+2
beq @wt
rts
.endproc
; data
sprite: .byte 24, 60, 126, 219, 255, 36, 90, 165
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 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 (oproti minulému článku se seznam instrukcí rozšířil):
| # | 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 |
| 34 | LDA | load accumulator | načtení hodnoty do akumulátoru |
| 35 | LDX | load X register | načtení hodnoty do index registru X |
| 36 | LDY | load Y register | načtení hodnoty do index registru Y |
| 37 | STA | store accumulator | uložení akumulátoru |
| 38 | STX | store X register | uložení index registru X |
| 39 | STY | store Y register | uložení index registru Y |
| 40 | TAX | transfer accumulator to X | přesun hodnoty z akumulátoru do index registru X |
| 41 | TAY | transfer accumulator to Y | přesun hodnoty z akumulátoru do index registru Y |
| 42 | TXA | transfer X to accumulator | přesun hodnoty z index registru X do akumulátoru |
| 43 | TYA | transfer Y to accumulator | přesun hodnoty z index registru Y do akumulátoru |
| 44 | TSX | transfer stack pointer to X | přesun ukazatele vrcholu zásobníku do index registru X |
| 45 | TXS | transfer X to stack pointer | přesun hodnoty z index registru X do ukazatele vrcholu 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:
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_stick_1.xex pmg_stick_2.xex \
pmg_stick_3.xex pmg_stick_4.xex \
pmg_stick_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:
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)
