HRA

Mojo's Revenge
Herní styl Pinball
Multiplayer Bez multiplayeru
Rok vydání
Programátor Bill Budge (Budge Co)
Grafik Knees Calhoon

INTRO

IRQ - Kdo pochopil, zvítězil !!

Když jsem před lety začínal s kódováním v assembleru na C64, věděl jsem asi tolik, co jste se mohli dozvědět v předchozích dílech. Jak vzít nějaké číslo, jak ho někam napsat, jak odskočit, když to číslo je nula a že když v Basic napíšu POKE 53280,1 bude okraj bílý. Co ale s tím, jak se dělají všechny ty efekty s grafikou? Jak se udělá, že na půlce obrazovky je text a na druhé půlce nějaký obrázek? Jak se dá udělat barevná čára přes celou šířku obrazovky (i přes okraj)? Bylo velmi málo české literatury, kde by něco takového bylo popsáno, a když už bylo, nebylo tomu rozumět. Alfou a Omegou grafických efektů, jako jsou třeba v intrech a demách, je zvládnutí IRQ přerušení a rasteru. Jeho základní použití se tady pokusím vysvětlit.

IRQ aneb na něm to stojí

Takže, IRQ je něco, s čím se dá operovat pouze v assembleru (míním tím C64). Když se pustí, běží program, co je v něm napsaný a hlavní program stojí, dokud neskončí IRQ přerušení a pak se zase rozeběhne. Raster je paprsek, který 50krát za sekundu vykreslí obrazovku. Vše, co vidíte na obrazovce C64 vykresluje raster paprsek. Jede od první horní řádky zleva doprava a když dojede na její konec, sestoupí na levý kraj řádky nižší. Celkem vykresluje $130 řádek, jeho pozice se snímá na adrese $D012 . Osmibitové číslo samozřejmě obsáhne jen $100 hodnot, proto nejvyšší bit rasteru je nejvyšší bit na adrese $D011 . To znamená, pokud chce odlišit $10 od $110, musíte kontrolovat $D012 pro hodnotu $10 a zároveň adresu $D011 pro nejvyšší bit.

Nyní příklad číslo jedna:

Jak můžete vidět, tento program udělá na černém pozadí bílý pruh. Každou z těch 1/50 sekundy kontroluje pozici rasteru a pokud dosáhl $98, změní barvu pozadí na bílou a pokračuje dále. Tam je další čekání, na raster řádku $B8 , a když jí dosáhne, změní barvu na černou a vrací se na začátek první smyčky.

Sice příklad lecos objasňující, nicméně nepraktický. Program nedělá nic jiného, než že čeká. Takhle si pustíte maximálně hudbu, ale jinak nic. Lepší by bylo, kdyby "něco" kontrolovalo pozici za nás a až by nastal ten správný moment, samo by to změnilo barvy. Něco takového existuje, je to IRQ když ho nastavíte tak, aby spolupracovalo s rasterem. Kromě toho mu ještě musíte nastavit jeho vector a ukončit ho. Takhle to může být až moc složité, proto uvedu další příklad a k němu komentář.

* = $2000 ;počáteční adresa

sei ;zakáží se systému přerušení

lda #$01
sta $d01a ;na co má přerušení reagovat, 1 znamená, že na raster
sta $dc0d ;register přerušení CIA; prostě stačí, když je tady hodnota 1, ;nejnižší bit musí být určitě 1 a nejvyšší určitě 0
lda #<irq1 ;nastavení vectoru, kde je přerušení, když např. rutina, kde je
ldx #>irq1 ;přerušení bude od $1000 , bylo by A = $00 a X = $10 ,
sta $0314 ;systém, když volá přerušení, skáče přes vector na adrese
stx $0315 ; $ 0314 (viz nepřímé skoky) na rutinu IRQ
lda #$98 ;přerušení se zapne, až raster bude na řádce $98
sta $d012
lda $d011 ;hodnota nejvyššího bitu adresy $D011 je implicitně 1,
and #%01111111 ;proto se musí vynulovat, jinak by raster
sta $d011 ;hledal hodnotu $ 198
cli ;povolí se systému přerušení
inc $ 0400
jmp *-3

irq1 ;počátek rutiny IRQ

