Programmare come se fosse il 1982. Parte III – Memoria e Indirizzi
Nel post precedente abbiamo visto che il processore ha tre registri di uso generale e un ciclo di esecuzione semplice e ininterrotto. Il problema è che tre registri non sono sufficienti per nessun programma reale. Hai bisogno di un posto dove conservare i dati mentre non li stai usando, conservare il programma stesso, conservare variabili e risultati intermedi. Quel posto è la RAM.
La RAM è un array gigante
Se hai mai usato un array in qualsiasi linguaggio, hai già capito la RAM. La memoria del C64 era esattamente questo:
RAM = [0] * 65536 # 65.536 posizioni, ognuna contiene 1 byte
Ogni posizione contiene un byte, un numero da 0 a 255. Nient’altro. Testo, immagini, suono, codice: tutto diventa byte. E ogni posizione ha un indirizzo, un numero che identifica dove si trova, esattamente come l’indice di un array.
Per leggere o scrivere, dicevi al processore: “vai all’indirizzo X e prendi ciò che c’è lì” oppure “vai all’indirizzo X e metti questo valore lì”. Nessuno strato, nessuna astrazione. Diretto.
Perché l’esadecimale
Gli indirizzi nell’assembly del C64 si scrivevano in esadecimale, con un $ davanti: $0000, $D418, $FFFF. A prima vista sembra arbitrario, ma c’è una ragione molto pratica.
L’esadecimale usa 16 cifre invece delle 10 del decimale: da 0 a 9, poi A, B, C, D, E, F. La conseguenza è che un byte, qualsiasi valore da 0 a 255, entra sempre in esattamente due cifre esadecimali, da $00 a $FF. Due byte, come un indirizzo di memoria, entrano sempre in quattro cifre, da $0000 a $FFFF.
In decimale, lo stesso byte richiederebbe da 1 a 3 cifre a seconda del valore. Allineare, confrontare e leggere indirizzi sarebbe un caos. In esadecimale, tutto ha una dimensione fissa e prevedibile. Una volta che ci si abitua, diventa naturale come leggere l’ora su un orologio di 12 ore.
| Decimale | Esadecimale |
|---|---|
| 0 | $00 |
| 15 | $0F |
| 16 | $10 |
| 255 | $FF |
| 256 | $0100 |
| 65535 | $FFFF |
La mappa di memoria
Ecco la differenza centrale tra programmare sul C64 e programmare oggi: la memoria non era tutta uguale.
Ogni intervallo di indirizzi aveva un ruolo fisso, e dovevi sapere a memoria cosa si trovava dove. Alcuni indirizzi erano RAM normale dove conservavi dati e codice. Altri erano porte dirette verso i chip hardware. Scrivere in quegli indirizzi non salvava nulla, attivava l’hardware immediatamente. E altri erano ROM, codice masterizzato nel chip che potevi leggere ma non modificare.
Nessun sistema operativo era lì a spiegare cosa fosse cosa. C’era la mappa di memoria, e la memorizzavi.
La Zero Page: 256 byte d’oro
L’intervallo da $0000 a $00FF aveva un nome proprio: Zero Page. Solo 256 byte, ma valevano molto più di qualsiasi altro byte della RAM.
Il motivo era tecnico. Un indirizzo normale ha bisogno di 2 byte per essere rappresentato. Per esempio, $0801 occupa un byte per $08 e un altro per $01. Un indirizzo nella Zero Page ha bisogno di solo 1 byte, perché il processore sa già che il primo byte è zero. Meno byte nell’istruzione significa un’istruzione più piccola e più veloce da eseguire.
Istruzione che accede alla RAM normale:
LDA $0801 → 3 byte in memoria, 4 cicli di clock
Istruzione che accede alla Zero Page:
LDA $42 → 2 byte in memoria, 3 cicli di clock
Un ciclo di clock in meno può sembrare irrilevante. Ma all’interno di un ciclo che gira migliaia di volte per fotogramma video, e il C64 disegnava lo schermo 50 volte al secondo, la differenza era enorme. I programmatori trattavano ogni byte della Zero Page come una risorsa scarsa. Le variabili critiche andavano lì. I contatori di ciclo andavano lì. I puntatori a cui si accedeva centinaia di volte al secondo andavano lì.
Era, in sostanza, una cache manuale. Decidevi tu cosa era abbastanza importante da meritare un indirizzo nella prima pagina.
Nel prossimo post lasciamo la mappa di memoria e vediamo cosa fa davvero il processore con tutto questo: come le istruzioni sono rappresentate in byte, cos’è un opcode e i primi veri comandi assembly del 6510.