E’ una domanda che si sono posti molti amighisti quando sono iniziate ad arrivare le prime schede grafiche basate sui chip provenienti dal mondo PC o Mac, le quali permettevano di raggiungere risoluzioni e refresh dello schermo più elevati, e palette dei colori più estesa.
Il dubbio è legittimo, in quanto AmigaOS è un sistema operativo che è stato confezionato su misura, sul meraviglioso chipset dell’Amiga, cercando di sfruttarne, riuscendoci, il più possibile le potenzialità.
In effetti lo stretto legame con l’hardware emerge già andando a rovistare fra le API messe a disposizione dalla libreria che più di tutte crea problemi: graphics.library, dal cui nome si può facilmente intuire che a essa è demandata tutta la gestione della grafica (cioè le API di più basso livello per manipolarla).
Routine come AddVSprite, AddBob, InitBitMap, WaitBlit, CMove, LoadRGB4, WaitTOF, ci ricordano la presenza di sprite, BOB (Blitter OBject; simili ai precedenti, ma generati in software, tramite il Blitter), bitmap, Blitter, Copper, palette dei colori, e pennello elettronico; tutti elementi che si trovano implementati nel chipset, appunto, e che difficilmente si trovano in altri computer. Anche andando a sbirciare le strutture dati utilizzate da queste API (ad esempio BitMap), si nota come calzino a pennello con l’hardware sottostante.
Sembrerebbe che non ci fosse stato spazio per null’altro, eppure le schede grafiche di cui parlavo all’inizio sono state commercializzate e, anzi, nei primi anni ’90 si è assistito a un fiorire di schede RTG (ReTargetable Graphics). Dunque l’operazione non solo era possibile, ma è stata portata avanti con successo.
L’idea che hanno avuto quei produttori è semplice quanto geniale, e si basa sulle caratteristiche intrinsecamente messe a disposizione dal s.o., in particolare quella di poter sostituire al volo e a caldo, in qualsiasi momento, tutte le API (persino quelle dello scheduler, o del manager della memoria) che riteniamo opportuno con altre di nostro gradimento.
Il tutto in maniera trasparente alle applicazioni, che non si accorgono di nulla, a patto che non venga fatto diretto uso dell’hardware. Si erge qui lo scoglio insormontabile con cui fare i conti: niente accesso diretto all’hardware, pena il malfunzionamento del sistema (magari verrebbe sollevato il famigerato alert di “Guru Meditation“, tanto noto e odiato dagli sviluppatori di questa macchina).
D’altra parte è bene sottolineare che AmigaOS espone già tantissime API, specialmente nella suddetta graphics.library, le quali coprono tutte le esigenze dei programmatori, che non avrebbero quindi bisogno di ricorrere a questa tecnica, pena legare indissolubilmente quel codice a quelle macchine. Aggiungo che per un’applicazione l’approccio os-friendly non è generalmente problematico, mentre per i videogiochi sì (per tutta una serie di motivazioni che scopriremo col tempo).
Personalmente non credo che gli acquirenti di schede RTG si ponessero il problema di veder girare i giochi preferiti sulle loro evolute schede grafiche. Il pensiero li avrà sicuramente sfiorati, ma gioielli del genere erano estremamente costosi, e chi se ne accaparrava uno lo faceva per sfruttarlo in ambiti professionali (disegno, fotoritocco, modellazione 3D, animazione 2D e 3D, CAD, DTP, ecc.).
Tornando alla sostituzione delle API (detta patch in gergo amighista), può essere sufficiente oppure no, a seconda degli scenari che si prospettano. Il più comune all’epoca era quello dell’aggiunta di una scheda grafica all’Amiga, che in genere era vista come una scheda d’espansione e dotata di apposita ROM con tanto di codice di inizializzazione.
In questo caso il chipset dell’Amiga rimaneva pienamente operativo, mentre il codice di boot della scheda RTG si occupava, sì, di patchare tutte le API necessarie della graphics.library, ma aggiungendo nuove modalità grafiche (permettetemi di semplificare il discorso) a quelle esistenti, e decidendo di richiamare il nuovo codice oppure il vecchio di ogni singola API a seconda dello schermo su cui l’applicazione stava lavorando.
Tutto ciò è stato possibile anche grazie al supporto per le schede RTG che è stato introdotto nella versione 3.0 di AmigaOS, che ne ha semplificato l’implementazione.
Molto più complicato è lo scenario di una macchina come il DraCo, su cui girava AmigaOS, ma su un hardware effettivamente sprovvisto del chipset (rimanevano soltanto i due CIA, praticamente per gestire il solo l’I/O) e dotato esclusivamente di una scheda video di nuova generazione (appositamente progettata per questo sistema).
Qui l’approccio precedente usato per le schede RTG non è più possibile, in quanto all’avvio il Kickstart (un insieme di librerie e codice di startup, che rappresenta il cuore di AmigaOS, e che risiede nella ROM di sistema) esegue diversi controlli e accede a precise locazioni di memoria, nelle quali sono mappati anche i registri dei chip custom. Un disassemblato commentato della versione 1.2 mostra tutto il processo (e anche le API esposte di exec.library, che si può considerare una sorta di BIOS e kernel di AmigaOS).
In particolare Exec (che è la parte del Kickstart che si occupa di tutta questa parte) assume che sia sempre presente la memoria chip (l’unica accessibile per i chip custom dell’Amiga), che parte dall’indirizzo 0 e si estende fino a 2MB. Inoltre, come dicevo, legge e scrive (soprattutto) nelle locazioni dove sono mappati i 256 registri del chipset, che vanno da $DFF000 a $DFF1FF.
Per risolvere questi problemi la soluzione non è difficile. E’ sufficiente mappare parte (o tutta) la memoria a partire dalla locazione 0 (zero), per almeno 256KB (che è il minimo di chip mem che dev’essere presente), e poi un’altra zona di memoria che va a coprire l’area dei registri dei chip custom.
Fatto questo è sufficiente che venga eseguito il codice presente nella ROM della scheda video, per provvedere quindi a patchare tutte le funzioni della graphics.library (in modo da eliminare ogni riferimento ai chip custom per la parte video, Copper, e Blitter, e accedere direttamente e soltanto ai registri della scheda video avanzata).
Ci sarebbero poi da patchare alcune API di exec.library che provvedono a gestire gli interrupt, in quanto nel chipset dell’Amiga vengono usati alcuni registri di Paula per la loro gestione. Altre patch sarebbero necessarie per l’audio.device, che è il dispositivo che si occupa di gestire il suono (di cui si occupa sempre Paula).
Anche il trackdisk.device dovrebbe subire la stessa sorte, in quanto il controller del floppy è implementato in maniera diversa (la parte più consistente era gestita sempre da Paula, e la codifica e la decodifica MFM della tracce scaricata sul Blitter). Lo stesso, infine, per il mouse, gestito anch’esso da Paula, tramite l’input.device.
A questo punto il sistema è pronto, per cui potrà procedere a completare la sequenza di boot cercando un disco bootabile, estraendone il primo KB, e lasciando il controllo alla routine in essa presente, che provvederà a caricare il Workbench. Il tutto senza accedere più in nessun modo ai chip custom, fatta eccezione per eventuali applicazioni che dovessero farlo e che, quindi, non funzionerebbero o potrebbero anche bloccare il sistema.
D’altra parte il compito di un s.o. è di astrarre l’accesso all’hardware da parte delle applicazioni, per cui dovrebbe essere compito suo e non dei programmi utente. Inoltre, anche se AmigaOS presenta parecchie API e strutture che si mappano naturalmente sui chip custom dell’Amiga, ciò non toglie che tutto ciò possa essere emulato da un hardware anche completamente differente e in maniera del tutto trasparente.
L’importante, al solito, è che le API facciano fede al loro compito, per cui se è stato chiesto di visualizzare uno sprite, ad esempio, interessa soltanto che a video sia presente la grafica che lo rappresenta, e poco importa se viene usato un buffer interno per conservare la grafica sottostante per poi ripristinarla (in modo da emulare la presenza dello sprite hardware).
Infine lo scenario peggiore è ovviamente rappresentato dal caso in cui nemmeno i due CIA siano presenti. Non sono al corrente di soluzioni di questo tipo, ma in linea teorica sarebbe possibile gestire anche questo caso, che risulta il più complicato, seguendo la stessa strada del Draco, avendo l’accortezza di mappare un po’ di memoria fra $BFD100 e $BDE00F, in modo da poter simulare la scrittura e la lettura nei registri dei due CIA.
Una soluzione alternativa potrebbe essere quella di gestire interamente la sequenza di avvio, sfruttando il fatto che la prima cosa che fa Exec è riconoscere una ROM opportunamente codificata a partire dall’indirizzo $F00000, e quindi cederle completamente il controllo. Generalmente l’esecuzione viene poi nuovamente passata al codice della ROM principale (che, per inciso, si trova all’indirizzo $FC0000), ma volendo la nuova ROM si può occupare di tutto il processo.
In questo caso potrebbero tranquillamente essere omesse tutte le istruzioni che accedono ai CIA e/o ai chip custom e, dunque, l’esigenza di mappare opportunamente della memoria nelle loro due aree, per simularne la presenza. Si potrebbe riciclare buona parte della vecchia ROM, ma il lavoro da fare sarebbe comunque notevole…