Una scheda audio per il Ceda
Negli ultimi due anni ho dedicato parte del mio tempo libero a un progetto di reverse engineering piuttosto fuori dal comune: documentare il Ceda, un computer a 8 bit degli anni ‘80 trovato per caso su un marciapiede 1. Questo computer esegue il CP/M (un sistema operativo predecessore del MS-DOS) caricandolo ad ogni accensione da un dischetto. Le capacità grafiche sono limitate, così come gli effetti sonori: è presente solamente un cicalino per la segnalazione degli errori. Dopotutto si tratta di un computer auspicabilmente usato per la contabilità d’ufficio, l’interfaccia utente è ridotta al minimo sindacale. Quindi, perché non provare a fargli fare qualcosa di più accattivante, per esempio… suonare un po’ di musica?
Recuperare un chip audio
Certo, la via breve sarebbe quella di utilizzare un chip moderno, o magari una implementazione FPGA… ma poi che gusto ci sarebbe? All’ultima fiera di Marzaglia2 ho recuperato una scheda guasta ricca di chip molto interessanti, penso proveniente da un cabinato. Fra questi, il chip PSG (Programmable Sound Generator) Texas Instruments SN76489. Perché non usare proprio lui, se funziona, per questo folle progetto?
Il 76489 è un chip molto usato negli home computer e nelle prime console anni ‘80 per la generazione di musica ed effetti sonori. Dispone di tre voci indipendenti, tutte rigorosamente ad onda quadra, e di una voce supplementare dedicata al generazione del rumore, tipicamente utilizzata per simulare percussioni o esplosioni. È facile da programmare: ciascuna voce dispone internamente di una coppia di registri, frequenza e volume, che possono essere modificati tramite una interfaccia parallela. La frequenza determina l’altezza del suono, mentre il volume ne regola l’intensità. Con l’aiuto di un programma appositamente scritto, che rappresenta la vera e propria partitura, questi due parametri sono sufficienti per generare melodie più o meno articolate.
Ma prima di arrivare a scrivere il software, progettare circuiti di interfaccia e mettere mano al saldatore, vale la pena provare il chip su breadboard utilizzando un Arduino. Perché tanto, se il chip è guasto, è inutile andare avanti.

