Di recente ho parlato del SID , il chip responsabile del sonoro nel glorioso Commodore 64, e di come sapientemente fosse stato sfruttato per regalarci delle bellissime colonne sonore, ancora oggi degne d’essere ascoltate.
Conoscendo le limitazioni dell’hardware e della dotazione software degli home computer dell’epoca, rimane abbastanza difficile comprendere in che modo tutto ciò sia stato reso possibile.
Infatti sappiamo che, nonostante fosse il linguaggio più abbordabile e di semplice utilizzo, il BASIC era molto lento e, soprattutto, spesso non offriva alcun supporto per l’accesso all’hardware, tanto meno per gestire la musica.
Si doveva passare necessariamente dal linguaggio macchina oppure, successivamente, dall’assembly con tutte le difficoltà che potete immaginare…
Oltre alla possibilità di poter manipolare a piacimento i registri del chip sonoro, l’uso di questi linguaggi si rendeva necessario anche e soprattutto per l’efficienza, sia in termini di tempo di calcolo speso nella gestione dell’audio, sia per quanto riguarda lo spazio occupato dal codice del player (questo è il nome utilizzato, in gergo, dal software di riproduzione musicale) e dai dati che dovevano essere spediti al chip.
Un videogioco (o una demo: il discorso è sempre lo stesso) non può essere monopolizzato dalla musica: le risorse sono poche e devono essere distribuite bilanciandone l’uso per tutte le componenti che concorrono alla realizzazione del prodotto finale. Fermo restando che si potrebbe benissimo decidere di privilegiare un aspetto piuttosto che un altro, a seconda delle esigenze.
Ovviamente la distribuzione delle risorse dipende dalla loro quantità e tipologia, e in particolare per la musica dipende anche dalle capacità dell’home computer per quanto riguarda la sintesi sonora. Capacità che non erano simili, ma, anzi, molto variegate nel panorama di questo florido mercato della prima metà degli anni ’80.
Già, perché si spaziava dal classico “cicalino” (lo speaker che è stato reso “famoso” dai PC) ai sintetizzatori dotati di più voci, come il SID. Generalmente erano disponibili anche espansioni (tramite cartucce o dispositivi collegati a porte esterne) che montavano chip più evoluti (dotati anche di DAC), ma non erano standard e, quindi, poco supportante, in quanto il target di riferimento dei programmatori era rappresentato dall’hardware “nudo e crudo”.
Programmare lo speaker concettualmente era estremamente semplice: bastava attivarlo oppure no, generando quindi un impulso sonoro. La difficoltà, però, si presentava subito nel momento in cui si decideva di voler generare un suono a una certa frequenza. Per far ciò si rendeva necessario programmare un timer che “scattasse” a una certa frequenza per accendere o spegnere opportunamente l’altoparlante.
Immaginate il lavoro per riprodurre più forme d’onda: si dovevano effettuare prima i calcoli per stabilire la forma d’onda risultante dalla “somma” di quelle da riprodurre, e successivamente programmare lo speaker per riprodurla effettivamente.
Tutto ciò comportava un enorme dispendio di risorse da parte della CPU, in particolare a causa delle numerosissime richieste di interrupt (provenienti dal citato timer) che doveva servire per arrivare allo scopo. Ma non c’erano altre soluzioni, se si volevano raggiungere risultati quanto meno decenti.
Di gran lunga più semplice il lavoro coi sistemi dotati di sintetizzatori multicanale programmabili: era sufficiente specificare la frequenza del segnale da riprodurre, la sua forma d’onda, ed eventualmente anche il volume, per arrivare all’obiettivo minimizzando il carico sul processore. Il limite in questi casi era ovviamente rappresentato dal numero di canali a disposizione.
Qualunque fosse il caso (speaker o sintetizzatore), generalmente il programma per la riproduzione della musica era scritto… dallo stesso musicista, e ciò ha portato a un proliferare di player, anche diversi per opere dello stesso autore.
Ciò era particolarmente vero nel caso dei sintetizzatori, dove i musicisti realizzavano lo “spartito” con applicazioni proprie, passando anche parecchio tempo a sperimentare coi registri del chip sonoro andando a caccia di effetti speciali da realizzare.
Il problema più grosso in questi casi era rappresentato dal fatto che per un medesimo home computer esistevano modelli diversi, con caratteristiche anche molto diverse, la cui causa principale era da imputarsi alla gestione di segnali televisivi come NTSC, PAL e SECAM, che comportavano l’uso di clock diversi per generare il segnale video.
All’epoca era molto comune l’uso di un solo clock “di base” da cui generare tutti quelli utilizzati dai chip presenti nel sistema (CPU, chip video, chip sonoro, timer). Il clock principale era ovviamente quello video, perché la necessità primaria era quella di generare un segnale corretto che fosse “agganciabile” senza problema dai televisori (che riconoscevano soltanto quel tipo di segnale).
Quindi un computer con uscita video NTSC “imponeva” un preciso clock anche a CPU e tutto il resto, che ovviamente avevano un clock diverso rispetto ad un altro dotato di uscita PAL.
La realizzazione dei player musicali diveniva quindi non semplice perché il sonoro generato doveva avere una precisa temporizzazione, indipendentemente dal clock della CPU, del chip video e dei timer. La temporizzazione serviva a impostare i registri del chip sonoro con una certa frequenza (ad esempio 60 volte al secondo), in modo da aggiornare i registri dei canali in maniera opportuna.
In realtà una pratica comune era quella di sfruttare l’interrupt generato dal chip video all’inizio di un vertical sync (in sostanza il chip aveva appena finito di “disegnare” lo schermo) per far partire il codice del player e, quindi, l’aggiornamento della musica.
Questo comportava ovviamente dei problemi passando, ad esempio, da un sistema NTSC (nel quale il vsync veniva generato 60 volte al secondo) a uno PAL (il vsync era generato 50 volte al secondo), con prevedibile e fastidiosa distorsione della musica, che veniva riprodotta più lentamente o più velocemente.
Una soluzione molto più precisa, e che permetteva di mantenere una temporizzazione praticamente perfetta, era quella di programmare un timer (generalmente ne era presente almeno uno liberamente utilizzabile) per far scattare un interrupt a una precisa frequenza, indipendentemente dal clock.
Sembrerà strano, ma la soluzione più comune non era questa, ma quella del vsync. Ciò perché il vsync veniva sempre utilizzato per aggiornare la grafica (leggendo l’input dell’utente, da tastiera o joystick) e, quindi, veniva comodo lanciare la routine del player. Col timer, invece, sarebbe stato necessario gestire due interrupt.
Non era, pertanto, inusuale avere versioni diverse dello stesso gioco (o, più raramente, delle demo) per il mercato nordamericano e quello europeo, proprio per le suddette differenze. Nonostante tutto, comunque, ci siamo goduti dei capolavori…