- Prerequisiti -
Sapere cosa sono i puntatori, perché è utile hackarli e avere una vaga idea su come farlo. Fare riferimento alle numerose guide disponibili online (ad esempio su Zophar),
nonché al nostro tutorial sui puntatori hirom.
- Convenzioni usate in questo testo -
- i numeri sono tutti in formato esadecimale, anche dove non indicato;
- vengono chiamate "banchi file" le porzioni di 64k di cui è composto il file di una rom;
- vengono chiamati "banchi snes" gli effettivi banchi in cui è suddiviso
lo spazio di indirizzamento della console.
- Introduzione -
I puntatori lorom, almeno per il sottoscritto, in passato erano un vero incubo.
Paradossalmente abbondavano i tutorial che ne parlavano (le lorom interessavano
molto ai primi rom hacker, forse perché i primi giochi hackati lo erano), ma
non se ne capiva nemmeno mezzo. Tutti accennavano a calcoli strani da fare sugli
offset per ottenere i puntatori (ogni volta con la propria interpretazione, tanto
da impedirmi di trovare un filo comune) ma nessuno spiegava il motivo di quei
calcoli. Ho dovuto aspettare di capirlo da solo :)
- Memory mapping del SNES -
Senza pretendere di fare una trattazione completa sul memory mapping del SNES (argomento fin troppo vasto e complesso), si danno qui alcune nozioni di base per agevolare la comprensione dei puntatori.
Il SNES ha un unico spazio di indirizzamento, con cui sono indirizzati la memoria, le periferiche, i vari registri e anche la ROM. Lo spazio di indirizzi va da $000000 a $FFFFFF, anche se non è completamente utilizzato. Risulta diviso in 256 (decimale) banchi da 64k, tali cioè che ogni banco ha indirizzi che vanno da $0000 a $FFFF. Fare riferimento alla memory map di SiMKiN, una delle più complete che io abbia mai trovato - pur non potendo assicurarne la totale correttezza.
Il modo in cui i dati della ROM vengono indirizzati distingue il "tipo" della ROM stessa. Esistono due tipi di indirizzamento, che forse sarebbe meglio definire "famiglie" in quanto si presentano spesso con piccole varianti: lo-rom e hi-rom. La caratteristica che deve distinguere questi due indirizzamenti agli occhi del rom hacker è appunto il modo in cui i dati della ROM sono mappati, in quanto influenza fortemente il formato e il funzionamento dei puntatori. Nel presente documento si vogliono descrivere i puntatori lo-rom (o più semplicemente lorom).
La costante che caratterizza i due tipi è il modo in cui la ROM viene mappata: in banchi da 32K per le lorom e in banchi da 64K per le hirom.
- Mapping della ROM -
Suddividendo il file della rom in porzioni di 64k, si ottengono quelli che
si potrebbero definire "banchi file" (o "chunk", come vengono spesso chiamati nei tutorial in inglese). In una hirom ogni banco file
risulta mappato esattamente da un banco SNES. I banchi SNES che mappano le hirom iniziano dall'indirizzo C00000 (il primo banco è C0); dunque quando si legge un offset all'interno del file per trasformarlo in un indirizzo SNES è sufficiente
sommargli C00000 (si trasla cioè in avanti l'offset). La semplicità delle
hirom è dovuta all'equivalenza banco file -> banco SNES.
Per le lorom il file viene mappato solo nella metà piu alta dei banchi SNES, cioè da 8000 a ffff. Questo rende tutto più complicato, non c'è una corrispondenza
diretta tra i banchi file e i banchi SNES. La parte bassa dei banchi risulta occupata da altre cose, ad esempio un mirror dei registri della console (cfr memory map).
Prendendo ad esempio il banco file 00 (i primi 64k del file), questo sarà
mappato per metà nel banco SNES 00, e per metà nel banco SNES 01. Questo
comporta che a ogni banco file corrispondano 2 banchi SNES: un banco per i
dati del banco file che vanno a 0 a 7FFF, il banco successivo per i dati
del banco file che vanno da 8000 a FFFF.
Si guardi il seguente esempio, rappresentante il mapping lorom dei primi 64k di una ROM:
file offset snes address
000000 - 007FFF => 008000 - 00FFFF
008000 - 00FFFF => 018000 - 01FFFF
Dovrebbe risultare evidente quanto detto fin'ora.
- Convertire un offset di una lorom in un indirizzo SNES -
- togliere $200 dall'offset del file (l'eventuale header);
- considerare l'offset letto come se fosse composto da due parti, banco
e indirizzo. es: AABBCC. AA è il byte del banco, BBCC sono i due byte
che compongono l'indirizzo;
- moltiplicare il banco per 2;
- Se l'indirizzo è >= $8000 aggiungere 1 al banco, altrimenti aggiungere
$8000 all'indirizzo;
- l'indirizzo SNES cosi trovato va byteswappato nel caso in cui si cerchi
il puntatore con un hex editor, per via della codifica little endian (es: $ABCDEF -> $EFCDAB).
- Spiegazioni sui calcoli -
L'offset letto dall'editor esadecimale va ovviamente elaborato per trasformarlo in un indirizzo SNES, cioè in un puntatore da cercare.
Il banco va moltiplicato per 2 perché i banchi SNES vengono
usati a metà per le lorom, quindi a ogni banco file corrispondono
due banchi SNES. Ad esempio un byte che si trova al banco 10 del file
si troverà mappato nel banco 20 del SNES (o al 21, vedi punto 4). Moltiplicare per 2 risolve il problema in generale.
Il punto 4 è meno immediato da capire. Si faccia ancora riferimento all'esempio mostrato prima:
file offset SNES address
000000 - 007FFF => 008000 - 00FFFF
008000 - 00FFFF => 018000 - 01FFFF
Viene preso in considerazione il primo banco del file, $00. La
prima metà di tale banco (con indirizzi da 0000 a 7FFF) è copiata
nel banco SNES 00, ma solo nella seconda metà! Ovvero da 8000
a FFFF, la "parte alta" del banco.
Il byte 0 deve essere quindi traslato in avanti di 8000 locazioni
per essere posizionato nella parte alta del banco SNES, questo
giustifica il fatto di sommare 8000 all'indirizzo quando questo
è compreso tra 0 e 8000.
La seconda metà del banco file (008000-00FFFF) deve essere invece mappata nel banco
SNES 01, sempre negli indirizzi 8000 - FFFF, perché il banco 0 è già completamente occupato. In questo caso, essendo l'indirizzo originale già compreso nel range di valori corretto, non è necessario alterarlo. E' invece necessario sommare uno al banco, come risulta evidente dall'esempio.
- I puntatori da due byte -
Quando si lavora con i puntatori, spesso sono disponibili solo
puntatori da 2 byte. Si tratta di istruzioni assembly di salto o caricamento
in cui viene esplicitato solo l'indirizzo, poichè il banco è specificato
già nel registro DB (o comunque altrove, infatti il processore può ricorrere a qualunque
artificio matematico per ricostruire un indirizzo partendo da un puntatore anche parziale). In tali situazioni l'indirizzo completo viene costruito dalla CPU in fase di esecuzione e non è disponibile in maniera esplicita al rom hacker. Riuscire a modificarlo richiede in genere interventi di asm hacking.
Con puntatori da due byte non è necessario effettuare operazioni
sul banco dell'offset, che anzi è un dato inutile: si legge l'offset del byte di cuoi vogliamo ricavare
il puntatore e si scarta l'eventuale banco (ad esempio, $234567 -> $4567)
ottenendo un indirizzo a 16 bit. Se tale indirizzo è minore di 8000 vi si
aggiunge 8000 altrimenti si lascia com'è, come descritto dal punto 4 della procedura. Infine si swappano i due byte
per via della codifica little endian, nel caso in cui si cerchi tale
puntatore con un hex editor. Certo, la ricerca così diventa molto più ambigua perché le corrispondenze
col puntatore cercato aumenteranno di numero rispetto a un puntatore da 3 byte. L'unica soluzione è provare a modificare una alla volta
tutte le occorrenze del
puntatore, fino a trovare quella giusta.
- I mirror -
Dalla memory map di SiMKiN si vede che sono presenti range di indirizzi equivalenti tra loro, nel senso che le destinazioni accessibili sono le stesse. Indirizzi di questo tipo sono detti mirror (o "banchi congruenti", come li chiama lui) e nel SNES ce ne sono molti.
Ce ne sono anche per l'accesso alla ROM ovviamente, e questo mi sembra un argomento assai poco trattato nei tutorial di rom hacking. Per fare un esempio, il banco $00 e il banco $80 accedono alla stessa porzione di ROM. Questo significa che il puntatore in grado di puntare a un dato offset della ROM non è univoco e bisogna provare tutti i casi possibili, non essendo nota a priori la scelta fatta dai programmatori. Nel dubbio conviene scartare il banco dell'offset e cercare solo l'indirizzo, in quanto il mirroring è un fenomeno che interessa solo i banchi.
Duke
Settembre 2006
|