Computer / Programmazione / Z80 · 27 aprile 2019 0

LM80C: sprite!

Altro passo con il mio computer basato sullo Z80. Ieri ho giocato con gli sprite. Dato che in gioventù ho avuto un Commodore 16, non ho mai avuto esperienza con gli sprite, a parte quelli che avevo visto a casa di un paio di miei amici che avevano computer C64 ed MSX. Ero perciò molto intrigato dal provare questi oggetti “eterei”. Tenete a mente che essi sono stati uno dei motivi per cui ho scelto di implementare un TMS9918A nel mio computer auto-costruito.

Quindi, cos’è uno sprite? In informatica, uno sprite è un oggetto grafico che può essere mosso a giro per lo schermo con un minimo impegno da parte del programmatore dato che nei computer e nelle console degli anni ’80 il lavoro di spostamento degli sprite era svolto dallo stesso chip video. Uno sprite non è un semplice motivo grafico mosso sullo sfondo: infatti, per muovere un motivo grafico bisogna salvare i pixel del posto dove si pensa di posizionare il motivo, piazzarlo e poi ridisegnare i pixel che il motivo ha liberato durante il suo movimento. Invece, uno sprite è una specie di livello che il chip video sovrappone all’immagine renderizzata prima di mandarla al circuito di uscita che genera il segnale video. Con un paio di byte, che indicano la posizione verticale ed orizzontale dello sprite, questo oggetto può essere spostato facilmente a giro per lo schermo. Questi tipi di oggetti sono noti come sprite hardware, ad indicare che sono gestiti direttamente da qualche tipo di hardware specifico.

Il TMS9918A ha 32 sprite hardware che possono essere piazzati ovunque sullo schermo. Ci sono comunque delle limitazioni:

  • la loro dimensione può essere solo di 8×8 o 16×16 pixel: quelli di quest’ultima dimensione sono semplicemente 4 sprite adiacenti che sono mossi come una singola entità;
  • possono essere solo monocromatici (1 solo colore);
  • non ci possono essere più di 4 sprite per singola riga video: il quinto sprite (e quelli oltre) sono resi trasparenti;
  • c’è un solo bit per le collisioni fra sprite, che viene impostato ad indicare che almeno 2 sprite si stanno sovrapponendo, ma non si ha nessuna indicazione su quali sprite sono entrati in collisione.

A parte questi limiti, il TMS9918A ha diverse caratteristiche interessanti: può usare un singolo motivo per più di uno sprite. Che significa? Significa che non appena si inserisce in memoria i bit che compongono il motivo dello sprite, il sistema assegna un numero a quell’oggetto e si può usare questo numero per replicare il suo motivo per quanti oggetti si vuole. Si possono ingrandire gli sprite, raddoppiando i loro pixel, impostando un singolo bit del VDP: uno sprite di 8×8 pixel può così essere espando a 16×16 pixel, e quelli di 16×16 pixel portati a 32×32 pixel. C’è uno bit detto “quinto sprite” che viene impostato quando 5 o più sprite appaiono sulla stessa riga video, così che il codice può controllare quali sprite sono allineati ed alternarli così che non appaiano più di 4 sprite per riga. L’effetto si chiama “sprite flickering” ed è stato ampiamente usato in passato su diversi computer e console per permettere che molti sprite potessere muoversi liberamente per lo schermo. Lo svantaggio di questa tecnica era proprio lo “sfarfallìo” degli oggetti quando il gioco alternava la visualizzazione degli sprite.

Ora che abbiamo visto i pro ed i contro degli sprite del TMS9918A, esaminiamo un esempio concreto di come manipolare questi oggetti. Prima di tutto dobbiamo impostare il TMS9918A in uno dei modi grafici al posto di quello testuale dato che quest’ultimo è l’unico che non permette di manipolare nessun tipo di caratteristica grafica del VDP, inclusi gli sprite. Ho compiuto questi test via BASIC dato che dovevo fare pratica con questa nuova cosa (almeno per me) e non volevo bruciare 50 volte la mia EEPROM per correggere i miei errori… Ho scelto di impostare il TMS9918A in modalità grafica 1 e per ottenere questo ho semplicemente inviato le seguenti impostazioni ai registri del VDP:

  • registro 0: $00 -> imposta il VDP per disabilitare l’input video esterno
  • registro 1: $C0 -> imposta il VDP in modalità grafica 1, disattiva gli interrupt, imposta 16 KB di VRAM, dimensiona gli sprite ad 8×8 pixel senza ingrandimento
  • registro 2: $06 -> indirizzo della “name table” (la porzione di memoria che lavora come buffer video per le “tessere” [i caratteri] che sono sullo schermo) impostato a $1800
  • registro 3: $80 -> indirizzo della “color table” (la porzione di memoria che contiene le informazioni dei colori dei motivi visualizzati sullo schermo) impostato a $2000
  • registro 4: $00 -> indirizzo della “pattern table” (la porzione di memoria che contiene i motivi delle “tessere” visualizzate sullo schermo) impostato a $0000
  • registro 5: $36 -> indirizzo della “sprite attribute table” (la porzione di memoria che contiene le informazioni sugli sprite come che coordinate verticali ed orizzantali) impostato a $1B00
  • registro 6: $07 -> indirizzo della “sprite pattern table” (la porzione di memoria che contiene i bit che formano i motivi degli sprite) impostato a $3800
  • registro 7: $05 -> colore di sfondo impostato su blu chiaro

Dopo queste impostazioni, ho copiato i motivi dei caratteri che ho impostato per i primi test nella pattern table così che potessi visualizzare qualche carattere sullo schermo. Infine, ho creato un semplice motivo per uno sprite di 8×8 pixel: una faccina sorridente colorata di nero. Il risultato è questo:

LM80C - text and sprite

LM80C – text e sprite

Ho poi replicato la faccina altre 31 volte con colori diversi. Questo è facile perché la sprite attribute table tiene traccia degli sprite attivi e li gestisce con soli 4 byte. Essi sono, nell’ordine:

  • la coordinata verticale (da 0 a 191)
  • la coordinata orizzontale (da 0 a 255)
  • il numero del motivo da usare (da 0 a 31): il motivo è caricato dalla sprite pattern table dove gli sprite sono memorizzati in ordine numerico partendo da 0. Per gli sprite 8×8, ogni motivo prende 8 byte ciascuno, per gli sprite 16×16 ogni motivo occupa 32 byte (8 byte per ognuno dei 4 singoli sprite come formano quello più grande)
  • il colore (da 0, trasparente, a 15, bianco)

Ho scritto un semplice programma che piazza gli sprite a formare una specie di scalinata e poi li ho mossi da sinistra a destra e ancora indietro. Potete notare la velocità dell’animazione: veramente orribile! Questo è il limite dei linguaggi interpretati dei computer ad 8 bit dove i programmi memorizzati in RAM devono essere letti, ognuna delle istruzioni decodificate ed interpretate, e poi eseguite. Ho promesso a me stesso di scrivere lo stesso codice usando il linguaggio macchina e vedere la differenza in termini di velocità… Al momento, date un’occhiata a questo semplice test:

 

Il firmware ed il codice BASIC sarà presto disponibile online sul mio repository GitHub.