Ready64 Forum
Commodore 64 => Programmazione, Grafica e Musica => Topic aperto da: antoniomauro - 19 Aprile 2010, 15:03:56
-
Dopo aver letto un bel po di materiale mi sono cimentato nella stesura della mia prima routine di scrolling orizzontale.
Per il momento non sono ancora in grado di comprendere le tecniche avanzate basate puramente sull'uso del VIC-II (es: dma delay) ed ho realizzato il tutto con un meccanismo di double buffer e split copy della memoria video.
Si può scorrere lo schermo in una sola direzione muovendo il joy a dx.
La colorazione del bordo indica il raster time dalla routine che ovviamente aumenta quando si deve copiare la memoria video.
La copia della color memory è per il momento disabilitata (mi crea un po di problemi)
Vorrei inserire anche lo scroll a sx e, considerando che in un ipotetico gioco si fa molto di più che scrollare uno schermo, mi chiedevo se era possibile ottimizzare qualcosa per ridurre ulteriormente il raster time occupato...
processor 6502
; Costanti
rastline = $fA;
screenD = $0400;
screen0 = $3800;
screen1 = $3c00;
color = $d800
; Variabili zero page
temp = $FF
joy2 = $FE
cscr = $FD
org $0801
word eop
word 7102
byte $9e,"2061",0
eop word 0
org $080D;Inizio del programma
main
sei
;Copio lo schermo di default
ldx #$00
lpCmpS
lda screenD,x
sta screen0,x
lda screenD+255,x
sta screen0+255,x
lda screenD+255*2,x
sta screen0+255*2,x
lda screenD+255*3,x
sta screen0+255*3,x
inx
cpx #$ff
bne lpCmpS
lda #$C4;Posizione di scrolling iniziale e scrolling HW nel mezzo
sta $D016
lda #$E4;Attivo schermo 0 (11100100 E4)
sta $d018
lda #$00;Indicatore dello schermo corrente
sta cscr
jsr initIRQ
cli
jmp * ;Loop infinito
rts
initIRQ
lda #<routineIRQ;Indirizzo codice (parte alta)
sta $0314
lda #>routineIRQ;Indirizzo codice (parte bassa)
sta $0315
lda #rastline;Raster line di attivazione IRQ raster
sta $d012
lda #$1b;Azzera il bit 7 che sarebbe il Raster Compare: (Bit 8) di $d012
sta $D011
lda #$7f;01111111 BIT 7 a 0
sta $dc0d;Azzera il bit 7 di CIA Interrupt Control Register (Read IRQs/Write Mask) per disattivarlo
sta $dd0d;Azzera il bit 7 di CIA Interrupt Control Register (Read NMls/Write Mask) per disattivarlo
lda $dc0d ;Non so a che derve !?!
lda $dd0d ;Non so a che derve !?!
lda #$01 ;Abilita IRQ raster
sta $d019 ;VIC Interrupt Flag Register (Bit = 1: IRQ Occurred)
sta $d01a ;IRQ Mask Register: 1 = Interrupt Enabled
rts
routineIRQ
inc $d019;Dico al vic che la routine è sata eseguita
;Colora una raster line per capire quanto raster consumo per scrollare
inc $d020;riga 1
; Leggo joy porta 2
lda $DC00
sta joy2;Salvo lo stato del joy2
;scrolla schermo a SX di un carattere
lda joy2;Superfluo in questo punto
and #%00001000;dx
bne lpExit;Se zero salta
;Se diverso da Zero Scroll
;X HW Scroll bit 2-0
ldx $D016
dex
cpx #$C3; - Copiare la prima parte da schermo a 3
bne lpCont1;NO
lda cscr
beq lpSCU;Screen 0
jsr scrollSsxU10
jmp lpCont1
lpSCU
jsr scrollSsxU01
lpCont1
cpx #$C1; - Copiare la seconda parte da schermo a 1
bne lpCont2;NO
lda cscr
beq lpSCD;Screen 0
jsr scrollSsxD10
jmp lpCont2
lpSCD
jsr scrollSsxD01
lpCont2
cpx #$BF;Superato il limite?
bne lpContE;NO
ldx #$C7;Riporta il bit di scroll a 7
lda cscr;Switch screens
beq lpSW1;Screen 0
lda #$E4;Attivo schermo 0
sta $d018
lda #$00;Indicatore dello schermo corrente
sta cscr
jmp lpSW0
lpSW1
lda #$F4;Attivo schermo 1 (11110100 F4)
sta $d018
lda #$01;Indicatore dello schermo corrente
sta cscr
lpSW0
;jsr scrollCsx;Scrolla anche Color Memory
lpContE
stx $D016
lpExit
;Rimetto colore riga a posto
dec $d020
;Queste ultime 6 righe determinano il ritorno del codice all'interrupt HANDLER
;Se voglio creare una routine che venga eseguita mentre il basic è attivo
;sostituisco le 6 righe con JMP $ea31
pla ;Pull Accumulator from Stack
tay ;transfer A to Y
pla ;Pull Accumulator from Stack
tax ;transfer A to X
pla ;Pull Accumulator from Stack
;JMP $ea31
rti ; return from interrupt
scrollSsxU01
YSU01 SET 0
REPEAT 12
XSU01 SET 0
REPEAT 39
lda screen0+XSU01+YSU01+1
sta screen1+XSU01+YSU01
XSU01 SET XSU01 + 1
REPEND
YSU01 SET YSU01 + 40
REPEND
rts
scrollSsxD01
YSD01 SET 12*40
REPEAT 13
XSD01 SET 0
REPEAT 39
lda screen0+XSD01+YSD01+1
sta screen1+XSD01+YSD01
XSD01 SET XSD01 + 1
REPEND
YSD01 SET YSD01 + 40
REPEND
rts
scrollSsxU10
YSU10 SET 0
REPEAT 12
XSU10 SET 0
REPEAT 39
lda screen1+XSU10+YSU10+1
sta screen0+XSU10+YSU10
XSU10 SET XSU10 + 1
REPEND
YSU10 SET YSU10 + 40
REPEND
rts
scrollSsxD10
YSD10 SET 12*40
REPEAT 13
XSD10 SET 0
REPEAT 39
lda screen1+XSD10+YSD10+1
sta screen0+XSD10+YSD10
XSD10 SET XSD10 + 1
REPEND
YSD10 SET YSD10 + 40
REPEND
rts
scrollCsx
YC SET 0
REPEAT 25
XC SET 0
REPEAT 39
lda color+XC+YC+1
sta color+XC+YC
XC SET XC + 1
REPEND
YC SET YC + 40
REPEND
rts
-
Ho aggiunto anche lo scroll della color memory che però ho dovuto ridurre di complessita (20*25) per riuscire a far stare tutto nel raster time del bordo inferiore.
Ovviamente rimane aperta la mia richiesta di consigli riguardo l'ottimizzazione della routine oppure lo stravolgimento della stessa nel caso in cui ci siano delle scelte di fondo poco azzeccate...
processor 6502
; Costanti
rastline = $fA;
screenD = $0400;
screen0 = $3800+$8000;
screen1 = $3C00+$8000;
color = $d800
; Variabili zero page
temp = $FF
joy2 = $FE
cscr = $FD
org $0801
word eop
word 7102
byte $9e,"2061",0
eop word 0
org $080D;Inizio del programma
main
sei
;Imposto color MAP
ldx #$00
lpCmpC
lda colormap20X25,x
sta color,x
lda colormap20X25+255,x
sta color+255,x
lda colormap20X25+255*2,x
sta color+255*2,x
lda colormap20X25+255*3,x
sta color+255*3,x
inx
cpx #$ff
bne lpCmpC
lda #$36;Libero A000-BFFF dalla BASIC ROM RAM (Freshness79)
sta $01
lda #$15;Seleziono banco 2 ($8000-$BFFF 00010101)
sta $dd00
lda #$C4;Posizione di scrolling iniziale e scrolling HW nel mezzo
sta $D016
lda #$E4;Attivo schermo 0 (11100100 E4)
sta $d018
lda #$00;Indicatore dello schermo corrente
sta cscr
jsr initIRQ
cli
jmp * ;Loop infinito
rts
initIRQ
lda #<routineIRQ;Indirizzo codice (parte alta)
sta $0314
lda #>routineIRQ;Indirizzo codice (parte bassa)
sta $0315
lda #rastline;Raster line di attivazione IRQ raster
sta $d012
lda #$1b;Azzera il bit 7 che sarebbe il Raster Compare: (Bit 8) di $d012
sta $D011
lda #$7f;01111111 BIT 7 a 0
sta $dc0d;Azzera il bit 7 di CIA Interrupt Control Register (Read IRQs/Write Mask) per disattivarlo
sta $dd0d;Azzera il bit 7 di CIA Interrupt Control Register (Read NMls/Write Mask) per disattivarlo
lda $dc0d ;Non so a che derve !?!
lda $dd0d ;Non so a che derve !?!
lda #$01 ;Abilita IRQ raster
sta $d019 ;VIC Interrupt Flag Register (Bit = 1: IRQ Occurred)
sta $d01a ;IRQ Mask Register: 1 = Interrupt Enabled
rts
routineIRQ
inc $d019;Dico al vic che la routine è sata eseguita
;Colora una raster line per capire quanto raster consumo per scrollare
inc $d020;riga 1
; Leggo joy porta 2
lda $DC00
sta joy2;Salvo lo stato del joy2
;scrolla schermo a SX di un carattere
lda joy2;Superfluo in questo punto
and #%00001000;dx
bne lpExit;Se zero salta
;Se diverso da Zero Scroll
;X HW Scroll bit 2-0
ldx $D016
dex
cpx #$C3; - Copiare la prima parte da schermo a 3
bne lpCont1;NO
lda cscr
beq lpSCU;Screen 0
jsr scrollSsxU10
jmp lpCont1
lpSCU
jsr scrollSsxU01
lpCont1
cpx #$C1; - Copiare la seconda parte da schermo a 1
bne lpCont2;NO
lda cscr
beq lpSCD;Screen 0
jsr scrollSsxD10
jmp lpCont2
lpSCD
jsr scrollSsxD01
lpCont2
cpx #$BF;Superato il limite?
bne lpContE;NO
ldx #$C7;Riporta il bit di scroll a 7
lda cscr;Switch screens
beq lpSW1;Screen 0
lda #$E4;Attivo schermo 0
sta $d018
lda #$00;Indicatore dello schermo corrente
sta cscr
jsr scrollCsx10;Scrolla anche Color Memory
jmp lpContE
lpSW1
lda #$F4;Attivo schermo 1 (11110100 F4)
sta $d018
lda #$01;Indicatore dello schermo corrente
sta cscr
jsr scrollCsx01;Scrolla anche Color Memory
lpContE
stx $D016 ;HW Scroll
lpExit
;Rimetto colore riga a posto
dec $d020
;Queste ultime 6 righe determinano il ritorno del codice all'interrupt HANDLER
;Se voglio creare una routine che venga eseguita mentre il basic è attivo
;sostituisco le 6 righe con JMP $ea31
pla ;Pull Accumulator from Stack
tay ;transfer A to Y
pla ;Pull Accumulator from Stack
tax ;transfer A to X
pla ;Pull Accumulator from Stack
;JMP $ea31
rti ; return from interrupt
scrollSsxU01
YSU01 SET 0
REPEAT 12
XSU01 SET 0
REPEAT 39
lda screen0+XSU01+YSU01+1
sta screen1+XSU01+YSU01
XSU01 SET XSU01 + 1
REPEND
YSU01 SET YSU01 + 40
REPEND
rts
scrollSsxD01
YSD01 SET 12*40
REPEAT 13
XSD01 SET 0
REPEAT 39
lda screen0+XSD01+YSD01+1
sta screen1+XSD01+YSD01
XSD01 SET XSD01 + 1
REPEND
YSD01 SET YSD01 + 40
REPEND
rts
scrollSsxU10
YSU10 SET 0
REPEAT 12
XSU10 SET 0
REPEAT 39
lda screen1+XSU10+YSU10+1
sta screen0+XSU10+YSU10
XSU10 SET XSU10 + 1
REPEND
YSU10 SET YSU10 + 40
REPEND
rts
scrollSsxD10
YSD10 SET 12*40
REPEAT 13
XSD10 SET 0
REPEAT 39
lda screen1+XSD10+YSD10+1
sta screen0+XSD10+YSD10
XSD10 SET XSD10 + 1
REPEND
YSD10 SET YSD10 + 40
REPEND
rts
scrollCsx01
YC01 SET 0
REPEAT 25
XC01 SET 0
REPEAT 19
lda color+YC01*40+XC01*2+2
sta color+YC01*40+XC01*2+1
XC01 SET XC01 + 1
REPEND
YC01 SET YC01 + 1
REPEND
rts
scrollCsx10
YC10 SET 0
REPEAT 25
XC10 SET 0
REPEAT 19
lda color+YC10*40+XC10*2+1
sta color+YC10*40+XC10*2
XC10 SET XC10 + 1
REPEND
YC10 SET YC10 + 1
REPEND
rts
colormap20X25
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
-
E' abbastanza semplice aggiungere anche l'altra direzione di scrolling ma utilizzando sempre le routine di copia "srotolate" mi ritrovo con quasi metà ram occupata! (da 093E a 9136)
Forse è meglio sacrificare qualcosa in termini di velocità (magari riducendo l'area di scroll) ed utilizzare un ciclo per evitare di consumare cosi tanta memoria?
-
Ciao AntonioMauro,
come hai già sottolineato tu nel programma usi dei cicli srotolati: considerato il fatto che ha poco senso ottimizzare la parte di impostazione che viene eseguita alla chiamata del raster interrupt (a spanne saranno al max una 50ina di cicli in tutto, stando abbondanti), direi che per il metodo utilizzato - double buffering - non credo ci siano ulteriori possibili ottimizzazioni. Meno di una coppia lda-sta da 8 cicli é proprio impossibile andare.
D'altra parte, come hai fatto giustamente notare, questa implementazione é piuttosto esosa in termini di memoria.
Tenendo dunque presente che qualsiasi compromesso si voglia utilizzare i cicli totali sono necessariamente destinati ad aumentare, ti propongo la mia modifica (trovi i commenti nel codice).
processor 6502
; Costanti
rastline = $B0;
finescrline = $FA;
screenD = $0400;
screen0 = $3800+$8000;
screen1 = $3C00+$8000;
color = $d800
; Variabili zero page
temp = $FF
joy2 = $FE
cscr = $FD
finescroll = $FC
page = $FB
org $0801
word eop
word 7102
byte $9e,"2061",0
eop word 0
org $080D;Inizio del programma
main
sei
;Imposto color MAP
ldx #$00
lpCmpC
lda colormap20X25,x
sta color,x
lda colormap20X25+$100,x
sta color+$100,x
lda colormap20X25+$100*2,x
sta color+$100*2,x
lda colormap20X25+$100*3,x
sta color+$100*3,x
inx
bne lpCmpC
lda #$36;Libero A000-BFFF dalla BASIC ROM RAM (Freshness79)
sta $01
lda #$15;Seleziono banco 2 ($8000-$BFFF 00010101)
sta $dd00
lda #$C4;Posizione di scrolling iniziale e scrolling HW nel mezzo
sta $D016
sta finescroll
lda #$E4;Attivo schermo 0 (11100100 E4)
sta $d018
sta page
lda #$00;Indicatore dello schermo corrente
sta cscr
jsr initIRQ
cli
jmp *;Loop infinito
rts
initIRQ
lda #<routineIRQ;Indirizzo codice (parte alta)
sta $0314
lda #>routineIRQ;Indirizzo codice (parte bassa)
sta $0315
lda #rastline;Raster line di attivazione IRQ raster
sta $d012
lda #$1b;Azzera il bit 7 che sarebbe il Raster Compare: (Bit 8) di $d012
sta $D011
lda #$7f;01111111 BIT 7 a 0
sta $dc0d;Azzera il bit 7 di CIA Interrupt Control Register (Read IRQs/Write Mask) per disattivarlo
sta $dd0d;Azzera il bit 7 di CIA Interrupt Control Register (Read NMls/Write Mask) per disattivarlo
lda $dc0d;Non so a che derve !?!
lda $dd0d;Non so a che derve !?!
lda #$01;Abilita IRQ raster
sta $d019;VIC Interrupt Flag Register (Bit = 1: IRQ Occurred)
sta $d01a;IRQ Mask Register: 1 = Interrupt Enabled
rts
routineIRQ
inc $d019;Dico al vic che la routine è sata eseguita
;Colora una raster line per capire quanto raster consumo per scrollare
inc $d020;riga 1
; ************
; **MODIFICA**
; ************
; Imposto il prossimo raster IRQ
lda #<secondirq
sta $0314
lda #>secondirq
sta $0315
lda #finescrline
sta $d012
; Leggo joy porta 2
lda $DC00
sta joy2;Salvo lo stato del joy2
;scrolla schermo a SX di un carattere
lda joy2;Superfluo in questo punto
and #%00001000;dx
bne lpExit;Se zero salta
;Se diverso da Zero Scroll
;X HW Scroll bit 2-0
ldx $D016
dex
cpx #$C3; - Copiare la prima parte da schermo a 3
bne lpCont1;NO
lda cscr
beq lpSCU;Screen 0
jsr scrollSsxU10
jmp lpCont1
lpSCU
jsr scrollSsxU01
lpCont1
cpx #$C1; - Copiare la seconda parte da schermo a 1
bne lpCont2;NO
lda cscr
beq lpSCD;Screen 0
jsr scrollSsxD10
jmp lpCont2
lpSCD
jsr scrollSsxD01
lpCont2
cpx #$BF;Superato il limite?
bne lpContE;NO
; ************
; **MODIFICA**
; ************
; Basta una sola chiamata allo scroller
; della memoria colore
jsr scrollCsx;Scrolla anche Color Memory
ldx #$C7;Riporta il bit di scroll a 7
lda cscr;Switch screens
beq lpSW1;Screen 0
lda #$E4;Attivo schermo 0
sta page
lda #$00;Indicatore dello schermo corrente
sta cscr
jmp lpContE
lpSW1
lda #$F4;Attivo schermo 1 (11110100 F4)
sta page
lda #$01;Indicatore dello schermo corrente
sta cscr
lpContE
stx finescroll;HW Scroll
lpExit
;Rimetto colore riga a posto
dec $d020
;Queste ultime 6 righe determinano il ritorno del codice all'interrupt HANDLER
;Se voglio creare una routine che venga eseguita mentre il basic è attivo
;sostituisco le 6 righe con JMP $ea31
iret
pla;Pull Accumulator from Stack
tay;transfer A to Y
pla;Pull Accumulator from Stack
tax;transfer A to X
pla;Pull Accumulator from Stack
;JMP $ea31
rti; return from interrupt
; ************
; **MODIFICA**
; ************
; (Da qua in poi e' tutto modificato a parte la mappa)
; IRQ di aggiornamento registri VIC
secondirq
; IRQ Acknowledge
lsr $d019
; Setto il prossimo raster interrupt
lda #<routineIRQ
sta $0314
lda #>routineIRQ
sta $0315
lda #rastline
sta $d012
; Imposto scrolling fine e pagina video:
; farlo in questo secondo interrupt handler
; garantisce di non creare garbage nello schermo
; perche' siamo sul bordo
lda finescroll
sta $d016
lda page
sta $d018
bne iret
; Questa funzione ora copia in un blocco
; unico tutti i byte necessari
scrollCsx
C01 SET 0
REPEAT 1000
lda color+C01+1
sta color+C01
C01 SET C01 + 1
REPEND
rts
; Ho modificato queste 4 funzioni:
; sono tutte e 4 parametriche, dipendono da FRAGMENT.
; FRAGMENT indica in sostanza il numero di cicli
; che deve fare ciascuna funzione.
; Con FRAGMENT alto la copia di impiega di piu' ma la funzione
; occupa meno spazio; con FRAGMENT basso vale il contrario
; i valori possibili per FRAGMENT sono $80,$40 ed $20 (per valori
; piu' bassi bisogna risistemare la bpl perche' diventa
; impossibile tornare all'inizio del ciclo con un branch)
; NB: in effetti, per semplicita', vengono copiati 512 byte
; al posto di 500 in ogni funzione. Su questo c'e' spazio per
; ottimizzare.
; Tieni pero' presente che quando si lavora con l'indirizzamento
; X indexed e' meglio allinearsi alle pagine, il page overflow
; si paga con un ciclo in piu'
FRAGMENT SET $20
scrollSsxU01
ldy #FRAGMENT-1
BASEADU01 SET 0
maincopyloopU01
REPEAT $100/FRAGMENT
lda screen0+BASEADU01+1,y
sta screen1+BASEADU01,y
lda screen0+$100+BASEADU01+1,y
sta screen1+$100+BASEADU01,y
BASEADU01 SET BASEADU01 + FRAGMENT
REPEND
dey
bpl maincopyloopU01
rts
scrollSsxD01
ldy #FRAGMENT-1
BASEADD01 SET 0
maincopyloopD01
REPEAT $100/FRAGMENT
lda screen0+$200+BASEADD01+1,y
sta screen1+$200+BASEADD01,y
lda screen0+$300+BASEADD01+1,y
sta screen1+$300+BASEADD01,y
BASEADD01 SET BASEADD01 + FRAGMENT
REPEND
dey
bpl maincopyloopD01
rts
scrollSsxU10
ldy #FRAGMENT-1
BASEADU10 SET 0
maincopyloopU10
REPEAT $100/FRAGMENT
lda screen1+BASEADU10+1,y
sta screen0+BASEADU10,y
lda screen1+$100+BASEADU10+1,y
sta screen0+$100+BASEADU10,y
BASEADU10 SET BASEADU10 + FRAGMENT
REPEND
dey
bpl maincopyloopU10
rts
scrollSsxD10
ldy #FRAGMENT-1
BASEADD10 SET 0
maincopyloopD10
REPEAT $100/FRAGMENT
lda screen1+$200+BASEADD10+1,y
sta screen0+$200+BASEADD10,y
lda screen1+$300+BASEADD10+1,y
sta screen0+$300+BASEADD10,y
BASEADD10 SET BASEADD10 + FRAGMENT
REPEND
dey
bpl maincopyloopD10
rts
colormap20X25
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
dc.b $01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$06,$07,$07,$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f,$0a,$0a,$0b,$0b,$0c,$0c,$0c,$0c,$0c,$0c
-
Interessante la parametrizzazione velocità/ram delle funzioni di copia della ram!
Mi piace molto anche la scelta di spostare ad un secondo interrupt (sul bordo inferiore) delle modifiche dei registri per evitare flickering!
Cosa accade se la routine al primo interrupt impiega troppo tempo? Lo scroll scatterà al frame successivo oppure si va in crash?
ps Mi sembra che la routine di copia della memoria video perda qualcosa nella parte bassa dello schermo....
-
Ti rispondo per punti:
- L'importante é che il primo interrupt (quello più oneroso in termini di CPU) non impieghi più di un frame intero: anche se sfora nel bordo superiore del frame successivo non cambia nulla perché tanto il display dei caratteri del nuovo frame deve ancora partire. Il secondo interrupt viene chiamato a prescindere dal primo, quindi anche nel caso che il primo sia ancora in esecuzione, il secondo risulta nidificato nel primo.
- Quello che ho fatto io é copiare il byte successivo nel byte corrente: per avere uno scroller vero e proprio dovresti avere una mappa da cui prendere i valori da assegnare all'ultima colonna del video in maniera tale da fornire dati nuovi. In questo momento, infatti, quando un byte sparisce a sinistra, riappare a destra nella riga precedente, fino a scomparire definitivamente nell'angolo in alto a sinistra.
-
Grazie della precisazione!
Per quanto riguarda la creazione e l'utilizzo di una mappa pensavo di utilizzare alcuni tools (win32) trovati su Covert BitOps ma sinceramente non ho ben capito in che formato vengono salvati i dati e come "trattarli" in 6502
-
Aggiungo la mia tecnica per ottimizzare i tempi, premettendo che non ho analizzato a fondissimo il codice dei post precedenti, per cui se è già quello che fate scusate la ripetizione.
La mia tecnica serve per distribuire il lavoro in modo che ad ogni aggiornamento ci sia più o meno la stessa mole di lavoro.
Utilizzando due buffer io divido il lavoro dello spostamento dello schermo di un intero carattere a dx o sx in 8 routines. Ad ogni vertical blanking aggiorno solo 1/8 del buffer nascosto e sposto di 1 pixel coi registri del VIC. Ogni otto volte scambio i buffer.
Alternativamente, per tenere in considerazione anche lo spostamento della color RAM, che deve per forza essere fatta tutta assieme, si divide il lavoro in 7 parti in modo che al momento dello swap dei buffers ci sia solo la copia della color RAM da fare...
-
Immagino che lo scrolling realizzato in questo modo deve per forza terminare tutto un char prima di poter invertire la direzione ed avere una velocità di 1px/frame...
Corretto?
Cosa usi per realizzare mappe,tiles,charset?
-
Ho provato per curiosità a lavorare sull'idea di senbei; trovo sia piuttosto efficiente. Posto qua sotto il codice di inizio thread rimaneggiato con la suddivisione del carico in 8 frame, come puoi notare il carico in effetti rimane sostanzialmente costante. Una volta fatta la direzione destra e' stato semplicissimo fare la sinistra.
Non ho aggiunto la copia della color RAM, ma basta mettere un paio di cicli completamente (o parzialmente) srotolati in corrispondenza dei cambi pagina.
Son comunque daccordo con senbei che, per fare un lavoro fatto bene, bisognerebbe dividere la copia della char RAM in 7 frame e lasciare l'ultimo per la color: il fatto é che dividere per 7 la copia di 1024 bytes é un po' scomodo.
Posto il codice:
processor 6502
; Costanti
rastline = $B0;
finescrline = $FA;
screenD = $0400;
screen0 = $3800+$8000;
screen1 = $3C00+$8000;
color = $d800
; Variabili zero page
temp = $FF
joy2 = $FE
cscr = $FD
finescroll = $FC
page = $FB
org $0801
word eop
word 7102
byte $9e,"2061",0
eop word 0
org $080D;Inizio del programma
main
sei
;Imposto color MAP
ldx #$00
lpCmpC
txa
sta screen0,x
sta screen0+$100,x
sta screen0+$200,x
sta screen0+$300,x
sta screen1+$001,x
sta screen1+$101,x
sta screen1+$201,x
sta screen1+$301,x
inx
bne lpCmpC
lda #$ff
sta screen1
lda #$36;Libero A000-BFFF dalla BASIC ROM RAM (Freshness79)
sta $01
lda #$15;Seleziono banco 2 ($8000-$BFFF 00010101)
sta $dd00
lda #$C7;Posizione di scrolling iniziale e scrolling HW nel punto piu' a destra
sta $D016
sta finescroll
lda #$E4;Attivo schermo 0 (11100100 E4)
sta $d018
sta page
lda #$00;Indicatore dello schermo corrente
sta cscr
jsr initIRQ
cli
jmp *;Loop infinito
rts
initIRQ
lda #<routineIRQ;Indirizzo codice (parte alta)
sta $0314
lda #>routineIRQ;Indirizzo codice (parte bassa)
sta $0315
lda #rastline;Raster line di attivazione IRQ raster
sta $d012
lda #$1b;Azzera il bit 7 che sarebbe il Raster Compare: (Bit 8) di $d012
sta $D011
lda #$7f;01111111 BIT 7 a 0
sta $dc0d;Azzera il bit 7 di CIA Interrupt Control Register (Read IRQs/Write Mask) per disattivarlo
sta $dd0d;Azzera il bit 7 di CIA Interrupt Control Register (Read NMls/Write Mask) per disattivarlo
lda $dc0d;Non so a che derve !?!
lda $dd0d;Non so a che derve !?!
lda #$01;Abilita IRQ raster
sta $d019;VIC Interrupt Flag Register (Bit = 1: IRQ Occurred)
sta $d01a;IRQ Mask Register: 1 = Interrupt Enabled
rts
routineIRQ
inc $d019;Dico al vic che la routine è sata eseguita
;Colora una raster line per capire quanto raster consumo per scrollare
inc $d020;riga 1
; ************
; **MODIFICA**
; ************
; Imposto il prossimo raster IRQ
lda #<secondirq
sta $0314
lda #>secondirq
sta $0315
lda #finescrline
sta $d012
; Leggo joy porta 2
lda $DC00
eor #%00001100; si lavora meglio in logica positiva.
and #%00001100; Seleziono le sole direzioni sx e dx
cmp #%00000100; spostamento a sinistra?
bne nextchk ; no
jmp sx ; si, sposta a sinistra
nextchk
cmp #%00001000; spostamento a destra?
beq dx ; si, sposta a destra
jmp lpExit ; nessun spostamento
dx
ldx $d016 ; leggo il finetune corrente
txa ; lo salvo in X
and #$07 ; quindi lo maschero per recuperare i 3 bit utili
tay ; e lo salvo in Y, questa e' la posizione corrente.
dex ; Aggiorno X per ottenere il nuovo valore
txa ; di scrolling fine
and #$07 ; dopo averlo opportunamente mascherato
ora #$C0 ; e "or-ato" lo salvo
sta finescroll; nella variabile che verra' poi usata nel secondo interrupt
lda cscr ; Che frame buffer
bne dx10 ; stiamo visualizzando?
dx01 ; il 1° quindi devo lavorare sul 2°
tya ; uso il valore in Y precedentemente salvato per
lsr ; calcolare l'high-byte dell'indirizzo dei due
ora #(screen0/$100); frame buffer. Ogni 2 pixel (lsr) salto da una pagina
sta dxfrom+2 ; a quella successiva. In 8 pixel passo progressivamente
eor #(screen0/$100); da $000, $100, $200 e $300 ("or-ati" alle rispettive
ora #(screen1/$100); basi dei due framebuffer).
sta dxto+2 ; Importante: il carry viene mantenuto per una successiva
bne dxcont ; elaborazione
dx10 ; il 2° quindi devo lavorare sul 1°
tya ; Stessa storia dell'altra opzione
lsr ; solo con i buffer invertiti
ora #(screen1/$100)
sta dxfrom+2
eor #(screen1/$100)
ora #(screen0/$100)
sta dxto+2
dxcont
ror ; uso il carry per avere il valore $00 o $80
and #$80 ; in questo modo ho la totale distribuzione del carico
sta dxto+1 ; negli 8 spostamenti: $000,$080,$100,$180,$200,$280,$300 e $380
clc ; la sorgente (FROM) e' un carattere piu' a destra
adc #$01 ; rispetto alla destinazione per avere l'effettivo
sta dxfrom+1; spostamento.
ldx #$7f ; copio i dati con un ciclo (qua si potrebbe srotolare tutto e ottimizzare)
dxfrom
lda $ffff,x ; Indirizzo calcolato nei passaggi precedenti
dxto
sta $ffff,x ; IDEM
dex
bpl dxfrom
cpy #$00 ; Abbiamo saltato un intero carattere?
bne dxExit ; No, possiamo uscire
lda cscr ; si, aggiorniamo l'unico carattere che i vari cicli non hanno
bne dxlastcopy ; potuto sistemare ovvero l'ultimo in basso a destra
lda screen0 ; (che in realta' non si vede perche' lo schermo finisce a $3E8
sta screen1+$3FF; ma dopo un po' a forza di scrollare torna visibile)
bne dxexchange ; Ovviamente a modifica si fa in base al frame buffer attivo
dxlastcopy
lda screen1
sta screen0+$3FF
dxexchange
lda cscr ; Cambio di framebuffer
eor #$01
sta cscr
lda page ; Cambio di pagina video
eor #$10
sta page
dxExit
jmp lpExit
; Gestione movimento a sinistra
; Sono indicate solamente le modifiche
; rispetto alla routine di destra
; il resto e' identico
sx
ldx $d016
txa
and #$07
tay
inx; ******* D016 viene incrementato
txa
and #$07
ora #$C0
sta finescroll
lda cscr
bne sx10
sx01
tya
lsr
ora #(screen0/$100)
sta sxfrom+2
eor #(screen0/$100)
ora #(screen1/$100)
sta sxto+2
bne sxcont
sx10
tya
lsr
ora #(screen1/$100)
sta sxfrom+2
eor #(screen1/$100)
ora #(screen0/$100)
sta sxto+2
sxcont
ror
and #$80
sta sxfrom+1; il FROM (sorgente) e' un carattere a sinistra
clc
adc #$01
sta sxto+1 ; rispetto al TO (destinazione)
ldx #$7f
sxfrom
lda $ffff,x
sxto
sta $ffff,x
dex
bpl sxfrom
cpy #$07 ; Se qua Y=7 significa che $D016=$C0 quindi bisogna cambiare page
bne lpExit
lda cscr
bne sxlastcopy
lda screen0+$3FF; copio l'ultimo byte del primo buffer
sta screen1 ; nel primo byte del secondo buffer
bne sxexchange
sxlastcopy
lda screen1+$3FF; copio l'ultimo byte del secondo buffer
sta screen0 ; nel primo byte del primo buffer
sxexchange
lda cscr
eor #$01
sta cscr
lda page
eor #$10
sta page
lpExit
dec $d020
iret
pla;Pull Accumulator from Stack
tay;transfer A to Y
pla;Pull Accumulator from Stack
tax;transfer A to X
pla;Pull Accumulator from Stack
rti; return from interrupt
; ************
; **MODIFICA**
; ************
; (Da qua in poi e' tutto modificato a parte la mappa)
; IRQ di aggiornamento registri VIC
secondirq
; IRQ Acknowledge
lsr $d019
; Setto il prossimo raster interrupt
lda #<routineIRQ
sta $0314
lda #>routineIRQ
sta $0315
lda #rastline
sta $d012
; Imposto scrolling fine e pagina video:
; farlo in questo secondo interrupt handler
; garantisce di non creare garbage nello schermo
; perche' siamo sul bordo
lda finescroll
sta $d016
lda page
sta $d018
bne iret
-
Ipotizzando l'utilizzo di tale tecnica con una mappa sarebbe necessario completare la sequenza di 7/8 scroll hw prima di poter invertire la direzione altrimenti ti troveresti con una parte di schermo contenente i dati scrollati a dx ed un'altra parte scrollati a sx.
E' corretto oppure mi sono perso qualcosa?
-
L'inversione di direzione può avvenire in qualsiasi momento perché quando si arriva al cambio carattere lo schermo é sempre pronto.
La cosa funziona in questo modo: se ad esempio sono al 2° pixel ho 2 blocchi da 128 pronti per lo spostamento a sinistra e 6 blocchi da 128 pronti per quello a sinistra, spostandosi a destra o a sinistra aumentano in una o nell'altra direzione.
L'aggiornamento prendendo i valori da una mappa può avvenire usando sempre la colonna di caratteri che rimane normalmente coperto dal restringimento dell'area dello schermo.
In allegato la versione comprensiva dello spostamento della color map; non dovrebbe essere troppo difficile inserire il codice per gestire una mappa.
Appena ho un attimo cerco di buttar giù qualcosa.
-
Me lo studio x benino!!
Scusa l'ignoranza ma ti posso chiedere la funzione di:
align $100
prima dei dati della mappa di colore?
Ho letto che la direttiva ALIGN serve ad allineare il PC a N byte ma mi sfugge il perchè l'hai utilizzata....
-
In realtà quell'align non é strettamente necessario; in linea di massima lo metto sempre quando ho tabelle di grosse dimensioni perché quando si va ad utilizzare l'indirizzamento X-indexed si ha un ciclo di penalty per il page crossing.
Mi spiego meglio.
Supponi di avere una tabella di 256 valori che parte da $4380 e finisce a $447F, se ho bisogno di leggere utilizzando LDA $4380,X e scorrendo i vari valori, di fatto mi ritrovo che per X<$7F mi costa 4 cicli, per X>=$80 me ne costa 5 perché c'é il cambio di pagina (da $4300 a $4400). Supponendo di fare un ciclo che legge tutti i 256 valori ho un sovraccarico di 128 cicli che potevo facilmente evitare allineando la tabella.
Nel caso specifico i valori vengono copiati in realtà una volta sola quindi l'align non serve a molto, resta il fatto che facendo così l'intera tabella occupa 4 pagine invece di 5 e la copia (ok, l'unica copia) risulta in ogni caso più rapida.
LDA $aaaa,x e STA $aaaa,x danno il loro meglio quando lavorano in una pagina, quando cioé sono del tipo LDA $aa00,x e STA $aa00,x.
-
Conoscevo il dazio da pagare in caso di cross-paging ma non mi è ancora chiaro come hai calcolato il valore $100....
E' come dire al compilatore di spostare il PC $100 byte più avanti rispetto alla posizione corrente prima di memorizzare la tabella?
-
Scrivendo align $100 si indica al compilatore che il codice a seguire deve avere un indirizzo multiplo di $100 (ad es $0800,$0c00,$1200 etc).
Se, per caso fortuito, in quel punto l'indirizzo é già allineato a $100 il compilatore non farà assolutamente nulla. Nel caso più frequente nel quale l'ultimo indirizzo non sia invece allineato il compilatore inserirà un certo numero di 0 finché non raggiungerà l'allineamento richiesto.
Ad esempio supponi di avere questo programma
org $1000
lda #$00
sta $d020
rts
align $08
lda #$01
sta $d020
rts
il compilatore genererà il seguente codice:
$1000 $A9 $00
$1002 $8d $20 $d0
$1005 $60
$1006 $00 -> Aggiunto dal compilatore per allineare
$1007 $00 -> Aggiunto dal compilatore per allineare
$1008 $A9 $01 -> Codice allineato a 8 ($1008)
$100a $8d $20 $d0
$100d $60
-
Ecco una versione con una semplice mappa costituita da 256 colonne da 32 byte ciascuna (dei quali solo 25 usati).
Non so se esistano tool che permettano di lavorare su una mappa di questo genere... solo ho trovato fosse la più semplice da implementare nello scroller.
Il codice in più parti non é probabilmente ottimizzato ma fa comunque il suo mestiere senza skippare alcun frame.
Andrebbe aggiunto l'aggiornamento della mappa colore (eventualmente usando in qualche modo i 7 byte rimanenti in ciascun blocco della mappa) e bisognerebbe inizializzare correttamente i due framebuffer: per il momento per poter visualizzare la mappa bisogna iniziare a muoversi.
-
Grazie della preziosa spiegazione!
Sono veramente colpito dalla soluzione adottata per calcolare dinamicamente (codice self-mod) l'indirizzo di origine e destinazione nella split-copy del frame buffer!
Spero di riuscire ad arrivare anch'io questi livelli qualitativi!
-
Per la spiegazione nessun problema, sei anzi altri dubbi sulla parte di codice che ho modificato, chiedi pure.
Per quanto riguarda certe routine...mah... non credo ci voglia poi molto ad avere un po' di dimestichezza con le istruzioni ed i metodi di indirizzamento del 6510 e, comunque, una volta acquisita vedrai che é più semplice di quanto tu possa pensare trovare nuove vie per risolvere i problemi. Alla fine é proprio questo il divertimento.
Detto questo... beh... compila lo zip, dovresti riconoscere la mappa che ho caricato ;).
-
Fantastico!
Tu non ci crederai ma era proprio il risultato a cui volevo arrivare....
A dir la verità un po mi dispiace avere già sotto mano il risultato finale (ci sarei voluto arrivare con le mie forze) ma sicuramente sarà un ottimo materiale di studio!
Concordo con te che il bello di questo tipo di programmazione è proprio riuscire a trovare dei modi sempre più efficienti per risolvere dei problemi
Attualmente ancora non ho capito bene la logica che sta dietro a
tya ; uso il valore in Y precedentemente salvato per
lsr ; calcolare l'high-byte di sorgente e destinazione
ora ixbuf01,x; con una OR ottengo la sorgente
sta dxfrom+2 ;
and #$03 ; rimaschero il valore per annullare la precedente OR
ora ixbuf10,x; con questa OR ottengo la destinazione
sta dxto+2 ;
lda #$00 ; uso il carry generato dalla precedente LSR per avere il valore $00 o $80
ror ; in questo modo ho la totale distribuzione del carico
sta dxto+1 ; negli 8 spostamenti: $000,$080,$100,$180,$200,$280,$300 e $380
adc #$01 ; la sorgente (FROM) e' un carattere piu' a destra rispetto alla destinazione per avere l'effettivo
sta dxfrom+1; spostamento.
Pensavo che fosse necessario moltiplicare il valore di finescroll presente in Y per $80 per avere l'indirizzo del framebuffer da aggiornare ma non mi pare che funzioni cosi......
-
hmmm.. cercherò di rovinarti meno il gioco nei miei prossimi interventi, magari dandoti suggerimenti piuttosto che postare codice.
Scusami ma spesso mi é più congeniale rispondere con del codice piuttosto che a parole. ;)
Comunque c'é ancora un sacco di roba implementabile e migliorabile, hai ancora tantissimo spazio d'azione!
Detto questo, il codice.
Dunque siamo daccordo che il risultato che cerchiamo é dato dalla formula:
Indirizzo = finetune * $80
In questo modo a seconda del valore di finetune indirizzo punterà a $0000,$0080,$0100... ...$0380.
Ora sarai daccordo che possiamo riformulare l'operazione in questo modo:
Indirizzo = (finetune * $100) /2
Perché scriverla in questo modo? Perché finetune*$100 non é un'operazione, é solamente considerare finetune come hibyte del risultato invece di lowbyte. Con una sola LSR, quindi, otteniamo l'hibyte del risultato e nel carry finisce quello che rappresenta il bit più significativo del lobyte del risultato.
Concretamente:
1 - TYA
mette nell'accumulatore finetune
2 - LSR
divide per 2 l'accumulatore e mette nel carry il bit che servirà al lobyte
A questo punto abbiamo già l'hibyte del risultato giusto
Quindi:
3 - ORA ixbuf01,x
serve ad aggiungere l'hibyte appena calcolato a quello di base del buffer prescelto (x vale 0 oppure 1 a seconda del framebuffer selezionato e ixbuf01 punta ad una coppia di valori che rappresentano il primo l'hibyte base del framebuffer 0 ed il secondo l'hibyte base del framebuffer 1).
4 - STA dxfrom+2
a questo punto ho il primo hibyte completamente calcolato e lo posso salvare nell'indirizzo puntato dall'istruzione dxfrom. Le istruzioni che usano indirizzamento assoluto (o assoluto indicizzato) si compongono di opcode, lobyte e hibyte dell'indirizzo puntato, esattamente in quest'ordine.
Salvando quindi il valore calcolato all'indirizzo dxfrom+2, di fatto imposto l'hibyte dell'indirizzo puntato dall'istruzione in dxfrom.
5 - AND #$03
serve per riottenere il vecchio risultato privo dell'ultima OR che ho fatto. In pratica nell'accumulatore ora ho lo stesso valore che avevo prima del punto 3
6 - ORA ixbuf10,x
con questa OR, in modo simile al punto 3, ottengo l'hibyte della destinazione. ixbuf10 punta a due valori che sono gli stessi di ixbuf01, solo invertiti
7 - STA dxto+2
salvo il risultato nell'hibyte dell'indirizzo puntato dall'istruzione alla locazione dxto
8 - LDA #$00
A questo punto ho completamente sistemato l'hibyte delle due istruzioni di copia, mi interessa mettere a posto il lobyte. Annullo l'accumulatore. Ricorda che c'é un informazione che mi porto dietro dal punto 2, il valore del Carry che rappresenta il bit meno significativo di finetune
9 - ROR
con questa semplice rotazione a destra faccio entrare il valore del carry nel bit più significativo dell'accumulatore: siccome l'accumulatore é nullo a causa dell'istruzione 8 con questa ROR potrà acquisire valore $00 oppure $80, a seconda del carry.
10 - STA dxto+1
lo salvo nel lobyte dell'indirizzo puntato dall'istruzione in dxto (in pratica aggiorno il lobyte della destinazione)
11 - ADC #$01
aggiungo 1 per la sorgente perché sto spostando lo schermo verso sinistra, non devo preoccuparmi del carry perché dopo la ROR dell'istruzione 9 é sicuramente 0
12 - STA dxfrom+1
salvo quest'ultimo valore come lobyte dell'indirizzo puntato dall'istruzione in dxfrom (aggiorno il lobyte della sorgente)
Ok, spero di essere stato comprensibile ed esauriente!
...A quando un paio di righe in alto sullo schermo con l'indicazione di vite, punti ecc? ;) E di sprite non ne mettiamo?
-
Innanzi tutto grazie!!!!
Non preoccuparmi per il mio divertimento, sicuramente il codice che posti vale più di mille spiegazione!
Per quanto riguarda la delucidazione sul codice devo dire che ora mi è tutto chiaro anche se rimango ancora molto impressionato dalla genialità ed allo stesso tempo dalla semplicità della soluzione adottata...
Prima di aggiungere sprite et simila voglio studiarmelo ancora un po!
-
Ho implementato il popolamento iniziale dei due framebuffer (manca ancora la color map) ma c'è un problemino che non riesco a risolvere:
Se all'avvio del programma faccio scorrere verso sinistra ho uno scatto di un carattere....
; Ciclo di inizializzazione della colormap e dei due framebuffer
ldy #$0
ldx #$0
lpCmpC
lda mappointer,x;carico valore
lpSMC
sta screen0 ;Aggiorna schermo
sta screen1 ;Aggiorna schermo
;lda #$01 ;carico valore
;lpSMCcolor
;sta color+990 ;aggiorna color map
clc ;Questo mi cambia dinamicamente il valore di screen0 e screen1 modificando il codice al volo
lda lpSMC+1 ;SCREEN0 h
adc #$28
sta lpSMC+1
clc
lda lpSMC+4 ;SCREEN1 h
adc #$28
sta lpSMC+4
bcc lpSMC1 ;Se non è modificato il carry continua il ciclo
inc lpSMC+2 ;SCREEN0 l
inc lpSMC+5 ;SCREEN1 l
lpSMC1
inx
cpx #$19 ;Sei arrivato al carattere 25
bne lpCmpC ;no allora ricomincia
;si allora mi sposto alla colonna successiva
ldx #$0 ;azzero X
iny ;incrementa Y
sty lpSMC+1 ;Ricarico il valore screen0 incrementandolo di 1
lda #$B8 ;B8 è la prima parte di $3800 + $8000
sta lpSMC+2
sty lpSMC+4 ;Ricarico il valore screen1 incrementandolo di 1
lda #$BC ;BC è la prima parte di $3C00 + $8000
sta lpSMC+5
clc ;Sposto anche mappointer in avanti di 32
lda lpCmpC+1
adc #$20
sta lpCmpC+1
bcc lpSMC2 ;Se non è modificato il carry continua il ciclo
inc lpCmpC+2
lpSMC2
cpy #$28
bne lpCmpC
-
Ciao Antonio,
mi fa piacere vedere che ancora ci lavori, la costanza va premiata.
Ho dato un'occhiata al tuo codice e l'ho trovato sostanzialmente corretto; l'ho sistemato un attimo solo per rendere più agibile la fase di test.
Non mi sembra comunque che possa generare dei "buchi".
Potresti mettere essere più preciso sul tipo di problema che hai avuto? Potresti anche postare il codice completo dello scroller, comprensivo del filler che hai realizzato?
; ***********************************************
; Inizializzazione variabili solo a scopo di test
;
*=$1000
screen0 equ $04
screen1 equ $bc
ptr0h equ ptr0+2
ptr0l equ ptr0+1
ptr1h equ ptr1+2
ptr1l equ ptr1+1
mappointer equ $8000
;
; ***********************************************
; Ciclo di inizializzazione della colormap e dei due framebuffer
ldy #$00
ldx #$00
stx ptr0l
stx ptr1l
lda #screen0
sta ptr0h
lda #screen1
sta ptr1h
lpCmpC
lda mappointer,x;carico valore
ptr0
sta $ffff ;Aggiorna schermo
ptr1
sta $ffff ;Aggiorna schermo
;lda #$01;carico valore
;lpSMCcolor
;sta color+990;aggiorna color map
clc ;Questo mi cambia dinamicamente il valore di screen0 e screen1 modificando il codice al volo
lda ptr0l ;SCREEN0 h
adc #$28
sta ptr0l
sta ptr1l
bcc lpSMC1 ;Se non è modificato il carry continua il ciclo
inc ptr0h ;SCREEN0 l
inc ptr1h ;SCREEN1 l
lpSMC1
inx
cpx #$19 ;Sei arrivato al carattere 25
bne lpCmpC ;no allora ricomincia
ldx #$00 ;si allora mi sposto alla colonna successiva azzero X
iny ;incrementa Y
sty ptr0l ;Ricarico il valore screen0 incrementandolo di 1
lda #screen0 ;B8 è la prima parte di $3800 + $8000
sta ptr0h
sty ptr1l ;Ricarico il valore screen1 incrementandolo di 1
lda #screen1 ;BC è la prima parte di $3C00 + $8000
sta ptr1h
clc ;Sposto anche mappointer in avanti di 32
lda lpCmpC+1
adc #$20
sta lpCmpC+1
bcc lpSMC2 ;Se non è modificato il carry continua il ciclo
inc lpCmpC+2
lpSMC2
cpy #$28
bne lpCmpC
rts
-
Una domanda...
Perché sta $ffff è ripetuto due volte all'inizio del sorgente?
-
Una domanda...
Perché sta $ffff è ripetuto due volte all'inizio del sorgente?
Perché quel codice viene modificato in un altro punto del programma, credo.
-
E' corretta la deduzione di Carmine. In linea di massima metto $ffff quando c'é del codice self-modifying, mi scuso per non averlo indicato.
-
Confermo che le 2 sta vengono alterate nelle istruzioni successive allo scopo di copiare i valori della mappa (memorizzati in maniera orizzontale) sulla memoria video come colonne verticali.
Ecco il codice completo dello scroller (in effetti avevo postato solo la routine di popolamento iniziale).
Per i files contenenti la grafica puoi scaricare l'archivio postato (da te) tempo fa.
Il problema è evidente se provi a scorrere verso sinistra subito dopo l'avvio del programma....lo sfondo scatta subito di un carattere verso destra.
processor 6502
; Costanti
rastline = $30+((25*8)/2)-8; La rasterline iniziale é grosso modo a centro schermo
finescrline = $FA ; quella finale é nel bordo inferiore
screen0 = $3800+$8000; Framebuffer 0
screen1 = $3C00+$8000;; Framebuffer 1
color = $d800 ; Memoria colore
mappointer = $4000 ; Mappa (256 "colonne" da 32 byte di cui 25 usati)
mapcolor = $4000+$2000; Mappa colore (256 "colonne" da 32 byte di cui 25 usati)
; Variabili zero page
; Attenzione! Questo blocco porta via 200 valori di zero page, precisamente
; da $20 a $E7; salvo variare il range non si possono usare queste locazioni
; di memoria per altri scopi
sxupc0 = $20 ; 50 valori che puntano alla colonna 0 del FB 0
sxupc1 = sxupc0+$32 ; 50 valori che puntano alla colonna 0 del FB 1
dxupc0 = sxupc1+$32 ; 50 valori che puntano alla colonna 39 del FB 0
dxupc1 = dxupc0+$32 ; 50 valori che puntano alla colonna 39 del FB 1
posx = $F9 ; Posizione nella mappa
pmap = $FA ; puntatore usato in più contesti
page = $FC ; pagina da assegnare a $D018
finescroll = $FD; scrolling fine da assegnare a $D016
cscr = $FE ; Framebuffer (avrà valore 0/1)
; Codice
org $0801
; *********
; Sys Basic
; *********
word eop
word 7102
byte $9e,"2061",0
eop word 0
org $080D; Inizio del programma
; ********************
; Impostazioni di base
; ********************
main
sei
; Ciclo di inizializzazione della colormap e dei due framebuffer
ldy #$0
ldx #$0
lpCmpC
lda mappointer,x;carico valore
lpSMC
sta screen0 ;Aggiorna schermo
sta screen1 ;Aggiorna schermo
;lda #$01 ;carico valore
;lpSMCcolor
;sta color+990 ;aggiorna color map
clc ;Questo mi cambia dinamicamente il valore di screen0 e screen1 modificando il codice al volo
lda lpSMC+1 ;SCREEN0 h
adc #$28
sta lpSMC+1
clc
lda lpSMC+4 ;SCREEN1 h
adc #$28
sta lpSMC+4
bcc lpSMC1 ;Se non è modificato il carry continua il ciclo
inc lpSMC+2 ;SCREEN0 l
inc lpSMC+5 ;SCREEN1 l
lpSMC1
inx
cpx #$19 ;Sei arrivato al carattere 25
bne lpCmpC ;no allora ricomincia
;si allora mi sposto alla colonna successiva
ldx #$0 ;azzero X
iny ;incrementa Y
sty lpSMC+1 ;Ricarico il valore screen0 incrementandolo di 1
lda #$B8 ;B8 è la prima parte di $3800 + $8000
sta lpSMC+2
sty lpSMC+4 ;Ricarico il valore screen1 incrementandolo di 1
lda #$BC ;BC è la prima parte di $3C00 + $8000
sta lpSMC+5
clc ;Sposto anche mappointer in avanti di 32
lda lpCmpC+1
adc #$20
sta lpCmpC+1
bcc lpSMC2 ;Se non è modificato il carry continua il ciclo
inc lpCmpC+2
lpSMC2
cpy #$28
bne lpCmpC
; Ciclo di inizializzazione dei pointer precalcolati per l'aggiornamento della prima ed ultima colonna
ldx #$31
copyupcpointers
lda sxupdatecol0,x
sta sxupc0,x
lda sxupdatecol1,x
sta sxupc1,x
lda dxupdatecol0,x
sta dxupc0,x
lda dxupdatecol1,x
sta dxupc1,x
dex
bpl copyupcpointers
lda #$36;Libero A000-BFFF dalla BASIC ROM RAM (Freshness79)
sta $01
lda #$15;Seleziono banco 2 ($8000-$BFFF 00010101)
sta $dd00
lda #$D7;Posizione di scrolling iniziale e scrolling HW nel punto piu' a destra
sta $D016
sta finescroll
lda #$E0;Attivo schermo 0 (11100000 E0)
sta $d018
sta page
lda #$00;Indicatore dello schermo corrente
sta cscr
lda #$00
sta $d020
sta $d021
lda #$0c
sta $d022
lda #$0b
sta $d023
jsr initIRQ
cli
jmp *;Loop infinito
initIRQ
lda #<routineIRQ; Indirizzo codice (parte alta)
sta $0314
lda #>routineIRQ; Indirizzo codice (parte bassa)
sta $0315
lda #rastline; Raster line di attivazione IRQ raster
sta $d012
lda #$1b; Azzera il bit 7 che sarebbe il Raster Compare: (Bit 8) di $d012
sta $D011
lda #$7f; 01111111 BIT 7 a 0
sta $dc0d; Azzera il bit 7 di CIA Interrupt Control Register (Read IRQs/Write Mask) per disattivarlo
sta $dd0d; Azzera il bit 7 di CIA Interrupt Control Register (Read NMls/Write Mask) per disattivarlo
lda $dc0d; Acknowledge di un eventuale IRQ generato dal CIA1 - assicuro che l'unica fonte di interruzione
lda $dd0d; Acknowledge di un eventuale NMI generato dal CIA2 - sia il VIC
lda #$01; Abilita IRQ raster
sta $d019; VIC Interrupt Flag Register (Bit = 1: IRQ Occurred)
sta $d01a; IRQ Mask Register: 1 = Interrupt Enabled
rts
routineIRQ
inc $d019; Dico al vic che la routine è stata eseguita
inc $d020; Colora una raster line per capire quanto raster consumo per scrollare
lda #<secondirq; Imposto il prossimo raster IRQ
sta $0314
lda #>secondirq
sta $0315
lda #finescrline
sta $d012
ldx $d016 ; leggo il finetune corrente
txa ; lo salvo in X
and #$07 ; quindi lo maschero per recuperare i 3 bit utili
tay ; e lo salvo in Y, questa e' la posizione corrente.
lda $DC00 ; Leggo il valore del joystick
eor #%00001100; si lavora meglio in logica positiva.
and #%00001100; Seleziono le sole direzioni sx e dx
cmp #%00000100; spostamento a sinistra?
beq jsx ; si, sposta a sinistra
cmp #%00001000; spostamento a destra?
beq dx ; si, sposta a destra
jmp lpExit ; nessun spostamento
jsx
jmp sx
dx
dex ; Aggiorno X per ottenere il nuovo valore
txa ; di scrolling fine
and #$07 ; dopo averlo opportunamente mascherato
ora #$D0 ; e "or-ato" lo salvo
sta finescroll; nella variabile che verra' poi usata nel secondo interrupt
ldx cscr ; Carico il frame (0 o 1)
tya ; uso il valore in Y precedentemente salvato per
lsr ; calcolare l'high-byte di sorgente e destinazione
ora ixbuf01,x; con una OR ottengo la sorgente
sta dxfrom+2;
and #$03; rimaschero il valore per annullare la precedente OR
ora ixbuf10,x; con questa OR ottengo la destinazione
sta dxto+2 ;
lda #$00; uso il carry generato dalla precedente LSR per avere il valore $00 o $80
ror ; in questo modo ho la totale distribuzione del carico
sta dxto+1 ; negli 8 spostamenti: $000,$080,$100,$180,$200,$280,$300 e $380
adc #$01 ; la sorgente (FROM) e' un carattere piu' a destra rispetto alla destinazione per avere l'effettivo
sta dxfrom+1; spostamento.
ldx #$7f ; copio i dati con un ciclo (qua si potrebbe srotolare tutto ed ottimizzare)
dxfrom
lda $ffff,x; Indirizzo calcolato nei passaggi precedenti
dxto
sta $ffff,x; IDEM
dex
bpl dxfrom
cpy #$00 ; Abbiamo saltato un intero carattere?
bne dxExit ; No, possiamo uscire
lda cscr ; Cambio frame
eor #$01
sta cscr
tax ; Lo metto in X per usarlo successivamente
lda page ; Cambio pagina video
eor #$10
sta page
inc posx; incremento la posizione nella mappa
lda #$00; calcolo l'indirizzo base nella mappa
sta pmap;
clc ;
lda posx;
adc #$27; Aggiungo 39 alla posizione orizzontale perché stiamo aggiornando l'ultima colonna
lsr ; La rudimentale mappa che ho implementato si compone di blocchi
ror pmap; di 32 byte che vanno da $4000 ad $8fff (256 blocchi). Ciascun
lsr ; blocco corrisponde ad una colonna verticale nello schermo perciò
ror pmap; dei 32 byte disponibili ne uso solamente 25 (i 7 rimanenti potrebbero
lsr ; essere usati ad esempio per aggiornare la mappa colore con 1 colore
ror pmap; ogni 4 caratteri - 7x4=28)
ora #(mappointer/$100)
sta pmap+1
lda dxupdbuf,x; Carico l'indirizzo della tabella di valori precalcolati usata
tax ; per salvare i dati nello schermo
ldy #$00
dxcpcolumn ;
lda (pmap),y; y scorre lungo i 25 byte del blocco selezionato
sta ($00),x; x scorre nella tabella di 50 valori (25 indirizzi lo/hi)
inx ; corrispondente alle posizioni dello schermo
inx ; X=X+2 perché ogni indirizzo porta via due byte
iny ; Y=Y+1
cpy #$19; abbiamo copiate tutte le 25 righe?
bne dxcpcolumn
jsr scrollCdx; Aggiornamento della mappa colore (verso destra)
lda cscr
bne dxfix1
jsr fixcoldx0
jmp lpExit
dxfix1
jsr fixcoldx1
dxExit
jmp lpExit
; Gestione movimento a sinistra
; Sono indicate solamente le modifiche
; rispetto alla routine di destra
; il resto e' identico
sx
inx ; *Incremento
txa
and #$07
ora #$D0
sta finescroll
ldx cscr
tya
lsr
ora ixbuf01,x
sta sxfrom+2
and #$03
ora ixbuf10,x
sta sxto+2
lda #$00
ror
sta sxfrom+1; sorgente e
adc #$01
sta sxto+1; destinatario invertiti
ldx #$7f
sxfrom
lda $ffff,x
sxto
sta $ffff,x
dex
bpl sxfrom
cpy #$07; Valore limite diverso (7)
bne lpExit
lda cscr
eor #$01
sta cscr
tax
lda page
eor #$10
sta page
dec posx; Decremento della posizione
lda #$00
sta pmap
lda posx; non serve addizionare $27
lsr
ror pmap
lsr
ror pmap
lsr
ror pmap
ora #(mappointer/$100)
sta pmap+1
lda sxupdbuf,x; uso il buffer per la direzione sinistra
tax
ldy #$00
sxcpcolumn
lda (pmap),y
sta ($00),x
inx
inx
iny
cpy #$19
bne sxcpcolumn
jsr scrollCsx; spostamento verso sinistra della mappa colore
lda cscr
bne fixsx1
jsr fixcolsx0
jmp lpExit
fixsx1
jsr fixcolsx1
lpExit
dec $d020 ; Fine barra consumo CPU
iret
pla;Pull Accumulator from Stack
tay;transfer A to Y
pla;Pull Accumulator from Stack
tax;transfer A to X
pla;Pull Accumulator from Stack
rti; return from interrupt
secondirq
lsr $d019 ; IRQ Acknowledge
lda #<routineIRQ; Setto la routine principale
sta $0314 ; come prossimo handler del raster interrupt
lda #>routineIRQ
sta $0315
lda #rastline; Fisso la linea alla quale avverrà l'interruzione
sta $d012
lda finescroll; Imposto il finescroll fuori dalla zona video
sta $d016
lda page ; switch della pagina video (pure questo fuori dalla zona video)
sta $d018
bne iret ; sempre diverso da 0 qua, equivalente a jmp iret
; *****************************************
; Routine srotolate per l'update del colore
; *****************************************
scrollCdx
C01 SET 0
ldx color
REPEAT 1023
lda color+C01+1
sta color+C01
C01 SET C01 + 1
REPEND
stx color+$3ff
rts
scrollCsx
ldx color+$200
ldy color+$3ff
C01 SET 511
REPEAT 512
lda color+C01
sta color+C01+1
C01 SET C01 -1
REPEND
C01 SET 510
REPEAT 511
lda color+$200+C01
sta color+$200+C01+1
C01 SET C01 -1
REPEND
sty color
stx color+$201
rts
fixcolsx0
ROW SET 0
REPEAT 25
ldx screen0+ROW
lda $6000,x
sta color+ROW
ROW SET ROW +40
REPEND
rts
fixcolsx1
ROW SET 0
REPEAT 25
ldx screen1+ROW
lda $6000,x
sta color+ROW
ROW SET ROW +40
REPEND
rts
fixcoldx0
ROW SET 0
REPEAT 25
ldx screen0+39+ROW
lda $6000,x
sta color+39+ROW
ROW SET ROW +40
REPEND
rts
fixcoldx1
ROW SET 0
REPEAT 25
ldx screen1+39+ROW
lda $6000,x
sta color+39+ROW
ROW SET ROW +40
REPEND
rts
; *******************************************
; Puntatori e tabelle con valori precalcolati
; *******************************************
align 16
ixbuf01
.BYTE screen0/$100,screen1/$100
ixbuf10
.BYTE screen1/$100,screen0/$100
sxupdbuf
.BYTE sxupc0,sxupc1
dxupdbuf
.BYTE dxupc0,dxupc1
dxupdatecol0
.BYTE $27,$00+screen0/$100,$4F,$00+screen0/$100,$77,$00+screen0/$100
.BYTE $9F,$00+screen0/$100,$C7,$00+screen0/$100,$EF,$00+screen0/$100
.BYTE $17,$01+screen0/$100,$3F,$01+screen0/$100,$67,$01+screen0/$100
.BYTE $8F,$01+screen0/$100,$B7,$01+screen0/$100,$DF,$01+screen0/$100
.BYTE $07,$02+screen0/$100,$2F,$02+screen0/$100,$57,$02+screen0/$100
.BYTE $7F,$02+screen0/$100,$A7,$02+screen0/$100,$CF,$02+screen0/$100
.BYTE $F7,$02+screen0/$100,$1F,$03+screen0/$100,$47,$03+screen0/$100
.BYTE $6F,$03+screen0/$100,$97,$03+screen0/$100,$BF,$03+screen0/$100
.BYTE $E7,$03+screen0/$100
dxupdatecol1
.BYTE $27,$00+screen1/$100,$4F,$00+screen1/$100,$77,$00+screen1/$100
.BYTE $9F,$00+screen1/$100,$C7,$00+screen1/$100,$EF,$00+screen1/$100
.BYTE $17,$01+screen1/$100,$3F,$01+screen1/$100,$67,$01+screen1/$100
.BYTE $8F,$01+screen1/$100,$B7,$01+screen1/$100,$DF,$01+screen1/$100
.BYTE $07,$02+screen1/$100,$2F,$02+screen1/$100,$57,$02+screen1/$100
.BYTE $7F,$02+screen1/$100,$A7,$02+screen1/$100,$CF,$02+screen1/$100
.BYTE $F7,$02+screen1/$100,$1F,$03+screen1/$100,$47,$03+screen1/$100
.BYTE $6F,$03+screen1/$100,$97,$03+screen1/$100,$BF,$03+screen1/$100
.BYTE $E7,$03+screen1/$100
sxupdatecol0
.BYTE $00,$00+screen0/$100,$28,$00+screen0/$100,$50,$00+screen0/$100
.BYTE $78,$00+screen0/$100,$A0,$00+screen0/$100,$C8,$00+screen0/$100
.BYTE $F0,$00+screen0/$100,$18,$01+screen0/$100,$40,$01+screen0/$100
.BYTE $68,$01+screen0/$100,$90,$01+screen0/$100,$B8,$01+screen0/$100
.BYTE $E0,$01+screen0/$100,$08,$02+screen0/$100,$30,$02+screen0/$100
.BYTE $58,$02+screen0/$100,$80,$02+screen0/$100,$A8,$02+screen0/$100
.BYTE $D0,$02+screen0/$100,$F8,$02+screen0/$100,$20,$03+screen0/$100
.BYTE $48,$03+screen0/$100,$70,$03+screen0/$100,$98,$03+screen0/$100
.BYTE $C0,$03+screen0/$100
sxupdatecol1
.BYTE $00,$00+screen1/$100,$28,$00+screen1/$100,$50,$00+screen1/$100
.BYTE $78,$00+screen1/$100,$A0,$00+screen1/$100,$C8,$00+screen1/$100
.BYTE $F0,$00+screen1/$100,$18,$01+screen1/$100,$40,$01+screen1/$100
.BYTE $68,$01+screen1/$100,$90,$01+screen1/$100,$B8,$01+screen1/$100
.BYTE $E0,$01+screen1/$100,$08,$02+screen1/$100,$30,$02+screen1/$100
.BYTE $58,$02+screen1/$100,$80,$02+screen1/$100,$A8,$02+screen1/$100
.BYTE $D0,$02+screen1/$100,$F8,$02+screen1/$100,$20,$03+screen1/$100
.BYTE $48,$03+screen1/$100,$70,$03+screen1/$100,$98,$03+screen1/$100
.BYTE $C0,$03+screen1/$100
*=$4000
incbin "map.bin"
incbin "colors.bin"
*=$8000
incbin "chars.bin"
-
Risolto!
In effetti è bastato inizializzare il secondo framebuffer con lo schermo già scrollato di un carattere (come è ovvio che sia)
Adesso mi manca solo l'init della colormap!
processor 6502
; Costanti
rastline = $30+((25*8)/2)-8; La rasterline iniziale é grosso modo a centro schermo
finescrline = $FA ; quella finale é nel bordo inferiore
screen0 = $3800+$8000 ; Framebuffer 0
screen1 = $3C00+$8000; ; Framebuffer 1
color = $d800 ; Memoria colore
mappointer = $4000 ; Mappa (256 "colonne" da 32 byte di cui 25 usati)
mapcolor = $4000+$2000 ; Mappa colore (256 "colonne" da 32 byte di cui 25 usati)
; Variabili zero page
; Attenzione! Questo blocco porta via 200 valori di zero page, precisamente
; da $20 a $E7; salvo variare il range non si possono usare queste locazioni
; di memoria per altri scopi
sxupc0 = $20 ; 50 valori che puntano alla colonna 0 del FB 0
sxupc1 = sxupc0+$32 ; 50 valori che puntano alla colonna 0 del FB 1
dxupc0 = sxupc1+$32 ; 50 valori che puntano alla colonna 39 del FB 0
dxupc1 = dxupc0+$32 ; 50 valori che puntano alla colonna 39 del FB 1
posx = $F9 ; Posizione nella mappa
pmap = $FA ; puntatore usato in più contesti
page = $FC ; pagina da assegnare a $D018
finescroll = $FD ; scrolling fine da assegnare a $D016
cscr = $FE ; Framebuffer (avrà valore 0/1)
; Codice
org $0801
; *********
; Sys Basic
; *********
word eop
word 7102
byte $9e,"2061",0
eop word 0
org $080D; Inizio del programma
; ********************
; Impostazioni di base
; ********************
main
sei
; Ciclo di inizializzazione della colormap e dei due framebuffer
ldy #$0
ldx #$0
lpCmpC
lda mappointer,x;carico valore
lpSMC
sta screen0 ;Aggiorna schermo
;lda #$01 ;carico valore
;lpSMCcolor
;sta color+990 ;aggiorna color map
clc ;Questo mi cambia dinamicamente il valore di screen0 e screen1 modificando il codice al volo
lda lpSMC+1 ;SCREEN0 h
adc #$28
sta lpSMC+1
bcc lpSMC1 ;Se non è modificato il carry continua il ciclo
inc lpSMC+2 ;SCREEN0 l
lpSMC1
inx
cpx #$19 ;Sei arrivato al carattere 25
bne lpCmpC ;no allora ricomincia
;si allora mi sposto alla colonna successiva
ldx #$0 ;azzero X
iny ;incrementa Y
sty lpSMC+1 ;Ricarico il valore screen0 incrementandolo di 1
lda #$B8 ;B8 è la prima parte di $3800 + $8000
sta lpSMC+2
clc ;Sposto anche mappointer in avanti di 32
lda lpCmpC+1
adc #$20
sta lpCmpC+1
bcc lpSMC2 ;Se non è modificato il carry continua il ciclo
inc lpCmpC+2
lpSMC2
cpy #$28
bne lpCmpC
; Ciclo di inizializzazione dei pointer precalcolati per l'aggiornamento della prima ed ultima colonna
ldx #$31
copyupcpointers
lda sxupdatecol0,x
sta sxupc0,x
lda sxupdatecol1,x
sta sxupc1,x
lda dxupdatecol0,x
sta dxupc0,x
lda dxupdatecol1,x
sta dxupc1,x
dex
bpl copyupcpointers
lda #$36;Libero A000-BFFF dalla BASIC ROM RAM (Freshness79)
sta $01
lda #$15;Seleziono banco 2 ($8000-$BFFF 00010101)
sta $dd00
lda #$D7;Posizione di scrolling iniziale e scrolling HW nel punto piu' a destra
sta $D016
sta finescroll
lda #$E0;Attivo schermo 0 (11100000 E0)
sta $d018
sta page
lda #$00;Indicatore dello schermo corrente
sta cscr
;Copia lo schermo già scrollato a SX su SCREEN 0
ldx #$00
lpInitScreen1
lda screen0,x
sta screen1+1,x
lda screen0+256,x
sta screen1+1+256,x
lda screen0+512,x
sta screen1+1+512,x
lda screen0+768,x
sta screen1+1+768,x
inx
bne lpInitScreen1
lda #$00 ;Continuia inizializzazione
sta $d020
sta $d021
lda #$0c
sta $d022
lda #$0b
sta $d023
jsr initIRQ
cli
jmp * ;Loop infinito
initIRQ
lda #<routineIRQ; Indirizzo codice (parte alta)
sta $0314
lda #>routineIRQ; Indirizzo codice (parte bassa)
sta $0315
lda #rastline; Raster line di attivazione IRQ raster
sta $d012
lda #$1b ; Azzera il bit 7 che sarebbe il Raster Compare: (Bit 8) di $d012
sta $D011
lda #$7f ; 01111111 BIT 7 a 0
sta $dc0d ; Azzera il bit 7 di CIA Interrupt Control Register (Read IRQs/Write Mask) per disattivarlo
sta $dd0d ; Azzera il bit 7 di CIA Interrupt Control Register (Read NMls/Write Mask) per disattivarlo
lda $dc0d ; Acknowledge di un eventuale IRQ generato dal CIA1 - assicuro che l'unica fonte di interruzione
lda $dd0d ; Acknowledge di un eventuale NMI generato dal CIA2 - sia il VIC
lda #$01 ; Abilita IRQ raster
sta $d019 ; VIC Interrupt Flag Register (Bit = 1: IRQ Occurred)
sta $d01a ; IRQ Mask Register: 1 = Interrupt Enabled
rts
routineIRQ
inc $d019 ; Dico al vic che la routine è stata eseguita
inc $d020 ; Colora una raster line per capire quanto raster consumo per scrollare
lda #<secondirq; Imposto il prossimo raster IRQ
sta $0314
lda #>secondirq
sta $0315
lda #finescrline
sta $d012
ldx $d016 ; leggo il finetune corrente
txa ; lo salvo in X
and #$07 ; quindi lo maschero per recuperare i 3 bit utili
tay ; e lo salvo in Y, questa e' la posizione corrente.
lda $DC00 ; Leggo il valore del joystick
eor #%00001100; si lavora meglio in logica positiva.
and #%00001100; Seleziono le sole direzioni sx e dx
cmp #%00000100; spostamento a sinistra?
beq jsx ; si, sposta a sinistra
cmp #%00001000; spostamento a destra?
beq dx ; si, sposta a destra
jmp lpExit ; nessun spostamento
jsx
jmp sx
dx
dex ; Aggiorno X per ottenere il nuovo valore
txa ; di scrolling fine
and #$07 ; dopo averlo opportunamente mascherato
ora #$D0 ; e "or-ato" lo salvo
sta finescroll; nella variabile che verra' poi usata nel secondo interrupt
ldx cscr ; Carico il frame (0 o 1)
tya ; uso il valore in Y precedentemente salvato per
lsr ; calcolare l'high-byte di sorgente e destinazione
ora ixbuf01,x; con una OR ottengo la sorgente
sta dxfrom+2 ;
and #$03 ; rimaschero il valore per annullare la precedente OR
ora ixbuf10,x; con questa OR ottengo la destinazione
sta dxto+2 ;
lda #$00 ; uso il carry generato dalla precedente LSR per avere il valore $00 o $80
ror ; in questo modo ho la totale distribuzione del carico
sta dxto+1 ; negli 8 spostamenti: $000,$080,$100,$180,$200,$280,$300 e $380
adc #$01 ; la sorgente (FROM) e' un carattere piu' a destra rispetto alla destinazione per avere l'effettivo
sta dxfrom+1; spostamento.
ldx #$7f ; copio i dati con un ciclo (qua si potrebbe srotolare tutto ed ottimizzare)
dxfrom
lda $ffff,x ; Indirizzo calcolato nei passaggi precedenti
dxto
sta $ffff,x ; IDEM
dex
bpl dxfrom
cpy #$00 ; Abbiamo saltato un intero carattere?
bne dxExit ; No, possiamo uscire
lda cscr ; Cambio frame
eor #$01
sta cscr
tax ; Lo metto in X per usarlo successivamente
lda page ; Cambio pagina video
eor #$10
sta page
inc posx ; incremento la posizione nella mappa
lda #$00 ; calcolo l'indirizzo base nella mappa
sta pmap ;
clc ;
lda posx ;
adc #$27 ; Aggiungo 39 alla posizione orizzontale perché stiamo aggiornando l'ultima colonna
lsr ; La rudimentale mappa che ho implementato si compone di blocchi
ror pmap ; di 32 byte che vanno da $4000 ad $8fff (256 blocchi). Ciascun
lsr ; blocco corrisponde ad una colonna verticale nello schermo perciò
ror pmap ; dei 32 byte disponibili ne uso solamente 25 (i 7 rimanenti potrebbero
lsr ; essere usati ad esempio per aggiornare la mappa colore con 1 colore
ror pmap ; ogni 4 caratteri - 7x4=28)
ora #(mappointer/$100)
sta pmap+1
lda dxupdbuf,x; Carico l'indirizzo della tabella di valori precalcolati usata
tax ; per salvare i dati nello schermo
ldy #$00
dxcpcolumn ;
lda (pmap),y; y scorre lungo i 25 byte del blocco selezionato
sta ($00),x ; x scorre nella tabella di 50 valori (25 indirizzi lo/hi)
inx ; corrispondente alle posizioni dello schermo
inx ; X=X+2 perché ogni indirizzo porta via due byte
iny ; Y=Y+1
cpy #$19 ; abbiamo copiate tutte le 25 righe?
bne dxcpcolumn
jsr scrollCdx; Aggiornamento della mappa colore (verso destra)
lda cscr
bne dxfix1
jsr fixcoldx0
jmp lpExit
dxfix1
jsr fixcoldx1
dxExit
jmp lpExit
; Gestione movimento a sinistra
; Sono indicate solamente le modifiche
; rispetto alla routine di destra
; il resto e' identico
sx
inx ; *Incremento
txa
and #$07
ora #$D0
sta finescroll
ldx cscr
tya
lsr
ora ixbuf01,x
sta sxfrom+2
and #$03
ora ixbuf10,x
sta sxto+2
lda #$00
ror
sta sxfrom+1; sorgente e
adc #$01
sta sxto+1 ; destinatario invertiti
ldx #$7f
sxfrom
lda $ffff,x
sxto
sta $ffff,x
dex
bpl sxfrom
cpy #$07 ; Valore limite diverso (7)
bne lpExit
lda cscr
eor #$01
sta cscr
tax
lda page
eor #$10
sta page
dec posx ; Decremento della posizione
lda #$00
sta pmap
lda posx ; non serve addizionare $27
lsr
ror pmap
lsr
ror pmap
lsr
ror pmap
ora #(mappointer/$100)
sta pmap+1
lda sxupdbuf,x; uso il buffer per la direzione sinistra
tax
ldy #$00
sxcpcolumn
lda (pmap),y
sta ($00),x
inx
inx
iny
cpy #$19
bne sxcpcolumn
jsr scrollCsx; spostamento verso sinistra della mappa colore
lda cscr
bne fixsx1
jsr fixcolsx0
jmp lpExit
fixsx1
jsr fixcolsx1
lpExit
dec $d020 ; Fine barra consumo CPU
iret
pla ;Pull Accumulator from Stack
tay ;transfer A to Y
pla ;Pull Accumulator from Stack
tax ;transfer A to X
pla ;Pull Accumulator from Stack
rti ; return from interrupt
secondirq
lsr $d019 ; IRQ Acknowledge
lda #<routineIRQ; Setto la routine principale
sta $0314 ; come prossimo handler del raster interrupt
lda #>routineIRQ
sta $0315
lda #rastline ; Fisso la linea alla quale avverrà l'interruzione
sta $d012
lda finescroll ; Imposto il finescroll fuori dalla zona video
sta $d016
lda page ; switch della pagina video (pure questo fuori dalla zona video)
sta $d018
bne iret ; sempre diverso da 0 qua, equivalente a jmp iret
; *****************************************
; Routine srotolate per l'update del colore
; *****************************************
scrollCdx
C01 SET 0
ldx color
REPEAT 1023
lda color+C01+1
sta color+C01
C01 SET C01 + 1
REPEND
stx color+$3ff
rts
scrollCsx
ldx color+$200
ldy color+$3ff
C01 SET 511
REPEAT 512
lda color+C01
sta color+C01+1
C01 SET C01 -1
REPEND
C01 SET 510
REPEAT 511
lda color+$200+C01
sta color+$200+C01+1
C01 SET C01 -1
REPEND
sty color
stx color+$201
rts
fixcolsx0
ROW SET 0
REPEAT 25
ldx screen0+ROW
lda $6000,x
sta color+ROW
ROW SET ROW +40
REPEND
rts
fixcolsx1
ROW SET 0
REPEAT 25
ldx screen1+ROW
lda $6000,x
sta color+ROW
ROW SET ROW +40
REPEND
rts
fixcoldx0
ROW SET 0
REPEAT 25
ldx screen0+39+ROW
lda $6000,x
sta color+39+ROW
ROW SET ROW +40
REPEND
rts
fixcoldx1
ROW SET 0
REPEAT 25
ldx screen1+39+ROW
lda $6000,x
sta color+39+ROW
ROW SET ROW +40
REPEND
rts
; *******************************************
; Puntatori e tabelle con valori precalcolati
; *******************************************
align 16
ixbuf01
.BYTE screen0/$100,screen1/$100
ixbuf10
.BYTE screen1/$100,screen0/$100
sxupdbuf
.BYTE sxupc0,sxupc1
dxupdbuf
.BYTE dxupc0,dxupc1
dxupdatecol0
.BYTE $27,$00+screen0/$100,$4F,$00+screen0/$100,$77,$00+screen0/$100
.BYTE $9F,$00+screen0/$100,$C7,$00+screen0/$100,$EF,$00+screen0/$100
.BYTE $17,$01+screen0/$100,$3F,$01+screen0/$100,$67,$01+screen0/$100
.BYTE $8F,$01+screen0/$100,$B7,$01+screen0/$100,$DF,$01+screen0/$100
.BYTE $07,$02+screen0/$100,$2F,$02+screen0/$100,$57,$02+screen0/$100
.BYTE $7F,$02+screen0/$100,$A7,$02+screen0/$100,$CF,$02+screen0/$100
.BYTE $F7,$02+screen0/$100,$1F,$03+screen0/$100,$47,$03+screen0/$100
.BYTE $6F,$03+screen0/$100,$97,$03+screen0/$100,$BF,$03+screen0/$100
.BYTE $E7,$03+screen0/$100
dxupdatecol1
.BYTE $27,$00+screen1/$100,$4F,$00+screen1/$100,$77,$00+screen1/$100
.BYTE $9F,$00+screen1/$100,$C7,$00+screen1/$100,$EF,$00+screen1/$100
.BYTE $17,$01+screen1/$100,$3F,$01+screen1/$100,$67,$01+screen1/$100
.BYTE $8F,$01+screen1/$100,$B7,$01+screen1/$100,$DF,$01+screen1/$100
.BYTE $07,$02+screen1/$100,$2F,$02+screen1/$100,$57,$02+screen1/$100
.BYTE $7F,$02+screen1/$100,$A7,$02+screen1/$100,$CF,$02+screen1/$100
.BYTE $F7,$02+screen1/$100,$1F,$03+screen1/$100,$47,$03+screen1/$100
.BYTE $6F,$03+screen1/$100,$97,$03+screen1/$100,$BF,$03+screen1/$100
.BYTE $E7,$03+screen1/$100
sxupdatecol0
.BYTE $00,$00+screen0/$100,$28,$00+screen0/$100,$50,$00+screen0/$100
.BYTE $78,$00+screen0/$100,$A0,$00+screen0/$100,$C8,$00+screen0/$100
.BYTE $F0,$00+screen0/$100,$18,$01+screen0/$100,$40,$01+screen0/$100
.BYTE $68,$01+screen0/$100,$90,$01+screen0/$100,$B8,$01+screen0/$100
.BYTE $E0,$01+screen0/$100,$08,$02+screen0/$100,$30,$02+screen0/$100
.BYTE $58,$02+screen0/$100,$80,$02+screen0/$100,$A8,$02+screen0/$100
.BYTE $D0,$02+screen0/$100,$F8,$02+screen0/$100,$20,$03+screen0/$100
.BYTE $48,$03+screen0/$100,$70,$03+screen0/$100,$98,$03+screen0/$100
.BYTE $C0,$03+screen0/$100
sxupdatecol1
.BYTE $00,$00+screen1/$100,$28,$00+screen1/$100,$50,$00+screen1/$100
.BYTE $78,$00+screen1/$100,$A0,$00+screen1/$100,$C8,$00+screen1/$100
.BYTE $F0,$00+screen1/$100,$18,$01+screen1/$100,$40,$01+screen1/$100
.BYTE $68,$01+screen1/$100,$90,$01+screen1/$100,$B8,$01+screen1/$100
.BYTE $E0,$01+screen1/$100,$08,$02+screen1/$100,$30,$02+screen1/$100
.BYTE $58,$02+screen1/$100,$80,$02+screen1/$100,$A8,$02+screen1/$100
.BYTE $D0,$02+screen1/$100,$F8,$02+screen1/$100,$20,$03+screen1/$100
.BYTE $48,$03+screen1/$100,$70,$03+screen1/$100,$98,$03+screen1/$100
.BYTE $C0,$03+screen1/$100
*=$4000
incbin "map.bin"
incbin "colors.bin"
*=$8000
incbin "chars.bin"
-
Ecco il codice completo anche dell' inizializzazione della color memory
-
Ottimo lavoro!
Che ne dici di provare ad aggiungere un po' di musica?
Scegli il sid più adatto e fai partire il player, se non sai bene come muoverti non hai che da chiedere (dove non arrivo io credo sia ben disposto ad aggiungere informazioni iAN).
Fa sempre piacere vedere qualcuno cimentarsi - con successo - con l'assembly!
Thumb up! ;)
-
In effetti non ho mai affrontato il discorso "musica".
Immagino che ci sia un'area di memoria dedicata a contenere i dati del brano ed una sorta di player che lo fa suonare senza interrompere l'esecuzione del codice.....
Mi puoi cosigliare qualche tutorial?
-
Dunque, tieni innanzittuto presente che i file sid in realtà hanno già dentro tutto il codice per il play. In sostanza non sono file musicali (tipo mod, xm o roba del genere) ma sono veri e propri programmi personalizzati ad hoc per emettere musica.
Ciò detto, per inserire sid io uso questo sistema:
- apro il player sidplayw (http://www.gsldata.se/c64/spw/sidplayw.html)
- scelgo il sid e lo carico
- in File->Properties mi leggo:
* Load Range: area di memoria occupata
* Init Address: chiamata di inizializzazione
* Play Address: chiamata di play
- mi regolo se la zona di memoria é compatibile con il mio programma (non devono sovrapporsi evidentemente)
- vado su File->Conversion, seleziono il file nella tendina a sinistra e come tipo metto C64 Data (.dat)
- converto il file
A questo punto ho un file .dat che posso includere nel sorgente: l'unica cosa da tener presente é che i primi due byte vanno saltati perché rappresentano l'indirizzo iniziale. Puoi scegliere tu se eliminare i byte a mano con un editor binario o includere il file con un indirizzo di partenza di due byte inferiore rispetto a quello reale del file.
Una volta incluso il file é sufficiente una sola chiamata (jsr) all'init address da qualche parte in fase di inizializzazione ed una chiamata al play address in sincrono con l'interruzione: in linea di massima basta chiamare il player ogni 50esimo di secondo ma se la musica ti sembra lenta prova a mettere due chiamate al player in successione.
NB: la frequenza con la quale va chiamato il player meriterebbe in realtà un discorso a parte. Molti sid andrebbero suonati a 60hz o magari a frequenze anche più alte. In molti casi, comunque, si rivela necessario impostare un timer interrupt con il CIA. Per il momento ti consiglio di scegliere un sid per il quale sidplayw indichi "50Hz VBI (PAL)". VBI, per la cronaca, sta per vertical blank interrupt.
-
Ci provo!
Una domanda riguardo lo scroller:
Come hai estratto i files di mappa,colore e char dal gioco originale?
-
Ehhh... con un po' di pazienza!
Ogni gioco ha i suoi algoritmi per la gestione delle mappe quindi preferisco darti un metodo generale piuttosto che gli strumenti specifici per GS.
- Apri il gioco in un emulatore (con VICE si va bene a far sta roba)
- Carica un particolare livello (al limite con un trainer)
- Entra nel monitor dell'emulatore e controlla di essere in una raster line ($D012/$D011) dentro l'area di gioco: entra ed esci dal monitor finché non riesci.
- Ok, sei entrato in una raster line buona: prendi nota del valore di $D018 e di DD00 e usali per capire dove si trova l'area caratteri usata per disegnare lo schermo.
- Metti un breakpoint in scrittura su un qualsiasi indirizzo di memoria compreso nei primi 1000 (tipicamente é meglio un valore centrale tipo 500) puntati dalla area caratteri del VIC
- Esci dal monitor
- Dovresti rientrare nel monitor a seguito di uno spostamento maggiore di un carattere (potrebbero essere necessari spostamenti più grossi, dipende dal gioco); il breakpoint comunque ti permetterà di trovare la routine di aggiornamento dello schermo
- Studiala! :D
E' un metodo piuttosto... ruspante... ma nel caso di Giana Sisters e di un altro giochino di cui non so il titolo originale, ha funzionato perfettamente.
PS: in alternativa puoi mettere anche un breakpoint in scrittura sulla memoria colore.
-
Una volta trovata l'area di memoria corretta come dumpi in un file dat?
Ps
Ho fatto qualche esperimento per suonare un file sid ma, per poterlo inserire nello scroller, devo rivedere un po di cose dato che mi pare di capire che, tale risorsa, funziona solo se inserita nell'area di memoria riportata nell'attributo:"Load Range"
corretto?
-
X il dump:
In vice puoi salvare un blocco di memoria con questo comando:
s "nomefile" 0 start end
ad esempio:
s "test.dat" 0 c000 cfff
X il SID:
Chiaramente si. Il codice del SID, salvo perderci del tempo sopra, non é rilocabile quindi devi caricarlo esattamente nell'area di memoria per la quale é pensato: quella per l'appunto indicata nel load range.
Ricordati i 2 byte iniziali del .dat.
-
grazie delle dritte!