Recenze - Navigátor

12 Assembler
41 Encyklopedie
30 Hardware
15 Hra
12 Párty
7 Programování
12 Sofware

HRA

Wordcracker
Herní styl Puzzle
Multiplayer Bez multiplayeru
Rok vydání 1984
Programátor Jeremy Hammett
Grafik (Unknown)

INTRO


Kdyby tisíc přerušení

V tomto článku nahlédneme na používání přerušení C64 jiným než nejběžnější způsobem, avšak nejedná se zdaleka o kompletní popis všech možností přerušení.

Nejčastější používání přerušení na C64 je IRQ vyvolávané rasterem, jehož spouštěcí rutina může vypadat nějak takto:

SEI
LDA #$01
STA $D01A
STA $DC0D
LDA #<IRQ
LDX #>IRQ
STA $0314
STX $0315
LDA #$1B
STA $D011
LDA #$60
STA $D012
CLI

Mnozí jistě tuto rutinu znají, takže pro začátek kontrolní otázka: co přesně dělá instrukce STA $DC0D a proč tam je?

CIA

Obvod 6526, v C64 použitý jako CIA1 a CIA2


Tři hlavní zdroje přerušení na C64 jsou tři. Jsou to soudruzi VIC, CIA1 a CIA2.

VIC a CIA1 vyvolávají IRQ přerušení, jehož vector (pointer) je na adrese $FFFE (a při zapnutém kernalu se provádí nepřímý skok přes $0314). CIA2 je zdrojem NMI přerušení, které má vector na $FFFA (a při zapnutém kernalu se provede nepřímý skok přes $0318).

VIC dokáže vyvolat přerušení v momentě, když raster paprsek dojede na hlídanou řádku, když se srazí dva sprity, když se srazí sprite a pozadí a když přijde signál od světelného pera. V jakých případech se má přerušení vyvolat, se nastavuje na adrese $D01A:


Interrupt control register $D01A.
  • Bit #0: 1 = Raster interrupt enabled.
  • Bit #1: 1 = Sprite-background collision interrupt enabled.
  • Bit #2: 1 = Sprite-sprite collision interrupt enabled.
  • Bit #3: 1 = Light pen interrupt enabled.

Příkaz:

LDA #$01
STA $D01A

aktivuje vyvolávání přerušení závislého na rasteru. Potom se do adres rasteru ($D012 a nejvyšší bit $D011) musí zapsat hlídaná řádka a až na ní raster dojede, vyvolá IRQ.

Kdyby tam bylo LDA #$07, vyvolávalo by se IRQ nejen rasterem, ale i srážkou spritů mezi sebou a s pozadím. Aby bylo možné odlišit, co bylo příčinou vyvolání IRQ, musí se v IRQ přečíst adresa $D019, jejíž bity mají stejný význam jako na adrese $D01A:


Interrupt status register $D019.
  • Bit #0: 1 = Current raster line is equal to the raster line to generate interrupt at.
  • Bit #1: 1 = Sprite-background collision occurred.
  • Bit #2: 1 = Sprite-sprite collision occurred.
  • Bit #3: 1 = Light pen signal arrived.
  • Bit #7: 1 = An event, that may generate an interrupt, occurred.

Bit7 má hodnotu 1, pokud alespoň jeden z dalších stavových bitů má také hodnotu 1. To znamená, že pokud je zdrojem IRQ VIC, nastaví se bity 0-3 (podle toho, co bylo příčinou) a zároveň se nastaví bit 7. Takže je možné pro počáteční větvení programu použít instrukce BMI/BPL, jestli zdrojem IRQ je VIC, nebo něco jiného.

Pro další vyvolání přerušení je nejdříve nutné potvrdit, že to předchozí již bylo zpracováno. To se dělá zápisem na adresu $D019 a význam bitů 0-3 je totožný jako při čtení. Proto se na konci IRQ, které je vyvoláváno pouze rasterem, píše INC $D019 v naději, že se změní hodnota registru z $00 na $01 (a tím potvrdí zpracování IRQ vyvolaného rasterem). Na C64 to funguje, ale správně by se mělo psát:

LDA #$01
STA $D019

Pokud na adrese $D019 není potvrzeno zpracování přerušení, nebude další vyvoláno i v případě, že nastala událost, která ho má znovu vyvolat.

Kromě VICu může IRQ vyvolávat také CIA1. V principu je použití stejné, jen přístup k registrům je trošku odlišný. Stále se jedná o IRQ, jehož vector je na adrese $FFFE (a potažmo $0314). V jakých případech má CIA1 vyvolat IRQ se nastavuje zápisem na adrese $DC0D:


Interrupt control and status register $DC0D. Write bits:
  • Bit #0: 1 = Enable interrupts generated by timer A underflow.
  • Bit #1: 1 = Enable interrupts generated by timer B underflow.
  • Bit #2: 1 = Enable TOD alarm interrupt.
  • Bit #3: 1 = Enable interrupts generated by a byte having been received/sent via serial shift register.
  • Bit #4: 1 = Enable interrupts generated by positive edge on FLAG pin.
  • Bit #7: Fill bit; bits #0-#6, that are set to 1, get their values from this bit; bits #0-#6, that are set to 0, are left unchanged.

CIA vyvolá přerušení, pokud doběhne jeden z jeho dvou časovačů (A a B), TOD (Time Of Day) doběhl do hlídaného času (alarm), byla přijata data na seriové lince CIA a nebo signál na pinu FLAG. Do této adresy se nezapisuje tak jako do adresy $D01A, že bit s hodnotou 1 znamená zapnutí příslušné funkce a 0 její vypnutí.

Hodnotu, kterou chceme některému z bitů nastavit, nastavíme v bitu 7 a bity 0-4, které mají hodnotu 1 převezmou tuto hodnotu. Bity 0-4 s hodnotou 0 zůstanou bez změny. Pro příklad:

LDA #%00011111
STA $DC0D

nastaví bity 0-4 na nulu, takže se vypnou všechny signály CIA1, které by vyvolávaly IRQ. Příkaz

LDA #%10000011
STA $DC0D

nastaví bity 0 a 1 na hodnotu 1, čímž CIA1 začne vyvolávat IRQ, pokud doběhne časovač A nebo B. Bity 2-4 zůstanou bez změny.

Pokud mají časovače CIA vyvolávat přerušení, musí se nastavit jejich čas a spustit odpočet. Nejčastější použití CIA časovače na C64 je pro přehrávání samplů. Čas časovače A se nastavuje na adresách $DC04-$DC05. To může vypadat nějak takto:

LDA #$80
LDX #$00
STA $DC04
STX $DC05

nastaví čas na hodnotu $0080. Časovač B by se nastavoval na adresách $DC06-$DC07 a jednotka času je jeden takt procesoru. Tuto hodnotu $0080 časovač začne odpočítávat a až doběhne na nulu, vyvolá přerušení, což v tomto případě bude každých $80 taktů procesoru. Časovače CIA běží přímo v těchto obvodech nezávisle na okolí, takže nejsou odstavovány VICem na badline a v místech načítání spritů, tak jako CPU.

Samotný běh časovače A se pustí na adrese $DC0E (časovač B má adresu pro spuštění $DC0F a její význam je totožný):


Timer A control register $DC0E
  • Bit #0: 0 = Stop timer; 1 = Start timer.
  • Bit #1: 1 = Indicate timer underflow on port B bit #6.
  • Bit #2: 0 = Upon timer underflow, invert port B bit #6; 1 = upon timer underflow, generate a positive edge on port B bit #6 for 1 system cycle.
  • Bit #3: 0 = Timer restarts upon underflow; 1 = Timer stops upon underflow.
  • Bit #4: 1 = Load start value into timer.
  • Bit #5: 0 = Timer counts system cycles; 1 = Timer counts positive edges on CNT pin.
  • Bit #6: Serial shift register direction; 0 = Input, read; 1 = Output, write.
  • Bit #7: TOD speed; 0 = 60 Hz; 1 = 50 Hz.

Aby časovač začal odečítat, musí se zapsat:

LDA #%00010001
STA $DC0E

Bit 0 s hodnotou 1 zapne časovač, bit 3 s hodnotou 0 aktivuje kontinuální běh (kdyby byl 1, časovač byl odpočítal jednou a zůstal zastavený) a bit 4 s hodnotou 1 při startu nastaví aktuální hodnotu času z adres $DC04-$DC05 (pokud by byl časovač stopnut ještě před tím než doběhl, zůstane v něm aktuální hodnota a aby při dalším startu začal počítat od začátku, zajistí tento bit).

Když je vyvoláno IRQ obvodem CIA1, musí se přečíst jeho status a potvrdit zpracování. Obojí se děje přečtením adresy $DC0D, pořadí a význam bitů je stejný jako při zápisu:


Interrupt control and status register $DC0D. Read bits:
  • Bit #0: 1 = Timer A underflow occurred.
  • Bit #1: 1 = Timer B underflow occurred.
  • Bit #2: 1 = TOD is equal to alarm time.
  • Bit #3: 1 = A complete byte has been received into or sent from serial shift register.
  • Bit #4: Signal level on FLAG pin, datasette input.
  • Bit #7: An interrupt has been generated.

Když se v rutině IRQ napíše:

LDA $DC0D

přečte se status a zároveň se tím potvrdí jeho zpracování. To má za následek, že další čtení této adresy vrací 0, dokud nepřijde nějaký další signál vyvolávající přerušení. Proto je nutné si pro další zpracování tuto hodnotu po přečtení uložit.

Bit 7 (podobně jako u VICu a adresy $D019) signalizuje, že zdrojem přerušení je CIA1 a kontrolou bitů 0-4 se zjistí, o jaký se konkrétně jedná signál.



Obvod CIA2 vyvolává přerušení NMI, použití je úplně stejné jako CIA1 a IRQ, pouze CIA2 má registry od adresy $DD00. NMI má vector na adrese $FFFA (a potažmo $0318).

Na závěr bych ještě zmínil, že NMI je nadřazeno IRQ, při výskytu v jeden moment má přednost. A nelze ho deaktivovat instrukcí SEI. Pokud během rutiny přerušení nastala mezitím opět událost, která ho má vyvolat, je přerušení po instrukci RTI vyvoláno znovu.

Detailní popis registrů jsem si vypůjčil z této stránky: http://sta.c64.org/cbm64mem.html



Odpověď na kontrolní otázku z úvodu:

CIA1 je používán Basicem a po resetu časovač A vyvolává IRQ. Aby se tato funkce odstavila a nekolidovala s přerušením vyvolávaném VICem, zajistí příkaz:

LDA #$01
STA $DC0D

Pro vypnutí všech přerušení od obou CIA obvodů, nezávisle na předchozím stavu, by se mělo psát:

LDA #%00011111
STA $DC0D
STA $DC0D

Na konci článku je uveden zdrojový kód programu, který využívá pro přerušení raster a všechny 4 CIA časovače.



LHS


start = $1000

* = $0801
.word ss, 1
.null $9e, ^start
ss   .word 0


* = start

jsr $ff5b
jsr $e544

sei
lda #%00011111
sta $dc0d
sta $dd0d

lda #<irq01
ldx #>irq01
sta $fffe
stx $ffff

lda #<nmi01
ldx #>nmi01
sta $fffa
stx $fffb

lda #$35
sta $01

lda $d011
and #%01111111
sta $d011
lda #$98
sta $d012

lda #<$2000
ldx #>$2000
sta $dc04
stx $dc05

lda #<$4000
ldx #>$4000
sta $dc06
stx $dc07

lda #<$8000
ldx #>$8000
sta $dd04
stx $dd05

lda #<$c000
ldx #>$c000
sta $dd06
stx $dd07

lda #%00010001
sta $dc0e
sta $dc0f
sta $dd0e
sta $dd0f

lda #%10000011
sta $dc0d
sta $dd0d

lda #$1
sta $d01a
sta $d019

cli

-

inc $0450
jmp -



irq01

pha
txa
pha
tya
pha

lda $dc0d
bpl irq_CIA1_end
and #%00000011
lsr
pha
bcc +

;source of IRQ: timer CIA 1-A

inc $0400

+

pla
lsr
bcc +

;source of IRQ: timer CIA 1-B

inc $0402

+
irq_CIA1_end

lda $d019
bpl irq_VIC_end
and #%00000001
sta $d019

;source of IRQ: raster

inc $d021
ldx #$10
dex
bne *-1
dec $d021

irq_VIC_end

jmp interrupt_end



nmi01

pha
txa
pha
tya
pha

lda $dd0d
bpl nmi_CIA2_end
and #%00000011
lsr
pha
bcc +

;source of NMI: timer CIA 2-A

inc $0404

+

pla
lsr
bcc +

;source of NMI: timer CIA 2-B

inc $406

+
nmi_CIA2_end

interrupt_end

pla
tay
pla
tax
pla
rti


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)

24.11.2014 - LHS

Jak jsem se dozvěděl, tak časovače CIA neodečítaj hodnotu směrem k nule, ale začínají počítat od nuly a vyvolají přerušení, až doběhnou do zadaného času.

17.07.2014 - PCH

Tak popravdě ... lepší článek pro pochopení možností na C64 ohledně přerušení jsem ještě nečetl !! Super LHS !

Advert

Programmed by PCH of UNREAL, Hardware support by RAY of UNREAL. Beta test and bugs guru SILLICON
Unreal 2014 - Czech republic