Memoria
🎉
Congratulazioni, avete appena finito le lezioni più difficili del tutorial! Dal momento che avete le basi, d’ora in poi vedremo codice sempre più concreto.
Se guardiamo alla riga 29 leggiamo ld a, [de]
.
Stando a quanto abbiamo appena appreso, questo copia un valore nel registro a
… ma da dove?
Cosa significano queste parentesi?
Per rispondere dobbiamo parlare della memoria.
Cos’è una memoria?
Lo scopo della memoria è quello di immagazzinare informazioni. Su un foglio di carta o su una lavagna si possono scrivere lettere per memorizzare, ad esempio, la lista della spesa. Ma cosa si può memorizzare nella memoria di un computer? La risposta a questa domanda è corrente1. La memoria del computer è costituita da piccole celle in grado di immagazzinare corrente. Ma, come abbiamo visto nella lezione sul binario, la presenza o l’assenza di corrente può essere utilizzata per codificare i numeri binari!
Per farla breve: la memoria immagazzina numeri. Difatti la memoria è una lunga lista di numeri, memorizzati in celle. Per identificare in modo univoco ogni cella, le viene assegnato un numero (e che altro!) chiamato indirizzo. Come i numeri civici! La prima cella ha l’indirizzo 0, poi l’indirizzo 1, 2 e così via. Sul Game Boy ogni cella contiene 8 bit, cioè un byte.
Quante cellule ci sono? In realtà questa è una domanda trabocchetto…
I tanti tipi di memoria
There are several memory chips in the Game Boy, but we can put them into two categories: ROM and RAM 2. ROM simply designates memory that cannot be written to3, and RAM memory that can be written to.
Per via del loro funzionamento il processore ed i circuiti di memoria possono utilizzare un solo numero per gli indirizzi. Torniamo all’analogia dei “numeri civici”: ogni circuito di memoria è una strada, con la sua serie di numeri, ma il processore non ha idea di cosa sia una strada, si occupa solo di numeri civici. Per consentire al processore di parlare con più circuiti serve una sorta di “servizio postale”, il selezionatore di circuito (“chip selector” in inglese), che ha il compito di tradurre i numeri civici del processore in una via ed un numero civico effettivi.
For example, let’s say a convention is established where addresses 0 through 1999 go to chip A’s addresses 0–1999, 2000–2999 to chip B’s 0–999, and 3000–3999 to chip C’s 0–999. Then, if the CPU asks for the byte at address 2791, the chip selector will ask chip B for the byte at its own address 791, and forward the reply to the CPU.
Poiché gli indirizzi trattati dal processore non corrispondono direttamente a quelli dei circuiti si parla di indirizzi logici (qui, quelli del processore) e di indirizzi fisici (qui, quelli dei circuiti), e la corrispondenza è chiamata mappa di memoria (“memory map” in inglese). Poiché stiamo programmando il processore ci occuperemo solo di indirizzi logici, ma è fondamentale tenere presente che indirizzi diversi possono essere supportati da circuiti di memoria diversi, poiché ogni circuito ha caratteristiche uniche.
This may sound complicated, so here is a summary:
- Memory stores numbers, each 8-bit on the Game Boy.
- Memory is accessed byte by byte, and the cell being accessed is determined by an address, which is just a number.
- The CPU deals with all memory uniformly, but there are several memory chips each with their own characteristics.
Mappa di memoria del Game Boy
Adesso rispondiamo alla domanda che ci aveva portato a questa sezione: quante celle di memoria ci sono sul Game Boy? Ora possiamo riformulare meglio questa domanda come “quanti indirizzi logici ci sono?” o “quanti indirizzi fisici ci sono in totale?”.
Gli indirizzi logici, che sono solo numeri, sono a 16 bit sul Game Boy. Pertanto, ci sono 2^16 = 65536 indirizzi logici, da $0000 a $FFFF. Ma quanti sono gli indirizzi fisici? Ecco una mappa della memoria per gentile concessione di Pan Docs (anche se la semplificherò un po’):
Start | End | Name | Description |
---|---|---|---|
$0000 | $7FFF | ROM | The game ROM, supplied by the cartridge. |
$8000 | $9FFF | VRAM | Video RAM, where graphics are stored and arranged. |
$A000 | $BFFF | SRAM | Save RAM, optionally supplied by the cartridge to save data to. |
$C000 | $DFFF | WRAM | Work RAM, general-purpose RAM for the game to store things in. |
$FE00 | $FE9F | OAM | Object Attribute Memory, where “objects” are stored. |
$FF00 | $FF7F | I/O | Neither ROM nor RAM, but this is where you control the console. |
$FF80 | $FFFE | HRAM | High RAM, a tiny bit of general-purpose RAM which can be accessed faster. |
$FFFF | $FFFF | IE | A lone I/O byte that’s separated from the rest for some reason. |
$8000 + $2000 + $2000 + $2000 + $A0 + $80 + $7F + 1 fanno in totale $E1A0, ovvero 57760 byte di memoria che si possono effettivamente usare. E a questo punto potreste chiedervi: “E i restanti 7776 byte? Cosa succede quando provo ad usarli?”; la risposta è: “Dipende, è complicato; evitate di accedervi”.
Etichette
Ok, tutto questo è molto bello, ma non vi aspetterete certo che io tenga in mente tutti questi indirizzi per conto mio, giusto?? Beh, non temere, perché abbiamo le etichette!
Le etichette sono simboli che in pratica permettono di assegnare un nome a un indirizzo di memoria.
Un’etichetta viene dichiarata come alla riga 9 (EntryPoint:
): all’inizio della riga si scrive il nome dell’etichetta, seguito da due punti, ed essa si riferirà al byte immediatamente successivo.
Quindi, per esempio, EntryPoint
si riferisce al ld a, 0
subito dopo (più precisamente, al primo byte di quell’istruzione, ma ci arriveremo quando ci arriveremo).
Sbirciando all’interno di hardware.inc
si vedrà che, per esempio, rNR52
non è definito come un’etichetta.
Il motivo è che queste sono costanti, di cui parleremo più in là; visto che si usano in modo quasi identico alle etichette, per il momento le considereremo uguali.
Scrivere il nome di un’etichetta equivale a scrivere l’indirizzo del byte a cui fa riferimento (con alcune eccezioni che vedremo nella Parte Ⅱ).
Per esempio, si consideri la ld de, Tiles
alla riga 25.
Tiles
(riga 64) si riferisce al primo byte dei dati delle mattonelle; se assumiamo che i dati delle mattonelle finiscono per essere memorizzati a partire da $0193, allora ld de, Tiles
è equivalente a ld de, $0193
!
Cosa sono queste parentesi?
Bene, siamo arrivati a questo punto perché volevamo sapere cosa significano le parentesi in ld a, [de]
.
In pratica, possono essere lette come “all’indirizzo…”.
Per esempio, ld a, b
può essere letto come “copia in a
il valore memorizzato in b
”; ld a, [$5414]
si legge come “copia in a
il valore memorizzato all’indirizzo $5414”, e ld a, [de]
si legge come “copia in a
il valore memorizzato all’indirizzo de
”.
Aspettate, cosa significa?
Beh, se de
contiene il valore $5414, allora ld a, [de]
farà la stessa cosa di ld a, [$5414]
.
Se avete familiarità con il C, queste parentesi sono sostanzialmente il modo in cui viene implementato l’operatore di dereferenziazione.
hli
I lettori attenti avranno notato il ld [hli], a
appena sotto il ld a, [de]
che abbiamo appena studiato.
[de]
ha senso perché è una delle coppie di registri che abbiamo visto un paio di lezioni fa, ma [hli]
?
In realtà, è una notazione speciale, che può essere scritta anche come [hl+]
.
Funziona come [hl]
, ma hl
viene incrementato subito dopo l’accesso alla memoria.
[hld]
/[hl-]
è lo specchio di questa, decrementando hl
invece di incrementarlo.
Un esempio
Quindi, se osserviamo le prime due istruzioni di CopyTiles
:
ld a, [de]
ld [hli], a
…possiamo vedere che stiamo copiando il byte in memoria puntato da de
(cioè il cui indirizzo è contenuto in de
) nel byte puntato da hl
.
Qui, a
serve come memoria temporanea, poiché la CPU non è in grado di eseguire direttamente ld [hl], [de]
.
Già che ci siamo, esaminiamo il resto di .copyTiles
nelle lezioni successive!
Actually, this depends a lot on the type of memory. A lot of memory nowadays uses magnetic storage, but to keep the explanation simple, and to parallel the explanation of binary given earlier, let’s assume that current is being used.
There are other types of memory, such as flash memory or EEPROM, but only Flash has been used on the Game Boy, and for only a handful of games; so we can mostly forget about them.
No, really!
Mask ROM is created by literally punching holes into a layer of silicon using acid, and e.g. the console’s boot ROM is made of hard-wired transitors within the CPU die.
Good luck writing to that!
“ROM” is sometimes (mis)used to refer to “persistent memory” chips, such as flash memory, whose write functionality was disabled.
Most bootleg / “repro” Game Boy cartridges you can find nowadays actually contain flash; this is why you can reflash them using specialized hardware, but original cartridges cannot be.