Raffaele Intorcia

Sprite multiplexing - parte 2

Ok, bene, compreso il concetto di sprite e ribadito che il C64 non può mostrarne più di 8 contemporaneamente, cerchiamo di capire come se ne possono aggiungere di più. Per farlo, è necessario introdurre lo strumento alla base della tecnica. Si chiama interrupt raster.

Una delle particolarità del Vic-II è la capacità di generare un “segnale” verso il processore in grado di interrompere (da qui la parola interrupt) l’esecuzione del programma nel momento in cui l’immagine viene fisicamente generata sullo schermo. E’ un po’ come se il Vic-II dicesse al processore: “Ehi, finisci l’istruzione che stai eseguendo in questo momento, poi dedicami del tempo”. Il processore, diligentemente, completa l’istruzione in corso poi trasferisce il controllo su una locazione di memoria nota che contiene uno script.

Al termine di questo script, il processore riprende l’esecuzione da dove aveva interrotto. In realtà ci sono altre fasi e attività svolte per rendere possibile questo processo ma, semplificando, il flusso è questo.

La griglia dello schermo su cui viene disegnata l’immagine è detta raster, le righe orizzontali sono dette scanline e i componenti della griglia sono detti pixel. Tramite alcune sequenze di comandi, è possibile indicare al Vic-II di generare un interrupt quando è in procinto di disegnare una determinata scanline.

Tutto bello, ma qual è l’idea di base del meccanismo del multiplexing? Si tratta di sfruttare gli 8 sprite a disposizione, illudendo chi sta guardando l’immagine di averne di più, semplicemente spostandoli durante il disegno della schermata.

Per capirci, se voglio disegnare 8 sprite alla riga 150 e altrettanti alla riga 200, ottenendo perciò 16 sprite visibili nello stesso momento, sarà opportuno seguire i seguenti passi:

  • Impostare un interrupt alla riga 149
  • Muovere gli sprite sulla riga 150
  • Impostare un interrupt alla riga 199
  • Muovere gli sprite sulla riga 200
  • Ricominciare da capo

Perciò, entrando più in dettaglio:

  • il programma viene interrotto quando sta iniziando il disegno della riga 149
  • la routine eseguita durante l’interruzione sposta tutti gli sprite alla riga 150
  • si imposta un nuovo interrupt alla riga 199
  • riprende l’esecuzione normale del programma nel frattempo gli sprite sono stati disegnati alla riga 150
  • il programma viene interrotto quando sta iniziando il disegno della riga 199
  • la routine eseguita durante l’interruzione (diversa dalla precedente) sposta tutti gli sprite alla riga 200
  • si imposta nuovamente un interrupt alla riga 149 nel frattempo gli sprite sono stati disegnati alla riga 200 (assieme a quelli della riga 150)

Q: Perchè l’interrupt viene impostato sulla riga 149 e gli sprite sono posizionati sulla riga 150?

A: Il posizionamento degli sprite su una determinata area deve avvenire prima che quell’area venga disegnata. Se l’interrupt venisse impostato dopo la riga 150, l’area di schermo precedente alla scanline è già stata disegnata e un riposizionamento degli sprite in quell’area non avrebbe effetto. Non è possibile nemmeno impostare l’interrupt sulla scanline 150 perchè ci deve essere del tempo per concludere le operazioni di spostamento e si rischia di avere un disegno parziale degli sprite.

Come impostare un interrupt

Le attività per indicare a quale linea il Vic-II deve lanciare un interrupt sono:

      lda #149
      sta $d012

      lda #<Irq
      sta $0314
      lda #>Irq
      sta $0315

Le prime due istruzioni caricano il valore 149 (la scanline dell’interrupt che vogliamo) nell’accumulatore e, da qui, nell’indirizzo $d012 (è l’indirizzo del Vic-II preposto a questo scopo). Le successive 4 istruzioni caricano l’indirizzo della routine che deve essere eseguita al lancio dell’interrupt. E’ un indirizzo a 16 bit da caricare in due locazioni da 8 bit, pertanto in $0314 ci sarà la parte bassa dell’indirizzo (meno significativa) della routine e in $0315 ci sarà la parte alta (più significativa). Nel nostro esempio, la routine è etichettata con Irq.

Listato completo

Di seguito il listato completo seguito da un po’ di spiegazioni.

:BasicUpstart2($0810)

* = $0810
Init:
// Set Interrupt bit, impedisce alla Cpu di rispondere agli interrupt
// Evita che, mentre stiamo definendo le cose, il programma venga
// interrotto
      sei

// Sprite pointer
      lda #$28
      sta $07f8
      sta $07f9
      sta $07fa
      sta $07fb
      sta $07fc
      sta $07fd
      sta $07fe
      sta $07ff

// Imposto la coordinata X di tutti gli sprite
      lda #31
      sta $d000
      lda #62
      sta $d002
      lda #93
      sta $d004
      lda #124
      sta $d006
      lda #155
      sta $d008
      lda #186
      sta $d00a
      lda #217
      sta $d00c
      lda #248
      sta $d00e

