Il precedente articolo “Il falso mito dei bitplane più efficienti (della grafica packed/chunky)“ ha generato diversi commenti (soprattutto nel gruppo Commodore Amiga Italia su Facebook) che hanno messo in dubbio le sue conclusioni. Ragione per cui ho deciso di procedere alla scrittura di un altro pezzo prendendo proprio l’Amiga come macchina di riferimento e mostrandone non soltanto il funzionamento e l’eventuale implementazione, ma anche la maggiore efficienza nella maggior parte dei casi se avesse impiegato la grafica packed anziché quella planare. Questa, dunque, sarebbe la sostanziale differenza (grafica packed anziché planare): quasi tutto il resto (inclusi registri e loro contenuto) sarebbe uguale, a parte qualche piccolo particolare (che verrà meglio delineato più avanti) o cose che non hanno senso con la grafica packed.
Premetto che l’articolo è estremamente lungo (il più lungo che abbia mai scritto) perché passare da grafica planare a packed richiede modifiche rilevanti e, in alcuni casi, completamente radicali, che necessitano di soluzioni precise alle varie problematiche nonché dovizia di particolari e spiegazioni, che purtroppo ricadono quasi interamente in ambito tecnico. Dunque non soltanto è richiesta conoscenza del funzionamento dell’Amiga (della sua architettura; dei suoi registri), ma anche alcune specifiche competenze tecniche (un po’ di elettronica digitale di base); per questo ho inserito dei link chiarificatori nel testo, ove ho ritenuto necessario. Infine e per cercare di aiutare la lettura ho suddiviso il pezzo in sezioni chiaramente riconoscibili, in modo da suddividere visivamente e logicamente le varie parti e rendere più facile riprendere l’escursus da precisi punti, oltre al fatto che verrà pubblicato suddiviso in più parti, per alleggerirne ulteriormente la fruizione.
Preciso ancora che gli unici elementi che sono impattati da questo “cambiamento” sono il controllore video (perché si occupa di generare la grafica da inviare poi al televisore o monitor), gli sprite (che comunque fanno capo al precedente) e il Blitter (che si occupa di manipolare la grafica); quale conseguenza / effetto collaterale, anche il sistema di gestione del DMA risulta parzialmente affetto da modifiche (ampiamente discusse, comunque). Non saranno trattati gli altri elementi (Copper, canali audio, disco, tastiera, ecc.), perché non hanno alcuna rilevanza in questo contesto. Infatti l’idea, lo ribadisco ancora una volta, è di avere a disposizione un Amiga con tutti i suoi elementi distintivi, ma con l’unico cambiamento che riguarda l’uso della grafica packed (e solo questa: niente bitplane e grafica planare).
Il controllore video
Il controllore video è già stato trattato estensivamente nel precedente articolo, ma ovviamente il discorso era generico, mentre quello dell’Amiga ha delle caratteristiche peculiari che hanno bisogno di essere trattate specificamente riportandone il funzionamento originale (quindi con la grafica planare / bitplane) e mostrando successivamente come sarebbero se la grafica fosse packed.
Fortunatamente ne ho già parlato estensivamente in tre precedenti articoli: Righe di raster, color clock, retrace, (semi)quadri: le basi del chipset dell’Amiga (e non solo), Divide et impera: i color clock nelle righe di raster dell’Amiga e I bitplane e le righe di raster: l’Amiga visualizza la grafica dello schermo, per cui qui mi concentrerò esclusivamente su come vengano utilizzati i canali DMA dei bitplane, e i dati da essi letti, per generare la grafica.
L’ultimo articolo, in particolare, è quello che tratta appositamente il funzionamento del controllore video. L’Amiga può visualizzare schermi a bassa risoluzione (320 pixel orizzontali), alta (640) o super-alta (1280. Soltanto per sistemi con chipset ECS o AGA), e questo determina anche il numero di bitplane utilizzabili (e, quindi, di colori visualizzabili. Tralasciando al momento la modalità HAM): massimo 6 (64 colori) per chipset OCS/ECS per la bassa, 4 (16 colori) per l’alta e 2 (4 colori. Soltanto a partire da ECS) per la super-alta. Mentre il chipset AGA consente di utilizzare sempre un massimo 8 bitplane, a prescindere dalla risoluzione utilizzata.
Mi concentrerò soltanto sulla bassa risoluzione per semplificare il discorso, ma le valutazioni sarebbero sostanzialmente le stesse per alta e super-alta risoluzione, con le differenze dovute al minor numero di bitplane a disposizione (tranne per l’AGA). Per maggiori dettagli si può sempre attingere, oltre che ai miei precedenti articoli, all’Amiga Hardware Reference Manual di Commodore, e specificatamente alle seguenti sezioni: video display, display window and data fetch, DMA time slot allocation e al preziosissimo diagramma annesso.
Il controllore video – bitplane (OCS/ECS)
Adesso e supponendo di voler visualizzare uno schermo a bassa risoluzione di 320 pixel orizzontali (il discorso è analogo per alta e super-alta risoluzione, per cui non saranno trattate in questo contesto), sappiamo che il controllore video inizierà a leggere i dati dei bitplane (usando i loro puntatori. BPL1PT
, che è suddiviso in due registri a 16 bit: BPL1PTH
e BPL1PTL
. Poi BPL2PT
, etc., fino a BPL6PT
) a partire dallo slot $38 della riga di raster che sta visualizzando e soltanto dopo altri 5 slot dalla fine della lettura dei dati comincerà a spedire al monitor (ometterò la TV per semplificare) i pixel da visualizzare, uno alla volta. Il che è anche logico: è necessario essere in possesso di tutti i dati prima di poter iniziare a elaborarli e, infine, spedire i pixel al monitor. I 5 slot è facile desumere che siano necessari per la fase di elaborazione dei dati prelevati (molto probabilmente ci sarà una pipeline a 5 stati che elabora, stadio dopo stadio, i dati racconti, li controlla e li combina, fino ad ottenere le componenti cromatiche da inviare al monitor).
A questo punto verranno spediti al display 16 pixel alla volta (ricordiamo che il bus dati dell’Amiga è a 16 bit, per cui di ogni bitplane vengono letti 16 bit alla volta = un bit per ognuno di 16 pixel), ma nel frattempo il controllore video aveva già iniziato (slot $40) a leggere i dati dei 16 pixel successivi. Poiché ogni volta che un bitplane ha letto i suoi dati essi vengono memorizzati in un apposito suo registro (BPL1DAT
per il primo bitplane, BPL2DAT
per il secondo, e così via…), è ragionevole pensare che, prima di iniziare la lettura dei dati dei successivi 16 pixel, il controllore video abbia provveduto a memorizzare il contenuto di quelli attuali in alcuni registri interni, per procedere poi comodamente alla loro elaborazione (la fase che richiede 5 slot di DMA in termini puramente temporali e non di occupazione del bus dati).
La fase di elaborazione è piuttosto semplice, e consiste nell’estrarre i singoli bit di ogni bitplane e combinarli, in modo da ottenere l’indice colore da utilizzare per accedere alla tavolozza che contiene i valori delle componenti cromatiche da spedire poi al monitor. Il processo è stato già illustrato nel precedente articolo e viene ancora meglio spiegato nella già citata sezione video display. Ciò va avanti un pixel alla volta, che corrisponde all’esecuzione di un ciclo clock di sistema, finché tutti e 16 i pixel sono stati visualizzati. Ogni volta che avviene questa operazione i dati dei singoli bitplane vengono shiftati di una posizione a sinistra, in modo da scartare i bit già elaborati e posizionare quelli successivi al posto giusto, ossia nel bit più significativo (ricordo che l’Amiga è un sistema big-endian: il primo bit è quello più significativo).
Il controllore video provvede poi a incrementare di due (due byte) tutti i bitplane (in realtà sono i puntatori ai bitplane, per essere più precisi) quando finisce di visualizzare i 16 pixel, per posizionarli ai dati dei successivi 16 pixel.
Il controllore video va avanti con questa procedura per ben 20 volte (20 x 16 pixel = 320 pixel), finché non arriva quindi alla fine della riga di raster da visualizzare. Poi aggiunge un valore, chiamato modulo (registro BPL1MOD
), a ogni bitplane, in modo da posizionarli in maniera corretta alla riga successiva di ognuno, per poter così cominciare nuovamente dall’inizio. Il valore del modulo dipende dal formato grafico utilizzato per i bitplane, ossia interleaved o meno, ma è irrilevante ai fini di questa trattazione (i dettagli sono presenti, comunque, nel precedente pezzo).
Ricapitolando, per visualizzare una riga dello schermo il controllore video dell’Amiga:
- inizia a prelevare i dati dei bitplane a partire dallo slot $38;
- allo slot $40 ha già finito e inizia a combinarli;
- per ogni pixel estrae i rispettivi bit dai bitplane e li combina per ottenere l’indice del colore;
- usando l’indice preleva le componenti cromatiche dall’apposita voce nella tavolozza dei colori;
- a partire dallo slot $45 inizia a visualizzare un pixel alla volta;
- viene visualizzato un pixel per ogni ciclo di clock di sistema (uno slot di DMA equivale a 2 cicli di clock di sistema);
- i buffer di tutti i bitplane vengono shiftati a sinistra in modo da scartare il bit del pixel visualizzato e caricare quello del successivo;
- ripete questa procedura finché non visualizza tutti e 16 i pixel;
- ogni 8 slot (pari a 16 cicli di clock di sistema) ripete la stessa operazione dall’inizio;
- dopo 20 operazioni (definite da un apposito registro) finisce di visualizzare la riga corrente e passa alla successiva (sommando il modulo ai bitplane).
Il controllore video – bitplane (OCS/ECS) – Costi di implementazione
Il tutto ovviamente ha un costo a livello di implementazione, ossia di hardware necessario. Non operando nel settore mi limito a elencare di quali elementi si abbia bisogno, approssimativamente, per rendere l’idea delle risorse in gioco.
- Per i 6 puntatori ai bitplane (OCS e ECS supportano al massimo 6 bitplane, per un massimo di 64 colori. Escludendo la modalità HAM per il momento) servono 6 x 2 = 12 registri a 16 bit (il bus dati è sempre a 16 bit) accessibili dalla CPU (o dal Copper. Per semplicità ometterò quest’informazione, che rimane sempre valida) per poterne impostare il valore. Internamente il chipset utilizza i puntatori sempre combinati, quindi come elemento unico a 32-bit. In realtà quando si accede alla memoria vengono sempre utilizzati meno bit di quelli conservati in questi puntatori, perché l’OCS può indirizzare massimo 512kB di memoria, e quindi utilizza soltanto i 19 bit meno significativi in esso memorizzati; anzi, ne usa 18 bit, considerato che il bit 0 è sempre a zero, visto che si può accedere soltanto a word, cioè 16 bit alla volta e mai ai singoli byte. L’ECS può indirizzare fino a 2MB (e anche l’AGA), quindi vengono usati 2 bit in più = 20 bit in totale. E’ molto importante sottolineare che tutti e 2 x 16 = 32 i bit vengono sempre memorizzati in questi registri (anche se poi non vengono utilizzati, come per qualunque altro registro del chipset), ma è soltanto quando il chipset accede alla memoria che utilizza soltanto quelli necessari a indirizzarla (18 o 20), ignorando tutti gli altri; ciò è riportato in alcune parti dell’Hardware Manual (ad esempio qui). Fatte queste importantissime precisazioni e tornando ai conti, i 12 registri a 16 bit accessibili dalla CPU si traducono nell’utilizzo di costose (ma molto veloci) celle SRAM, che in genere fanno uso di 6 o 4 transistor per implementare un singolo bit (è molto comune l’uso di 6).
- I dati prelevati dai bitplane richiedono 6 registri a 16 bit (accessibili anche dalla CPU, tra l’altro), e altrettanti quando vengono trasferiti nei registri interni che il controllore utilizza quando deve effettuare le elaborazioni e poi visualizzare finalmente i pixel. Anche qui si fa sempre uso di celle SRAM.
- Per costruire l’indice del colore serve logica per estrarre da ogni singolo bitplane l’apposito bit più significativo, per posizionarlo poi opportunamente (il bit estratto dal primo bitplane finisce nel bit 0 dell’indice, quello del secondo nel bit 1, e così via). I bit provenienti dai bitplane non utilizzati vanno opportunamente mascherati in modo che siano eliminati e non utilizzati nella costruzione dell’indice del colore, perché potrebbero contenere dati spuri (da precedenti elaborazioni); quindi se, ad esempio, si usano 4 bitplane, allora i bit provenienti dal quinto e sesto bitplane vanno forzati a zero.
- Per ogni pixel visualizzato serve uno shifter che scarti il bit più significato dei dati di ogni bitplane e sposti tutti gli altri 15 bit a sinistra di un posto (in modo da caricare il dato del prossimo pixel nel bit più significativo). Quindi con 6 bitplane servono 6 shifter a 16-bit da un bit (si spostano i bit soltanto di una posizione alla volta).
- Ogni 16 pixel serve incrementare di due (byte) i puntatori di tutti i bitplane; quindi serve un sommatore a 32 bit (se ne potrebbero usare da 18 o 20, visto che gli altri bit vengono ignoranti quando si accede alla memoria, come già chiarito sopra, ma al momento questo dettaglio non è importante). Serve un solo sommatore, utilizzato per un bitplane alla volta, se l’operazione di incremento di due viene effettuata mentre vengono prelevati i dati di un preciso bitplane (che è lo scenario più probabile), altrimenti servono 6 sommatori diversi. Più dettagli, in merito, sono forniti nel seguente e ultimo punto.
- Per ogni riga da visualizzare sono necessari 6 sommatori da 32 bit per aggiornare ogni bitplane utilizzando il modulo per spostarsi correttamente alla riga successiva. Se gli aggiornamenti vengono scaglionati, un bitplane alla volta, durante l’intervallo cosiddetto di horizontal blanking (ossia dopo aver visualizzato l’ultimo pixel della riga) allora è sufficiente utilizzare un solo sommatore (scenario abbastanza più plausibile). In generale è ipotizzabile l’utilizzo di un solo sommatore sia per aggiornare tutti i bitplane ogni 16 pixel sia per aggiornarli per la riga successiva, ma sarà in ogni caso necessario un costo implementativo (considerato che si devono programmare opportunamente queste operazioni, prelevando un bitplane alla volta, aggiornandolo, e memorizzandone il nuovo valore). In ogni caso serve un registro a 16 bit per memorizzare il valore del modulo (quindi ancora celle SRAM da utilizzare allo scopo).
Il controllore video – bitplane (AGA)
Il chipset AGA consente di utilizzare fino a 8 bitplane, per cui valgono le medesime considerazioni di cui sopra, ma sostituendo 8 al posto di 6, e 16 al posto di 12 quando di parla di questi elementi o dei loro registri & buffer interni in cui vengono memorizzati i loro dati, oppure per gli shifter. Soltanto per il momento tralascio il fatto che il bus dati utilizzabile non sia più soltanto a 16 bit, ma anche a 32 o 64 bit (in realtà anche per l’ECS è a 32 bit per macchine come l’Amiga 3000, ma esclusivamente per la CPU: il chipset è e rimane a 16-bit, e dunque non cambia niente da questo punto di vista), anche se questo ha enormi implicazioni, come vedremo nei prossimi articoli.
Arbitraggio dei canali DMA e slot liberi per la CPU
A tutto ciò si deve aggiungere l’implementazione della complessa logica di arbitraggio dei canali DMA dei bitplane, che è stata già trattata dai precedenti articoli e nei link all’Hardware Manual (questo, in particolare), il cui scopo è di cercare di lasciare possibilmente fino a 4 slot di accesso alla memoria liberi per la CPU (quando si usano fino a 4 bitplane: a partire da 5 bitplane vengono via via tolti slot disponibili per la CPU, uno per ogni bitplane aggiuntivo) ogni 8 slot.
Il problema è che questa caratteristica, apparentemente stupenda, cozza terribilmente contro due fatti molto importanti. Il primo è che va bene soltanto per un 68000 (considerato che ha un accesso molto lento alla memoria ed è incapace di sfruttare al meglio gli accessi in memoria: al massimo ne potrebbe sfruttare la metà i quelli disponibili): già a partire dal 68020 non funziona più bene come dovrebbe, visto che questo processore riesce ad accedere più velocemente alla memoria, ma col rigidissimo schema implementato dal chipset risulterebbe molto spesso bloccato per almeno due cicli di clock (di sistema. Quindi sono molti di più se il processore opera a frequenze superiori ai 7Mhz: cioè sempre!) in più del necessario. Dunque sarebbe meglio, in questi casi, che il processore fosse libero di utilizzare qualunque slot / color clock disponibile, sia esso pari o dispari (maggiori dettagli in merito si trovano, come al solito, nei link già forniti), e ciò perché per alcune operazioni un 68020 risulta più veloce del Blitter, per cui sarebbe stato saggio poterlo utilizzarlo al meglio delle sue potenzialità senza queste catene che lo bloccassero o, per sottolineare meglio il concetto, castrassero.
Secondo, e non meno importante, è che proprio per il fatto che il 68000 sia molto lento per quanto riguarda operazioni che coinvolgano gli accessi in memoria, sarebbe di gran lunga preferibile utilizzare coprocessori et similia (Blitter, Copper, Sprite). Di fatti è proprio quello che si è cercato sempre di fare quando si è trattato di spremere al massimo un Amiga: delegare il più possibile a queste unità, utilizzando il processore soltanto per programmarle (caricare i loro registri) e far partire le operazioni a loro carico (e aspettando il loro completamento, per poi programmare l’operazione successiva). Ovviamente c’è anche la logica di gestione di un gioco da considerare e che è a carico esclusivo della CPU, ma è ben poca roba rispetto a tutto il resto. Il processore, insomma, è stato usato sostanzialmente come servo del chipset (e non il viceversa, com’è rimasto impresso nell’immaginario collettivo).
In merito ci sarebbe un’altra considerazione da fare, ma per il momento la sposto più avanti, in un altro pezzo in cui parlerò dei vantaggi / guadagni della grafica packed.
Con ciò finisce la prima parte di questa serie di articoli, avendo analizzato lo status quo dell’Amiga riguardo il funzionamento del suo controllore video con la grafica planare, inclusi i costi (approssimativi) della sua implementazione (per quanto concerne l’oggetto del pezzo) nonché alcune informazioni necessarie per comprendere meglio alcune cose che si differenzieranno con la versione packed.
Nel prossimo sarà illustrato il funzionamento del controllore video “packed“.