Per cui, seguendo le direttive del manuale - breve e conciso - ho collegato i fili come richiesto ed ho scritto un primo programma di prova. L’interfaccia hardware è molto semplice:
- i pin da
D0aD7rappresentano il bus parallelo ad 8 bit, ovvero dove si immettono i dati di tono e frequenza; /OE,/WEeREADYsi utilizzano per cadenzare l’immissione di un nuovo dato. controllando OE insieme a WE si richiede che il dato venga trasferito dal bus ai registri interni, mentre leggendo lo stato diREADYsi ha conferma dell’avvenuto trasferimento (peraltro, curiosa la scelta di Texas Instruments di cambiare il nome di/OEin/CEstrada facendo, tanto per rendere tutto più chiaro…) 3;- senza troppe sorprese, CLOCK è il segnale di clock in ingresso, utilizzato come frequenza base per la sintesi sonora. In questo caso è stato usato direttamente il clock di sistema dello Z80 a 4MHz;
- ed infine, AUDIO OUT è l’uscita verso le cuffie, che va rigorosamente disaccoppiata con un condensatore.
Senza molti fronzoli, l’idea era di caricare i parametri di una sola voce per generare un tono fisso. Ma, come si può immaginare, non ha funzionato… In prima battuta ho classificato il chip come guasto, ma qualcosa si sentiva sebbene non fosse quel che avevo chiesto. Modificando leggermente il codice si apprezzavano dei cambiamenti, tanto è bastato per proseguire con le indagini.
In effetti, il manuale aveva qualcosa di strano: i numeri dei bit sembravano tutti scritti alla rovescia. Tipicamente si numerano da destra a sinistra, iniziando dal bit meno significativo (LSB) e poi a crescere. Ma in questo caso era tutto al contrario: sia i bit dei registri che, soprattutto, i bit del bus dati. Nel primo caso avevo inconsciamente interpretato correttamente l’inversione, ma non nel secondo. Il risultato? I dati arrivavano al chip rovesciati, ed il poveretto li interpretava per quel che erano.
Scambiando l’ordine dei fili il tono desiderato si è fatto sentire: il chip funziona! Ma adesso… cosa dovrebbe suonare?
Farlo suonare per davvero
Fortunatamente il web ha infinite risorse e qualcuno ha una risposta al mio interrogativo. Infatti ho scoperto l’esistenza del formato VGM (Video Game Music), pensato appunto per riprodurre le musiche scritte per le console basate sul 76489 ed esteso per supportare anche altri chip sonori. Però non tutte le melodie scaricabili sono riproducibili col mio setup, perché tipicamente le console affiancavano altri chip al PSG, così da avere una polifonia più spinta (ben 6 voci invece di 3!). Fortunatamente, sfruttando i filtri del sito, c’è stato modo di recuperare qualche traccia dedicata esclusivamente ad un solo chip.
Le specifiche del formato VGM sono pubbliche e non è stato difficile scrivere un semplice parser e player per Arduino. Peraltro, non c’è stato neanche bisogno di implementare la specifica per intero, visto che per questo chippino bastano sì e no cinque comandi:
| Comando | Argomenti | Significato |
|---|---|---|
0x50 |
dd |
Scrivi il dato dd sul bus |
0x61 |
nn nn |
Attendi nnnn campioni (1 campione = 1/44100 s) |
0x62 |
- |
Attendi 1/60 di secondo |
0x66 |
- |
Fine del brano |
0x7n |
- |
Attendi n+1 campioni |
Poi ho banalmente copiato volta volta ciascun file VGM nella flash interna del microcontrollore, senza dare la possibilità di cambiare pezzo se non caricando un nuovo programma. Il player su Arduino non è il punto di arrivo di questo progetto e tanto mi basta per fare qualche prova un po’ più elaborata rispetto al banale tono.
Collegamento al computer
La motherboard del Ceda era stata progettata per avere già tutto il necessario per svolgere i tipici compiti quotidiani, senza necessità di installare componenti aggiuntivi. Ma, come tutte le macchine di quell’epoca, c’è sempre una porta di espansione, qui sotto forma di provvidenziale connettore a vaschetta marchiato EXBUS. Come suggerisce il nome, espone l’intero bus del processore, insieme ad alcuni segnali di controllo aggiuntivi utili per agganciarsi alla logica preesistente sulla scheda madre.
Per poter interfacciare il chip al bus Z80 è necessario almeno un ulteriore componente: un decoder. Questo è necessario per riservare una fetta di indirizzi I/O alla scheda audio, evitando che questa andasse in conflitto con le periferiche preesistenti.
Ho inizialmente ignorato il pin di READY del 76489, perché immaginavo di doverlo rileggere manualmente, ma non avevo molta voglia di dover implementare una logica bidirezionale di lettura e scrittura.
Dopotutto, il manuale riporta che il READY serve soltanto per accertarsi che ciascuna scrittura venga registrata correttamente, ma soprattutto che il 76489 richiede circa 32 cicli di clock per caricare i dati nei registri di controllo.
Quindi basterà aspettare un po’ di più dopo ogni scrittura, e tutto dovrebbe funzionare.
Ovviamente il primo test, la riproduzione del solito tono monofonica, non ha funzionato neanche per sbaglio. Cercando di capire come mai questa sequenza di comandi funzionasse su Arduino ma non sul Ceda, mi sovviene un dubbio: la scrittura I/O dello Z80 dura pochi cicli, indipendentemente dalle pause che si possono inserire in seguito. Vuoi vedere che il chip ha letteralmente bisogno che la scrittura tanga il dato fermo per 32 cicli di clock? E, in caso, c’è un modo per mettere la CPU in pausa su richiesta?
Ovviamente sì, all’epoca dello Z80 si aveva spesso a che fare con periferiche lente, per questo motivo è stato introdotto il pin di /WAIT, che chiede alla CPU di fermarsi ad aspettare.
/WAIT è esattamente il duale di READY, per cui dovrebbe bastare collegarli insieme senza alcun componente in mezzo. Ed infatti, con questo semplice collegamento, la nota ha cominciato a suonare.
Naturalmente, a questo punto è necessario portare il player VGM, scritto per Arduino, sulla piattaforma finale. In pochi minuti, e facendo un po’ di copia-incolla da altri software scritti per il Ceda, c’è una versione funzionante di VGMPLAY per CP/M, ovviamente tutto scritto in Z80 ASM.
Disponendo di una memoria di massa, il dischetto da 5"¼, il VGMPLAY permette di caricare i file VGM salvati, stampandone i metadati e poi riproducendo la musica.
Ma ovviamente non è finita qui…
Le ultime rifiniture
Ogni tanto si verificavano fenomeni paranormali: durante l’uso del Ceda, eseguendo applicativi che non c’entrano niente con la nuova scheda audio, il computer si bloccava. Evidentemente, ho pensato, sarà il circuito instabile su breadboard che si presta maggiormente a glitch e rimbalzi di segnale. Per cui ho pensato di riportare il circuito su di una millefori, così da stabilizzarlo e renderlo più compatto.
Certo, questa mossa ha rimosso dei ronzii fastidiosi sul canale audio, ma i blocchi continuavano ad avvenire.
Ad una prima occhiata, mi sono accorto che la causa immediata del blocco stava nella linea di /WAIT tirata permanentemente giù proprio dal chip audio.
Chiaramente, in questa condizione la CPU non può far altro che aspettare e diventa addirittura insensibile al reset, per cui gli unici modi per riprendere l’esecuzione sono spegnere il computer o staccare fisicamente la scheda audio.
Ma questa non è certo una soluzione, per quale ragione il chip veniva indirizzato casualmente?
Curiosamente, avevo osservato che i blocchi avvenivano con il sistema operativo avviato e quando utilizzavo la tastiera. Per quanto ho avuto modo di vedere, la tastiera è l’unica periferica che viene configurata per generare delle interruzioni, e questo è presagio di magagne. Ma per risolvere questo mistero è stato necessario sfoderare l’oscilloscopio digitale per ispezionare il bus ed i segnali di controllo, operazione per niente facile visto che il modulo di analisi segnali ha 16 canali, e solo il bus degli indirizzi dello Z80 è a 16 bit… Ma insomma, dopo ore di ispezione, esce fuori un pattern costante ad ogni condizione di blocco, e chi conosce bene lo Z80 avrà già capito.
Qui mi torna in mente com’è che è cablato il decoder delle periferiche interne che è sulla scheda madre: per attivarsi, oltre che IOREQ, deve essere presente anche il segnale di lettura RD oppure quello di scrittura WR.
Cosa che avevo giudicato senza senso: è ovvio che se la CPU indirizza una periferica, vorrà o scrivere o leggere, cos’altro ci dovrebbe fare?
E invece, manuale alla mano, c’è un’altra cosa che potrebbe voler fare… questo pattern si scatena solo in una particolare modalità di interrupt e serve per mettersi in attesa del cosiddetto “vettore di interruzione” 4.
Senza perdersi troppo in tecnicismi, è solo un caso che la scheda audio venisse indirizzata: il povero chip si vedeva interpellato e per tutta risposta faceva richiesta di /WAIT per poter gestire con i suoi tempi una richiesta di scrittura… che non sarebbe mai arrivata.
Saldatore alla mano, modifico il circuito di indirizzamento in maniera leggermente diversa, per non dover introdurre altra logica, e ripetendo le prove mi accerto che, a questo punto, non vi siano più condizioni di blocco.
Sembrerebbe funzionare…
Conclusioni
La scheda audio è completata e, seppur in una forma estetica non propriamente eccelsa, fa il suo lavoro. Il player VGM gira correttamente sotto CP/M ed il Ceda ha adesso una propria voce, qualcosa in più di un banale beep-beep.
Sul piano tecnico, ora è finalmente chiaro perché la logica di indirizzamento I/O del sistema includa quel controllo in più sui segnali di lettura e scrittura. Certo, lo si poteva dedurre leggendo con attenzione il manuale dello Z80, ma scoprirlo sul campo è stato decisamente più istruttivo e divertente.
Già così il risultato è più che soddisfacente, ma sarebbe interessante fare un passo in più: integrare la riproduzione musicale in una piccola demo interattiva, che metta in evidenza anche tutte le altre modifiche hardware realizzate nel frattempo 5.
Infatti, oltre al puro piacere di smanettare e sperimentare, questa demo potrebbe avere un secondo fine: stupire con effetti speciali e suoni ultravivaci gli avventori della prossima mostra del retrocomputing di Brusaporto 6. Le nostre modifiche vorrebbero essere un simpatico pretesto per raccontare la storia del reverse engineering di questo computer dimenticato.
Tutti i codici usati per questo progetto sono liberi e consultabili nel repository GitHub ceda-76489.
Note
-
le mirabolanti avventure di questo ritrovamento ed il conseguente processo di ricerca sono oggetto di un talk tenuto al FOSDEM insieme a giomba. Il resto della documentazione tecnica è raggiungibile tramite il repository ceda-home; ↩︎
-
per chi non lo sapesse, a Marzaglia si tiene due volte l’anno un mercatino di elettronica, meta di radioamatori a caccia di occasioni. Ma la sua fama è nota anche nel mondo del retrocomputing, visto che non è raro trovare qualche pezzo vintage a buon prezzo; ↩︎
-
anche leggendo il manuale potrebbe sembrare che
/CEe/WEfacciano la stessa cosa, per cui verrebbe da chiedersi perché avere due pin anziché uno solo. Questa è una predisposizione per interfacciarsi con un bus standard di un processore, come può essere quello dello Z80. È previsto che ciascuna periferica abbia un segnale, comune con tutte le altre periferiche, di richiesta di scrittura. Questo è appunto/WE. Poi, per selezionare la specifica periferica, serve un segnale aggiuntivo di abilitazione, generato a partire dall’indirizzo. E questo è il/CE. ↩︎ -
per ulteriori approfondimenti, rimando direttamente al manuale Z80, pagina 12: Interrupt Request/Acknowledge Cycle; ↩︎
-
giomba, collega di sperimentazioni, ha già aggiunto una modifica alla “scheda video” per poter mostrare semplici immagini raster; ↩︎
-
brusaporto retrocomputing, manifestazione di riferimento nel settore; ↩︎