lda #$01
sta $d020 ;změň barvu (zde je teď řádek $ 98)
lda #$b8 ;čekej, až bude řádek $B8
cmp $d012
bne *-3
lda #$00
sta $d020 ;změň barvu
lda #$01 ;nastav nejnižší bit na 1, ten zajistí, že až příště rater bude
sta $d019 ;mít hodnotu $ 98, vyvolá přerušení
jmp $ea7e ;ukončí přerušení, vrátí se do hlavního programu

Nejdříve se systému zakáže IRQ přerušení, abychom vůbec mohli přepsat jeho rutinu. Pak na adrese $D01A systému řekneme, že má IRQ pracovat s rasterem (další alternativy jsou s kolizí spritů mezi sebou a s pozadím, světelné pero). Na adrese $DC0D se aktivuje časovač, aby pouštěl přerušení. Dále vezmete LO a HI byte adresy, kde je program, co má běžet v přerušení a zapíšete do adres $0314 a $0315 . Do rasteru zapíšete hodnotu $ 98, to znamená, že když bude raster na řádce $98 , spustí se IRQ přerušení (které je nastaveno, aby reagovalo na raster). Adresa rasteru má totiž tu vlastnost, že když jí čtete, dostáváte aktuální řádku, kde raster je, a když do ní zapisujete, říkáte tím rasteru, na které řádce má vyvolat přerušení (ale pořád tam můžete i číst jeho pozici). Pak ještě vynulovat nejvyšší bit na adrese $ D011, protože implicitně je (dost nelogicky) nastaven na 1 a tak když byste napsali např. do $D012 hodnotu $ 10 a $D011 nechali bez povšimnutí, bude se IRQ pouštět na řádce $110 . Když byste do $D012 napsali $50 , bude se přerušení pouštět na řádce $150 . Ale protože maximální počet řádků je $ 130, vlastně by tento moment nikdy nenastal. Pak už zase můžete systému přerušení povolit, vše důležité pro jeho použití bylo přepsáno. Hlavní program končí smyčkou JMP *-3, ale klidně by mohl dále pokračovat.

IRQ rutina se spustí až bude raster na řádce $ 98, začíná změnou barvy okraje na bílou, pokračuje čekací smyčkou do řádky $ B8, pak změní barvu na černou, systému oznámí, aby při další shodě rasteru s do něj zapsanou hodnotou vyvolal IRQ (adresa $ D019 se po ukončení IRQ vynuluje, proto se musí pokaždé znovu nastavit) a ukončí se skokem na kernalovou (systémovou) rutinu. Pokud byste potřebovali při tomto přerušení používat klávesnici, použijte skok JMP $ EA31. Ukončení bude trvat o něco déle, ale klávesnice bude fungovat.

I když s tímto příkladem si můžete vystačit dlouho, přeci jen ještě není úplně dobrý. Ta nedokonalost je v IRQ rutině. Vyvolá se na řádku $ 98, změní barvu a pak čeká na řádku $ B8, aby změnila barvu zpět. A ta doba, kdy se čeká, je ztráta času, kterou můžete lehce pocítit. Lepší je udělat to tak, že když IRQ splní vše, co od něj požadujeme, ukončíme ho a až ho budeme znova potřebovat, vyvoláme ho někde jinde.

Rutina hlavního programu zůstává stejná, mění se pouze přerušení:

irq1

lda #$01
sta $d020 ;změní barvu
lda #<irq2 ;vezme LO a HI byte adresy přerušení,
ldx #>irq2 ;které se má vyvolat jako další
ldy #$b8 ;a zapíše řádek, na kterém se má vyvolat
sta $0314
stx $0315
sty $d012
inc $d019
jmp $ea7e

irq2

lda #$00 ;změní barvu
sta $d020
lda #<irq1 ;vezme LO a HI byte adresy přerušení,
ldx #>irq1 ;které se má vyvolat jako další
ldy #$98 ;a zapíše řádek, na kterém se má vyvolat.
sta $0314
stx $0315
sty $d012
inc $d019
jmp $ea7e

