Ready64 Forum
Commodore 64 => Programmazione, Grafica e Musica => Topic aperto da: Elder0010 - 22 Maggio 2011, 23:28:35
-
I CIA irqs e i Raster interrupts possono coesistere durante l'esecuzione di un programma?
Ho provato a modificare questo codice (http://codebase64.org/doku.php?id=base:timerinterrupts) inserendo un interrupt raster, in modo da combinare l'utilizzo di cia interrupts (per scatenare eventi con un timer) e raster interrupts (per eventi sincronizzati al raster).
(Nella mia versione, la parte superiore dello schermo viene distorta alterando $d016).
Non riesco a capire però cosa c'è di sbagliato nell'impostazione del raster interrupt, in quanto non viene mai eseguito!
Commentando le linee:
lda #$35; Select big bank without roms
sta $01 ; Change to big bank
il cia irq non viene eseguito perchè la zona di memoria dei vettori è occupata dalla kernal rom, giusto? (spero di aver capito bene). In questo caso, il raster irq si comporta correttamente.
Deduco quindi che ci sia un conflitto nel setup dei due interrupts.
*=$0801
.byte $0a,$08,$0a,$00,$9e,$32,$30,$36,$32
.byte $00,$00,$00,$00; a BASIC SYS LINE FOR STARTING CODE AT $080E
*=$080e
sei
ldx #$ff
txs ; Reset Stack pointer
;Via la rom, mappo piu ram
lda #$35; Select big bank without roms
sta $01 ; Change to big bank
;punto irq CIA
lda #<irq
sta $fffe; Set low IRQ adress in selected bank
lda #>irq
sta $ffff; Set high IRQ adress in selected bank
;Abilito tutti gli interrupts
;generati dai timer
lda #$7f
sta $dc0d; Clear IRQ interruptmask for CIA
;Timer A control register. Bits:
;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.
lda $dc0e
ora #%10011000
and #%11110110
sta $dc0e; count o2 pulses and forceload and continious and stop
; timer A
tay ; Save for later use in y
;4cc7 = un frame
lda #$c7; Cia time $4cc7 is a frame on a pal system with old
; cia (6526) 63 cycles per line * 312 lines - 1
sta $dc04
lda #$4c
sta $dc05; Set timer A to one frame
lda #$81
sta $dc0d; Activate CIA timer A irq
tya
ora #%00000001 ; Start CIA Timer A
sta $dc0e
lda #<interrupt1
ldx #>interrupt1
sta $314
stx $315
lda #$01
sta $d012
asl $d019
lda #$01
sta $d01a ; Accendi raster interrupts
cli
jmp * ; do nothing while no IRQ, just jump to this line
interrupt1
inc $d020
asl $d019 ; ACK interrupt
pla
tay
pla
tax
pla
rti
;CIA IRQ
irq
php ; 3 cycles
pha ; 3 cycles
lda $dc0d; 4 cycles;Acknowledge CIA timer a interrupt
tya ; 2 cycles
pha ; 4 cycles
txa ; 2 cycles
pha ; 4 cycles
lda $d012
cmp #$90
bpl skip
;Shakero lo schermo
lda $d011
ora #%00011011
sta $d011
dec $d016
inc $d016
lda $d016
ora #%00000111
sta $d016
lda $d016
and #%00000110
sta $d016
lda $d016
and #%00000100
sta $d016
lda $d016
and #%00000101
sta $d016
inc $d016
lda $d016
and #%00000011
sta $d016
lda $d016
and #%00000001
sta $d016
inc $d016
lda $d016
and #%00000000
sta $d016
skip
pla
tax
pla
tay
pla
plp
rti
Spero di non aver fatto troppi strafalcioni al solito :)
-
Finalmente un post giusto nel posto giusto!
A buoni intenditori...
Allora Elder, premesso che i CIA sono gli integrati che conosco di meno nel biscottone, credo comunque di poterti chiarire le idee almeno su come si comporta il primo dei due.
Il punto focale é che il 6510 ha un solo piedino IRQ (più un NMI ma non c'entra con questo discorso).
Ciò comporta due cose:
- qualsiasi sorgente di interruzione (VIC/CIA/Porta espansione..) deve necessariamente agire su quel pin per 'triggerare' un IRQ
- il 6510 non ha nessun modo "autonomo" per capire chi possa aver generato l'IRQ: lui sa solo che deve salvare lo status flag e l'IP e poi saltare alla routine puntata da $fffe/$ffff
Nella tua routine avevi impostato $fffe/$ffff e $0314/$0315: impostare la prima é coppia é corretto, impostare la seconda é inutile.
Mi spiego meglio.
Il 6510, in risposta ad un IRQ, salta sempre (SEMPRE!) all'indirizzo puntato da $fffe/$ffff. Ora, se il kernal é attivo (leggi, se le ROM sono visibili) tale coppia viene letta appunto dalla ROM e punta all'handler del kernal(indirizzo $ff48). Questo handler in sostanza va ad eseguire a sua volta la routine puntata da $0314/$0315.
Ecco da dove arriva la seconda coppia.
E' chiaro che levando la ROM sparisce il puntamento ad $ff48 e perciò $0314/$0315 diventano due locazioni come le altre.
Il punto quindi é determinare nell'unico handler a disposizione chi sia stato a produrre l'IRQ; ci sono almeno due modi per risolvere il problema, io ho scelto quello che mi ricordo meglio.
- puoi leggere il bit 7 di $d019 (quando é attivo significa che é stato generato un IRQ dal VIC)
- puoi leggere non-mi-ricordo-dove nel CIA1 (in modo analogo c'é un bit che indica se é stato generato un IRA dal CIA1)
Altro miniappunto: negli interrupt handler non serve salvare lo status flag perché ci pensa il processore quando esegue l'IRQ (ed in modo analogo ripristina il flag con RTI).
Il codice (ho modificato qualcosina anche sulla parte di vibrazione anche se probabilmente non é come la intendevi tu).
*=$0801
.byte $0a,$08,$0a,$00,$9e,$32,$30,$36,$32
.byte $00,$00,$00,$00; a BASIC SYS LINE FOR STARTING CODE AT $080E
*=$080e
sei
; Blocco l'NMI altrimenti salta tutto (manca l'handler del NMI)!
lda #$7f
sta $dd0d
lda $dd0d
ldx #$ff
txs; Reset Stack pointer
;Via la rom, mappo piu ram
lda #$35; Select big bank without roms
sta $01; Change to big bank
;punto irq CIA
lda #<irq
sta $fffe; Set low IRQ adress in selected bank
lda #>irq
sta $ffff; Set high IRQ adress in selected bank
;Abilito tutti gli interrupts
;generati dai timer
lda #$7f
sta $dc0d; Clear IRQ interruptmask for CIA
;Timer A control register. Bits:
;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.
lda $dc0e
ora #%10011000
and #%11110110
sta $dc0e; count o2 pulses and forceload and continious and stop
; timer A
tay; Save for later use in y
waitrst; allineo il CIA ad una certa posizione dello schermo
lda $d012
cmp #$90
bne waitrst
;4cc7 = un frame
lda #$c7; Cia time $4cc7 is a frame on a pal system with old
; cia (6526) 63 cycles per line * 312 lines - 1
sta $dc04
lda #$4c
sta $dc05; Set timer A to one frame
lda #$81
sta $dc0d; Activate CIA timer A irq
tya
ora #%00000001; Start CIA Timer A
sta $dc0e
lda #$01
sta $d012
asl $d019
lda #$01
sta $d01a; Accendi raster interrupts
cli
jmp *; do nothing while no IRQ, just jump to this line
IRQbyVic
inc $d020
asl $d019; ACK interrupt
jmp skip
;CIA IRQ
irq
pha; 3 cycles
txa; 2 cycles
pha; 4 cycles
tya; 2 cycles
pha; 4 cycles
bit $d019; E' stato il VIC a generare l'IRQ
bmi IRQbyVic; Si => vai alla routine specifica
IRCbyCIA
; No, allora é stato il timer del CIA
lda $dc0d; 4 cycles;Acknowledge CIA timer a interrupt
;Shakero lo schermo
lda $d011
eor #%00000111
sta $d011
lda $d016
eor #%00000111
sta $d016
skip ; Da qua escono entrambi gli IRQ
pla
tay
pla
tax
pla
rti
-
Ok, estendendo la ram le location $314/$315 diventano come tutte le altre! ecco perchè il mio irq non veniva mai eseguito!
Su indicazioni di Ian, aggiungo il disassemblato della zona puntata da $FFFE (cioè $FF48):
.C:ff48 48 PHA
.C:ff49 8A TXA
.C:ff4a 48 PHA
.C:ff4b 98 TYA
.C:ff4c 48 PHA
.C:ff4d BA TSX
.C:ff4e BD 04 01 LDA $0104,X
.C:ff51 29 10 AND #$10
.C:ff53 F0 03 BEQ $FF58
.C:ff55 6C 16 03 JMP ($0316)
.C:ff58 6C 14 03 JMP ($0314); <- puntamento all'IRQ settato normalmente
.C:ff5b 20 18 E5 JSR $E518
.C:ff5e AD 12 D0 LDA $D012
.C:ff61 D0 FB BNE $FF5E
.C:ff63 AD 19 D0 LDA $D019
.C:ff66 29 01 AND #$01
.C:ff68 8D A6 02 STA $02A6
.C:ff6b 4C DD FD JMP $FDDD
.C:ff6e A9 81 LDA #$81
.C:ff70 8D 0D DC STA $DC0D
Inoltre, è possibile controllare tramite il bit #7 di $DC0D se un interrupt è stato generato!
Ora è chiaro: complicando un po' le cose, mi chiedo se questo schema può funzionare (scrivo in pseudocodice)
- init del sistema
- punto cia_interrupt_1
jmp *
- cia_interrupt_1
bit $d019; E' stato il VIC a generare l'IRQ
bmi raster_interrupt_handle; Si => vai alla routine specifica
...codice cia interrupt...distorci schermo
jmp uscita
- raster_interrupt_handle
cmp $d012
-> se maggiore di $40 jmp raster_interrupt_1
-> se maggiore di $80 jmp raster_interrupt_2
- raster_interrupt_1
...codice raster interrupt 1
jmp uscita
- raster_interrupt_2
...codice raster interrupt 2
jmp uscita
- uscita dall'interrupt (tutti i casi)
pla
tay
pla
tax
pla
rti
In questo caso avremmo lo schermo splittato dai due raster interrupt, e la distorsione temporizzata dal cia, tutto gestito con un solo handler!
-
Ho meditato un po' sulla questione: in realtà la soluzione che ho proposto in pseudocodice non risponde alla mia domanda perchè effettua semplicemente un controllo sulla rasterline, invece di gestire cia + raster irqs.
Ho pensato quindi di generare 3 irq: uno cia e due raster, in sequenza.
Il cia irq viene chiamato una volta per frame: fa il suo lavoro, e poi si occupa di puntare al primo raster irq.
Idem per il primo raster irq, che punta al secondo, e poi il giro ricomincia da capo. Spero sia corretto!
*=$0801
.byte $0a,$08,$0a,$00,$9e,$32,$30,$36,$32
.byte $00,$00,$00,$00; a BASIC SYS LINE FOR STARTING CODE AT $080E
*=$080e
sei
; Blocco l'NMI altrimenti salta tutto (manca l'handler del NMI)!
lda #$7f
sta $dd0d
lda $dd0d
ldx #$ff
txs; Reset Stack pointer
;Via la rom, mappo piu ram
lda #$35; Select big bank without roms
sta $01; Change to big bank
;punto irq CIA
lda #<irq
sta $fffe; Set low IRQ adress in selected bank
lda #>irq
sta $ffff; Set high IRQ adress in selected bank
;Abilito tutti gli interrupts
;generati dai timer
lda #$7f
sta $dc0d; Clear IRQ interruptmask for CIA
;Timer A control register. Bits:
;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.
lda $dc0e
ora #%10011000
and #%11110110
sta $dc0e; count o2 pulses and forceload and continious and stop
; timer A
tay; Save for later use in y
;4cc7 = un frame
lda #$c7; Cia time $4cc7 is a frame on a pal system with old
; cia (6526) 63 cycles per line * 312 lines - 1
sta $dc04
lda #$4c
sta $dc05; Set timer A to one frame
waitrst; allineo il CIA ad una certa posizione dello schermo
lda $d012
cmp #$80
bne waitrst
lda #$ff
sta $dc0d; Activate CIA timer A irq
tya
ora #%00000001; Start CIA Timer A
sta $dc0e
lda #$01
sta $d012
asl $d019
lda #$01
sta $d01a; Accendi raster interrupts
cli
jmp *; do nothing while no IRQ, just jump to this line
;CIA IRQ
irq
bit $d019; E' stato il VIC a generare l'IRQ
bmi raster_interrupt_handle; Si => vai alla routine specifica
IRCbyCIA
; No, allora é stato il timer del CIA
pha; 3 cycles
txa; 2 cycles
pha; 4 cycles
tya; 2 cycles
pha; 4 cycles
lda #$80
cmp $d012
BPL skip
;Shakero lo schermo
lda $d016
eor #%00000111
sta $d016
lda #<raster_irq
sta $fffe;
lda #>raster_irq
sta $ffff;
;Il prossimo evento deve essere un raster irq
lda #$ff
sta $d012
lda $dc0d; 4 cycles;Acknowledge CIA timer a interrupt
jmp skip
raster_interrupt_handle
lda #$ff
cmp $d012
BPL raster_irq_2
raster_irq
pha; 3 cycles
txa; 2 cycles
pha; 4 cycles
tya; 2 cycles
pha; 4 cycles
inc $d020
lda #$ff
sta $d012
;Il prossimo evento deve essere il Raster interrupt 2
lda #<raster_irq_2
sta $fffe;
lda #>raster_irq_2
sta $ffff;
asl $d019
jmp skip
raster_irq_2
pha; 3 cycles
txa; 2 cycles
pha; 4 cycles
tya; 2 cycles
pha; 4 cycles
dec $d020
inc $0400
;Il prossimo evento deve essere il CIA interrupt
lda #<irq
sta $fffe;
lda #>irq
sta $ffff;
asl $d019
jmp skip
skip ; Da qua escono tutti gli IRQ
pla
tay
pla
tax
pla
rti