// Coloro bordo e sfondo di nero
      lda #0
      sta $d020
      sta $d021

// Disattiva gli interrupt che possono arrivare dalla CIA-1
      lda #%01111111
      sta $dc0d

// Azzera il bit 7 del registro raster del Vic-II
      and $d011
      sta $d011

// Conferma per gli interrupt generati da CIA-1 e CIA-2
      lda $dc0d
      lda $dd0d

// Imposto il primo interrupt alla riga 149
      lda #149
      sta $d012

// Imposto la routine all'indirizzo Irq
      lda #<Irq
      sta $0314
      lda #>Irq
      sta $0315

// Abilita il Vic-II a lanciare gli interrupt
      lda #%00000001
      sta $d01a

// Abilito tutti gli sprite
      lda #%11111111
      sta $d015

// Consente alla Cpu di rispondere agli interrupt che arrivano
      cli
      rts

Irq:
// Spostamento degli sprite sulla riga 150
      lda #150
      sta $d001
      sta $d003
      sta $d005
      sta $d007
      sta $d009
      sta $d00B
      sta $d00D
      sta $d00F

// Coloro tutti gli sprite di verde
      lda #GREEN
      sta $d027
      sta $d028
      sta $d029
      sta $d02a
      sta $d02b
      sta $d02c
      sta $d02d
      sta $d02e

// Imposto il prossimo interrupt alla riga 199
      lda #199
      sta $d012

// Imposto la routine all'indirizzo Irq2
      lda #<Irq2
      sta $0314
      lda #>Irq2
      sta $0315

// Confermiamo al Vic-II l'esecuzione della routine
      asl $d019

// Avvia una routine del KERNAL per ripristinare lo stato precedente al
// lancio dell'interrupt, gestire il blink del cursore...altre n-mila cose
// e riprende la normale esecuzione del programma
      jmp $ea81

Irq2:
      lda #200
      sta $d001
      sta $d003
      sta $d005
      sta $d007
      sta $d009
      sta $d00B
      sta $d00D
      sta $d00F

      lda #RED
      sta $d027
      sta $d028
      sta $d029
      sta $d02a
      sta $d02b
      sta $d02c
      sta $d02d
      sta $d02e

      lda #149
      sta $d012

      lda #<Irq
      sta $0314
      lda #>Irq
      sta $0315

      asl $d019

// Avvia una routine del KERNAL per ripristinare lo stato precedente al
// lancio dell'interrupt, simile alla precedente ma più leggera
      jmp $ea31

* = $0a00
Sprites:

.byte $00,$00,$00,$00,$00,$00,$03,$f8,$00,$07,$f8,$00,$0f,$f8,$00,$1f
.byte $f8,$00,$1f,$07,$f0,$3e,$07,$e0,$3c,$07,$c0,$3c,$07,$80,$3c,$00
.byte $00,$3c,$07,$80,$3c,$07,$c0,$3e,$07,$e0,$1f,$07,$f0,$1f,$f8,$00
.byte $0f,$f8,$00,$07,$f8,$00,$03,$f8,$00,$00,$00,$00,$00,$00,$00,$07

Rispetto al listato precedente, si può osservare che nella parte di Init sono state eliminate le parti che impostano il colore degli sprite e il posizionamento sulla riga. Il colore è stato tolto perchè viene impostato dinamicamente sulle due routine di interrupt, che dopo vedremo in dettaglio. Il posizionamento verticale non è più presente sulla Init perchè è diventato superfluo in quanto ogni sprite viene riposizionato su due righe diverse ad ogni refresh dello schermo.

Subito dopo l’impostazione del colore bordo/sfondo ci sono alcune istruzioni di contorno per attivare correttamente il sistema al lancio degli interrupt. Prendiamole così come sono, non sono rilevanti per la tecnica.

Arriviamo quindi alla routine Irq. Da come è stato impostato il programma, sappiamo che verrà lanciata sulla scanline 149. A questo punto possiamo spostare gli sprite sulla riga 150, impostiamo il colore in verde e prepariamo il prossimo interrupt. Fine.

Subito dopo c’è la routine Irq2. Partirà alla scanline 199 quindi, come per la precedente, spostiamo gli sprite in riga 200 e aggiorniamo il colore.

Il risultato del listato è il seguente. Sprite

Q: ma si può fare anche altro nelle routine di interrupt?

A: beh… si… vedremo qualche esempio nella prossima puntata

Q: si deve impostare la scanline pari a un pixel in meno rispetto agli sprite?

A: no, basta che sia precedente alla posizione degli sprite. Talvolta è necessario usare un margine maggiore di 1 perché se la routine è complessa potrebbe impiegare più tempo per essere eseguita… mentre nel frattempo il disegno procede spedito.

Se volete chiarimenti su qualche punto di questo post, scrivetemi su Gitter!

Le discussioni più interessanti verranno aggiunte qui.

Share on: