Let's build a video game
3 May 2023UniROM Vic20 e C64
13 July 2023chi non sa cosa sia un Game Boy alzi la mano ! Nessuno ? Bene, ne ero sicuro ! Cosi come sono sicuro che anche il Game Boy Color sia nella memoria di tutti !!
Non tutti però sanno che il suo nome in codice era DMG-01 (1989-1990), mentre quello del Game Boy Color (1998) era CGB-01.
In ogni caso le prime “piccole” e portatili di casa Nintendo sono di sicuro tra le più famose perle mai uscite nel mondo delle consoles ed anche loro non sono da meno rispetto alle “sorellone” riguardo i sistemi di protezione !
Il Game Boy è infatti dotato di una BootROM che ritroviamo anche nel Game Boy Color (GBC) e nel Super Game Boy (cartuccia-adattatore per giocare ai giochi GB su Super Nintendo).
LA PROTEZIONE
La BootROM di cui è dotato l’hardware di GB, GBC e SGB è di 256bytes ed ha 3 particolari caratteristiche “protettive”:
1 – La stessa BootROM viene esclusa dalla lettura con l’ultima istruzione del suo codice (questo “lock out” forse non è stato voluto ma si è probabilmente reso necessario per motivi legati alle (scarse) risorse di sistema; riassumendo la BootROM viene mappata in memoria dall’offest 0x0000 all’offset 0x00FF (256bytes) e gli ultimi 2 bytes rappresentano una istruzione che scrive in un registro speciale bloccando tale indirizzo di memoria dalla lettura in modo tale che tutte le istruzioni successive (che sono, a partire dall’offset 0x0100, quelle contenute nella cartuccia) non possano leggerla.
2 – al suo interno contiene del codice che legge i 48 bytes del logo NINTENDO presenti nella cartuccia (offsets che vanno da 0x104 a 0x133 di ogni cartuccia)…
… e li mostra a schermo facendo scorrere dall’alto in basso il notissimo logo.
Dopodichè li confronta con quelli memorizzati nella BootROM: se sono diversi la console si blocca.
Il fatto di leggere il logo direttamente dalla cartuccia spiega il perchè, se accendete un Game Boy senza inserire alcun gioco, appare il rettangolo nero a destra (i bytes del logo sono tutti ad FF).
(per una descrizione completa di come funzionano i bytes del logo vi rimando a questo meraviglioso articolo)
3 – un secondo controllo effettuato dopo quello del logo è quello di sommare i valori che vanno dall’offset 0x0134 to 0x014C ed aggiungere poi il valore presente in 0x014D: se la somma è zero il programma continua altrimenti la console si blocca !
Ecco uno screenshot di una ROM GB a caso dove potete verificare gli offsets menzionati:
Questa protezione si rese necessaria per evitare che altri sviluppatori producessero software non licenziato da Nintendo; come nota a margine c’è da dire che anche SEGA successivamente tento’lo stesso approccio con il Genesis ma la sua causa contro la Accolade non andò come previsto.
COME BYPASSARE LA PROTEZIONE
Il contenuto di questo mini-sistema operativo rimase un mistero fino al 2003 quando il coder Neviksti effettuo’un suo decapping (cioè una “scoperchiatura” fisica del chip) e lesse “a occhio nudo” il suo contenuto bit per bit (256×8 = 2048 bits totali) dopo averli fotografati tramite un microscopio; eccoli qui sotto in tutto il loro micro-splendore:
Ogni “pallino” dell’immagine soprastante rappresenta un bit a 1, l’assenza del pallino rappresenta un bit a 0 ! I pallini non sono altro che le cariche elettriche memorizzate nelle celle di memoria !!!
Qui a sinistra un esempio ancora più zoomato ottenuto dal decap di un altro chip visto al microscopio elettronico (non ha nulla a che fare con le consoles):
Ecco qui a destra un esempio di come vengono lette manualmente le cariche elettriche… davvero incredibile !
Una volta convertiti in bytes e successivamente in ASM, il codice (commentato) della BootROM appare essere il seguente:
LD (HL-),A ; $0007
BIT 7,H ; $0008
JR NZ, Addr_0007 ; $000a
LD HL,$ff26 ; $000c Setup Audio
LD C,$11 ; $000f
LD A,$80 ; $0011
LD (HL-),A ; $0013
LD ($FF00+C),A ; $0014
INC C ; $0015
LD A,$f3 ; $0016
LD ($FF00+C),A ; $0018
LD (HL-),A ; $0019
LD A,$77 ; $001a
D (HL),A ; $001c
LD A,$fc ; $001d Setup BG palette
LD ($FF00+$47),A ; $001f
LD DE,$0104 ; $0021 Convert and load logo data from cart into Video RAM
LD HL,$8010 ; $0024
Addr_0027:
LD A,(DE) ; $0027
CALL $0095 ; $0028
CALL $0096 ; $002b
INC DE ; $002e
LD A,E ; $002f
CP $34 ; $0030
JR NZ, Addr_0027 ; $0032
LD DE,$00d8 ; $0034 Load 8 additional bytes into Video RAM
LD B,$08 ; $0037
Addr_0039:
LD A,(DE) ; $0039
INC DE ; $003a
LD (HL+),A ; $003b
INC HL ; $003c
DEC B ; $003d
JR NZ, Addr_0039 ; $003e
LD A,$19 ; $0040 Setup background tilemap
LD ($9910),A ; $0042
LD HL,$992f ; $0045
Addr_0048:
LD C,$0c ; $0048
Addr_004A:
DEC A ; $004a
JR Z, Addr_0055 ; $004b
LD (HL-),A ; $004d
DEC C ; $004e
JR NZ, Addr_004A ; $004f
LD L,$0f ; $0051
JR Addr_0048 ; $0053
; === Scroll logo on screen, and play logo sound===
Addr_0055:
LD H,A ; $0055 Initialize scroll count, H=0
LD A,$64 ; $0056
LD D,A ; $0058 set loop count, D=$64
LD ($FF00+$42),A ; $0059 Set vertical scroll register
LD A,$91 ; $005b
LD ($FF00+$40),A ; $005d Turn on LCD, showing Background
INC B ; $005f Set B=1
Addr_0060:
LD E,$02 ; $0060
Addr_0062:
LD C,$0c ; $0062
Addr_0064:
LD A,($FF00+$44) ; $0064 wait for screen frame
CP $90 ; $0066
JR NZ, Addr_0064 ; $0068
DEC C ; $006a
JR NZ, Addr_0064 ; $006b
DEC E ; $006d
JR NZ, Addr_0062 ; $006e
LD C,$13 ; $0070
INC H ; $0072 increment scroll count
LD A,H ; $0073
LD E,$83 ; $0074
CP $62 ; $0076 $62 counts in, play sound #1
JR Z, Addr_0080 ; $0078
LD E,$c1 ; $007a
CP $64 ; $007c
JR NZ, Addr_0086 ; $007e $64 counts in, play sound #2
Addr_0080:
LD A,E ; $0080 play sound
LD ($FF00+C),A ; $0081
INC C ; $0082
LD A,$87 ; $0083
LD ($FF00+C),A ; $0085
Addr_0086:
LD A,($FF00+$42) ; $0086
SUB B ; $0088
LD ($FF00+$42),A ; $0089 scroll logo up if B=1
DEC D ; $008b
JR NZ, Addr_0060 ; $008c
DEC B ; $008e set B=0 first time
JR NZ, Addr_00E0 ; $008f ... next time, cause jump to "Nintendo Logo check"
LD D,$20 ; $0091 use scrolling loop to pause
JR Addr_0060 ; $0093
; ==== Graphic routine ====
LD C,A ; $0095 "Double up" all the bits of the graphics data
LD B,$04 ; $0096 and store in Video RAM
Addr_0098:
PUSH BC ; $0098
RL C ; $0099
RLA ; $009b
POP BC ; $009c
RL C ; $009d
RLA ; $009f
DEC B ; $00a0
JR NZ, Addr_0098 ; $00a1
LD (HL+),A ; $00a3
INC HL ; $00a4
LD (HL+),A ; $00a5
INC HL ; $00a6
RET ; $00a7
Addr_00A8:
;Nintendo Logo
.DB $CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83,$00,$0C,$00,$0D
.DB $00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6,$DD,$DD,$D9,$99
.DB $BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F,$BB,$B9,$33,$3E
Addr_00D8:
;More video data
.DB $3C,$42,$B9,$A5,$B9,$A5,$42,$3C
; ===== Nintendo logo comparison routine =====
Addr_00E0:
LD HL,$0104 ; $00e0 ; point HL to Nintendo logo in cart
LD DE,$00a8 ; $00e3 ; point DE to Nintendo logo in DMG rom
Addr_00E6:
LD A,(DE) ; $00e6
INC DE ; $00e7
CP (HL) ; $00e8 ;compare logo data in cart to DMG rom
JR NZ,$fe ; $00e9 ;if not a match, lock up here
INC HL ; $00eb
LD A,L ; $00ec
CP $34 ; $00ed ;do this for $30 bytes
JR NZ, Addr_00E6 ; $00ef
LD B,$19 ; $00f1
LD A,B ; $00f3
Addr_00F4:
ADD (HL) ; $00f4
INC HL ; $00f5
DEC B ; $00f6
JR NZ, Addr_00F4 ; $00f7
ADD (HL) ; $00f9
JR NZ,$fe ; $00fa ; if $19 + bytes from $0134-$014D don't add to $00
; ... lock up
LD A,$01 ; $00fc
LD ($FF00+$50),A ; $00fe ;turn off DMG rom
LD (HL-),A ; $0007 BIT 7,H ; $0008 JR NZ, Addr_0007 ; $000a LD HL,$ff26 ; $000c Setup Audio LD C,$11 ; $000f LD A,$80 ; $0011 LD (HL-),A ; $0013 LD ($FF00+C),A ; $0014 INC C ; $0015 LD A,$f3 ; $0016 LD ($FF00+C),A ; $0018 LD (HL-),A ; $0019 LD A,$77 ; $001a D (HL),A ; $001c LD A,$fc ; $001d Setup BG palette LD ($FF00+$47),A ; $001f LD DE,$0104 ; $0021 Convert and load logo data from cart into Video RAM LD HL,$8010 ; $0024 Addr_0027: LD A,(DE) ; $0027 CALL $0095 ; $0028 CALL $0096 ; $002b INC DE ; $002e LD A,E ; $002f CP $34 ; $0030 JR NZ, Addr_0027 ; $0032 LD DE,$00d8 ; $0034 Load 8 additional bytes into Video RAM LD B,$08 ; $0037 Addr_0039: LD A,(DE) ; $0039 INC DE ; $003a LD (HL+),A ; $003b INC HL ; $003c DEC B ; $003d JR NZ, Addr_0039 ; $003e LD A,$19 ; $0040 Setup background tilemap LD ($9910),A ; $0042 LD HL,$992f ; $0045 Addr_0048: LD C,$0c ; $0048 Addr_004A: DEC A ; $004a JR Z, Addr_0055 ; $004b LD (HL-),A ; $004d DEC C ; $004e JR NZ, Addr_004A ; $004f LD L,$0f ; $0051 JR Addr_0048 ; $0053 ; === Scroll logo on screen, and play logo sound=== Addr_0055: LD H,A ; $0055 Initialize scroll count, H=0 LD A,$64 ; $0056 LD D,A ; $0058 set loop count, D=$64 LD ($FF00+$42),A ; $0059 Set vertical scroll register LD A,$91 ; $005b LD ($FF00+$40),A ; $005d Turn on LCD, showing Background INC B ; $005f Set B=1 Addr_0060: LD E,$02 ; $0060 Addr_0062: LD C,$0c ; $0062 Addr_0064: LD A,($FF00+$44) ; $0064 wait for screen frame CP $90 ; $0066 JR NZ, Addr_0064 ; $0068 DEC C ; $006a JR NZ, Addr_0064 ; $006b DEC E ; $006d JR NZ, Addr_0062 ; $006e LD C,$13 ; $0070 INC H ; $0072 increment scroll count LD A,H ; $0073 LD E,$83 ; $0074 CP $62 ; $0076 $62 counts in, play sound #1 JR Z, Addr_0080 ; $0078 LD E,$c1 ; $007a CP $64 ; $007c JR NZ, Addr_0086 ; $007e $64 counts in, play sound #2 Addr_0080: LD A,E ; $0080 play sound LD ($FF00+C),A ; $0081 INC C ; $0082 LD A,$87 ; $0083 LD ($FF00+C),A ; $0085 Addr_0086: LD A,($FF00+$42) ; $0086 SUB B ; $0088 LD ($FF00+$42),A ; $0089 scroll logo up if B=1 DEC D ; $008b JR NZ, Addr_0060 ; $008c DEC B ; $008e set B=0 first time JR NZ, Addr_00E0 ; $008f … next time, cause jump to “Nintendo Logo check” LD D,$20 ; $0091 use scrolling loop to pause JR Addr_0060 ; $0093 ; ==== Graphic routine ==== LD C,A ; $0095 “Double up” all the bits of the graphics data LD B,$04 ; $0096 and store in Video RAM Addr_0098: PUSH BC ; $0098 RL C ; $0099 RLA ; $009b POP BC ; $009c RL C ; $009d RLA ; $009f DEC B ; $00a0 JR NZ, Addr_0098 ; $00a1 LD (HL+),A ; $00a3 INC HL ; $00a4 LD (HL+),A ; $00a5 INC HL ; $00a6 RET ; $00a7 Addr_00A8: ;Nintendo Logo .DB $CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83,$00,$0C,$00,$0D .DB $00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6,$DD,$DD,$D9,$99 .DB $BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F,$BB,$B9,$33,$3E Addr_00D8: ;More video data .DB $3C,$42,$B9,$A5,$B9,$A5,$42,$3C ; ===== Nintendo logo comparison routine ===== Addr_00E0: LD HL,$0104 ; $00e0 ; point HL to Nintendo logo in cart LD DE,$00a8 ; $00e3 ; point DE to Nintendo logo in DMG rom Addr_00E6: LD A,(DE) ; $00e6 INC DE ; $00e7 CP (HL) ; $00e8 ;compare logo data in cart to DMG rom JR NZ,$fe ; $00e9 ;if not a match, lock up here INC HL ; $00eb LD A,L ; $00ec CP $34 ; $00ed ;do this for $30 bytes JR NZ, Addr_00E6 ; $00ef LD B,$19 ; $00f1 LD A,B ; $00f3 Addr_00F4: ADD (HL) ; $00f4 INC HL ; $00f5 DEC B ; $00f6 JR NZ, Addr_00F4 ; $00f7 ADD (HL) ; $00f9 JR NZ,$fe ; $00fa ; if $19 + bytes from $0134-$014D don’t add to $00 ; … lock up LD A,$01 ; $00fc LD ($FF00+$50),A ; $00fe ;turn off DMG rom |
Nel 2009 un altro reverser (Costis Sideris) riuscì a dumpare la BootROMdel Super Game Boy utilizzando, tramite un FPGA, un glitch hardware che disabilitava l’ultima istruzione di di lock out della memoria dove era salvata la copia della BootROM grazie all’invio di impulsi overclockati riuscendo così a dumpare il codice dall’offset 0x0000 all’offset 0x00FF con dello specifico codice inserito dall’offset 0x0100 in poi. Ecco una foto del “semplice” sistema utilizzato:
Esaminando il codice di questa BootROM si è scoperto che, se il check sul logo oppure se il secondo checksum della sommatoria che deve essere uguale a 0 è diverso da 0, la cartuccia-adattatore non disabilita la cartuccia di gioco ma lo fa il Super Nintendo vero e proprio. Inoltre dissaldando la CPU di un Super Game Boy e saldandola al posto di quella di un DMG-01 si bypassa addirittura la scritta Nintendo facendo risparmiare circa 5 secondi di boot perchè è il SNES a gestire i controlli e non più la CPU del SGB “sproteggendo” de facto il DMG !
Sempre Costis Sideris e sempre nel 2009 riuscì a dumpare anche la BootROM del Game Boy Color utilizzando una combinazione di glitch questa volta sia di clock che di alimentazione attraverso un FPGA. In questo dump si è accorto che il check del logo avveniva solamente per la prima metà dei bytes e non di tutti e 48. Assieme ad essa riuscì a dumpare il contenuto di una seconda ROM di 1792 bytes presente in questa console. Dr. Decapitator (alias Christopher Tarnovsky) tentò un parziale decapping identificando addirittura 3 ROMs (256, 1792 e 512 bytes) ma di quella da 512 non è stato ottenuto alcun dump (è possibile che contenga il microcodice della CPU oppure codice legato al LCD).
Si tratta dunque di segreti violati dopo più di un decennio ! Inoltre nel 2012 è scaduto anche il brevetto che descrive le funzioni della BootROM a confermare quanto scoperto dai reverse engineers.
Una volta scoperto il funzionamento della BootROM alcuni sviluppatori si sono ingegnati per far si che il Game Boy leggesse un logo custom dalla cartuccia al solito offset (prima fase effettuata dalla bootrom che permette la visualizzazione del logo) ma poi verificasse un secondo logo (quello ufficiale durante la seconda fase di rilettura dello stesso e checking) memorizzato ad un altro offset della cartuccia. I risultati sono simili al seguente:
Potete trovare una bella descrizione delle varie tipologie di loghi custom prodotti da alcuni sviluppatori HERE ed ulteriori informazioni sulle BootROM (compresa quella trovata in una cartuccia prototipo di Pokemon Stadium per Nintendo 64 mancante nella release ufficiale della stessa) here.
Altre informazioni sulla BootROM del GBC le potete leggere here.