Tento příklad je ten nejjednodušší, jak se dá efektivně IRQ využít. Když potřebujete IRQ vícekrát za jeden průběh rasteru obrazovkou, vyvoláte ho v první moment, následně ukončíte a vyvoláte až zase dále, kde je potřeba. Mezitím může hlavní program běžet dále. Ovšem pozor na délku času, po který bude IRQ trvat. Pokud ten čas bude delší než čas, který raster paprsek potřebuje, aby se dostal na řádek, kde požadujete druhé přerušení, přerušení vznikne až při dalším průchodu rasteru obrazovkou. To znamená, že by IRQ nebylo 50krát za vteřinu, ale jen 25krát (každou druhou obrazovku) a to způsobí problémy ve všem, co něm zrovna děláte (zobrazujete grafiku, pouštíte hudbu atd.).

Zájemcům - začátečníkům - o práci s grafikou a přerušením doporučuji si tento příklad důkladně nastudovat. Pokud ho ovládnou, je to něco, co jim otvírá velké obzory a vystačí na dost dlouhou dobu. Samozřejmě, že IRQ nemusí být jenom dvě, může jich být mnohem více, záleží jen na tom, jak si program navrhnete a pak zrealizujete. K tomu budete také potřebovat alespoň ty nejdůležitější registry VICu. Jen napovím z těch základních, že kromě barvy okraje a obrazovky samotné se dá měnit i multi/hires, bitmapa/znaky a i znaková sada (její změna se projeví ovšem až na následujícím novém řádku textu). Je možné pomocí tohoto přerušení posouvat (multiplexovat) sprity. Schválně si zkuste zobrazit dvě řady po 8mi spritech. Finta je v tom ve vhodný okamžik měnit Y souřadnici spritu.

Jak jsem zmiňoval, je toto už dost mocné používání IRQ přerušení. Ale možná se mezi vámi najdou jedinci, kterým i toto po čase bude těsné (možná ne). Vězte že určitě narazíte na badline, což je řádka rasteru, která se nachází vždy na začátku textového řádku a která se nedá moc ovládat (špatně se na ní vychytá přepínaní tří hodnot najednou, např. $D020, $D021 a $D011) . I si všimnete, že když máte sprite přes řádek, kde se pouští/kontroluje IRQ, je ten řádek nějaký takový "divný". To je tím, že raster paprsek je více zatížen když sprite vykresluje, než když ho nevykresluje. Stejně tak vás napadne, jestli by šla snímat i X-ová souřadnice rasteru. Odpověď je ne, protože je to příliš rychle se měnící hodnota, než abyste jí mohli snímat. Když chcete s rasterem operovat i v X-ovém směru, musíte si jeho polohu odpočítávat a redukovat nějakou pauzou. Nějaké zpomalení (např. pro zarovnání barevného přechodu) budete potřebovat i teď, proto do něj vložte nějakou malou pauzu (třeba smyčku LDX#$0A, DEX, BNE *-1). I vás napadne (správně), že pomocí rasteru a IRQ se dělají všechny ty úžasné efekty jako FLI nebo otevřený okraj. Ale o tom až jindy, cesta k těmto problémů je ještě hodně vzdálená.

LHS

Nový příspěvek k článku

podpis :
První znak podpisu musí být vykřičník, jinak se příspěvek neodešle (ochrana proti spamu)

25.05.2024 - PCH

V poodě :)
bne *-1 znamená, že pointer se počítá před instrukcí BNE minus jedna instrukce zpět. A protože má INX/DEX jednu instrukci, skáče *-1 na ni.

13.05.2024 - martin

díky. tuším už že xbne*-1x se čte (odspoda nahoru --> zprava doleva, po bajtech)
(programuju c64 assembly asi rok. tak sem tak trochu začátečník)

10.05.2024 - PCH

No. Ne jeden takt, ale jeden byte ... omlouvam se za nepresnost :)

07.05.2024 - PCH

Kde na předposledním?
Pokud skáču na DEX, který má jeden takt, skáču na něj BNE *-1

12.04.2024 - martin

nema na tom predposlednim radku byt bne*-2 ? ____(skok na DEX)
(sem tak trochu zacatecnik )

Advert

Hardcode and datamining by PCH of UNREAL, Hardware guru by RAY of UNREAL, Bugs report by SILLICON of UNREAL
UNREAL 2014-2021 Czech republic