Nei retrocomputer le risorse di memoria sono, spesso, assai limitate e in tali condizioni la dimensione di un programma eseguibile (comprensivo di codice e dati embedded) è limitata necessariamente a quella della memoria centrale.
Ad esempio, questa è la memoria contigua disponibile1) su vari elaboratori della Commodore:
Questo può costituire un limite molto forte, soprattutto se si programma con linguaggi come il C.
Per superare questa limitazione si può adottare una tecnica molto diffusa negli anni '80 e chiamata overlay. L'overlay è un metodo di programmazione che consente ai programmi di essere più grandi della memoria principale del computer, ed è una tecnica oggi molto comune nei sistemi embedded, proprio a causa della limitazione della memoria fisica.
Il concetto degli overlay è semplice: quando un programma è in esecuzione, di solito non utilizza tutto il codice allo stesso tempo ma ne utilizzerà solo una parte. Quella parte è necessaria mentre le altre non lo sono. Ne consegue che non è necessario disporre effettivamente dell'intero programma all'interno della memoria principale.
Abbiamo bisogno della parte richiesta in quell'istanza e sarà quindi sufficiente caricare, di volta in volta, la parte necessaria sostituendola a una precedente (e non più necessaria) perché il programma possa continuare a funzionare.
La costruzione di un programma overlay comporta, pertanto, la divisione manuale di un programma in blocchi di codice oggetto autonomi chiamati “moduli”. I moduli condividono la stessa area di memoria che viene chiamata “regione di sovrapposizione” o area di destinazione (“overlay area”). Un gestore di overlay carica l'overlay richiesto dalla memoria esterna nella sua area di destinazione, quando è necessario.
Un programma che sfrutta questa tecnica dovrà, quindi, essere suddiviso in due parti:
La parte residente conterrà tutte le funzioni che sono utilizzate con maggior frequenza, e che potranno (eventualmente) essere richiamate anche dalle parti che saranno di volta in volta caricate. Si divide, quindi, il resto del programma in moduli in modo tale che non tutti i moduli debbano risiedere nella memoria allo stesso tempo, e tali moduli saranno caricati (di volta in volta) nell'area di destinazione.
Questa tecnica permette, pertanto, di eseguire un programma molto più grande della dimensione della memoria fisica, mantenendo in memoria solo le istruzioni e i dati che sono necessari in un dato momento. Questo è il vantaggio principale e unico.
Gli svantaggi di tale tecnica sono:
Si sottolinea che è responsabilità del programmatore occuparsi degli overlay, perché il sistema operativo non fornirà alcun tipo di supporto. Ciò significa che il programmatore dovrà esplicitamente scrivere anche quale parte è richiesta da ogni modulo. Questa responsabilità è nota, in gergo, come “gestione overlay”. L'implementazione corretta di questa gestione permette di spostarsi nelle varie sezioni del codice.
Il programma dimostrativo è stato scritto per il Commodore 64, e quindi è stato scelto il Commodore VIC 20 come hardware obiettivo, essendo caratterizzato dalla poca memoria contigua, ed è stato dotato di uno strumento di memoria di massa (disk drive). Il risultato è stato testato su un Emulatore VICE in esecuzione sotto Microsoft Windows 10.
A livello di algoritmi la scelta è caduta su CC65, un ambiente di cross compilazione in linguaggio C per retrocomputer basati su CPU 6502/6510 e che è dotato di un linker che gestisce l'overlay.
Ho scritto un programma dimostrativo molto semplice, che mostra parte dei primi due canti dell'Inferno della Divina Commedia, in lingua italiana e inglese. Il programma è stato predisposto senza tenere particolar conto di ottimizzazioni, quali il memorizzare i testi su di un file esterno, e questo allo scopo di dare maggior evidenza dello spazio occupato.
Il programma consta di una serie di funzioni che interagiscono con l'utente, tra loro disgiunte, e orchestrate dalla funzione principale (main). Alcune di queste, a loro volta, fanno uso di altre funzioni di servizio.
Ai soli fini di organizzazione, le varie funzioni sono state distribuite tra alcuni sorgenti. Ne consegue che, compilando tutti i sorgenti, si ottiene un unico eseguibile che occupa circa 5 Kb.
Tale dimensione, pur essendo gestibile dal Commodore 64, va putroppo oltre la disponibilità di memoria sul VIC 20. In altri termini, nonostante il CC65 possa compilare tale programma su entrambe le piattaforme hardware, il VIC 20 (inespanso) non è in grado di eseguirlo.
Adottando la tecnica dell'overlay, si procede promuovendo il modulo “main.c” a “modulo residente” del processo; i restanti moduli si alterneranno, quindi, nell'area di destinazione. In tal modo, l'eseguibile occupa ora circa 2 Kb e quindi anche il VIC 20 (inespanso) è in grado di caricare il primo modulo. Quest'ultimo, sfruttando la logica del gestore di overlay, si occuperà di caricare gli altri.
Usando l'emulatore VICE, è possibile richiamare il programma attaccando l'immagine disco del VIC 20, e caricando il primo programma.
Questa è la schermata di un programma da 5 Kb in esecuzione su un Commodore VIC20 inespanso:
Il repository ufficiale per questo progetto è qui.