ROMHACKING GAMEBOY PRÁCTICO v. 0.0

Transcripción

ROMHACKING GAMEBOY PRÁCTICO v. 0.0
ROMHACKING GAMEBOY PRÁCTICO
Este tutorial trata de explicar cómo modificar juegos de Gameboy a través de ejemplos reales
cambiando algunas características de éstos.
Los aspectos que se cubren en este documento son aplicables a otros juegos en su gran
mayoría y van a permitir a cualquiera adquirir los conocimientos suficientes para hacer sus
propias modificaciones y buscar información en juegos de esta consola.
Se va a utilizar varios juegos de Gameboy y varias técnicas. En internet hay tutoriales para
hacer modificaciones en juegos de esta consola pero la mayoría se centran en pocos juegos
(sobre todo los de Pokemon y Super Mario) por lo que para el resto no se encuentra mucha
información.
No voy a tratar sobre en este tutorial sobre los juegos de Pokemon ya que hay bastantes
páginas y foros con información tanto en español como inglés sobre estos juegos.
Para utilizar este manual se va a utilizar una serie de herramientas que se pueden conseguir
fácilmente en internet:
-
BGB (Emulador de Gameboy que cuenta con un debugger). También se puede utilizar
No$Gmb que es similar aunque me centraré en el anterior.
- HxD (Editor hexadecimal gratuito o cualquier otro)
- TilED2002 (Editor de tiles).
- GBAsmEdit.
- Game Genie Encoder/Decoder 0.91A
- IPS Peek
- IPSWin (Parchear juegos).
- Hat Trick (Editor de niveles del juego “Snoopy Magic Show” para una parte del tutorial)
- File Search 0.0.a (Programa propio)
También voy a explicar como modificar los niveles de varios juegos de Gameboy y si alguien
tiene conocimientos de programación pueda crear un editor para que cualquiera pueda
modificarlo más fácilmente con una utilidad específica.
Los juegos para los que tengo información y que no hay editores de niveles todavía para ellos
son:
- Battlebull
- Block Kuzushi (SGB)
- Bomb Jack
- Boomer in Asmik World 2
- Kesamaru
- Little Magic (GBC)
- Mr. Do
- Pac-Attack
CONCEPTOS BÁSICOS
Hexadecimal: Sistema numérico que utiliza 16 valores para representar los números para lo
que utiliza (0-9, letras A-F).
Decimal: Sistema numérico que utiliza 10 valores para representar los números (0-9).
Binario: Sistema numérico que utiliza 2 valores para representar los números para lo que
utiliza (0-1)
Estos sistemas numéricos se pueden convertir de unos a otros utilizando la calculadora que
incluye Windows.
INSTRUCCIONES BÁSICAS
Aunque este tutorial no pretende aburrir demasiado con muchas instrucciones pero si es
bueno tener algunos conocimientos sobre todo de algunas (las más utilizadas).
“LD”
Esta es una de las más utilizadas y se utiliza tanto para leer como escribir valores en registros o
en una determinada dirección.
Por ejemplo, para cargar en el registro “A” el valor que hay en la dirección contenida en el
registro “DE” se utiliza la instrucción:
ld a,(de)
Sin embargo, para escribir el valor del registro “A” en la dirección contenida en el registro “DE”
se utiliza la instrucción:
Ld (de),a
Igual se puede utilizar el registro “DE” y “HL” para leer o escribir un valor.
También se puede utilizar para poner en el registro “A”, “B”, “C”, “D”, “E”, “H”, “L”, “AF”, “BC”,
“DE”, “HL” un determinado valor.
Por ejemplo:
Ld a,$03
(Carga en el registro “A” el valor $03)
Ld b,$05
(Carga en el registro “B” el valor $05)
Ld de,$8000 (Carga en el registro “DE” la dirección $8000)
Hay una serie de direcciones a partir de la dirección $FF00 que se pueden leer o escribir de
varias formas utilizando la instrucción “LD”.
Para cargar en el registro “a” el valor que hay en la dirección $FF41 que se encarga del estado
del LCD podemos utilizar:
21 41 FF
Ld a,[$FF41]
F0 41
Ld a,[$FF00+$41]
Normalmente se utiliza la segunda forma porque se requieren 2 bytes en lugar de los 3 de la
primera instrucción pero también podemos encontrar la primera.
Esto sólo se puede utilizar para direcciones a partir de $FF00.
“LDI”
Esta instrucción es similar a la anterior. Se suele utilizar de la siguiente forma:
Ldi [hl],a
Pone el valor del registro “a” en la dirección contenida en “hl” e incrementa “hl”.
Por ejemplo:
A=$00, HL=$C000.
Esta instrucción pondría el valor $00 en $C000 e incrementa el registro “hl” con lo que valdría
$C001. Se suele utilizar para continuar escribiendo en la siguiente dirección.
“INC”, “DEC
Esta instrucción se utiliza para incrementar o disminuir los valores que hay en un determinado
registro.
Son importantes ya que se utilizará para incrementar o disminuir valores de la RAM como
puede ser vidas, energía,...
“NOP”
“No Operation”: Esta instrucción es la que vamos a utilizar para eliminar instrucciones cuando
queramos por ejemplo tener vidas o energía infinita.
Esta instrucción equivale a no hacer nada y se corresponde con el valor hexadecimal $00. Si
tenemos una instrucción que disminuye un valor podemos sustituirla por una o varias
instrucciones “nop” dependiendo del número de bytes que ocupe la instrucción que queramos
eliminar y conseguiremos si se trata de “DEC” que no disminuya ese valor.
USO DEL EMULADOR BGB
El emulador que vamos a utilizar en este manual es “BGB” que tiene una gran cantidad de
opciones tales como debugger, VRAM viewer,…
El emulador “NO$GMB” también tiene muchas opciones como las del “BGB” y su
funcionamiento es parecido a éste por lo que también podrían utilizarlo para modificar juegos
de Gameboy.
Uno de las opciones más interesantes que tiene el debugger de BGB es la de poner
breakpoints.
Los breakpoints permiten hacer que el emulador se pare cuando se lea o escriba un
determinado valor en una dirección o cuando se lea o ejecute parte de la información
contenida en el juego.
Para mostrar el debugger una vez cargado el juego podemos pulsar la tecla “Esc” (Escape) del
teclado o a través de la opción “Debugger” del menú “Other” que aparece al pulsar el botón
derecho del ratón sobre el emulador.
En esta pantalla obtenemos bastante información interesante del juego en la ventana.
En la parte izquierda nos aparece el código fuente del juego con los offsets (los que empiezan
por ROM), a continuación los códigos que representan cada instrucción y a continuación la
instrucción.
Tal y como aparece en la captura aparece marcado en azul la línea de código que corresponde
a la dirección $00:0040 del juego (offset) que contiene la instrucción “jp 200A” y que se
codifica con los bytes “C3 0A 20”.
Cada instrucción se codifica con una serie de bytes variable dependiendo de la que sea. En este
caso se utiliza el byte “C3” para indicar la orden “jp” (jump: salto a una dirección) y los
siguientes 2 bytes representan la dirección en formato hexadecimal inverso ($200A, se
representa como “0A”, “20”).
A la derecha vemos los registros de la consola y sus valores actuales cuando se hizo la captura.
En este caso el registro “AF” tiene el valor $00A0. Este registro es de 16 bits (o 2 bytes) ya que
tiene 4 dígitos.
El registro “AF” se puede dividir en 2 registros de 8 bits (1 byte). El registro “A” que tiene el
valor $00, y el registro “F” que tiene el valor $A0.
Igual ocurre con el resto de registros de 16 bits: BC, DE, HL.
Estos registros los utiliza la consolar para escribir información y poner datos.
El registro SP (stack de la pila) se utiliza para poner la dirección donde se guardan datos
(aunque éste no lo veremos en el tutorial).
El registro “PC” indica la dirección actual que como se ve es la $0040.
Otro valor interesante es el que aparece con el nombre “rom” y que indica el banco actual y es
importante ya que no es lo mismo la dirección dependiendo de este valor.
Por ejemplo, si encontramos la siguiente instrucción:
“ld hl,$4523”
Esta instrucción lo que hace es cargar en hl la dirección $4523. Sin embargo, los juegos suelen
estar divididos en banco de 16 kbs. y debemos saber el banco que aparece en “rom”.
Si en “rom” aparece el valor “1” se estará cargando en hl la dirección $01:4523 y si estuviera el
valor “2” sería la dirección $02:4523.
A la derecha del todo de los registros aparecen los valores de los flags de carry “c”,
Debajo encontramos los valores de algunas direcciones de HRAM (High RAM), ROM,…
En la parte inferior izquierda aparece en formato hexadecimal la ROM, High RAM,… como si
fuera un editor hexadecimal.
Una de las utilidades del Debugger y que es interesante es la obtención del código fuente del
juego para lo cual podemos situarnos sobre el inicio del juego utilizando “Go to…” y poniendo
la dirección “00:0000” para irnos al inicio.
Nos situamos en el menú “File” y seleccionamos “save asm” para que nos aparezca una
ventana para seleccionar donde vamos a guardar el código fuente y el nombre del fichero.
En mi caso le he puesto el nombre “Battle Bull (U) [¡] Banco 00.txt” para saber el juego del que
es el código y el banco que contiene.
En la pantalla que nos aparece a continuación nos aparece la dirección inicial que vamos a
dejar igual que nos aparece.
Al pulsar en “OK” nos aparece otra ventana en la que ponemos el número de bytes a guardar
en el fichero de código fuente que cambiamos a “4000” para guardar el banco 0 al completo.
Cada banco ocupa $4000 bytes.
Podemos hacer lo mismos para el resto de bancos del juego para obtener ficheros de cada uno
de ellos para lo cual para el banco $01 cambiaríamos en “Go to…” la dirección por “01:4000”
para el banco 1, “02:4000” para el banco 2, y así sucesivamente dependiendo del número de
bancos del juego.
Cada 16 kilobytes son 1 banco por lo que un juego de 32 kbs. tendrá 2 bancos (banco 0 y 1),
uno de 64 kbs. tendrá 4 bancos (banco 0, 1, 2 y 3).
El banco 0 empieza en la dirección $00:0000 y el resto en la dirección $xx:4000 donde xx es el
número de banco.
Con esto ya tendríamos todo el código del juego aunque el debugger trata todo como si fuera
instrucciones y sin embargo aquí hay también datos del juego, música del juego, tiles, textos,
punteros,… y debemos analizar el código para ver que es cada cosa lo que es el proceso más
complicado.
También podemos obtener códigos fuentes parciales (a partir de una dirección en concreto y
un número de bytes determinado).
Analizar un juego completo para averiguar todos los aspectos del mismo es casi imposible y
requiere mucho tiempo y comprender muy bien cómo funciona la consola Gameboy, los
registros, instrucciones,…. sobre todo si incluye información comprimida.
Otra opción interesante es guardar parte de la memoria en un fichero que se puede guardar y
abrir con un editor hexadecimal.
Lo más interesante para guardar de la memoria son los tiles del juego, la RAM y la High RAM
(aunque esto menos).
Para guardar los tiles del juego seleccionamos la opción “save memory dump…” del menú
“File” con lo que nos guarda un fichero con el nombre del juego y con extensión “.dump”
aunque podemos ponerle el nombre que queramos.
Igual que ocurría antes debemos ponerle la dirección de inicio y el número de bytes que
queremos guardar en formato hexadecimal.
En el caso de los tiles dependiendo de lo que aparezca en el “BGB VRAM Viewer” podemos
seleccionar una dirección u otra.
Por ejemplo, si el juego tiene muchos tiles y aparece la pantalla completa del “BGB VRAM
Viewer” podemos poner la dirección $8000 como inicial y $1800 como número de bytes a
guardar en el fichero con lo que guardaríamos los datos que hay entre las direcciones $8000$97FF que corresponden a todos los tiles del juego.
Los tiles del juego se almacenan en las direcciones $8000-$87FF, $8800-$8FFF y $9000-$97FF.
Si no estuviera llena la pantalla de “BGB VRAM Viewer” podemos poner otra dirección y otro
número de bytes.
También es interesante grabar la pantalla del juego como si fueran datos para buscar esta
información en el juego y ver donde está almacenada en la ROM para modificarla si queremos
a nuestro gusto.
En Gameboy disponemos de 2 direcciones (como si se tratara de 2 pantallas) donde podemos
poner información y que aparezca en la consola.
Por ejemplo, en una podemos poner la pantalla del nivel y en otra varias líneas con
información tales como puntos, tiempo, energía que aparecería a la vez en la pantalla del
juego pero que está en dos áreas de memoria diferente.
Estas direcciones donde se almacenan datos de las pantallas se denominan Map Address
encontrándonos un área en las direcciones $9800-$9BFF y en $9C00-$9FFF (con $0400 bytes
cada una).
Tal y como vemos en la pantalla vemos como en la pantalla del “BGB VRAM Viewer” tenemos
en la pestaña “BG Map” información de la pantalla.
En la parte izquierda aparece la pantalla tal y como aparece en la consola. Como vemos hay
una gran parte en verde y que corresponde a zonas no visibles y que pueden tener tiles para
poner zonas no visibles en la pantalla que al hacer scroll aparecerían en la pantalla del juego.
En la parte derecha en el recuadro superior aparecería el tile en formato gráfico tal y como se
representa en la pantalla y debajo detalles del mismo tales como coordenada X e Y en pantalla,
el número de tile que será $00-$FF. La Map Address donde está el tile en pantalla, la Tile
Address donde están los datos que representan el tile,…
Por defecto, “Map” y “Tiles” está puesto en “Auto” pero se puede cambiar el valor de “Map”
por “9800” o “9C00”. Cambiando esto podemos ver las 2 áreas de pantalla que juntas se
muestran en la pantalla del juego tal y como se ve en las siguientes capturas:
Pantalla donde se dibuja el nivel ($9800-$9BFF)
Pantalla donde se pone información de puntos, tiempo y energía ($9C00-$9FFF):
Pantalla de juego en el emulador donde aparecen ambas pantallas:
En ninguna de las anteriores pantallas aparecían los sprites del personaje que movemos ni de
los enemigos cuya información aparece en la OAM donde están los datos de cada uno de los
tiles que lo componen y sus datos.
Los 4 primeros conjuntos de valores se utilizan para dibujar el personaje que movemos en el
juego que unidos forman el sprite.
En X-loc, Y-loc aparecen las coordenadas X e Y de cada tile, el número de tile y su atributo.
También aparece la dirección “OAM addr” donde están dichos datos que para el tercer tile que
hemos seleccionado con el ratón corresponde a la dirección $FE08.
Cada tile ocupa 4 bytes en la OAM (Y-loc, X-loc, Tile No y Atributo). Los datos de la OAM
empiezan en la dirección $FE00.
Poniendo la dirección $9800 en la dirección y $0400 como número de bytes a guardar
generamos un fichero .dump con la información de una de las pantallas y con $9C00 y $0400
guardamos la otra pantalla en un fichero .dump.
La utilidad de esto es para encontrar donde están los datos para dibujar la pantalla y poder
localizarla.
Sin embargo, en los juegos podemos encontrar muchas formas diferentes de poner la pantalla
del juego siendo de mayor utilidad cuando se dibuja la pantalla en su totalidad dibujando las
18 líneas de la pantalla con los 20 tiles que forman cada línea o cuando se dibujan las 18 líneas
de la pantalla con 32 tiles que ocupa la pantalla completa incluyendo la parte no visible.
Esto ocurre en algunos juegos de Gameboy pero no es el caso del juego “Battle Bull” donde la
pantalla de título se dibuja de forma diferente.
En este caso podemos utilizar también otra opción del emulador BGB y un editor hexadecimal
para encontrar donde están los datos para dibujar la pantalla de título.
Para ello en el Debugger nos situamos sobre la dirección $9840 por ejemplo, porque en esta
línea aparece la parte superior del rótulo “BULL” en negro (el primer tile está en la dirección
$984A y corresponde al tile $2C.
Podemos seleccionar “Copy data” sobre la dirección $9840 y seleccionar en el número de
bytes 16 aunque los que realmente nos interesan son los 3 que tienen valores distintos de 00
($2C, $34, $3B).
Si abrimos el juego con un editor hexadecimal y buscamos la cadena hexadecimal “2C343B” lo
encontramos en la dirección $29E5-$29E7.
Ya hemos encontrado donde están los datos que buscamos y vemos como sólo aparecen los
valores buscados y no los valores 00 que aparecen antes y después de estos en pantalla. Esto
es debido a que sólo se ponen en pantalla en la zona correspondiente una serie de tiles para
dibujar la pantalla y el resto tiene el valor $00.
Es como llenar la pantalla con el valor $00 y luego situar los tiles correspondientes para dibujar
los elementos que forman el rótulo del título “BATLLE BULL” en la pantalla.
Si nos fijamos en la dirección $29E2 vemos algo que nos es conocido y es el valor $98 seguido
por $4A que juntos forman la dirección $984A=Map Address donde se ponen los 3 tiles en la
pantalla del juego (tal y como aparece en la pantalla de más arriba del “BGB VRAM Viewer”
donde está la pantalla de título.
En $29E4 aparece el valor $03 que es precisamente el número de tiles que pone en la pantalla
a partir de la dirección $984A.
En $29E8 encontramos la dirección $9851=Map Address donde pone tiles en pantalla, y en
$29EA encontramos $02 que indica el número de tiles a poner en pantalla y en $29EB los
valores $5B, $63 que son los tiles que dibuja y así sucesivamente para ir dibujando el rótulo
“BATTLE BULL”.
La última parte que se utiliza para dibujar el rótulo se ponen en la dirección $992E donde se
ponen $04 tiles con los valores $4C, $53, $5A, $62 que aparecen en las direcciones $2A44$2A47.
En la dirección $2A48 encontramos el valor $00 que puede indicar el fin de los datos para
dibujar el rótulo.
Como las direcciones están entre $0000 y $4000 vemos que corresponden al banco $00 por lo
que los datos para dibujar el rótulo están entre las direcciones $00:29E2 y $00:2A48.
Puede parecer un proceso complejo pero realmente no lo es y haciendo el proceso con otros
juegos puedes comprobarlo.
En ocasiones la dirección Map Address no aparece como aquí sino que está en orden inverso
(en lugar de $98, $4A, podemos encontrar los valores $4A, $98).
En algunos juegos nos podemos encontrar que no aparece la dirección Map Address donde se
ponen los tiles en pantalla sino que están las coordenadas X e Y donde se ponen los tiles en
pantalla y a partir de ellas calcula la Map Address para dibujar los tiles.
En este caso los 3 primeros tiles que hemos encontrado para dibujar el rótulo se ponen en la
coordenada X=$0A (10 decimal) y coordenada Y=$02. La parte superior izquierda de la pantalla
corresponde a las coordenadas (0,0) que para esta pantalla corresponde a la dirección
$9800=Map Address.
El tile que se pone en la coordenada X=$01 e Y=$00 tiene la Map Address=$9801 y así
sucesivamente.
Cada línea son $20 (32 en decimal) tiles con lo que la línea con coordenada Y=0 comienza en la
dirección $9800 y termina en $981F.
La línea con Y=$01 corresponde a la Map Address entre $9820-$983F. La línea con Y=$02
corresponde a la Map Address entre $9840-$985F.
De los $20 tiles que componen cada línea sólo los $14 primeros son visibles en la pantalla de la
consola, el resto no son visibles.
La fórmula para pasar de coordenadas a dirección de la Map Address son las siguientes:
$9800+X+$20*Y para la pantalla que comienza en $9800.
El tile X=$0A, Y=$02 corresponde a la dirección $9800+$0A+$02*20=$9800+$0A+$40=$984A.
$9C00+X*$20*Y para la pantalla que comienza en $9C00.
Una vez que hemos localizado donde están los datos de la pantalla ($00:29E2) podemos buscar
este valor sin el número de banco en el código fuente que podemos obtener con el emulador
BGB con lo que encontramos lo siguiente:
ROM0:279D 21 E2 29
ld hl,29E2
ROM0:27A0 11 00 D3
ld de,D300
ROM0:27A3 01 1A 00
ld bc,001A
ROM0:27A6 CD 14 04
call 0414
ROM0:27A9 3E 01
ld a,01
ROM0:27AB EA 52 C1
ld (C152),a
ROM0:27AE 01 01 00
ld bc,0001
ROM0:27B1 CD DE 05
call 05DE
ROM0:27B4 21 FC 29
ld hl,29FC
ROM0:27B7 11 00 D3
ld de,D300
ROM0:27BA 01 17 00
ld bc,0017
ROM0:27BD CD 14 04
call 0414
ROM0:27C0 3E 01
ld a,01
ROM0:27C2 EA 52 C1
ld (C152),a
ROM0:27C5 01 01 00
ld bc,0001
ROM0:27C8 CD DE 05
call 05DE
ROM0:27CB 21 13 2A
ld hl,2A13
ROM0:27CE 11 00 D3
ld de,D300
ROM0:27D1 01 1B 00
ld bc,001B
ROM0:27D4 CD 14 04
call 0414
ROM0:27D7 3E 01
ld a,01
ROM0:27D9 EA 52 C1
ld (C152),a
ROM0:27DC 01 01 00
ld bc,0001
ROM0:27DF CD DE 05
call 05DE
ROM0:27E2 21 2E 2A
ld hl,2A2E
ROM0:27E5 11 00 D3
ld de,D300
ROM0:27E8 01 1B 00
ld bc,001B
ROM0:27EB CD 14 04
call 0414
ROM0:27EE 3E 01
ld a,01
ROM0:27F0 EA 52 C1
ld (C152),a
ROM0:27F3 01 01 00
ld bc,0001
ROM0:27F6 CD DE 05
call 05DE
ROM0:27F9 C9
ret
En la primera línea de este código vemos que aparece la dirección $29E2 que corresponde a
(ROM0:279D).
El código guardado contiene cada línea el texto “ROM” y a continuación el número de banco
que en este caso es el 0 y la dirección que es $279D.
ROM0:279D 21 E2 29
ld hl,29E2
Esta instrucción carga en hl la dirección $00:29E2 donde empiezan los datos para dibujar el
rótulo.
En la siguiente línea encontramos lo siguiente:
ROM0:27A0 11 00 D3
ld de,D300
Esto lo que hace es cargar en el registro “de” la dirección $D300 que corresponde a la RAM.
Las líneas siguientes:
ROM0:27A3 01 1A 00
ld bc,001A
ROM0:27A6 CD 14 04
call 0414
Cargan en el registro “bc” el valor $001A (1ª línea) y la siguiente llama a una subrutina que se
encuentra en la dirección $00:0414.
Esta utilización de los registros es algo habitual en todos los juegos de Gameboy donde se
pone en hl y de las direcciones de carga y escritura. En bc se pasa el número de bytes a
leer/copiar y se llama a la subrutina que lee de una dirección y la pone en otra dirección.
En el resto de código aparecen otras direcciones donde hay datos para dibujar el rótulo y
donde se pone en la RAM.
La rutina que llama en la dirección $00:0414 tiene las siguientes líneas:
ROM0:0414 2A
ldi a,(hl)
ROM0:0415 12
ld (de),a
ROM0:0416 13
inc de
ROM0:0417 0B
dec bc
ROM0:0418 78
ld a,b
ROM0:0419 B1
or c
ROM0:041A 20 F8
ROM0:041C C9
jr nz,0414
ret
Lo que hace es que carga los datos que hay en hl (datos del rótulo) y los pone en de (RAM). En
bc se le pasa el número de bytes.
Se utiliza ldi que carga en a el valor que hay en la dirección contenida en hl que sería $00:29E2
para el primer tile que tiene el valor $98 e incrementa hl con lo que hl=$00:29E3).
Se utiliza “ldi” para incrementar a la vez que se carga la dirección hl. A veces aunque no es lo
normal se puede utilizar “ld a,(hl)” aunque luego debe incluirse la instrucción “inc hl” para
incrementar la dirección “hl”. Por eso se suele utilizar “ldi a,(hl)” para ahorrarse una
instrucción (“inc hl”).
A continuación pone el valor de a que es $98 (el valor que hay en la dirección $00:29E2 en la
dirección contenida en de=$D300).
Seguidamente aparece “inc de” que incrementa la dirección contenida en de con lo que pasa a
ser $D301 y luego con “dec bc” disminuye el valor de bc (o sea el número de bytes que quedan
por leer y escribir.
Las 2 siguientes son operaciones que aparecen y que no hace falta que comprendan. Luego
aparece “jr nz,$0414” que lo que hace es comprobar si ya no quedan bytes por leer/escribir
cuando bc vale $00 y si no es el caso (“nz” ) salta a la dirección $00:0414 que es el inicio de la
rutina para seguir leyendo y escribiendo bytes.
En caso de que se hubieran leído y escrito todos los bytes en lugar de saltar a la dirección
$00:0414 se ejecutaría la instrucción en la dirección $00:041C (que regresaría a la dirección
que llamó a esta subrutina una vez que se han leído y escrito todos los bytes).
“Ret” es la abreviatura de “Return” (Regresar).
La posición donde se encuentra la instrucción de carga de datos también la podríamos haber
encontrado con el editor hexadecimal buscando los dígitos hexadecimales $E292 aunque luego
habría que ir a esa posición en el código fuente.
Para modificar la pantalla de título podríamos cambiar las direcciones Map Address donde
situamos los tiles con lo que se puede situar el rótulo en una posición distinta. También se
puede modificar los tiles y hacer otros propios para hacer una pantalla de título
completamente distinta.
Hemos localizado los datos del rótulo, el fragmento de carga de datos y los ponen en RAM y
una rutina de uso general que carga datos de una dirección y la pone en otra.
Además de esto aparecen los textos “HISCORE-” y “SCORE-” con las puntuaciones y varios
textos del menú principal del juego que no hemos localizado todavía.
Si buscamos “HISCORE-” con un editor hexadecimal como texto no encontraremos nada pero
si buscamos los dígitos hexadecimales “E8 E9 F3 E3 EF F2 E5 CD” lo encontraremos en la
dirección $2A5C-$2A63 ($00:2A5C-$00:2A63).
En la dirección $00:2A59-$00:2A5A encontramos la dirección $9802 que es la Map Address
donde pone el texto en pantalla y en $00:2A5B encontramos $08 que es el número de tiles
para poner el texto “HISCORE-”.
En $00:2A64-$00:2A6C encontramos los datos para poner “SCORE” en pantalla y termina con
$00 para indicar el fin del texto. A continuación encontramos los textos del menú principal.
En $00:2A64 tenemos: 98 24 (Map Address=$9824)
$00:2A66: 06 (Nº de tiles=$06)
$00:2A67: F3 E3 EF F2 E5 CD (Texto “SCORE-”)
Vamos a hacer una modificación para ver el proceso cambiando el texto “SCORE-“ por el texto
“PUNTOS” para traducirlo al español sin poner el símbolo “-“ que ocupa 6 tiles también.
Si observamos la pantalla de “BGB VRAM Viewer” para ver los tiles que corresponden a cada
letra vemos que los valores son los siguientes:
“P”: $F0
“U”: $F5
“N”: $EE
“T”: $F4
“O”: $EF
“S”: $F3
Poniendo estos valores con un el editor hexadecimal HxD a partir de la dirección $00:2A67
obtenemos la siguiente pantalla en el emulador con lo que vemos como hemos hecho una
modificación sencilla del juego.
Si cambiamos además el byte que aparece en la dirección $00:2A65 que tiene el valor $24 por
$22 cambiamos la Map Address a $9822 con lo que la pantalla ahora quedaría como en la
captura siguiente:
Así hemos conseguido situar el texto en otra posición diferente en la pantalla de título.
El problema puede surgir cuando queramos poner un texto con más tiles en la pantalla ya que
no tenemos espacio para situar el texto en la posición donde está pero podemos buscar alguna
zona donde haya espacio en blanco en el juego.
En los juegos normalmente aparecen secuencias de valores $00 o con $FF para rellenar el
espacio en blanco ya que las ROMs de los juegos se tenían que adaptar a tamaños de 32, 64,
128,… kilobytes y para completar se añadía estos valores.
Debemos asegurarnos que la secuencia corresponde realmente a espacio en blanco porque
puede tratarse de un tile o a datos. Normalmente si son más de 16 bytes seguidos puede
tratarse de espacio.
En las direcciones $00:0061-$00:00FF encontramos un área de bytes con el valor $FF
suficientemente grande para poner datos. Normalmente esta zona suele estar vacía o con
fragmentos de rutinas.
Podemos empezar si queremos en la dirección $00:0063 donde vamos a poner los bytes:
98 02 08 E8 E9 F3 E3 EF F2 E5 CD 98 2E 07 F0 F5 EE F4 EF F3 CD 00
Aquí hemos dejado el texto “HISCORE-“ igual que estaba pero hemos puesto en lugar de
“SCORE-“ el texto “PUNTOS-“ que en lugar de 6 tiles tiene 7 tiles. También se ha cambiado la
Map Address $9824 por $9823 para posicionar el texto en pantalla de forma correcta.
Pero si cargamos el juego no se vería la modificación ya que todavía se cargan los textos que
empiezan en la dirección $00:2A59 que son los originales del juego por lo que debemos buscar
los valores hexadecimales 592A en el editor hexadecimal para ver dónde está este valor que
encontramos en $2A4B-$2A4C.
Aquí cambiamos 592A (para la dirección $00:2A59) por 6300 (para la dirección $00:0063)
donde hemos puesto ahora los textos cambiados. En la dirección donde estaban los textos
originales podríamos utilizarlos para otra cosa si no los utilizamos.
Ya con ambas modificaciones la pantalla de título del juego quedaría como:
Ya está hecha bien la modificación. Igualmente podríamos cambiar el texto “HISCORE” por
otro texto como “MAXPUNTOS” y el resto de textos de la pantalla de título.
Con la modificación anterior todavía nos han quedado los bytes $00:0079-$00:00FF sin
modificar lo que hace un total de $87 bytes=135 bytes para poner algunos textos más si
quisiéramos.
Otra opción que se podría haber utilizado es crear nuestros propios tiles aunque requiere más
trabajo para hacer varios tiles que pongan el texto que queramos intentando utilizar un
tamaño más chico de la fuente para poner con menos tiles más texto.
Esto requiere más experiencia y supone mucha más complicación. Para ello podemos buscar
donde están por ejemplo los datos de los tiles $BB-$BF que aparecen en blanco según aparece
en el “BGB VRAM Viewer”.
Hemos buscado en el banco $00 que es donde estaba el texto original para evitarnos
complicaciones de cambios de bancos. De todas formas, aunque el texto original hubiese
estado en otro banco podríamos haber utilizado el banco $00 ya que al estar en la dirección
$00:0063 no hay problemas.
Si un texto está en la dirección $00:0063 si se pasara en hl la dirección no habría que
especificar banco, lo que no ocurre cuando la dirección está comprendida entre $4000-$7FFF
que si hay que especificarlo.
No es lo mismo $01:4000 que $02:4000, $03:4000. Cuando no se especifica nada se supone
que el banco es el mismo que el del banco donde se encuentra la instrucción.
Por ejemplo, si encontramos en la dirección $01:4000 la siguiente instrucción:
Ld hl,$4036
Se supone que se carga en hl la dirección $01:4036.
También si se encuentra en el banco $00 se supone que corresponde al banco $01. Si
quisiéramos cargar un texto de otro banco tendríamos que añadir un fragmento de código
para seleccionar el banco y la cosa se complica.
Para seleccionar un banco se escribe el valor del banco entre la dirección $2000-$3FFF.
Normalmente se utiliza en la mayoría de juegos la dirección $2000 aunque se puede utilizar
cualquiera del intervalo anterior.
Si ponemos:
Ld a,$03
Ld (2000),a
Ld hl,$4036
Se carga en a el valor $03 y se selecciona el banco $03 poniendo este valor en la dirección
$2000. En hl se pone la dirección $4036 con lo que se carga en hl la dirección $03:4036.
Esto es para que vean la complicación que puede surgir si tienen que cambiar de bancos los
textos o rutinas.
Alternativamente para localizar los textos podríamos haber utilizado otra herramienta
(Translhextion) que permite la búsqueda relativa de textos.
La búsqueda comparativa permite encontrar textos que no aparecen como tales sino en
formato de tiles independientemente del valor que tenga cada letra pero esto se ve mejor con
un ejemplo.
Si abrimos con el programa el juego “Battle Bull” que hemos utilizado antes podemos buscar el
texto “HISCORE” que aparece en él y que antes localicemos.
Aparentemente es un editor hexadecimal aunque incluye esta opción de búsqueda relativa
además de la búsqueda normal.
Seleccionando la opción “Scan Relative” del menú “Search” nos aparece un cuadro de diálogo
donde ponemos el texto a buscar.
Como se ve en la captura lo encuentra en la dirección $2A5C-$2A62 tal y como habíamos visto
anteriormente. En amarillo aparece marcado el texto encontrado que como vemos no se
parece nada al texto sino que representa los tiles para poner dicho texto.
En esta ventana si se ha encontrado el texto nos permite guardar los resultados o generar una
tabla (“Generate Table”).
Podemos guardar la tabla con los valores de los tiles que corresponden a cada letra con el
nombre del juego (“battle bull.tbl”).
Si abrimos el fichero creado con el bloc de notas podemos comprobar que se trata de un
fichero normal y corriente sólo que tiene la extensión .tbl y que podemos modificar a nuestro
gusto.
Podemos ver como al valor “E1” le corresponde la letra “A” (el tile $E1=”A”), al valor “E2” le
corresponde la letra “B”,….
Si vemos en el “BGB VRAM Viewer” la pestaña “Tiles” en la pantalla de título del juego
podemos comprobar que estos datos están bien pero faltan algunos números y símbolos.
$D0=”0”, $D1=”1”,…, $D9=”9”, $DA=”:”, $DB=”;”,… que podemos añadir incluyéndolos en el
fichero “battle bull.tbl”.
D0=0
D1=1
…
Una vez hecho esto y guardada la tabla podemos abrir el fichero de tabla creada con el
programa utilizando la opción “Open Thingy Table…” del menú “Script”.
Yo he incluido los números y símbolos en el fichero de tabla lo que hace un total de 62
elementos tal y como se ve en la captura.
He marcado en el recuadro “Table Toolbar” la opción “Thingy View Active” para que utilice la
tabla con sus equivalencias con lo que ya vemos en la parte derecha los textos.
También marcando los textos podemos guardar el script (un fichero con todos los textos) para
lo cual los seleccionamos y pulsamos con el botón derecho para que nos aparezca un menú.
En este menú seleccionamos la opción “Dump Script…” y nos aparece una ventana donde
seleccionamos en “Dump bytes to” la opción “File” para guardarlo en un fichero.
Este fichero tiene el siguiente contenido:
{98}{02}{08}HISCORE-{98}{24}{06}SCORE{00}{99}{45}{0A}PUSH{C0}START{99}{67}{07}1PLAYER{99}{87}{08}2PLAYERS{99}{A7}{08}PASSW
ORD{00}{99}${0A}{6B}{6D}{6F}{71}{73}{75}{77}{79}{7B}{7B}{99}D{0A}{6C}{6E}{70}{72}{74}{76}{7
8}{7A}{7A}{7C}{00}{9A}{00}{14}LICENSED{C0}BY{C0}NINTENDO{00}{02}{00}{01}{00}{00}{01}{02}
{01}{01}{02}{00}{02}{66}{99}{86}{99}{A6}{99}{0E}{00}I{2A}{0E}{00}W{2A}{99}{45}{0A}GAME{C0}
START{00}{99}{45}{0A}{C0}{C0}{C0}{C0}{C0}{C0}{C0}{C0}{C0}{C0}
También nos da la opción de reemplazar el script que lo que hace es cargar un fichero de script
con formato similar al anterior y cambiar por el anterior.
Por ejemplo si modificamos en el script creado el texto “SCORE-“ por “PUNTOS” tal y como
aparece a continuación en el script:
{98}{02}{08}HISCORE{98}{24}{06}PUNTOS{00}{99}{45}{0A}PUSH{C0}START{99}{67}{07}1PLAYER{99}{87}{08}2PLAYER
S{99}{A7}{08}PASSWORD{00}{99}${0A}{6B}{6D}
{6F}{71}{73}{75}{77}{79}{7B}{7B}{99}D{0A}{6C}{6E}{70}{72}{74}{76}{78}{7A}{7A}{7C}{00}{9A}{00
}{14}LICENSED{C0}BY{C0}NINTENDO{00}{02}{00}{01}{00}{00}
{01}{02}{01}{01}{02}{00}{02}{66}{99}{86}{99}{A6}{99}{0E}{00}I{2A}{0E}{00}W{2A}{99}{45}{0A}G
AME{C0}START{00}{99}{45}{0A}{C0}{C0}{C0}{C0}{C0}{C0}{C0}
{C0}{C0}{C0}
Ahora debemos seleccionar el fichero con el script cambiado para que nos cambie el texto que
hemos modificado.
“RST”: UN BYTE PARA DOMINAR EL MUNDO
Esto es una broma pero hay una instrucción “RST” que es bastante útil para saltar a unas
determinadas posiciones del juego con un único byte.
Por ejemplo, la instrucción “jp
representa con los bytes C3 08 00.
0008” equivale a saltar a la dirección $0008 y que se
La instrucción “call 0008” equivale a llamar a la subrutina que hay en la dirección $0008 y
que se representa con los bytes CD 08 00.
Pero hay una instrucción “RST $08” que se representa con el byte $CF (1 byte) y que equivale a
la instrucción anterior “call 0008” pero ocupando sólo un byte con lo que nos ahorramos 2
bytes y podemos ponerla cuando no tenemos espacio para poner otra instrucción.
Hay una serie de instrucciones RST (RST $00, $08, $10, $18, $20, $28, $30, $38) que podemos
utilizar y otras que hacen referencia a rutinas de la consola.
RST $40: Dirección de la interrupción Vertical Blank (VBlank)
RST $48: Dirección de la interrupción de LCDC Status
RST $50: Dirección de la interrupción de Timer Overflow
RST $58: Dirección de la interrupción de Serial Transfer Completion
RST $60: Dirección de la interrupción de High-to-Low de P10-P13
Podemos utilizar el inicio de la ROM para poner rutinas siempre que no haya código en esta
área (esté llena de valores $00 o $FF indicando espacio en blanco).
Si está en blanco podemos poner fragmento de código como puede ser salto a subrutinas,…
Esto se puede ver de una forma más fácil con un ejemplo de su utilidad para lo cual vamos a
utilizar el juego “Boulder Dash”.
Si buscamos el fragmento de código donde se ponen las vidas iniciales del jugador 1 y 2 lo
encontramos en el fragmento:
ROM0:027B 3E 03
ld a,03
ROM0:027D EA 62 D7
ld (D762),a
ROM0:0280 EA 63 D7
ld (D763),a
Supongamos que quisiéramos que el jugador 2 empiece con 7 vidas y el jugador 1 con 03 tal y
como aparecía.
Aquí no tenemos espacio para poner un fragmento de código para que el jugador 2 empiece
con 7 vidas que sería:
3E 07
EA 63 D7
ld a,07
ld (D763),a
La modificación que podríamos hacer sería:
ROM0:0280 3E 07
ld a,07
ROM0:0282 E7
rst 20
Esto lo que hace es poner en el registro “a” el valor $07 y la siguiente instrucción “rst 20”
equivale a llamar a la rutina que hay en la dirección $00:0020 que está en blanco (está llena de
valores $FF).
En $0020 es donde debemos poner lo que nos falta para poner este valor $07 en la dirección
donde se ponen las vidas del jugador 2 ($D763).
ROM0:0020 EA 63 D7
ld (D763),a
ROM0:0023 C9
ret
La última instrucción “ret” lo que hace es regresar a la siguiente instrucción que aparece
después del “rst 20” que se encuentra en $00:0283 para continuar con el proceso.
Otro juego que podemos modificar para utilizar esta instrucción puede ser el “Magical Chase”
de Gameboy Color que dispone de un truco que permite mostrar una opción adicional en el
menu principal para seleccionar el nivel donde empezar el juego.
Esto se consigue con la combinación de botones Arriba, Abajo, Arriba, Abajo, B, B, B, B,
Izquierda, Derecha, Izquierda, Derecha, A.
Los primeros $40 bytes del juego tienen el valor $00 con lo que podemos utilizarlos para poner
fragmentos de código.
El botón pulsado se guarda en las direcciones de la RAM $C0BA y $C0BB . En la dirección
$D829 se pone el valor $00 (cheat no active) y $01 (cheat active cuando se ha pulsado la
combinación de botones para activar el truco).
Si ponemos un breakpoint en la dirección $D829 para comprobar cuando se escribe en esa
dirección el emulador se para en el fragmento:
ROM0:084F 21 00 C0
ld hl,C000
ROM0:0852 AF
xor a
ROM0:0853 47
ld b,a
ROM0:0854 0E 20
ld c,20
ROM0:0856 22
ldi (hl),a
ROM0:0857 05
dec b
ROM0:0858 20 FC
ROM0:085A 0D
ROM0:085B 20 F9
ROM0:085D C9
jr nz,0856
dec c
jr nz,0856
ret
Sin embargo, aquí no se pone el valor $00 sólo en $D829 sino desde $C000 en adelante
llenando la RAM con este valor.
Si se hubiese puesto sólo el valor $00 en la dirección $D829 podríamos haber modificado la
instrucción para poner $01 en su lugar con lo que se activaría el cheat pero aquí no podemos
hacerlo.
Podemos hacer una modificación cambiando la instrucción en $00:085D por:
ROM0:085D C7
rst $00
También tenemos que poner un fragmento en la dirección $00:0000:
ROM0:0000 3E 01
ld a,01
ROM0:0002 EA 29 D8
ROM0:0005 C9
ld (D829),a
ret
Esto lo que hace es una vez que se ha llenado la RAM con el valor $00 se utiliza “RST $00” para
llamar a la rutina en $00:0000 donde se pone el valor $01 en la dirección $D829 para que el
cheat esté activo independientemente del botón o botones pulsados. Finalmente cómo
habíamos quitado la instrucción “RET” en $00:085D tenemos que ponerla para que regrese de
la subrutina.
Se puede jugar el juego y comprobar que esta modificación realmente tiene el efecto buscado
y que todo funciona correctamente igual que en el juego original.
Esta modificación no es la única que podemos utilizar para que funcione el cheat de forma
automatic sin pulsar ningún botón. Podemos localizar el fragmento de código que comprueba
la combinación de botones y pone el valor $01 si es correcto poniendo un breakpoint cuando
se escriba el valor $D829 en $01.
Esto ocurre en $06:5F5C donde se pone este valor y un poco más arriba encontramos:
ROM6:5F23 21 6F DA
ROM6:5F26 2A
ROM6:5F27 CB 57
ld hl,DA6F
ldi a,(hl)
bit 2,a
ROM6:5F29 C8
ret z
ROM6:5F2A 2A
ldi a,(hl)
ROM6:5F2B CB 5F
bit 3,a
ROM6:5F2D C8
ret z
ROM6:5F2E 2A
ldi a,(hl)
ROM6:5F2F CB 57
bit 2,a
ROM6:5F31 C8
ret z
ROM6:5F32 2A
ldi a,(hl)
ROM6:5F33 CB 5F
bit 3,a
ROM6:5F35 C8
ret z
ROM6:5F36 2A
ldi a,(hl)
ROM6:5F37 CB 6F
bit 5,a
ROM6:5F39 C8
ret z
ROM6:5F3A 2A
ldi a,(hl)
ROM6:5F3B CB 6F
bit 5,a
ROM6:5F3D C8
ret z
ROM6:5F3E 2A
ldi a,(hl)
ROM6:5F3F CB 6F
bit 5,a
ROM6:5F41 C8
ret z
ROM6:5F42 2A
ldi a,(hl)
ROM6:5F43 CB 6F
bit 5,a
ROM6:5F45 C8
ret z
ROM6:5F46 2A
ldi a,(hl)
ROM6:5F47 CB 4F
ROM6:5F49 C8
bit 1,a
ret z
ROM6:5F4A 2A
ROM6:5F4B CB 47
ldi a,(hl)
bit 0,a
ROM6:5F4D C8
ret z
ROM6:5F4E 2A
ldi a,(hl)
ROM6:5F4F CB 4F
bit 1,a
ROM6:5F51 C8
ret z
ROM6:5F52 2A
ldi a,(hl)
ROM6:5F53 CB 47
bit 0,a
ROM6:5F55 C8
ret z
ROM6:5F56 2A
ldi a,(hl)
ROM6:5F57 CB 67
ROM6:5F59 C8
ROM6:5F5A 3E 01
bit 4,a
ret z
ld a,01
ROM6:5F5C EA 29 D8
ROM6:5F5F C9
ld (D829),a
ret
En la primera instrucción $06:5F23 se carga la dirección $DA6F que es también donde se
guarda la combinación de botones que introducimos ($DA6F-$DA7B).
Se comprueba el bit 2 (Arriba), el bit 3 (Abajo), el bit 5 (B) cuatro veces seguidas, el bit 1
(Izquierda), el bit 0 (Derecha) y el bit 4 (A). También podríamos cambiar la combinación de
botones para el cheat cambiando estas comprobaciones.
Podemos buscar donde se llama a esta rutina lo que se encuentra en $06:5F1A:
ROM6:5F1A CD 23 5F
call 5F23
Si sustituimos esta instrucción por:
ROM6:5F1A CD 5A 5F
call 5F5A
Se salta la rutina de comprobación y va directamente a la dirección $06:5F5A donde se pone el
valor $01 en $D829.
Aquí tendremos que pulsar algún botón para que funcione.
Buscando esto encontré un segundo cheat que te da 99999 orbs, invulnerabilidad y la opción
de seleccionar el nivel con lo que aparece la cuarta opción del menú y en la pantalla de nivel
aparecen 2 opciones extras:
Esto puede ser accesible poniendo el valor $02 en la dirección $D829 por lo que si queremos
tener este menú ampliado podríamos haber cambiado el fragmento que pusimos en $00:0000
para poner el valor $02 en lugar de $01.
El fragmento que comprueba la combinación de botones para activar este nuevo cheat lo
encontramos en $06:5F60-$06:5FAC:
ROM6:5F60 21 6B DA
ROM6:5F63 2A
ROM6:5F64 CB 57
ld hl,DA6B
ldi a,(hl)
bit 2,a
ROM6:5F66 C8
ret z
ROM6:5F67 2A
ldi a,(hl)
ROM6:5F68 CB 5F
bit 3,a
ROM6:5F6A C8
ret z
ROM6:5F6B 2A
ldi a,(hl)
ROM6:5F6C CB 57
bit 2,a
ROM6:5F6E C8
ret z
ROM6:5F6F 2A
ldi a,(hl)
ROM6:5F70 CB 5F
bit 3,a
ROM6:5F72 C8
ret z
ROM6:5F73 2A
ldi a,(hl)
ROM6:5F74 CB 77
bit 6,a
ROM6:5F76 C8
ret z
ROM6:5F77 2A
ldi a,(hl)
ROM6:5F78 CB 4F
bit 1,a
ROM6:5F7A C8
ret z
ROM6:5F7B 2A
ldi a,(hl)
ROM6:5F7C CB 6F
bit 5,a
ROM6:5F7E C8
ret z
ROM6:5F7F 2A
ldi a,(hl)
ROM6:5F80 CB 6F
bit 5,a
ROM6:5F82 C8
ret z
ROM6:5F83 2A
ldi a,(hl)
ROM6:5F84 CB 57
bit 2,a
ROM6:5F86 C8
ret z
ROM6:5F87 2A
ldi a,(hl)
ROM6:5F88 CB 5F
bit 3,a
ROM6:5F8A C8
ret z
ROM6:5F8B 2A
ldi a,(hl)
ROM6:5F8C CB 57
bit 2,a
ROM6:5F8E C8
ret z
ROM6:5F8F 2A
ldi a,(hl)
ROM6:5F90 CB 5F
bit 3,a
ROM6:5F92 C8
ret z
ROM6:5F93 2A
ldi a,(hl)
ROM6:5F94 CB 77
bit 6,a
ROM6:5F96 C8
ret z
ROM6:5F97 2A
ldi a,(hl)
ROM6:5F98 CB 47
bit 0,a
ROM6:5F9A C8
ret z
ROM6:5F9B 2A
ldi a,(hl)
ROM6:5F9C CB 6F
bit 5,a
ROM6:5F9E C8
ret z
ROM6:5F9F 2A
ldi a,(hl)
ROM6:5FA0 CB 6F
bit 5,a
ROM6:5FA2 C8
ret z
ROM6:5FA3 2A
ldi a,(hl)
ROM6:5FA4 CB 67
bit 4,a
ROM6:5FA6 C8
ROM6:5FA7 3E 02
ret z
ld a,02
ROM6:5FA9 EA 29 D8
ROM6:5FAC C9
ld (D829),a
ret
Si analizamos el código podemos hallar la combinación de botones que activa el cheat viendo
los bits que comprueba: 2 (Arriba), 3 (Abajo), 2 (Arriba), 3 (Abajo), 6 (Select), 1 (Izquierda), 5
(B), 5 (B), 2 (Arriba), 3 (Abajo), 2 (Arriba), 3 (Abajo), 6 (Select), 0 (Derecha), 5 (B), 5 (B), 4 (A)
según parece.
Podemos activar este cheat también introduciendo en la pantalla de cheats la dirección $D829
y el valor $02.
Como se ha visto el uso de la instrucción “RST” no es posible cuando hay código en esta parte
del juego pero también se podría hacer aunque es más complicado ya que hay que mover más
código.
A veces también se aprovecha espacio en blanco para poner ahí rutinas introduciendo saltos a
estas rutinas que colocamos en áreas con valores $00 o $FF. En este caso debemos
asegurarnos que se trata de espacio en blanco realmente.
PASSWORDS DE LOS JUEGOS
Hay algunos juegos que tienen un sistema de passwords para empezar en un nivel
determinado.
En estos juegos los passwords pueden estar como tales en la ROM en formato texto, en
formato tile, o con alguna codificación.
En otras ocasiones no hay passwords en la ROM sino que hay una rutina para comprobar si el
password introducido es correcto y en los que es más complicado localizar la rutina y obtener
los passwords válidos.
A través de ejemplos se va a tratar varias de las opciones que nos podremos encontrar en
juegos de Gameboy.
Para el caso de passwords en el juego en formato texto vamos a utilizar el juego “Judge
Dredd”.
Este juego permite la introducción de 8 letras para el password del juego. Podemos
encontrarnos con el caso de que no conozcamos ningún password y tendríamos que partir de
cero localizando tanto los passwords como la rutina.
También puede darse el caso de que conozcamos los passwords del juego y baste con
localizarlos en el juego (que es tan simple como buscar el password en formato texto) en el
fichero del juego (ROM) utilizando un editor hexadecimal. Posteriormente podemos localizar la
rutina de comprobación de passwords.
Suponiendo que no conocemos ninguna password del juego vamos a localizar donde se
almacena las letras del password que vamos introduciendo en la RAM para lo cual vamos a
utilizar el buscador de cheats que incluye el emulador BGB.
El proceso es simple basta con poner por ejemplo, la letra “A” en la primera letra del password
e iniciar la búsqueda de cheats utilizando la opción “Cheat searcher” del menú “Other” y
pulsar el botón “Start”.
Aquí aparecen las 8319 direcciones posibles de la RAM donde están almacenadas las letras que
introducimos para el password.
Ahora sin cerrar esta ventana podemos ir cambiando la primera letra del password poniendo
“B” y pulsando “above” en la pantalla de arriba y luego “Search” con lo que se buscan
direcciones de la RAM en las que ha aumentado el valor que incluyen con lo disminuye el
número de direcciones de la RAM que en mi caso se queda sólo en 22.
Podemos poner “C” y darle otra vez a “Search” con lo que a mí me quedan 9 direcciones sólo y
una de ellas es donde se guarda la 1ª letra introducida del password.
Repetiendo el proceso nos quedamos con 3 direcciones posibles:
DB90=06,
DEE6=46,
DEEA=46.
Para asegurarnos cuál de las 3 es la verdadera podemos hacer el mismo proceso con la 2ª letra
para lo cual anotadas las anteriores pulsamos “Start” para iniciar la búsqueda con las 8319
direcciones de la RAM.
Al hacer esto nos encontramos con las 3 direcciones posibles para la 2ª letra del password:
DB91=06,
DEE6=46,
DEEA=46.
Si comparamos ambos grupos de direcciones vemos que DEE6 y DEEA aparecen en las dos y
corresponden a la letra actual del password que estamos introduciendo en formato ASCII. El
código 46 corresponde a la letra “F” pero estas direcciones no son donde se almacenan las
letras del password introducidas.
Si nos fijamos en las 2 que nos quedan y que no se repiten son las que buscamos. La de arriba
DB90=06 corresponde a la 1ª letra del password, DB91=06 corresponde a la 2ª letra del
password, con lo que las siguientes son DB92, DB93, DB94, DB95, DB96 y DB97 (ya que hay 8
letras en el password).
Los valores que toman estas direcciones son $01=”A”, $02=”B”, $03=”C”, $04=”D”, $05=”E”,
$06=”F”,…., $1A=”Z”.
Por lo tanto, debe haber una rutina que comprueba los valores que hemos introducido que
están almacenadas en las direcciones de la RAM comprendidas entre DB90-DB97 con las que
hay en el juego.
Para averiguarlo vamos a introducir el password que queramos dando igual que sea incorrecto
y una vez hecho esto antes de pulsar “Start” para que compruebe si el password es correcto
vamos a poner un breakpoint por ejemplo en la dirección $DB90 (1ª letra del password) pero
en lugar de cuando se escribe (“w”) debemos ver cuando se lee la dirección $DB90 ya que la
rutina lo que hace es leer las letras del password.
Seleccionamos la opción “Access breakpoints” del menú “Debug” y lo dejamos tal y como se ve
en la siguiente imagen:
Donde pone la dirección “DB90” podemos poner un rango de direcciones (como “DB90-DB97”
pero ponemos sólo una dirección). En “value” podemos poner un valor para que se pare el
emulador cuando se escriba o lea esa dirección y coincida con ese valor.
“on write” se selecciona cuando se quiere ver cuando se escribe en esa dirección, “on execute”
cuando se ejecuta una determinada dirección y “on jump” para saltos a una dirección.
Si ahora pulsamos el botón “Start” (que hayamos configurado en el emulador que corresponde
a ese botón de la consola) haremos que se pare el emulador porque se lee la dirección $DB90.
Si nos fijamos en la pantalla del Debugger del emulador vemos como se ha parado en la
dirección $03:6256 que contiene la instrucción “cp (hl)”.
En realidad es “cp a,(hl)” que compara el valor de a con el que hay en la dirección contenida en
hl que como vemos en la parte derecha donde están los valores de los registros tiene “hl=
DB90”.
Si nos fijamos un poco más arriba vemos como en $03:624E se pone en el registro “de” el valor
$42EB por lo que en “de” se pone la dirección $03:42EB donde está uno de los passwords del
juego (“SENTENCE”). En el fichero de la ROM si lo abrimos con el editor hexadecimal HxD la
encontramos a partir de C2EB ya que se trata del banco $03 que comienza en $C000.
Banco $03:4000 corresponde a $C000 y por tanto $03:42EB corresponde a $C2EB y que
podemos modificar al estar en formato texto por cualquier cadena de 8 letras con lo que
modificaríamos el primer password (“SENTENCE”) por otra.
Si ponemos “SENCILLO” que tiene 8 letras al poner este password el juego lo daría como
correcto y empezaríamos en el nivel que corresponda.
La rutina de comprobación completa es la siguiente y podemos guardarla en un fichero para
analizarla:
ROM3:624B 21 90 DB
ld hl,DB90
ROM3:624E 11 EB 42
ld de,42EB
ROM3:6251 06 08
ROM3:6253 1A
ROM3:6254 D6 40
ROM3:6256 BE
ld b,08
ld a,(de)
sub a,40
cp (hl)
ROM3:6257 C2 63 62
jp nz,6263
ROM3:625A 13
inc de
ROM3:625B 23
inc hl
ROM3:625C 05
dec b
ROM3:625D C2 53 62
jp nz,6253
ROM3:6260 C3 9C 62
jp 629C
ROM3:6263 21 90 DB
ld hl,DB90
ROM3:6266 11 F7 42
ld de,42F7
ROM3:6269 06 08
ROM3:626B 1A
ROM3:626C D6 40
ROM3:626E BE
ROM3:626F C2 7B 62
ld b,08
ld a,(de)
sub a,40
cp (hl)
jp nz,627B
ROM3:6272 13
inc de
ROM3:6273 23
inc hl
ROM3:6274 05
dec b
ROM3:6275 C2 6B 62
jp nz,626B
ROM3:6278 C3 A2 62
jp 62A2
ROM3:627B 21 90 DB
ld hl,DB90
ROM3:627E 11 03 43
ld de,4303
ROM3:6281 06 08
ROM3:6283 1A
ROM3:6284 D6 40
ROM3:6286 BE
ROM3:6287 C2 93 62
ld b,08
ld a,(de)
sub a,40
cp (hl)
jp nz,6293
ROM3:628A 13
inc de
ROM3:628B 23
inc hl
ROM3:628C 05
dec b
ROM3:628D C2 83 62
jp nz,6283
ROM3:6290 C3 A8 62
jp 62A8
ROM3:6293 11 EB 41
ld de,41EB
ROM3:6296 CD B2 5E
call 5EB2
ROM3:6299 C3 D9 60
jp 60D9
Básicamente lo que hace en primer lugar es poner en el registro “hl” la dirección que
corresponde a la 1ª letra del password en RAM y en el registro “de” se ponen las direcciones
donde están los passwords reales del juego que son ($03:42EB, $03:42F7 y $03:4303):
“SENTENCE”, “SOFTWARE” y “ANDERSON”.
En el registro “b” se pone el número de letras que tiene el password que es $08.
Los passwords que están en el juego se leen y se disminuye el valor de cada letra que hay en la
ROM en $40 para convertirlo al formato que está en la RAM para así poder compararlo.
En la ROM el password “SENTENCE” está almacenado como 53 45 4E 54 45 4E 43 45 pero en la
RAM está almacenada como 13 05 0E 14 05 0E 03 05.
Así al restarle a los primeros valores $40 obtendríamos los segundos para lo cual el programa
utiliza la instrucción “sub a,40”.
Los passwords terminan con el valor $FF para indicar que se ha acabado y antes de ellos
aparecen los dígitos hexadecimales 16 0C 12 que todavía no he comprobado si tienen alguna
utilidad.
Las instrucciones “inc hl” e “inc de” se utilizan para incrementar las direcciones donde están
las letras del password en RAM y en el juego y así ir comparando cada una de las letras. La
instrucción “dec b” disminuye el número de letras que quedan por comprobar del password.
Si eliminamos la primera instrucción “sub a,40” que se encuentra la dirección $03:6254
($E254) que se codifica como “D6 40” cambiandólo por “00 00” que equivale a 2 instrucciones
“nop” (NOt oPeration: No hace nada) conseguimos que no se disminuya el valor de cada una
de las letras del password.
Sin embargo, ahora si introducimos el password “SENTENCE” con esta modificación no
aceptará el password como correcto para ello tendríamos que cambiar el texto “SENTENCE”
(53 45 4E 54 45 4E 43 45 en hexadecimal en la ROM) que encontramos en la dirección
$03:42EB-03:42F2 ($C2EB-$C2F2) por los valores hexadecimales que se ponen la RAM (13 05
0E 14 05 0E 03 05).
Ahora si aceptaría este password como correcto y tendría un sistema híbrido de passwords (el
primero con una codificación sin restar $40 que no está en formato de texto y las otras dos
passwords en formato texto y que restan $40 para comprobarlas).
Esta modificación es para trastear un poco en la ROM y ver hasta qué punto podemos
modificarla. Los programadores del juego podrían haberlo hecho así si hubieran querido sin
tener que utilizar 3 veces la instrucción “sub a,40” con lo que se habrían ahorrado 6 bytes de
espacio (lo que en realidad no es mucho) pero también podría haber supuesto un pequeño de
ahorro de tiempo de proceso al no tener que ejecutar esta instrucción.
Podríamos convertir las otras dos passwords al formato que aparece en la RAM eliminando las
otras instrucciones “sub a,40” que aparecen si quisíeramos.
En otros juegos no se ponen las diferentes direcciones donde están los passwords del juego
sino que están todos los passwords juntos y se va incrementando la dirección que hay en “de”
hasta que se llega al final de los passwords. Si se llega al final es porque el password es
incorrecto y si coincide se coge un contador que se pone en otro registro para saber a que
nivel corresponde ese password.
La rutina de comprobación acaba con la instrucción en $03:6290 pero un poco más adelante
encontramos lo siguiente:
ROM3:629C 3E 03
ld a,03
ROM3:629E EA 30 DB
ROM3:62A1 C9
ROM3:62A2 3E 04
ld (DB30),a
ret
ld a,04
ROM3:62A4 EA 30 DB
ROM3:62A7 C9
ROM3:62A8 3E 06
ld (DB30),a
ret
ld a,06
ROM3:62AA EA 30 DB
ROM3:62AD C9
ld (DB30),a
ret
En esta dirección ($DB30) se almacena el valor del nivel que tiene el valor $00 para el nivel
inicial, el valor $03 para el nivel con password “SENTENCE”, el valor $04 para el nivel con
password “SOFTWARE”y $06 para el nivel con password “ANDERSON”.
En la dirección $03:6260 toma como válido el password “SENTENCE” y salta a $03:629C para
poner el valor $03 en $DB30, en $03:6278 toma como válido el password “SOFTWARE” y salta
a $03:62A2 para poner el valor $04 en $DB30 y en $03:6290 toma como válido el password
“ANDERSON” y salta a la dirección $03:62A8 para poner el valor $06 en la dirección $DB30.
Con la búsqueda de los passwords del juego hemos localizado éstos, la rutina de password y
donde se almacena el número de nivel en la RAM.
Pero podríamos conocer algo más como es el fragmento de código que se encarga de cambiar
la posición sobre la que está el cursor del password y cambiar la letra actual y ponerla en la
pantalla.
Si utilizamos el “bgb cheat searcher” para averiguar la dirección de la RAM donde se guarda la
posición sobre la que está el cursor del password basta con situarnos en la primera raya que
aparece para poner letras e ir avanzando viendo cómo va cambiando el valor en la RAM.
Al hacer esto comprobamos que se guarda en la dirección $D93D de la RAM con los valores
$00 (1ª letra), $01 (2ª letra),…, $07 (8ª letra).
Es decir, si el cursor que aparece en la pantalla de introducción de password está sobre la 5ª
letra del password tendrá la dirección $D93D el valor $04 por lo que poniendo un breakpoint
en esta dirección cuando se escribe podemos se pone el valor inicial (que es $00, ya que al
inicio se sitúa sobre la 1ª letra) y donde se aumenta o disminuye este valor.
En el siguiente fragmento se pone el valor inicial:
ROM3:6228 AF
ROM3:6229 EA 3D D9
xor a
ld (D93D),a
La instrucción “xor a” (o “xor a,a”) equivale a “a=$00) con lo que se pone el valor de a=$00=1ª
letra password en la dirección $D93D=Posición letra password.
En este fragmento se encuentra el código que cambia la posición de la letra del password
(cuando se incrementa o disminuye el valor que hay en la dirección $D93D) y también el que
cambia la letra que vamos poniendo en el password ($DB90-$DB97) en la RAM:
ROM3:6322 FA 3D D9
ld a,(D93D)
ROM3:6325 A7
and a
ROM3:6326 C8
ret z
ROM3:6327 3D
dec a
ROM3:6328 EA 3D D9
ROM3:632B 3E 1E
ld (D93D),a
ld a,1E
ROM3:632D CD 35 23
call 2335
ROM3:6330 C3 AE 62
jp 62AE
ROM3:6333 FA 3D D9
ld a,(D93D)
ROM3:6336 FE 07
cp a,07
ROM3:6338 C8
ret z
ROM3:6339 3C
inc a
ROM3:633A EA 3D D9
ROM3:633D 3E 1E
ld (D93D),a
ld a,1E
ROM3:633F CD 35 23
call 2335
ROM3:6342 C3 AE 62
jp 62AE
ROM3:6345 FA 3D D9
ld a,(D93D)
ROM3:6348 6F
ROM3:6349 26 00
ROM3:634B 11 90 DB
ld l,a
ld h,00
ld de,DB90
ROM3:634E 19
add hl,de
ROM3:634F 7E
ld a,(hl)
ROM3:6350 3C
inc a
ROM3:6351 FE 1B
cp a,1B
ROM3:6353 20 01
jr nz,6356
ROM3:6355 AF
xor a
ROM3:6356 77
ld (hl),a
ROM3:6357 3E 1E
ld a,1E
ROM3:6359 C3 35 23
jp 2335
ROM3:635C FA 3D D9
ld a,(D93D)
ROM3:635F 6F
ROM3:6360 26 00
ROM3:6362 11 90 DB
ld l,a
ld h,00
ld de,DB90
ROM3:6365 19
add hl,de
ROM3:6366 7E
ld a,(hl)
ROM3:6367 3D
dec a
ROM3:6368 FE FF
cp a,FF
ROM3:636A 20 02
jr nz,636E
ROM3:636C 3E 1A
ld a,1A
ROM3:636E 77
ROM3:636F 3E 1E
ld (hl),a
ld a,1E
ROM3:6371 C3 35 23
jp 2335
ROM3:6374 CD DD 1E
call 1EDD
ROM3:6377 CD 78 1C
call 1C78
En la dirección $03:6351 se comprueba si el valor de la letra actual del password es $1B y en
caso afirmativo se pone el valor $00 cuando se incrementa la letra.
En la dirección $03:6368 se comprueba si el valor de la letra actual del password es $FF cuando
se disminuye este valor y en caso afirmativo se pone el valor $1A=”Z”.
Vamos a avanzar un poco más para localizar el fragmento donde se pone el password inicial “-------“ en pantalla y se va poniendo también el pasword que vamos introduciendo en la
pantalla.
Si abrimos el “BGB VRAM Viewer” en la pestaña “BG map” podemos comprobar como en las
direcciones $9A06-$9A0D=Map Address se pone el tile $E4 que corresponde al símbolo “-“ por
lo que ponemos un breakpoint en la dirección $9A06 con el valor $E4 cuando se escriba en
esta dirección.
Esta parte es bastante más difícil en este juego pero al poner el breakpoint el emulador se para
en la dirección $03:5F2B donde encontramos la siguiente rutina:
ROM3:5EFD D5
push de
ROM3:5EFE 6F
ld l,a
ROM3:5EFF 26 40
ROM3:5F01 7E
ROM3:5F02 C6 C8
ROM3:5F04 5F
ROM3:5F05 FA 4E D9
ROM3:5F08 D6 03
ROM3:5F0A 6F
ROM3:5F0B 26 00
ld h,40
ld a,(hl)
add a,C8
ld e,a
ld a,(D94E)
sub a,03
ld l,a
ld h,00
ROM3:5F0D 29
add hl,hl
ROM3:5F0E 29
add hl,hl
ROM3:5F0F 29
add hl,hl
ROM3:5F10 29
add hl,hl
ROM3:5F11 29
add hl,hl
ROM3:5F12 01 00 98
ROM3:5F15 09
ROM3:5F16 FA 4D D9
ROM3:5F19 D6 06
ROM3:5F1B 4F
ROM3:5F1C 06 00
ROM3:5F1E 09
ld bc,9800
add hl,bc
ld a,(D94D)
sub a,06
ld c,a
ld b,00
add hl,bc
ROM3:5F1F F0 41
ld a,(ff00+41)
ROM3:5F21 E6 02
and a,02
ROM3:5F23 28 FA
jr z,5F1F
ROM3:5F25 F0 41
ld a,(ff00+41)
ROM3:5F27 E6 02
and a,02
ROM3:5F29 20 FA
jr nz,5F25
ROM3:5F2B 73
ROM3:5F2C FA 4D D9
ROM3:5F2F 3C
ROM3:5F30 EA 4D D9
ROM3:5F33 D1
ld (hl),e
ld a,(D94D)
inc a
ld (D94D),a
pop de
ROM3:5F34 C9
ret
Como se muestra el código realiza muchas operaciones y utiliza los registros para poner los
datos en la pantalla.
En la dirección $03:5F2B está lo que buscamos y es que pone el valor que hay en el registro “e”
en la dirección contenida en el registro “hl” (que contiene la Map Address donde pone los
passwords en pantalla). Este fragmento pone tanto el password inicial como se encarga de
poner el password que vamos introduciendo.
El registro “de” tiene el valor $41E4 donde el registro “e”=$E4=Tile “-“ y el registro “d” tiene el
valor $41 que puede hacer referencia a la dirección $03:4100 ($C100) donde están en la ROM
las posibles letras que podemos seleccionar para poner el password “ABCDEFGHIJKLMNOPQRSTUVWXYZ”.
En $03:5F1F encontramos la instrucción “ld a,(ff00+41)” que carga en a el valor que hay en la
dirección $FF41=LCD Status y la siguiente instrucción “and a,02” es una operación lógica en la
que coge el valor que hay en a y se queda con el bit marcado en x (000000x0).
Esto lo hace para comprobar V-Blank para poner datos en pantalla.
KID DRACULA PASSWORDS
Este juego utiliza un sistema de passwords en el que tenemos que introducir 4 dígitos
numéricos y en los que los passwords del juego están almacenados en formato tile teniendo
los valores de cada dígito siguientes:
“1”: $A2, “2”: $A3, “3”: $A4, “4”: $A5, “5”: $A6, “6”: $A7, “7”: $A8, “8”: $A9, “9”: $AA.
Las direcciones de la RAM donde se guardan los dígitos que vamos introduciendo están en las
direcciones $CDA2-$CDA5.
Encontramos estos passwords desde la dirección $3137D-$313D3 ($0C:537D-$0C:53D3)
incluidas y aparecen dos veces seguidas cada password.
Poniendo un breakpoint cuando se lea entre las direcciones $0C:537D-$0C:53D3 encontramos
que el emulador se para en la dirección $00:16DD donde se van comparando cada uno de los
dígitos del password donde encontramos:
ROM0:16BE 21 C3 C9
ld hl,C9C3
ROM0:16C1 35
dec (hl)
ROM0:16C2 C0
ret nz
ROM0:16C3 3E 10
ld a,10
ROM0:16C5 EA CE C8
ld (C8CE),a
ROM0:16C8 C3 37 1E
jp 1E37
ROM0:16CB 1E 00
ld e,00
ROM0:16CD CD 93 2C
call 2C93
ROM0:16D0 21 7D 53
ld hl,537D
ROM0:16D3 01 A2 CD
ld bc,CDA2
ROM0:16D6 16 04
ld d,04
ROM0:16D8 7B
ld a,e
ROM0:16D9 87
add a
ROM0:16DA 87
add a
ROM0:16DB EF
rst 28
ROM0:16DC 0A
ld a,(bc)
ROM0:16DD BE
cp (hl)
ROM0:16DE 23
inc hl
ROM0:16DF 20 2C
jr nz,170D
ROM0:16E1 0C
inc c
ROM0:16E2 15
dec d
ROM0:16E3 20 F7
jr nz,16DC
En el registro “hl” se pone la dirección $0C:537D donde están los passwords del juego, en el
registro “bc” se pone la dirección $CDA2=1º dígito del password y en el registro “d” se pone el
valor $04=Nº de dígitos del password y que se va disminuyendo una vez que se compara cada
dígito.
Sin embargo, aquí no encontramos aparentemente ninguna referencia que nos indique que el
banco sea $0C pero en la dirección $00:16CD encontramos una llamada a la dirección
$00:2C93:
ROM0:16CD CD 93 2C
call 2C93
En esta dirección encontramos una rutina donde se selecciona el banco $0C poniendo este
valor en la dirección $2345:
ROM0:2C93 3E 0C
ld a,0C
ROM0:2C95 EA 45 23
ROM0:2C98 C9
ld (2345),a
ret
PAC-ATTACK PASSWORDS
El anterior juego tenía un sistema basado en letras en las que las passwords están en el juego
en formato texto y ahora vamos a ver otro juego en el que los passwords no aparecen en
formato texto sino con otra codificación.
Buscando con el “bgb cheat searcher” encontramos que el password se guarda en la RAM en
las direcciones $C3E8-$C3EA.
Este juego tiene un sistema de 3 letras para formar el password de las que aparecen en la
tabla.
Poniendo un breakpoint en la dirección $C3EA al leer el emulador se para en la dirección
$00:1333 donde encontramos lo siguiente:
ROM0:130D 0E 00
ROM0:130F 21 BA 1C
ROM0:1312 2A
ROM0:1313 EA 42 C3
ROM0:1316 2A
ROM0:1317 EA 44 C3
ROM0:131A 2A
ROM0:131B EA 46 C3
ROM0:131E 23
ROM0:131F FA E8 C3
ROM0:1322 47
ROM0:1323 FA 42 C3
ROM0:1326 90
ROM0:1327 20 2D
ROM0:1329 FA E9 C3
ROM0:132C 47
ROM0:132D FA 44 C3
ROM0:1330 90
ROM0:1331 20 23
ROM0:1333 FA EA C3
ROM0:1336 47
ROM0:1337 FA 46 C3
ROM0:133A 90
ROM0:133B 20 19
ROM0:133D 79
ROM0:133E EA A7 C3
ROM0:1341 AF
ld c,00
ld hl,1CBA
ldi a,(hl)
ld (C342),a
ldi a,(hl)
ld (C344),a
ldi a,(hl)
ld (C346),a
inc hl
ld a,(C3E8)
ld b,a
ld a,(C342)
sub b
jr nz,1356
ld a,(C3E9)
ld b,a
ld a,(C344)
sub b
jr nz,1356
ld a,(C3EA)
ld b,a
ld a,(C346)
sub b
jr nz,1356
ld a,c
ld (C3A7),a
xor a
ROM0:1342 EA E7 C3
ld (C3E7),a
ROM0:1345 EA E6 C3
ld (C3E6),a
ROM0:1348 EA 8E C3
ld (C38E),a
ROM0:134B 3E 0B
ld a,0B
ROM0:134D EA 8D C3
ROM0:1350 3E 78
ld (C38D),a
ld a,78
ROM0:1352 EA A9 C3
ROM0:1355 C9
ld (C3A9),a
ret
---ROM0:1356 0C
inc c
ROM0:1357 79
ld a,c
ROM0:1358 FE 66
cp a,66
ROM0:135A 38 0C
jr c,1368
ROM0:135C AF
xor a
ROM0:135D EA E7 C3
ld (C3E7),a
ROM0:1360 FA 8E C3
ld a,(C38E)
ROM0:1363 3D
ROM0:1364 EA 8E C3
ROM0:1367 C9
dec a
ld (C38E),a
ret
En la primera línea se pone en el registro “c” el valor $00 con lo que se inicia el contador donde
se va a poner el número de nivel en caso de que sea correcto y en la siguiente línea
encontramos “ld hl,1CBA” que pone en hl la dirección $00:1CBA donde están los passwords
del juego pero no en formato texto.
Más en concreto en la dirección $00:1CBE-$00:1CC0 encontramos los valores 0E 0F 0D.
Si tomamos la letra “B” que es la primera con el valor $00 podemos numerar las siguientes
letras que aparecen en la pantalla de password con lo que el código anterior corresponde al
password “ STR” que es el que corresponde al nivel 1 del modo “PUZZLE MODE”.
Si nos vamos a esa dirección con el editor hexadecimal vemos como a continuación de estos
códigos hexadecimales aparece un código hexadecimal y a continuación va la siguiente
password.
La rutina va recorriendo la lista de passwords para comprobar la que introducimos con las del
juego y se va incrementando el registro “c” en la dirección $00:1356 para poner el número de
nivel que se pone en la dirección de la RAM $C3A7.
Aquí la posición de la letra del password la encontramos en la dirección $C3E7 y toma los
valores $00 (1ª letra), $01 (2ª letra) y $02 (3ª letra).
El siguiente fragmento lo que hace es incrementar la posición de la letra:
ROM0:1306 E5
ROM0:1307 21 E7 C3
ROM0:130A 34
push hl
ld hl,C3E7
inc (hl)
ROM0:130B E1
pop hl
ROM0:130C C9
ret
En el registro “hl” se pone la dirección $C3E7=Posición de la letra, y en la siguiente línea se
incrementa el valor que hay en la dirección contenida en hl.
Una vez que se llega a la tercera letra se debe pasar a la primera posición con lo que se ejecuta
lo siguiente para poner el valor $00 en la dirección $C3E7 que corresponde a la 1ª letra:
ROM0:135C AF
ROM0:135D EA E7 C3
xor a
ld (C3E7),a
La operación “xor a” o “xor a,a” equivale a la expresión “a=$00”.
En la parte inferior donde están las posibles letras que podemos elegir tenemos otro cursor y
que también tiene una dirección de la RAM donde se guarda la posición de dicho cursor según
la letra. En nuestro caso se trata de la dirección $C3E6 que se puede obtener fácilmente
utilizando el “bgb cheat searcher”.
En este caso se utiliza sólo esta dirección pero en algún juego se utilizan dos direcciones
independientes que corresponden una a la fila y la otra a la columna de la posición del cursor.
En este caso es única y toma los valores $00=”B”,…,$14=”Z”.
La posición inicial donde se pone el cursor de las letras de abajo que se situa sobre la letra “B”
($00) se pone en la dirección $00:0650 en un fragmento de código donde además se ponen en
otras direcciones también el valor $00.
ROM0:061F AF
xor a
ROM0:0620 EA 99 C5
ld (C599),a
ROM0:0623 EA 9A C5
ld (C59A),a
ROM0:0626 EA 9B C5
ld (C59B),a
ROM0:0629 EA 9C C5
ld (C59C),a
ROM0:062C EA 9D C5
ld (C59D),a
ROM0:062F EA 5E C4
ld (C45E),a
ROM0:0632 EA 90 C5
ld (C590),a
ROM0:0635 EA 92 C5
ld (C592),a
ROM0:0638 EA 91 C5
ld (C591),a
ROM0:063B EA 93 C5
ld (C593),a
ROM0:063E EA 8B C3
ld (C38B),a
ROM0:0641 EA 89 C3
ld (C389),a
ROM0:0644 EA 8A C3
ld (C38A),a
ROM0:0647 EA 45 C4
ld (C445),a
ROM0:064A EA 1E C4
ld (C41E),a
ROM0:064D EA 3B C4
ld (C43B),a
ROM0:0650 EA E6 C3
ld (C3E6),a
ROM0:0653 EA 48 C4
ld (C448),a
ROM0:0656 EA 47 C4
ld (C447),a
El fragmento que se encarga de cambiar la posición del cursor inferior donde están las letras se
encuentra en el siguiente fragmento:
(Disminuir la posición):
ROM0:1A94 FA 8C FF
ld a,(FF8C)
ROM0:1A97 CB 6F
bit 5,a
ROM0:1A99 28 0D
jr z,1AA8
ROM0:1A9B FA E6 C3
ROM0:1A9E 3D
ld a,(C3E6)
dec a
ROM0:1A9F CB 7F
bit 7,a
ROM0:1AA1 28 01
jr z,1AA4
ROM0:1AA3 AF
xor a
ROM0:1AA4 EA E6 C3
ROM0:1AA7 C9
ld (C3E6),a
ret
----(Incrementar la posición):
ROM0:1AA8 CB 67
bit 4,a
ROM0:1AAA 28 0E
jr z,1ABA
ROM0:1AAC FA E6 C3
ROM0:1AAF 3C
ld a,(C3E6)
inc a
ROM0:1AB0 FE 15
cp a,15
ROM0:1AB2 38 02
jr c,1AB6
ROM0:1AB4 3E 14
ld a,14
ROM0:1AB6 EA E6 C3
ROM0:1AB9 C9
ld (C3E6),a
ret
------(Compara la posición de la letra con $07):
ROM0:1ABA CB 77
bit 6,a
ROM0:1ABC 28 0E
jr z,1ACC
ROM0:1ABE FA E6 C3
ld a,(C3E6)
ROM0:1AC1 FE 07
cp a,07
ROM0:1AC3 30 01
jr nc,1AC6
ROM0:1AC5 C9
ret
-------(Disminuye la posición a una línea anterior disminuyéndola en $07):
ROM0:1AC6 D6 07
ROM0:1AC8 EA E6 C3
ROM0:1ACB C9
sub a,07
ld (C3E6),a
ret
---------(Compara la posición de la letra con $0E):
ROM0:1ACC CB 7F
bit 7,a
ROM0:1ACE 28 0E
jr z,1ADE
ROM0:1AD0 FA E6 C3
ld a,(C3E6)
ROM0:1AD3 FE 0E
cp a,0E
ROM0:1AD5 38 01
jr c,1AD8
ROM0:1AD7 C9
ret
-------(Incrementa la posición a una línea posterior incrementándola en $07):
ROM0:1AD8 C6 07
add a,07
ROM0:1ADA EA E6 C3
ld (C3E6),a
ROM0:1ADD C9
ret
ROM0:1ADE C9
ret
Además de la dirección $C3E6 que es donde se guarda la posición del cursor de la parte
inferior encontramos otra dirección en $00:1A94 que es la dirección $FF8C que corresponde a
la High RAM una parte de la memoria RAM que comienza en la dirección $FF80.
Esta dirección nos va a servir para explicar una rutina (rutina de joypad) que se encuentra en
todos o casi todos los juegos de Gameboy que es la que se encarga de leer los botones
pulsados en la consola que encontramos en el fragmento de código:
ROM0:03A7 3E 20
ld a,20
ROM0:03A9 EA 00 FF
ld (FF00),a
ROM0:03AC FA 00 FF
ld a,(FF00)
ROM0:03AF FA 00 FF
ld a,(FF00)
ROM0:03B2 2F
cpl
ROM0:03B3 E6 0F
and a,0F
ROM0:03B5 CB 37
swap a
ROM0:03B7 47
ROM0:03B8 3E 10
ld b,a
ld a,10
ROM0:03BA EA 00 FF
ld (FF00),a
ROM0:03BD FA 00 FF
ld a,(FF00)
ROM0:03C0 FA 00 FF
ld a,(FF00)
ROM0:03C3 FA 00 FF
ld a,(FF00)
ROM0:03C6 FA 00 FF
ld a,(FF00)
ROM0:03C9 FA 00 FF
ld a,(FF00)
ROM0:03CC FA 00 FF
ld a,(FF00)
ROM0:03CF 2F
ROM0:03D0 E6 0F
cpl
and a,0F
ROM0:03D2 B0
or b
ROM0:03D3 4F
ld c,a
ROM0:03D4 FA 8B FF
ld a,(FF8B)
ROM0:03D7 A9
xor c
ROM0:03D8 A1
and c
ROM0:03D9 EA 8C FF
ROM0:03DC 79
ld (FF8C),a
ld a,c
ROM0:03DD EA 8B FF
ROM0:03E0 3E 30
ROM0:03E2 EA 00 FF
ROM0:03E5 C9
ld (FF8B),a
ld a,30
ld (FF00),a
ret
Aquí basta con que conozcan que la dirección $FF00 se encarga del joypad leyendo o
escribiendo en ella. Lo más normal aunque no siempre encontramos la dirección o direcciones
donde se guarda el botón pulsado en la consola y que en este caso corresponde a las
direcciones $FF8B y $FF8C.
¿Les suena de algo la dirección $FF8C? Es la que aparecía en el fragmento de código que
cambia la posición del cursor de la parte inferior donde están las letras y precisamente la
primera instrucción era:
ROM0:1A94 FA 8C FF
ld a,(FF8C)
Esta instrucción lo que hace es carga en el registro “a” el valor que hay en la dirección
$FF8C=Botón pulsado de la consola para comprobar si se ha pulsado el botón “Derecha”,
“Izquierda”, “Arriba, “Abajo”.
Ya que conocemos que en $FF8C se guarda el botón pulsado vamos a asignar un valor a cada
botón para lo cual vamos a utilizar los valores $01, $02, $04, $08, $10, $20, $40 y $80.
El valor $00 correspondería a cuando no se ha pulsado ningún botón.
Estos valores no son al azar sino que son lo más utilizados por los juegos de Gameboy aunque
a veces se utilizan otros como $FF, $FD, …
Para conocer a que botón le corresponde el valor $01 vamos a poner un breakpoint en la
dirección $FF8C=Botón pulsado para que se pare cuando se escriba ese valor y para saber el
botón basta con ir pulsando las teclas que hayamos asignado a cada uno de los botones de la
consola Gameboy en el emulador.
Por ejemplo, si hemos asignado la tecla “Flecha derecha” al botón de la consola “Derecha”,.. la
tecla “Z” en mi caso para el botón “A” de la consola,…
Cuando se pare el emulador sabremos que ese botón se le asigna el valor $01. En el caso de
este juego se trata del botón “A”. El resumen de cada botón es:
A: $01
B: $02
Select: $04
Start: $08
Derecha: $10
Izquierda: $20
Arriba: $40
Abajo: $80
Un byte contiene 8 bits siendo el bit 0 el que corresponde a “A”, el bit 1 a “B”, el bit 2 a
“Select”, el bit 3 a “Start”, el bit 4 a “Derecha”, el bit 5 a “Izquierda”, el bit 6 a “Arriba” y el bit
7 a “Abajo”.
Es por ello por lo que la siguiente línea:
ROM0:1A97 CB 6F
bit 5,a
Comprueba el bit 5 (botón “Izquierda”) con lo que disminuía la posición del cursor inferior si se
ha pulsado dicho botón.
ROM0:1AA8 CB 67
bit 4,a
Comprueba el bit 4 (botón “Derecha”) con lo que incrementa la posición del cursor inferior si
se ha pulsado dicho botón.
Igualmente se comprueba el bit 6 para comprobar si se ha pulsado el botón “Arriba” con lo
que se pasaría a la fila que haya por encima si no es la primera disminuyendo la posición en
$07. Con el bit 7 se comprueba si se ha pulsado el botón “Abajo” con lo que se pasaría a la fila
que haya por debajo sino es la última incrementando la posición en $07.
En la pantalla de password podemos buscar también donde se encuentran los textos que
aparecen en la pantalla (“ENTER PASSWORD”, “B-ENTER”, “A-MENÚ”) y el código que se
encarga de mostrarlo.
Buscando estos textos encontramos lo siguiente:
$01:5D08 98 A3
$01:5D0A 0E
(Map Address=$98A3)
(Nº de tiles=$0E)
$01:5D0B 95 9E A4 95 A2 AF A0 91 A3 A3 A7 9F A2 94 (Texto “ENTER PASSWORD”)
$01:5D19 99 02
$01:5D1B 10
(Map Address=$9902)
(Nº de tiles=$10)
$01:5D1C 92 8E 95 9E A4 95 A2 AF AF AF 91 8E 9D 95 9E A5
(Texto “B-ENTER A-MENÚ”)
-----(Datos dibujar recuadro donde se ponen las letras que podemos utilizar para el password):
$01:5D2C 99 42
$01:5D2E 0F
(Map Address=$9942)
(Nº de tiles=$0F)
$01:5D2F F5 F6 F6 F6 F6 F6 F6 F6 F6 F6 F6 F6 F6 F6 F7
$01:5D3E 99 62 (Map Address=$9962)
$01:5D40 0F
(Nº de tiles=$0F)
$01:5D41 2B 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2D
$01:5D50 99 82(Map Address=$9982)
$01:5D52 0F
(Nº de tiles=$0F)
$01:5D53 2B 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2D
$01:5D62 99 A2
$01:5D64 0F
(Map Address=$99A2)
(Nº de tiles=$0F)
$01:5D65 2B 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2D
$01:5D74 99 C2(Map Address=$99C2)
$01:5D76 0F
(Nº de tiles=$0F)
$01:5D77 2B 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2C 2D
$01:5D86 99 E2 (Map Address=$99E2)
$01:5D88 0F
(Nº de tiles=$0F)
$01:5D89 2B 50 2C 52 2C 54 2C 56 2C 58 2C 5A 2C 5C 2D
$01:5D98 9A 02
$01:5D9A 0F
(Map Address=$9A02)
(Nº de tiles=$0F)
$01:5D9B 2B 51 2C 53 2C 55 2C 57 2C 59 2C 5B 2C 5D 2D
$01:5DAA 9A 22
$01:5DAC 0F
(Map Address=$9A22)
(Nº de tiles=$0F)
$01:5DAD 2B 5E 2C 60 2C 62 2C 64 2C 66 2C 68 2C 6A 2D
$01:5DBC 9A 42
$01:5DBE 0F
(Map Address=$9A42)
(Nº de tiles=$0F)
$01:5DBF 2B 5F 2C 61 2C 63 2C 65 2C 67 2C 69 2C 6B 2D
$01:5DCE 9A 62
$01:5DD0 0F
(Map Address=$9A62)
(Nº de tiles=$0F)
$01:5DD1 2B 6C 2C 6E 2C 70 2C 72 2C 74 2C 76 2C 78 2D
$01:5DE0 9A 82
$01:5DE2 0F
(Map Address=$9A82)
(Nº de tiles=$0F)
$01:5DE3 2B 6D 2C 6F 2C 71 2C 73 2C 75 2C 77 2C 79 2D
$01:5DF2 9A A2
$01:5DF4 0F
(Map Address=$9AA2)
(Nº de tiles=$0F)
$01:5DF5 2E 2F 2F 2F 2F 2F 2F 2F 2F 2F 2F 2F 2F 2F 30
Antes de éstos textos también encontramos una gran cantidad de textos a partir de la
dirección $01:5C41-$01:5D07.
Buscando el valor hexadecimal 415C lo encontramos en la dirección $01:5B0E donde se
encuentran una serie de direcciones que son punteros a textos del juego entre los que se
encuentran los que corresponden a los textos que he enumerado y que terminan en la
dirección $01:5B6B.
Normalmente hay direcciones (punteros) que apuntan a datos (textos, datos de nivel,…) y que
se leen a través de una rutina.
Si se busca 0E5B que corresponde a la dirección donde están los punteros en formato inverso,
lo encontramos en la dirección $01:400A-$01:400B. Desde la dirección $01:4002-$01:400B
encontramos punteros que apuntan a los otros punteros y a datos.
Normalmente, se suele leer los punteros poniendo en el registro “hl” o en “de” la dirección
donde están estos.
Si buscamos, los tiles que representan los ladrillos que hay en el fondo en la pantalla de
selección de password poniendo un breakpoint en la dirección $9800=Map Address cuando se
escribe el valor $AF salta el emuladore en la dirección $00:03FF.
En este punto el valor de los registros es:
“af”=AF80 (a=80),
“de”=9800,
“bc”=$0400”, hl=$4044”,
rom=4.
Aquí encontramos una rutina de uso general y que encontramos en la mayoría de los juegos de
Gameboy:
ROM0:03FE 2A
ldi a,(hl)
ROM0:03FF 12
ld (de),a
ROM0:0400 13
inc de
ROM0:0401 0B
dec bc
ROM0:0402 78
ld a,b
ROM0:0403 B1
or c
ROM0:0404 20 F8
ROM0:0406 C9
jr nz,03FE
ret
Básicamente lo que hace es leer los datos que hay en la dirección contenida en hl=$04:4043
(datos para dibujar el fondo de la pantalla de password) y los pone en la dirección contenida
en de=$9800=Map Address (en pantalla). En bc=$0400 se pone el número de bytes que hay
que leer o escribir.
La instrucción “dec bc” disminuye el número de bytes que queda por leer/escribir hasta ya no
queden bytes.
El registro “bc” es de 16 bits por lo que puede tomar valores entre $0000-$FFFF (0-65535) con
lo que se puede leer/escribir una gran cantidad de bytes. A veces cuando el número de bytes
no es muy grande se utiliza el registro “c” de 8 bits que puede tomar valores entre $00-$FF (0255 bytes).
Lo que encontramos en la dirección $04:4043-$04:4442 ($10000-$10442) son los datos para
dibujar el fondo de los ladrillos con el rótulo “RECORDS” y sin los textos de “ENTER
PASSWORD”,… ni el recuadro donde se pone el passwords y las letras posibles.
El rótulo “RECORDS” no se ve demasiado en la imagen pero se encuentra encima del texto
“ENTER PASSWORD”. Éste rótulo no es visible ya que está en el área no visible de la pantalla.
El área visible de la pantalla empieza en la dirección $9880 de la Map Adress.
A veces cuando se dibuja la pantalla del juego no se hace completamente como en este caso
sino que se escriben sólo $12 líneas (18 líneas) de $14 tiles (20 tiles) que es lo que corresponde
a la parte visible de la pantalla.
En este caso se ha dibujado la pantalla completa incluyendo la parte no visible, es decir $20
líneas (32 líneas) de $20 tiles (32 tiles) lo que hace un total de $0400 bytes (1024 bytes).
Si buscáramos el valor 4340 lo encontramos en la dirección $04:4002-$04:4009 ($10002$10009), y en la dirección $04:400A-$04:400B ($1000A-$1000B) encontramos el valor 4344 y
en $04:400C-$04:400D ($1000C-$1000D) encontramos el valor 384D.
En $04:4000 ($10000) tenemos el valor 0400 que corresponde al número de banco ($04).
Igual que hemos hecho con la pantalla de password podemos hacer con el resto de pantallas
que se encuentran en el juego.
Poniendo un breakpoint en la dirección $9800=Map Address cuando se escribe el valor $00 el
emulador se para en la misma dirección donde está la rutina de uso general pero ahora los
datos de la pantalla de “NAMCO” se encuentran en la dirección $05:7264-$05:7663 ($17264$17663).
Si vemos esta pantalla en el “BGB VRAM Viewer” vemos que en la zona no visible aparecen
gráficos pero que no parecen tener sentido ya que no se ve nada legible. En esta pantalla la
zona visible comienza en la dirección de la Map Address=$9800.
En esta pantalla podríamos cambiar el texto “PRODUCED” por “PRODUCIDO” y “BY” por “POR”
ya que hemos localizado donde están los datos.
El texto “PRODUCED” lo encontramos en la dirección $05:738A-$05:7391 ($1738A-$17391) y
podemos poner “PRODUCIDO” por ejemplo en la dirección $05:7389-$05:7391 ya que ocupa 1
byte más.
Igualmente podemos hace con el texto “BY” que se encuentra en la dirección $05:73CD$05:73CE ($173CD-$173CE) y poner en $05:73CC-$05:73CE el texto “POR” con lo que la
pantalla quedaría como:
La pantalla de título la encontramos en la dirección $01:582E-$01:5C2D del juego. En la
dirección $01:5954-$01:595E encontramos el texto “NORMAL MODE” y en $01:5994-$01:599E
el texto “PUZZLE MODE” que podemos cambiar igualmente por “MODO NORMAL” y “MODO
PUZZLE”.
Sin embargo, si quisiéramos cambiar el texto “LICENSED BY NINTENDO” por “LICENCIADO POR
NINTENDO” no podríamos hacerlo así ya que no está como texto sino que es una secuencia de
tiles la que forman el texto y no aparece lo que queremos poner en pantalla por lo que
debemos modificar los tiles $E6-$F1 (Tile Address=$8E60-$8F1F) y si es necesario tendríamos
que ver algún tile que no se use en la pantalla y que esté en blanco como el $FC y $FF (como
parece que es).
Si buscamos estos tiles los encontramos en la dirección $01:4E8E-$01:4F4D. Para hacer la
búsqueda en el debugger del emulador seleccionamos “Go to…” y ponemos la dirección $8E60
donde se encuentra el primer tile y seleccionamos “Copy data” poniendo en número de bytes
el valor 192 que corresponde a 12 tiles que van desde el $E6-$F1.
A continuación, abrimos con el editor hexadecimal HxD el juego y buscamos la secuencia de
bytes que hemos copiado con lo que la encontramos en la dirección $01:4E8E-$01:4F4D.
Para modificar los tiles podemos abrir el juego con un editor de tiles como el “TileED2002” y
modificarlos para que muestre el texto que queremos poner que en este caso es “LICENDIADO
POR NINTENDO”.
Este editor de tiles pone la dirección en decimal por lo que tendríamos que convertir la
dirección inicial $4E8E en decimal que corresponde a 20110.
Las herramientas del “Editor Control” paso a explicar a continuación aunque no suelo utilizar la
opción de guardar y recargar todos los tiles pero si el resto de opciones ya que se me bloquea
el programa y tengo que cerrarlo utilizando en su lugar el “Tile Tool”.
Para modificar los tiles vamos a utilizar las herramientas de que dispone el TilEd 2002 para lo
cual lo primero que vamos a hacer es seleccionar en la parte izquierda donde están los tiles los
que vamos a modificar y pulsar sobre ellos para que se añadan al recuadro donde pone editor
(los que aparecen en la pantalla de arriba no vamos a tener que modificarlos todos sino los
que tienen el texto “SED BY” y 2 que vamos a utilizar adicionalmente.
Una vez seleccionado puesto el tile correspondiente en el “Editor” pulsamos sobre el segundo
icono del “Editor Control” para seleccionar “Paint Mode” y poder modificarlos. Una vez hecho
esto podemos pulsar donde pone “Color Palette” para seleccionar el color con el que vamos a
dibujar en el tile e ir modificando el tile copiado a nuestro gusto.
Una vez modificado el tile que hemos copiado en el “Editor” pulsamos sobre el primer icono
que aparece en el “Tile Tool” con lo que el tile que hemos modificado se cambia por el original
que tenía el juego y repetimos el proceso con el resto de tiles que debemos modificar.
También podríamos utilizar el primer icono de la segunda fila para guardar el tile modificado
en la posición que le indiquemos.
Podemos cambiar los tiles para que ponga el texto que queremos con lo que podría quedar
algo así:
Sin embargo, todavía nos quedaría algo por hacer ya que si ejecutamos ahora el juego está
prácticamente correcto a excepción que no aparece el texto “OR” de la palabra “POR” ya que
hemos utilizado 2 tiles adicionales ($FE y $FF) que no se utilizaban para la pantalla de título.
En la dirección $01:5A52-$01:5A5D encontrabamos el texto “LICENSED BY NINTENDO” en
formato tile y debemos dejar espacio para meter 2 tiles más con lo que empezaremos a
escribir en $01:5A50.
Desde $01:5A52-$01:5A58 encontramos ahora el texto “LICENCIADO P” que corresponde a los
valores hexadecimales E6 E7 E8 E9 EA EB EC.
Debemos añadir $FE y $FF a continuación para aparezca el texto bien.
Con la modificación en la dirección $01:5A50-$01:5A5D encontraríamos ahora lo siguiente:
E6 E7 E8 E9 EA EB EC FE FF ED EE EF F0 F1
La pantalla quedaría entonces así:
Aquí podríamos cambiar también la posición de los textos “MODO NORMAL” y “MODO
PUZZLE” para que aparecieran en una posición distinta de la pantalla pero para que el icono en
forma de triángulo se situara correctamente tendríamos que hacer alguna modificación extra.
Para la opción “NORMAL MODE” del cursor los datos que se ponen en la OAM (Object Address
Memory) son “X-loc”: $28, “Y-loc”: $58, “Tile No.”: $F2 y “Attribute”: $00.
Para la opción “PUZZLE MODE” del cursor los datos son similares a excepción a “Y-loc” que
ahora toma el valor $68.
Aquí el proceso es un poco complicado ya que se utilizan varias direcciones de la RAM.
Poniendo un breakpoint cuando se escribe el valor $58 en la dirección $C00 encontramos lo
siguiente:
ROM0:059D E5
ROM0:059E 21 42 C3
push hl
ld hl,C342
ROM0:05A1 2A
ldi a,(hl)
ROM0:05A2 12
ld (de),a
El registro “de” tiene el valor $C000, “hl” el valor $C342 y “a” el valor $58, con lo que se carga
el valor que hay en la dirección $C342 que es $58 y se pone en $C000.
Sin embargo, debemos retroceder para ver cuando se puso el valor $58 en la dirección $C342
para lo cual ponemos un breakpoint con lo que el emulador se para en:
ROM0:057D FA 4B C3
ROM0:0580 81
ROM0:0581 EA 42 C3
ld a,(C34B)
add c
ld (C342),a
Se carga en el registro “a” el valor que hay en la dirección $C34B y en la siguiente línea se
añade al valor de “a” el valor que hay en “c” (el registro “bc”=$C100, con lo que “c”=$00) y se
pone el resultado en la dirección $C342.
Como el valor del registro “c” es $00 debe tener a el registro “a” el valor $58 con lo que en la
dirección $C34B se debe haber puesto el valor $58 con anterioridad con lo que ponemos un
breakpoint en la dirección $C34B cuando se escriba el valor $58 y se para en la dirección
$00:056B.
ROM0:056A 2A
ldi a,(hl)
ROM0:056B EA 4B C3
ld (C34B),a
Al pararse el emulador en el registro “hl” está la dirección $C1B0 pero en realidad la que
buscamos es la $C1AF ya que la instrucción en $00:056A ha aumentado ya el valor del registro
“hl”.
Ldi a,(hl): Carga en “a” el valor que hay en la dirección contenida en “hl” e incrementa “hl”.
Ahora vamos a poner un breakpoint en la dirección $C1AF cuando se ponga el valor $58 con lo
que el emulador se parará en la dirección $00:090D:
ROM0:0902 21 11 09
ROM0:0905 06 00
ROM0:0907 FA 3E C4
ld hl,0911
ld b,00
ld a,(C43E)
ROM0:090A 4F
ld c,a
ROM0:090B 09
add hl,bc
ROM0:090C 7E
ld a,(hl)
ROM0:090D EA AF C1
ROM0:0910 C9
ld (C1AF),a
ret
ROM0:0911 58 (Coordenada Y opción “NORMAL MODE”)
ROM0:0912 68 (Coordenada Y opción “PUZZLE MODE”)
Ha costado un poco pero a fuerza de breakpoints hemos localizado donde están estas
coordenadas y ya podríamos modificarlas a nuestro gusto para situar el cursor en las
coordenadas Y que queramos.
La primera instrucción lo que hace es poner en el registro “hl” la dirección $00:0911 donde
empiezan las coordenadas para situar el cursor y precisamente otra de las direcciones que
aparece $C43E corresponde a la opción del menú principal que podemos encontrar utilizando
el buscador de cheats del emulador (“bgb cheat searcher”).
Para la opción “NORMAL MODE” se pone el valor $00 en la dirección $C43E y para “PUZZLE
MODE” se pone el valor $01 en la dirección $C34E.
Por eso lo que hace el código es poner en el registro “b” el valor $00 y se carga en el registro
“a” el valor que hay en la dirección $C34E=Opción del menú principal. Luego se pasa al registro
“c” con lo que “c=a=Valor de la opción del menú principal).
Se le suma a continuación a la dirección contenida en el registro “hl” que es $00:0911 el valor
que hay en el registro “bc” que como “b=$00” debe ser $0000 para la opción “NORMAL
MODE” y $0001 para la opción “PUZZLE MODE” con lo que se cargaría en “hl” la dirección
$00:0911 para “NORMAL MODE” donde está la coordenada correspondiente
($00:0911+$0000) y $00:0912 para “PUZZLE MODE” donde está la coordenada
correspondiente ($00:0911+$0001).
La pantalla que nos aparece cuando seleccionamos la opción “NORMAL MODE” en el menú
principal se encuentra en la dirección $02:5022-$02:5281 ($9022-$9281) sin los textos que
aparecen:
En el inicio del segundo banco encontramos punteros entre los que se encuentra el que apunta
a los datos de esta pantalla encontrando a partir de la dirección $02:4000 ($8000) lo siguiente:
02 00 22 40 22 40 22 48 22 50 82 52 00 00 00 00
Los dos primeros bytes indican que se trata del banco $02 para poner en los punteros y el
resto son puntero (22 40=$02:4022, 22 48=$02:4822, 22 50=$02:5022, 82 52=$02:5282).
Los textos de esta pantalla los encontramos utilizando breakpoints a partir de la dirección
$01:5CBC-$01:5CEB donde encontramos lo siguiente:
01:5CBC 98 85 (Map Address=$9885)
01:5CBE 05
(Nº de tiles=$05)
01:5CBF 6B 6C 6D 6C 6B
(Texto “LEVEL”)
01:5CC4 98 A5 (Map Address=$98A5)
01:5CC6 06
(Nº de tiles=$06)
01:5CC7 6E 6C 6B 6C 6F 70
(Texto “SELECT”)
01:5CCD 99 05 (Map Address=$9905)
01:5CCF 04
(Nº de tiles=$04)
01:5CD0 6C 71 6E 72
(Texto “EASY”)
01:5CD4 99 45 (Map Address=$9945)
01:5CD6 06
(Nº de tiles=$06)
01:5CD7 73 74 75 76 71 6B
(Texto “NORMAL”)
01:5CDD 99 85 (Map Address=$9985)
01:5CDF 04
(Nº de tiles=$04)
01:5CE0 77 71 75 78
(Texto “HARD”)
01:5CE4 99 C5 (Map Address=$99C5)
01:5CE6 06
(Nº de tiles=$06)
01:5CE7 77 72 79 6C 75 (Texto “HYPER”)
JOYPAD Y TRUCOS
ADVENTURE ISLAND
Algunos juegos de Gameboy disponen de una combinación de botones que hay que pulsar
para activar algún truco como puede ser seleccionar nivel,… y que con unos pequeños
conocimientos podemos modificar para hacerlo más sencillo.
Vamos a utilizer el juego “Adventure Island” que tiene un truco para seleccionar nivel que
consiste en pulsar los botones Derecha, Izquierda, Derecha, Izquierda, A, B, A, B después de
que Higgins sea golpeado por un coco en la pantalla de título.
Si ponemos un breakpoint en la dirección $FF00 cuando se escribe podemos ver donde se
encuentra la rutina de joypad que se encarga de comprobar los botones pulsados y las
direcciones de la RAM donde se guardan dichos botones y que encontramos a partir de la
dirección $05:7E06-$05:7E36.
Las direcciones de la RAM donde se guarda el botón pulsado son $FF8C y $FF8D.
Los valores de los botones son los siguientes:
01: A
02: B
04: Select
08: Start
10: Derecha
20: Izquierda
40: Arriba
80: Abajo
Con lo que la combinación de botones debería ser 10 20 10 20 01 02 01 02 pero si los
buscamos con un editor hexadecimal no lo encontramos (normalmente al buscarlos si
aparecen pero no en este caso).
Podemos poner un breakpoint en la dirección $FF8D cuando se lea por ejemplo el valor $20
(Izquierda) con lo que al pulsar este botón en la pantalla de título se parará el emulador en la
dirección $01:7CAA. Un poco más arriba en el código Fuente encontramos lo siguiente:
ROM1:7C9F 04
inc b
ROM1:7CA0 05
dec b
ROM1:7CA1 04
inc b
ROM1:7CA2 05
dec b
ROM1:7CA3 00
nop
ROM1:7CA4 01 00 01
ld bc,0100
-------ROM1:7CA7 21 8D FF
ROM1:7CAA 7E
ld hl,FF8D
ld a,(hl)
Lo que aparece a partir de $01:7C9F-$01:7CA6 se parece bastante (04 05 04 05 00 01 00 01) a
lo que estábamos buscando (10 20 10 20 01 02 01 02).
Si tomamos los valores $00 (A), $01 (B), $02 (Select), $03 (Start), $04 (Derecha), $05
(Izquierda), Arriba ($06), Abajo ($07) comprobamos que los valores encontrados corresponden
a la combinación de botones para activar los cheats.
ROM1:7C9F 04 05 04 05 00 01 00 01
Podemos obviar las instrucciones que aparecían a la derecha de los valores en el código fuente
ya que éstos no son instrucciones sino que son los datos que contienen la combinación de
botones para el truco.
Si quisiéramos cambiar la combinación de botones para que sea más fácil para nosotros
podríamos por ejemplo cambiar estos bytes por:
ROM1:7C9F 00 00 00 00 00 00 00 00
Con esto se activa el truco cuando pulsamos 8 veces el botón “A” (valor $00) y nos aparecería
la pantalla:
Si buscamos la opción de este menú la encontramos en la dirección $C432 que toma el valor
$00: FERN ISLAND,…, $07: DINOSAUR ISLAND y el fragmento que se encarga de cambiar dicha
opción se encuentra en el siguiente fragmento:
ROM0:0594 11 32 C4
ld de,C432
ROM0:0597 21 8D FF
ld hl,FF8D
ROM0:059A CB 76
bit 6,(hl)
ROM0:059C 28 08
jr z,05A6
ROM0:059E 1A
ld a,(de)
ROM0:059F A7
and a
ROM0:05A0 20 01
jr nz,05A3
ROM0:05A2 78
ld a,b
ROM0:05A3 3D
dec a
ROM0:05A4 18 29
ROM0:05A6 7E
ROM0:05A7 E6 84
jr 05CF
ld a,(hl)
and a,84
ROM0:05A9 C8
ret z
ROM0:05AA 1A
ld a,(de)
ROM0:05AB 3C
inc a
ROM0:05AC B8
cp b
ROM0:05AD 38 20
jr c,05CF
ROM0:05AF AF
ROM0:05B0 18 1D
xor a
jr 05CF
ROM0:05B2 11 31 C4
ld de,C431
ROM0:05B5 21 8D FF
ld hl,FF8D
ROM0:05B8 CB 6E
bit 5,(hl)
ROM0:05BA 28 08
jr z,05C4
ROM0:05BC 1A
ld a,(de)
ROM0:05BD A7
and a
ROM0:05BE 20 01
jr nz,05C1
ROM0:05C0 79
ld a,c
ROM0:05C1 3D
dec a
ROM0:05C2 18 0B
ROM0:05C4 7E
jr 05CF
ld a,(hl)
ROM0:05C5 E6 14
and a,14
ROM0:05C7 28 0E
jr z,05D7
ROM0:05C9 1A
ld a,(de)
ROM0:05CA 3C
inc a
ROM0:05CB B9
cp c
ROM0:05CC 38 01
jr c,05CF
ROM0:05CE AF
xor a
ROM0:05CF 12
ld (de),a
ROM0:05D0 3E 38
ld a,38
ROM0:05D2 CD 63 02
call 0263
ROM0:05D5 37
scf
ROM0:05D6 C9
ret
Como se observa en el código se pone en el registro “hl” la dirección $FF8D=Botón pulsado y
se lee para comprobar el botón y cambiar la posición del cursor según dicha opción.
Si buscamos los textos de esta pantalla podemos localizarlos a partir de la dirección $01:7D04$01:7D91 y buscando el valor 047D en el editor hexadecimal lo localizamos en $698-$699 que
corresponde al código:
ROM0:068F 3E 01
ld a,01
ROM0:0691 E0 99
ld (ff00+99),a
ROM0:0693 EA 00 20
ld (2000),a
ROM0:0696 21 04 7D
ld hl,7D04
ROM0:0699 CD 68 03
call 0368
ROM0:069C CD 95 02
call 0295
ROM0:069F 21 D0 06
ld hl,06D0
ROM0:06A2 CD DA 05
call 05DA
ROM0:06A5 3E 01
ld a,01
ROM0:06A7 CD D8 02
call 02D8
ROM0:06AA 21 D0 06
ld hl,06D0
ROM0:06AD CD 8A 05
call 058A
Al principio se selecciona el banco $01 poniendo en “a” en la dirección $2000 y en “hl” se pone
el valor $7D04 con lo que la dirección es $01:7D04 donde están los textos. Posteriormente se
pone en el registro “hl” la dirección $00:06D0 donde encontramos:
$00:06D0 01 08
$00:06D2 00 07
(Atributo=$00, Tile=$07 del cursor para seleccionar nivel)
$00:06D4 A1 98
(Map Address=$98A1, “FERN ISLAND”)
$00:06D6 C1 98
(Map Address=$98C1, “LAKE ISLAND”)
$00:06D8 E1 98
(Map Address=$98E1, “DESERT ISLAND”)
$00:06DA 01 99
(Map Address=$9901, “ICE ISLAND”)
$00:06DC 21 99
(Map Address=$9921, “CAVE ISLAND”)
$00:06DE 41 99
(Map Address=$9941, “CLOUD ISLAND”)
$00:06E0 61 99
(Map Address=$9961, “VOLCANO ISLAND”)
$00:06E2 81 99
(Map Address=$9981, “DINOSAUR ISLAND”)
Estas son las Map Address donde pone el cursor para elegir el nivel correspondiente. Si
cambiamos el valor que hay en la dirección $00:06D3 poniendo por ejemplo $70
conseguiremos cambiar el punto del cursor por el tile $70 que corresponde al símbolo “!”.
En la dirección $C432 se guarda el nivel que seleccionamos en esta pantalla.
WE´RE BACK! A DINOSAUR STORY
Este juego tiene una serie de cheats utilizando combinaciones de botones:
Vidas infinitas: Select, A, Abajo, B, Arriba, Abajo
(04, 01, 80, 02, 40, 80)
Proyectiles infinitos: B, A, Derecha, B, A, Derecha, A
(02, 01, 10, 02, 01, 10, 01)
Tiempo infinito: Arriba, Abajo, Arriba, B, Izquierda, B
(40, 80, 40, 02, 20, 02)
Invulnerabilidad: Derecha, A, Abajo, Derecha, A, Abajo (10, 01, 80, 10, 01, 80)
Seleccionar nivel: Select, Arriba, Arriba, Select, Izquierda, Izquierda
Especial 1: Arriba, B, A, Abajo, B, A
(04, 40, 40, 04, 20, 20)
(40, 02, 01, 80, 02, 01)
Especial 2: Izquierda, A, Izquierda, B, Izquierda (20, 01, 20, 02, 20)
Entre paréntesis aparecen los valores que podemos buscar para localizar las combinaciones de
botones en el juego.
En $17FA0-$17FA5 ($05:7FA0-$05:7FA5) encontramos la combinación para “Vidas infinitas”,
en $17FA7-$17FAC ($05:7FA7-$05:7FAC) encontramos la combinación para “Tiempo infinito”,
en $17FAE-$17FB3 ($05:7FAE-$05:7FB3) encontramos la combinación para “Seleccionar nivel”,
en $17FB5-$17FBA ($05:7FB5-$05:7FBA) encontramos la combinación para “Invulnerabilidad”,
en $17FBC-$17FC2 ($05:7FBC-$05:7FC2) encontramos la combinación para “Proyectiles
infinitos”, en $17FC4-$17FC9 ($05:7FC4-$05:7FC9) encontramos la combinación para “Especial
1”, en $17FCB-$17FCF encontramos la combinación para “Especial 2”.
Cada combinación de botones termina con el valor $FF para indicar el fin de los datos.
Si buscamos la secuencia A07F hexadecimal con el editor “HxD” lo encontramos a partir de la
dirección $17F68 ($05:7F68) donde encontramos además de punteros a las combinaciones de
botones otros punteros que todavía desconozco su utilidad.
$05:7F68 A1 D1($D1A1)
$05:7F6A 98 D1($D198)
$05:7F6C A0 7F ($05:7FA0=Combinación de botones)
$05:7F6E FF FF
$05:7F70 A4 D1($D1A4)
$05:7F72 99 D1 ($D199)
$05:7F74 A7 7F ($05:7FA7=Combinación de botones)
$05:7F76 FF FF
$05:7F78 A5 D1($D1A5)
$05:7F7A 9A D1 ($D19A)
$05:7F7C AE 7F ($05:7FAE=Combinación de botones)
$05:7F7E FF FF
$05:7F80 A6 D1($D1A6)
$05:7F82 9B D1 ($D19B)
$05:7F84 B5 7F ($05:7FB5=Combinación de botones)
$05:7F86 FF FF
$05:7F88 A9 D1($D1A9)
$05:7F8A 9C D1 ($D19C)
$05:7F8C BC 7F ($05:7FBC=Combinación de botones)
$05:7F8E FF FF
$05:7F90 A2 D1($D1A2)
$05:7F92 9D D1 ($D19D)
$05:7F94 C4 7F ($05:7FC4=Combinación de botones)
$05:7F96 FF FF
$05:7F98 A3 D1($D1A3)
$05:7F9A 9E D1($D19E)
$05:7F9C CB 7F ($05:7FCB=Combinación de botones)
$05:7F9E FF FF
La carga de estos punteros lo encontramos en el siguiente fragmento de código:
ROM5:7F07 AF
ROM5:7F08 EA A0 D1
ROM5:7F0B 0E 07
ROM5:7F0D 3C
ROM5:7F0E 21 68 7F
xor a
ld (D1A0),a
ld c,07
inc a
ld hl,7F68
ROM5:7F11 2A
ldi a,(hl)
ROM5:7F12 5F
ld e,a
ROM5:7F13 2A
ldi a,(hl)
ROM5:7F14 57
ld d,a
ROM5:7F15 D5
push de
ROM5:7F16 1A
ld a,(de)
ROM5:7F17 B7
or a
ROM5:7F18 20 49
jr nz,7F63
ROM5:7F1A 2A
ldi a,(hl)
ROM5:7F1B 5F
ld e,a
ROM5:7F1C 2A
ldi a,(hl)
ROM5:7F1D 57
ld d,a
ROM5:7F1E D5
push de
ROM5:7F1F E5
push hl
ROM5:7F20 2A
ldi a,(hl)
ROM5:7F21 66
ld h,(hl)
ROM5:7F22 6F
ld l,a
ROM5:7F23 1A
ld a,(de)
ROM5:7F24 85
add l
ROM5:7F25 6F
ld l,a
ROM5:7F26 8C
adc h
ROM5:7F27 95
sub l
ROM5:7F28 67
ld h,a
ROM5:7F29 7E
ld a,(hl)
ROM5:7F2A B8
cp b
ROM5:7F2B 28 07
jr z,7F34
ROM5:7F2D AF
xor a
ROM5:7F2E 12
ld (de),a
ROM5:7F2F E1
pop hl
ROM5:7F30 D1
pop de
ROM5:7F31 D1
pop de
ROM5:7F32 18 16
jr 7F4A
ROM5:7F34 3E 01
ld a,01
ROM5:7F36 EA A0 D1
ld (D1A0),a
ROM5:7F39 23
inc hl
ROM5:7F3A 7E
ld a,(hl)
ROM5:7F3B FE FF
cp a,FF
ROM5:7F3D 28 13
jr z,7F52
ROM5:7F3F D1
pop de
ROM5:7F40 E1
pop hl
ROM5:7F41 34
inc (hl)
ROM5:7F42 62
ld h,d
ROM5:7F43 6B
ld l,e
ROM5:7F44 D1
pop de
ROM5:7F45 3E 0F
ld a,0F
ROM5:7F47 CD 49 1D
call 1D49
ROM5:7F4A 23
inc hl
ROM5:7F4B 23
inc hl
ROM5:7F4C 23
inc hl
ROM5:7F4D 23
inc hl
ROM5:7F4E 0D
dec c
ROM5:7F4F 20 C0
ROM5:7F51 C9
jr nz,7F11
ret
En el registro “c” se pone el valor $07 (nº de combinaciones de botones) y en “hl” la dirección
$05:7F68 donde están los punteros.
LOCALIZAR UBICACIÓN OBJETOS PARA DIBUJAR EN PANTALLA
En muchos juegos se utilizan objetos de tamaño 2*2 o de otro tamaño que se ponen en la
pantalla para dibujar el nivel, el fondo,…. Por ejemplo, en el juego “Addams Family” en la
siguiente pantalla podemos ver objetos con forma de cara:
El recuadro de las caras está formado por 2 tiles arriba y otros 2 abajo para dibujarlo (los dos
primeros tienen los valores $01, $02 y los de debajo $03 y $04).
El primer bloque con cara que aparece en la parte superior izquierda de la pantalla comienza
en la dirección $9842=Map Address tal y como se ve en la captura por lo que si ponemos un
breakpoint cuando se escriba en la dirección $9842 el valor $01.
El emulador se para en la dirección $00:1301, “pc” tiene el valor 4 y el registro “hl” tiene el
valor $5740 (con lo que corresponde a la dirección $04:5740=$11740 y en $04:573F=$1173F
tenemos los datos para dibujar el bloque 2*2) y en $04:573B ($1173B encontramos el inicio de
los datos para dibujar objetos).
El fragmento de código que dibuja objetos en la pantalla es el siguiente:
ROM0:12E6 6F
ROM0:12E7 26 00
ld l,a
ld h,00
ROM0:12E9 29
add hl,hl
ROM0:12EA 29
add hl,hl
ROM0:12EB F0 BA
ld a,(ff00+BA)
ROM0:12ED 85
add l
ROM0:12EE 6F
ld l,a
ROM0:12EF F0 BB
ld a,(ff00+BB)
ROM0:12F1 8C
adc h
ROM0:12F2 67
ld h,a
ROM0:12F3 F0 41
ROM0:12F5 2F
ld a,(ff00+41)
cpl
ROM0:12F6 E6 03
and a,03
ROM0:12F8 20 F9
jr nz,12F3
ROM0:12FA F0 41
ld a,(ff00+41)
ROM0:12FC E6 03
and a,03
ROM0:12FE 20 FA
jr nz,12FA
ROM0:1300 2A
ldi a,(hl)
ROM0:1301 12
ld (de),a
ROM0:1302 1C
inc e
ROM0:1303 2A
ldi a,(hl)
ROM0:1304 12
ld (de),a
ROM0:1305 3E 1F
ld a,1F
ROM0:1307 83
add e
ROM0:1308 5F
ld e,a
ROM0:1309 8A
adc d
ROM0:130A 93
sub e
ROM0:130B 57
ld d,a
ROM0:130C 2A
ldi a,(hl)
ROM0:130D 12
ld (de),a
ROM0:130E 1C
inc e
ROM0:130F 7E
ld a,(hl)
ROM0:1310 12
ld (de),a
ROM0:1311 1C
inc e
ROM0:1312 C9
ret
Este fragmento de código se encarga de dibujar objetos en la pantalla y parece que se le pasa
la dirección Map Address a través de las direcciones $FFBA y $FFBB.
ROM0:1305 3E 1F
ld a,1F
Esto lo que hace es añadir $1F para incrementar la Map Address para la parte inferior del
bloque. Para el primer bloque que hemos visto pone el valor $01 en la dirección $9842=Map
Address, luego pone el valor $02 en la dirección $9843=Map Address.
A continuación le suma $1F para obtener $9843+$1F=$9862=Map Address donde pone el valor
$03 para la parte inferior del bloque e incrementa la dirección Map Address=$9863 donde
pone el valor $04 con lo que ya está dibujado completamente el bloque.
OAM
La OAM (Object Address Memory) es una parte de la memoria donde se almacenan datos de
los sprite que se utilizan para realizer algunas animaciones en el juego como puede ser
representar el personaje que manejamos y sus diferentes posiciones y movimientos.
Esta memoria ocupa $A0 bytes (160 bytes) lo que permite almacenar los datos de 40 sprites
que como máximo puede poner en pantalla la Gameboy con lo que cada sprite ocupa 4 bytes
(1 byte para Y-loc, 1 byte para X-loc, 1 byte para Tile, 1 byte para Atributo).
Además de en este área de la pantalla los juegos de Gameboy utilizan una rutina para poner
una copia de estos datos en la memoria RAM y que tiene la siguiente estructura:
ROM0:04C2 3E DD
ld a,DD
ROM0:04C4 E0 46
ld (ff00+46),a
ROM0:04C6 3E 28
ld a,28
ROM0:04C8 3D
ROM0:04C9 20 FD
ROM0:04CB C9
dec a
jr nz,04C8
ret
Esta es la rutina que encontramos en el juego “Kid Dracula (U) [!]” y que pone el valor $DD en
la dirección $FF46 con lo que la copia de la OAM se encuentra en la dirección $DD00-$DD9F lo
que hace un total de $A0 bytes.
Para localizar esta rutina basta con poner un breakpoint cuando se escribe en la dirección
$FF46 en cualquier juego de Gameboy. Los juegos que he visto hasta ahora cargan en “a” el
valor $28 que según un manual que hay en inglés sobre la Gameboy es la longitude del loop.
Si miramos en $DD00-$DD9F encontramos los mismos valores que aparecen en la pestaña
“OAM” del “bgb vram viewer”.
MODIFICACIONES EN JUEGOS
Las modificaciones en los juegos o ROMs son permanentes por lo que una vez cambiados
serán permanente lo que no ocurre para el caso de los cheats que se pueden activar y
desactivar en cualquier momento.
VIDAS
Esta es una de las modificaciones que podemos realizar en los juegos para cambiar el número
de vidas iniciales o lo que ocurre cuando perdemos una vida.
Para modificar las vidas tenemos primero que buscar la dirección donde se guarda en la RAM.
Para el caso del juego “Boulder Dash” lo encontramos en la dirección $D762.
Para encontrar el fragmento de código donde se ponen las vidas iniciales ponemos un
breakpoint cuando se escribe en la dirección $D762 al iniciar el juego con lo que vamos a
esperar a que se pare el emulador.
En $00:06FC se para el emulador pero no es aquí lo que buscamos con lo que se para en la
dirección $00:027D donde encontramos el fragmento que buscamos:
ROM0:027B 3E 03
ld a,03
ROM0:027D EA 62 D7
ld (D762),a
ROM0:0280 EA 63 D7
ld (D763),a
Este juego permite la opción de 2 jugadores por lo que la dirección $D762=Vidas jugador 1 y
$D763=Vidas jugador 2.
Lo que hace esto es poner el valor $03 en las direcciones $D762 y $D763 para poner las vidas
iniciales de ambos jugadores.
Cambiando la primera instrucción podemos hacer que tengamos más o menos vidas:
ROM0:027B 3E 09
ld a,09
Empezamos con 9 vidas. Podemos hacerlo cambiando el byte $27C con un editor hexadecimal.
Igualmente, se parará el emulador en la dirección $01:4026 cuando perdemos un vida donde
encontramos el fragmento de código:
ROM1:401F 21 62 D7
ROM1:4022 7E
ROM1:4023 D6 01
ROM1:4025 27
ld hl,D762
ld a,(hl)
sub a,01
daa
ROM1:4026 77
ld (hl),a
En $01:4023 encontramos la instrucción “sub a,01” que lo que hace es disminuir en $01 el
número de vidas.
Si cambiamos esa instrucción:
ROM1:4023 D6 00
sub a,00
Se disminuye en $00 el valor de las vidas con lo que no cambia y tendríamos siempre el mismo
número de vidas (es decir vidas infinitas).
Otro cambio que podríamos hacer es cambiando esa instrucción por:
ROM1:4023 C6 01
add a,01
Con esto lo que hacemos es que en lugar de perder vidas en el juego cuando nos caiga una
roca encima se incrementa el número de vidas ya que se añade el valor $01 al número de
vidas.
Esto ocurre para las vidas del jugador 1 pero podemos hacerlo también para el jugador 2
poniendo un breakpoint en la dirección $D763 y viendo donde se disminuyen las vidas del
jugador 2.
ENERGÍA
Algunos juegos tienen indicadores de energía además de un indicador de vidas que va
disminuyendo conforme nos golpean los enemigos.
Para el ejemplo, podemos utilizar el juego “Addams Family: Pugsley´s Scavenger Hunt” que
cuenta con un indicador de energía.
En la dirección $C196 se guarda la energía en la memoria RAM por lo que poniendo un
breakpoint cuado se escriba en esta dirección podemos averiguar cuando se pone el valor
inicial de la energía y cuando se disminuye.
La energía inicial se pone en el siguiente fragmento de código:
ROM0:0257 FA A5 C1
ld a,(C1A5)
ROM0:025A EA 96 C1
ld (C196),a
Como se observa no se pone directamente el valor en la dirección $C196=Energía sino que se
carga el valor que hay en la dirección $C1A5 (que tiene el valor $03) y se pone este valor en la
dirección $C196=Energía.
Podríamos poner un breakpoint en la dirección $C1A5 para averiguar cuando se pone el valor
$03 que es la energía inicial en esta dirección que encontramos en el código:
ROM2:7FE8 0E 03
ld c,03
ROM2:7FEA CB 67
bit 4,a
ROM2:7FEC 28 01
jr z,7FEF
ROM2:7FEE 0C
inc c
ROM2:7FEF E6 20
and a,20
ROM2:7FF1 28 01
jr z,7FF4
ROM2:7FF3 0C
inc c
ROM2:7FF4 79
ld a,c
ROM2:7FF5 EA A5 C1
ROM2:7FF8 C9
ld (C1A5),a
ret
En $02:7FF4 se pone el valor del registro “c” en el registro “a” (a=c) que vale $03 y se pone
este valor en la dirección $C1A5 (en la instrucción en $02:7FF5).
Como vemos, es un proceso complicado al no poner directamente el valor de la energía en la
dirección $C196 por lo que podríamos cambiar la instrucción en $00:0257:
ROM0:0257 FA A5 C1
ld a,(C1A5)
Como ocupa 3 bytes la instrucción (FA A5 C1) podemos poner una instrucción que ocupe como
mucho ese espacio.
Si ponemos en esa dirección la instrucción:
ROM0:0257 3E 05
ld a,05
Con esto ponemos en el registro “a” el valor $05, pero ocupa sólo 2 bytes (3E 05) por lo que
nos queda 1 byte de espacio que podemos ocupar con la instrucción “nop” (00) que ocupa un
byte y que equivale a “NOt oPeration: No hace nada) con lo que quedaría como:
ROM0:0257 3E 05
ld a,05
ROM0:0259 00
nop
ROM0:025A EA 96 C1
ld (C196),a
Esta modificación lo que hace es poner el valor $05 en la dirección $C196, con lo que
empezaremos con $05 de energía inicial.
Ahora ya da igual el valor que haya en la dirección $C1A5 ya que ahora ya no se lee el valor
que hay en esa dirección y se pone directamente el valor que hemos puesto en $C196=Energía.
La instrucción “nop” es muy útil ya que nos va a permitir llenar el espacio en blanco cuando
cambiamos instrucciones que ocupan menos espacio del que había.
El código donde se disminuye la energía lo encontramos en:
ROM0:390F 21 96 C1
ld hl,C196
ROM0:3912 7E
ld a,(hl)
ROM0:3913 A7
and a
ROM0:3914 C8
ret z
ROM0:3915 35
dec (hl)
ROM0:3916 CD 3A 0F
call 0F3A
La instrucción en $00:3915 encontramos la instrucción que disminuye el valor que hay en la
dirección contenida en hl=$C196 (que se pone en $00:390F) con lo que disminuimos la
energía.
Eliminando esta instrucción en $00:3915 podemos conseguir tener energía infinita:
ROM0:3915 00
nop
Con este cambio en lugar de disminuir la energía, el juego no haría nada y seguiría la energía
tal cual lo que tiene el efecto de tener energía infinita.
La instrucción “nop” (Not oPeration: No operación) es muy útil para rellenar el espacio que nos
sobra cuando cambiamos instrucciones.
OBJETOS
En determinados juegos debemos coger un número de objetos para obtener una vida extra o
pasar de nivel por lo que podemos hacer algunas modificaciones en este sentido. Para el
ejemplo, vamos a utilizar el juego “Super Mario Land” donde debemos coger 100 monedas
para que nos den una vida extra.
Buscando la dirección donde se guardan las monedas en la RAM lo encontramos en la
dirección $FFFA y poniendo un breakpoint cuando se escriba en esta dirección cada vez que
cogemos las monedas el emulador se parará con lo que obtenemos:
ROM0:1C04 F0 FA
ld a,(ff00+FA)
ROM0:1C06 C6 01
add a,01
ROM0:1C08 27
ROM0:1C09 E0 FA
daa
ld (ff00+FA),a
En $00:1C06 encontramos la instrucción que incrementa las monedas y que podemos cambiar
por:
ROM0:1C06 C6 10
add a,10
Con esto cuando cogemos una moneda se incrementa el número de monedas que hemos
cogido en 10 en lugar de en 1. Haciendo esto con cada 10 monedas (equivalen a 100)
obtendremos una vida adicional en el juego.
Podemos situarnos con el emulador en el “bgb debugger” en la instrucción $00:1C06
seleccionado “Go to…” e introducciendo esta instrucción.
Seleccionando “Modify code/data” e introducciendo la anterior instrucción podemos ver como
afecta el cambio cuando cogemos una moneda.
Seleccionando la opción “Save ROM as…” del menu “File” podemos guardar el juego
modificado con otro nombre como “Super Mario Land (W) (V1.0) [!] facil.gb”.
También se puede hacer la modificación con el programa “GameBoy Assembly Editor”
abriendo el juego y seleccionando la opción “Goto…” del menú “Search” e introduciendo la
dirección $00:1C06.
Con el botón derecho sobre la dirección podemos seleccionar la opción “Change Instruction…”
e introducir la nueva orden “add a,10” para hacer el cambio.
Esta modificación también podemos hacerla en otros juegos como en “McDonalland” en el
que tenemos que coger 100 objetos con la letra “M” para obtener una vida adicional en el
fragmento de código:
ROM0:36B4 47
ROM0:36B5 FA FE D3
ld b,a
ld a,(D3FE)
ROM0:36B8 FE 99
cp a,99
ROM0:36BA 30 07
jr nc,36C3
ROM0:36BC 80
add b
ROM0:36BD 27
daa
ROM0:36BE EA FE D3
ld (D3FE),a
Cambiando este fragmento podemos hacer que sea mucho más fácil obtener una vida extra:
ROM0:36B4 FA FE D3
ld a,(D3FE)
ROM0:36B7 FE 99
cp a,99
ROM0:36B9 30 08
jr nc,36C3
ROM0:36BB C6 11
add a,11
ROM0:36BD 27
ROM0:36BE EA FE D3
daa
ld (D3FE),a
Con esta modificación con cada objeto “M” se incrementa el contador en $11 unidades con lo
que bastan sólo 10 para obtener una vida más.
También se puede cambiar por ejemplo, la instrucción en $00:36B8:
ROM0:36B8 FE 05
cp a,05
Con este cambio sólo necesitaríamos 5 objetos para obtener una vida más.
CÓDIGOS GAME GENIE
La consola Gameboy disponía de un accesorio llamado “Game Genie” que permitía obtener
ventajas en los juegos. El emulador “BGB” permite también la introducción de estos códigos en
la pantalla de “bgb cheats” en el apartado de “Code”.
En internet hay páginas donde podemos encontrar códigos Game Genie para una gran
variedad de juegos de Gameboy como www.bsfree.org, www.gamegenie.com,...
Por ejemplo, el juego “The Jetsons” tiene el código Game Genie 09F-16C-E66 (empezar con 9
vidas) que podemos introducer en el emulador “BGB” en la pantalla de “bgb cheats” tal y
como se ve en la imagen.
Una vez pulsado el botón “OK” nos mostrará la siguiente pantalla:
En esta pantalla en la izquierda encontramos el código Game Genie introducido (“09F-16CE66”) para empezar con 9 vidas y a continuación aparece “G-E” para indicar que este código
está active.
A continuación aparece “(3F16)=09<-03” que indica que la dirección $00:3F16 es el que tiene
que modificar para empezar con 9 vidas. “=09” es el valor que se pone para empezar con ese
número de vidas y “<-03” es el valor que aparece por defecto en esa dirección (ya que en este
juego el valor de vidas inicial es de 3).
El aparecer este valor $03 es para indicarle al emulador el valor a buscar ya que en el caso de
que hubiera sido una dirección entre $4000-$7FFF correspondiente a algún banco no se sabe
de que banco estamos tratando.
Así, por ejemplo, si hubiese sido la dirección $7BCA y el valor a buscar hubiese sido $03,
entonces debería el emulador mirar en los diferentes bancos en cual de ellos aparece en esa
dirección el valor $03 para cambiarlo por $09 para tener 9 vidas.
Si en $01:7BCA=$02, $02:7BCA=$03 entonces se pone el valor $09 en la dirección $02:7BCA
para tener 9 vidas.
Estos no son modificaciones en la ROM sino que son temporales y se pueden activar y
desactivar en cualquier momento igual que los que introducimos en las direcciones de la RAM.
Esta información también nos la muestra el programa “Game Genie Encoder/Decoder 0.91a”
introduciéndola tal y como se muestra en la imagen:
También nosotros con este programa podemos crear nuestros propios código Game Genie
introduciendo el valor (“Value”), la dirección (“Address”) y el valor de comparación
(“Compare”).
En el juego “Boulder Dash” veíamos como había un fragmento de código donde se ponen las
vidas iniciales del jugador 1 y 2:
ROM0:027B 3E 03
ld a,03
ROM0:027D EA 62 D7
ld (D762),a
ROM0:0280 EA 63 D7
ld (D763),a
El byte $00:027C tiene el valor $03 por lo que podemos obtener un código Game Genie para
empezar por ejemplo con 7 vidas en lugar de 3 introduciendo en el programa los datos:
Value=07
Address=027C
Compare=03
El programa nos genera el código Game Genie correspondiente (072-7CF-E66) para que los
jugadores 1 y 2 empiecen con 7 vidas.
BÚSQUEDA DE CHEATS NO TÍPICOS
Normalmente se suele buscar el número de vidas y energía cuando se buscan valores en la
RAM pero sin embargo podemos buscar cualquier cosa que se pueda almacenar en la
memoria.
Algunos casos pueden ser los siguientes entre otros muchos:
DIRECCIÓN DEL PERSONAJE
En los juegos de plataformas el personaje que manejamos se mueve hacia la derecho o
izquierda de la pantalla por lo que podemos buscar este valor que cambiará cuando cambie la
dirección de éste.
El proceso es similar a cualquier otro valor bastando con estar en una dirección e iniciar la
búsqueda para cambiar luego de posición buscando valores que no sean iguales repitiendo así
el proceso con cambios de valores cuando cambias de dirección o no cambios de valores
cuando está el personaje en la misma dirección.
Haciendo esto encontramos la dirección y los valores que aparecen en la captura.
COORDENADAS X,Y PERSONAJE Y ENEMIGOS
Otro valor que podemos buscar en los juegos son las coordenadas de los personajes que
encontramos en la pantalla del juego tal y como se ve en la siguiente captura:
Estos valores podemos activarlos para que se queden quietos los enemigos en una posición de
la pantalla o desactivarlos para que se muevan normalmente.
PUNTUACIÓN
Para los tramposos que quieran aparecer en la pantalla de High Scores de forma fácilmente
podemos buscar las direcciones donde se guarda la puntuación que tenemos en el juego y
cambiarla por el valor que queramos.
Para este caso las direcciones que se ven implicadas son $C490-$C492.
$C490: Score xx00000
$C491: Score 00xx000
$C492: Score 0000xx0
Los valores “xx” indicant el valor que ponemos en esa dirección. Por ejemplo, para los valores
$C490=$76, $C491=$32, $C492=$89 tendremos una puntuación de “763289”.
Igualmente ocurre con las direcciones donde se almacena en este juego el HI-SCORE que son
las direcciones $C4A0-$C4A2 con la misma estructura que el anterior:
$C4A0: Hi-Score xx00000
$C4A1: Hi-Score 00xx000
$C4A2: Hi-Score 0000xx0
Como se comprueba el ultimo dígito es siempre “0” y no se almacena en la RAM. Sin embargo,
esta no es la única forma que nos podemos encontrar a la hora de poner la puntuación en la
RAM.
También la forma de almacenarse los valores puede cambiar por lo que en un juego un valor
de $32 puede corresponder a “32” y para otro puede corresponder a “50”.
$32 hexadecimal=50 decimal.
En este caso, se utilizan 2 digitos por dirección de la RAM pero podemos encontrarnos una
dirección por cada dígito de la RAM y podemos encontrarnos que el ultimo dígito cambie por
lo que en este caso se utilizaría otra dirección para ese dígito.
A veces la puntuación también se puede almacenar en formato hexadecimal utilizando 2 o 3
direcciones de la RAM poniéndose el valor en formato hexadecimal normal o inverso y utilizar
o no el ultimo dígito como en este juego.
Por ejemplo, supongamos que tenemos en una dirección de la RAM el valor $12 y en la
siguiente dirección $35 donde se pone la puntuación en la RAM con lo que dependiendo de la
forma que utilice el juego la puntuación podría ser:
$1235=4661 puntos
$1235=46610 puntos (no se utiliza el último dígito y es siempre 0)
$3512=13586 puntos
$3512=135860 puntos (no se utiliza el ultimo dígito y es siempre 0)
Esta forma no es la más habitual pero también podríamos encontrárnosla en algún juego.
Otra forma que nos podemos encontrar es la que ocurre por ejemplo en el juego “Mr. Do!” en
la que cada dígito se pone en una dirección de la RAM pero en formato tile (Tile $30=”0”, Tile
$31=”1”,…,$39=”9”).
En el juego “Mr. Do!” la puntuación se guarda en las direcciones $D0D3-$D0D8 para poner los
6 dígitos.
$D0D3: Score x00000
$D0D4: Score 0x0000
$D0D5: Score 00x000
$D0D6: Score 000x00
$D0D7: Score 0000x0
$D0D8: Score 00000x
OPCIÓN MENÚ PRINCIPAL
Otro valor que podemos buscar es la opción de los menús como puede ser el menú principal (a
veces una misma dirección se utiliza para varios menús). La búsqueda de este valor se puede
utilizar más que por tener ventajas (que no se va a tener) que por conocer como funciona el
juego para localizar el fragmento de código que se encarga de poner la opción del menu
principal, situar el cursor (si lo tiene), cambiar la opción del menú,...
Este es un valor muy fácil de localizar ya que basta con situar el cursor sobre una u otra opción
y realizando búsquedas.
Así para el caso del menu principal del juego “Jurassic Park” encontramos que en la dirección
$FFAE se guarda la opción del menú principal.
Los valores que se ponen en la dirección $FFAE son los siguientes:
$00: START
$01: INFO
$02: SOUND
OBJETOS PARA PASAR DE NIVEL
En algunos juegos como “Boulder Dash” tienen un número de objetos en el nivel que tenemos
que coger para poder pasar al nivel siguiente.
Para el caso que vamos a tratar tenemos que coger diamantes y una vez hecho ésto debemos
llegar a la salida del nivel que estará active para ir al siguiente nivel.
Realizando una búsqueda del cheat viendo cuando va disminuyendo el número de diamantes
del nivel comprobamos que se guarda en la dirección $D7C9 de la RAM.
Podemos añadir un cheat en la dirección $D7C9 con el valor $01 con lo que nos quedaría 1
diamante solo para poder activar la salida del nivel. Hay que activar el cheat para que tenga el
valor $01 y una vez hecho esto desactivarlo para que se quede ese valor.
Si no lo desactivamos no podríamos pasar de nivel porque siempre quedaría un diamante. Al
desactivarlo cuando cojamos un diamante más ya no quedarán diamantes y se active la salida.
Hay veces en juegos que simplemente poniendo el valor $00 se pasar automáticamente de
nivel ya que no quedan objetos pero en este caso no. Cuando se pasa automáticamente
podemos desactivar el cheat cuando queramos o pasará todos los niveles solo.
TIEMPO
Si nos fijamos en el anterior juego (“Boulder Dash”) vemos como en la pantalla del nivel
aparece un contador del tiempo que disminuye y que podemos buscar para poner una
cantidad mayor de la que disponemos e incluso tiempo infinito.
Realizando la búsqueda cuando disminuye el valor encontramos la dirección $D7F0 donde se
pone el tiempo y puede tomar los valores entre $00 y $FF (0 y 255). El valor máximo que
podemos poner es de 255.
Si nos fijamos en un único dígito del tiempo para ver si disminuye o se incrementa
encontramos otras direcciones.
Para ello, por ejemplo si nos fijamos en el dígito de las unidades, si en la búsqueda tenemos
“94” y en la siguiente búsqueda “87” pondríamos que busque valores que han aumentado ya
que el valor de las unidades ha pasado de “4” a “7” (“keep values which are above the
previous value”).
Haciendo esto encontramos que en la dirección $D792 se pone el valor del dígito de las
unidades en formato texto o ASCII (el valor $30=”0”, $31=”1”,…,$39=”9”.
Encontramos en el juego las siguientes direcciones de la RAM:
Tiempo x00 (Texto): D790
Tiempo 0x0 (Texto): D791
Tiempo 00x (Texto): D792
Diamantes quedan x00 (Texto): D794
Diamantes quedan 0x0 (Texto): D795
Diamantes quedan 00x (Texto): D796
Diamantes nivel (?): D798-D79A (mismo formato que los anteriores)
Diamantes nivel (?): D79C-D79E (mismo format que los anteriores. Tiene el mismo valor que
las anteriores direcciones D798-D79A).
CONTADOR TIEMPO PANTALLA “CONTINUE”
Otro valor poco frecuente pero que podemos buscar es el contador de tiempo que aparece
cuando perdemos todas las vidas en algunos juegos y nos permite continuar o terminar el
juego tal y como ocurre en el juego “Boulder Dash”.
Esto no tiene demasiada utilidad salvo para a través de breakpoints conocer el fragmento de
código que pone el tiempo del contador y disminuirlo.
En este juego lo encontramos en la dirección $D7BA.
TIEMPO DE INVULNERABILIDAD
En ciertos juegos al iniciar el nivel el personaje que manejamos tiene un tiempo en el que es
invulnerable a los enemigos y durante el cual no pueden eliminarnos como ocurre en el juego
“Addams Family: Pugsley´s Scavenger Hunt”.
Podemos buscar la dirección donde se guarda este tiempo realizando búsquedas cuando el
personaje está parpadeando indicando que estamos en el tiempo de invulnerabilidad.
Debemos hacerlo más o menos rápido porque no dura mucho pero no es muy complicado.
En este caso se encuentra dicho valor en la dirección $C1C7 tomando los valores $00 cuando
está desactivado, $01 cuando está activado y si el valor es mayor a $01 el personaje puede ser
atravesado por algunos enemigos como se ve en la pantalla sin perder energía.
Si activamos el cheat no disminuirá la energía del personaje y no nos podrán eliminar los
enemigos.
ENERGÍA
Un valor habitual que se puede encontrar es la energía y podemos hacer que el personaje que
manejamos no pierda la energía cuando nos golpeen los enemigos. Para el ejemplo podemos
utilizar el juego “Addams Family: Pugsley´s Scavenger Hunt” que habíamos visto antes y que se
guardaba en la dirección $C196.
POSICIÓN ENEMIGO FINAL
En algunos juegos al final de determinados niveles tenemos un enemigo final que se va
moviendo por la pantalla y que podemos localizar la posición y dejarlo inmóvil para lo cual
podemos buscar sus coordenadas como hacíamos antes en otro juego. Aquí podemos utilizar
el juego “Addams Family”.
Buscando la coordenada X del personaje lo encontramos en la dirección $C1D9 y la
coordenada Y en la dirección $C1D7. Sin embargo, si ponemos un valor bajo en $C1D7
(Coordenada Y) veremos como la parte superior del enemigo no se muestra la parte de arriba
como si estuviera detrás de los ladrillos del fondo.
COORDENADAS X,Y OBJETO PANTALLA TÍTULO
En el juego “Adventure Island” encontramos que en la pantalla de título aparece el personaje
que manejamos siguiendo a un huevo y que podríamos buscar las coordenadas de dicho huevo
que se va moviendo por la pantalla.
Esta búsqueda es más por intentar comprender el juego que por conseguir ventaja alguna en
el juego pero es una de las muchas posibilidades que tiene la búsqueda de cheats.
Los valores donde se guardan las coordenadas son los que aparecen en la captura ($C211:
Coordenada Y Huevo y $C213: Coordenada X Huevo).
OBJETO DEL PERSONAJE O PODER
En el juego “Adventure Island” podemos coger elementos que nos dan un determinado objeto
o personaje y que buscándolo en la RAM nos daría la dirección $C428 para lo cual debemos
poner los valores que aparecen en la captura antes de que comience el juego (o el nivel creo).
ESTADO DEL PERSONAJE
En el juego “B.C. Kid” el personaje puede tomar varios estados o personajes que adopta y que
se guardan en la RAM en la dirección $C5B0.
Estos valores son:
00: Normal
01: Frankenstein
02: Punky
03: Tortuga
04: Alien
05: Patinador
También puede adopter otros valores que son mezcla de alguno de éstos pero los anteriores
son los principales.
Podemos activar con cheats cualquier de estos valores para cambiar el estado del personaje.
NIVEL
Este es uno de los valores más útiles que podemos encontrar y que nos puede permitir
empezar en cualquier nivel para lo cual debemos jugar al juego y cuando pasemos a otro nivel
buscar los valores que se han incrementado. Debemos realizar el proceso hasta que
encontremos el valor donde se almacena en la RAM que corresponde a la dirección $C413.
La primera imagen de la izquierda corresponde al inicio de “FERN ISLAND” y la de la derecha
con el valor $04 corresponde a otra area de “FERN ISLAND”. Los valores para el inicio de cada
nivel tienen los valores siguientes correspondiendo los valores intermedios a areas de cada
isla:
00: FERN ISLAND
08: LAKE ISLAND
10: DESERT ISLAND
18: ICE ISLAND
20: CAVE ISLAND
28: CLOUD ISLAND
30: VOLCANO ISLAND
38: DINOSAUR ISLAND
Podemos activar el cheat en cualquier momento aunque si estamos ya en un nivel concreto el
efecto lo notaremos cuando perdamos una vida que cambia al nivel que tengamos
seleccionado.
También cuando estemos llegando al final del nivel que hemos seleccionado podemos
desactivarlo para que pasemos al siguiente nivel.
DIRECCIÓN PERSONAJE
En el juego “Adventures of Lolo” el personaje toma 4 direcciones (de frente, de espalda,
izquierda y derecha) con lo que podemos buscar el valor de la RAM donde se guarda en la
dirección $C4B1 tal y como se ve en la captura con sus correspondientes valores:
Si activamos el cheat con uno de estos valores el personaje se moverá a lo largo de la pantalla
en la dirección que hayamos puesto.
DATOS DE LAS MÁXIMAS PUNTUACIONES EN LA RAM
En el juego “Mr. Do!” las máximas puntuaciones están en la RAM aunque esto puede no darse
en todos los juegos que tienen esta pantalla sino que estén en el juego (ROM) y se carguen
desde ahí sin ponerla en la RAM pasándose directamente desde la ROM a pantalla.
En este caso los datos están almacenados en formato texto y en formato hexadecimal.
También podríamos hacerlo con juegos que tengan textos para intentar localizar si los textos
están en la RAM.
En los juegos normalmente se utilizan las direcciones $C000-$DFFF ($2000 bytes) para
almacenar datos en la RAM aunque hay juegos que utilizan más memoria RAM sobre todo en
juegos de Gameboy Color y a partir de la dirección $FF80-$FFFF que es la High RAM donde se
ponen también datos.
En estos juegos el número de bytes de la RAM son $207F (8319 tal y como aparece cuando
iniciamos la búsqueda de cheats).
Si nos situamos en el debugger podríamos poner “Go to…” e introducir la dirección $C000 que
es el inicio de la RAM y selecionar la opción “save memory dump…” del menú “File”.
Le ponemos el nombre que queramos al fichero y de longitude podemos poner $4000 que
engloba las direcciones ($C000-$FFFF) para tener tanto la RAM como la High RAM en el
fichero.
Si abrimos el fichero con un editor hexadecimal podríamos buscar para ver si nos aparece
algún tipo de texto del que aparece en la pantalla de máximas puntuaciones o seleccionar
“Buscar” e introducir alguno de los textos de esta pantalla como “LEG” que encontramos en el
fichero creado en $1306-$1308.
Como se observa la captura va desde $1300-$135F que como la RAM empieza en la dirección
$C000 debemos sumarle a estas direcciones con lo que en $D300-$D35F tenemos estos datos
en la RAM.
Como se observan la estructura de cada línea de puntos es la siguiente:
6 bytes con la puntuación en formato texto: “147000” (31 34 37 30 30 30)
3 bytes con el nombre en formato texto: “LEG” (4C 45 47)
1 byte para el nivel en formato hexadecimal: “15” (0F)
2 bytes para el tiempo en formato hexadecimal: “08´42” (0A 02).
0A 02 corresponde a $020A hexadecimal=522 que son 8 minutos y 42 segundos.
Por tanto se utilizan 12 bytes para cada línea de la puntuación. Si buscamos toda estos valores
en el juego (ROM) lo encontramos en la dirección $00:0B2C-$00:0B8B ($B2C-$B8B).
OBTENER DATOS DE NIVEL DE UN JUEGO
BATTLE BULL
La obtención de los datos de nivel de un juego es junto con la música de los juegos de
Gameboy uno de los aspectos más complicados de localizar y modificar.
Voy a utilizar una serie de juegos a los que he encontrado dichos datos y trataré de explicar
cómo se puede obtener dicha información utilizando el emulador BGB de Gameboy.
Para el ejemplo, voy a utilizar el juego “Battle Bull” pero que igual podría funcionar para otros
juegos.
Si observamos el “BGB VRAM Viewer” donde se muestra la pantalla del nivel 1 completo
vemos que hay bloques de 2*2 para dibujarlo.
En este nivel encontramos bloques de diferentes tipos:
El primero de la parte superior izquierda se forma por los tiles $04, $06, $05, $07. (Bloque tipo
$01)
El bloque en blanco está formado por los tiles $24, $24, $24, $24 (Bloque tipo $09)
El bloque verde sólido de un color sin líneas se forma por los tiles $00, $02, $01, $03 (Bloque
tipo $00).
Podemos buscar los datos para el primer bloque (04 06 05 07) con un editor hexadecimal lo
encontramos en la dirección $C0F4-$C0F7 ($03:40F4-$03:40F7) y el bloque verde sólido (00 02
01 03) en la dirección $C0F0-$C0F3 ($03:40F0-$03:40F3).
Si miramos en la memoria RAM podemos encontrar la pantalla completa del nivel pero tal y
como se muestra en la pantalla desde la dirección $C158-$C557 ($400=1024 bytes).
Es por esto por lo que asignamos los valores de los tipos de bloques.
Bloque tipo $00: 00 02 01 03
Bloque tipo $01: 04 06 05 07
Bloque tipo $02: 08 0A 09 0B
Bloque tipo $03: 0C 0E 0D 0F
Bloque tipo $04: 10 12 11 13
Bloque tipo $05: 14 16 15 17
Bloque tipo $06: 18 1A 19 1B
Bloque tipo $07: 1C 1E 1D 1F
Bloque tipo $08: 20 22 21 23
Bloque tipo $09: 24 24 24 24
Si ponemos un breakpoint cuando se lea en la dirección $03:40F0 vemos como se para el
emulador en la dirección $00:1FC9 y se dibuja el nivel en pantalla.
Si tomamos la primera línea del nivel vemos que tenemos todos los bloques tipo $01 (16) y en
la segunda línea del nivel tenemos un bloque tipo $01, 10 bloques tipo $09, 1 bloque tipo $00,
3 bloques tipo $09 y un bloque tipo $01.
Si buscáramos la secuencia para dibujar las 2 líneas sería: 01 01 01 01 01 01 01 01 01 01 01 01
01 01 01 01 01 09 09 09 09 09 09 09 09 09 09 00 09 09 09 01.
Esta secuencia de valores hexadecimales lo encontramos en la dirección $C6C8-C6E7
($03:46C8-$03:46E7).
Si nos fijamos en la pantalla del nivel comprobamos que está formado por 16 líneas de 16
bloques cada uno lo que hace un total de 256 (como cada bloque es un bytes necesitará 256
bytes para cada nivel o $100 en hexadecimal).
Por lo que el primer nivel empieza en la dirección $C6C8 ($03:46C8) y el siguiente si van
consecutivos debe empezar $100 bytes más adelante o sea en $C7C8 ($03:47C8).
Buscando el valor C846 lo encontramos en varias posiciones de la ROM pero en $C000
($03:4000) está el valor que encontramos y en $C002 ($03:4002) encontramos C847 y en
$C004 ($03:4004) encontramos C848.
Desde la dirección $C000-$C06F están los punteros a los datos de los niveles del juego:
$03:46C8, $03:47C8, $03:48C8,…
Con esto ya hemos localizado donde están los punteros de los niveles y con esto ya tenemos
los datos para cada uno de los niveles del juego.
Modificando los bytes $03:46C8-$03:47C7 ($C6C8-$C7C7) podemos cambiar el nivel 1 del
juego a nuestro gusto.
Por ejemplo, cambiando el byte que hay en $C6C8 que tiene el valor $01 (bloque tipo $01 con
raya) por el valor $09 (sin recuadro) podemos observar como se borra el primer bloque de la
parte superior izquierda de la pantalla.
Ahora si quisiéramos podríamos hacer un programa que nos permitiera abrir cualquier nivel
del juego, mostrarlo en una pantalla, modificarlo y guardarlo en la ROM con los cambios
realizados.
En la captura vemos gráficamente dicho cambio en el nivel 1 del juego cambiando ese byte.
Igualmente si ponemos un breakpoint en la dirección $03:4000 cuando se lea se parará el
emulador en la dirección $00:1F7B.
Igualmente en otros juegos podemos buscar objetos 2*2 y buscar los datos que lo forman en la
ROM e intentar asignarle valores a cada uno de ellos e intentar localizarlos.
Aquí en la RAM se ponen 32 tiles y luego otros 32 tiles para formar la primera línea de bloques
del nivel 1.
LITTLE MAGIC (GAMEBOY COLOR)
Este juego tiene un sistema parecido al “Battle Bull” pero con una pequeña diferencia a la hora
de almacenar los objetos 2*2 para dibujar el nivel en la pantalla.
En este juego se pone el atributo y el tile correspondiente. Así por ejemplo para el árbol la
parte superior izquierda se guarda con los datos 0642 tal y como se ve en la parte derecha de
la captura con lo que se utilizan 8 bytes para cada objeto.
Para el árbol utilizaría los valores 06 42 06 43 06 52 06 53 que encontramos en las direcciones
$1224-$122B aunque voy a explicar el proceso hasta que he averiguado esta información.
En primer lugar he puesto un breakpoint en la dirección $9844=Map Address cuando se pone
el valor $42 comprobamos que el emulador se para en la dirección $00:203A donde
encontramos el fragmento de código:
ROM0:202A 44
ld b,h
ROM0:202B 4D
ld c,l
ROM0:202C 21 F4 11
ROM0:202F 09
ld hl,11F4
add hl,bc
ROM0:2030 3E 01
ld a,01
ROM0:2032 E0 4F
ld (ff00+4F),a
ROM0:2034 2A
ldi a,(hl)
ROM0:2035 12
ld (de),a
ROM0:2036 AF
ROM0:2037 E0 4F
xor a
ld (ff00+4F),a
ROM0:2039 2A
ldi a,(hl)
ROM0:203A 12
ld (de),a
ROM0:203B 13
inc de
El emulador cuando se para en el registro “hl” tiene el valor $1226 que corresponde a la
dirección $00:1226 donde se encuentran datos para dibujar el árbol. Esta dirección es mayor
que la de inicio de los datos ya que se ha incrementado el valor del registro “de” pero ya lo
tenemos más o menos localizado donde están los datos.
Como los tiles que forman el árbol son 42 43 52 53 vemos con el editor hexadecimal HxD que
es lo que aparece a excepción del valor $06 que aparece antes de estos valores y viendo el
“BGB VRAM Viewer” comprobamos que corresponde al atributo que también aparece en los
datos.
En el fragmento de código anterior encontramos lo siguiente:
ROM0:202C 21 F4 11
ld hl,11F4
Esta instrucción pone en el registro “hl” la dirección $00:11F4 que si vamos a esta posición
($11F4) con el editor hexadecimal comprobamos que tiene la misma estructura que para
dibujar el árbol con lo que aquí es donde empiezan los datos para dibujar los objetos que se
ponen en el nivel.
Cada objeto hemos visto que ocupa 8 bytes con lo que los objetos que encontramos son:
Objeto tipo $00 (Hierba): 06 60 06 61 06 70 06 71
Objeto tipo $01: 06 66 06 67 06 76 06 77
($11F4-$11FB)
($11FB-$1203)
Objeto tipo $02 (Cuadro marrón): 05 40 05 41 05 50 05 51
Objeto tipo $03: 05 64 05 65 05 74 05 75
Objeto tipo $04 (Corazón): 02 02 02 03 02 12 02 13
Objeto tipo $05 (Agua): 01 4C 01 4C 01 4C 01 4C
Objeto tipo $06 (Árbol): 06 42 06 43 06 52 06 53
($1224-$122B)
Objeto tipo $07 (Salida): 00 22 00 23 00 32 00 33
($122C-$1233)
Con esto ya tenemos información para buscar los posibles datos del nivel en el juego.
Si nos fijamos en la segunda línea porque tiene más variedad de valores aunque nos
podríamos haber fijado en la primera tendríamos:
02 05 06 06 06 00 00 00 06 06 06 05 02
Buscándolo lo encontramos en las direcciones $240F0-$240FC ($09:40F0-$09:40FC).
Cada línea de objetos 2*2 tiene 16 bloques por lo que se utilizan 16 bytes o $10 en
hexadecimal por lo que la primera línea si la comprobamos empieza en $240E0 ($09:24E0).
Ya tenemos localizados la ubicación de los objetos y donde están los datos para el nivel 1
aunque todavía no sabemos cuantas líneas se utilizan para cada nivel.
Podemos pasar el nivel 1 para mostrar el nivel 2 y buscar los datos de ese nivel para
encontrarlo en el juego.
La primera línea del nivel sería: 02 02 02 02 02 02 02 02 02 02 02 02 02 00 00 00
La segunda línea del nivel sería: 02 05 05 05 06 06 06 05 05 05 05 05 02 00 00 00
Buscando toda la secuencia (02 02 02 02 02 02 02 02 02 02 02 02 02 00 00 00 02 05 05 05 06
06 06 05 05 05 05 05 02 00 00 00) para asegurarnos que se trata de los datos que buscamos la
encontramos en la dirección $241C0-$241DF ($09:41C0-$09:41DF) y en $25CE0-$25CFF
($09:5CE0-$09:5CFF).
Vamos a quedarnos con las primeras direcciones. Las otras serán de otro nivel que tiene las 2
primeras líneas iguales a las del nivel 2.
Ahora tenemos la dirección donde empieza el nivel 1 ($240E0=$09:40E0) y donde empieza el
nivel 2 ($241C0=$09:41C0).
Si nos situamos en el editor hexadecimal y seleccionamos desde $240E0-$241BF (que
corresponde al nivel 1) comprobamos que se trata de $E0 bytes lo que ocupa un nivel o sea
224 bytes que dividido por 16 bytes que tiene cada línea nos da 14 líneas cada nivel.
Un nivel ocupa 224 bytes y tiene 14 líneas de 16 bytes (o 16 objetos 2*2) cada una.
Ahora podemos buscar para ver si hay punteros a donde están los datos de estos 2 niveles con
lo que buscaríamo en el editor hexadecimal los bytes E0 40 C0 41 para ver si los encontramos.
Haciendo esto comprobamos que se encuentra en los bytes $16F5-$16F8 con lo que ya
tenemos donde están los punteros a los datos de cada uno de los niveles.
Tenemos los punteros en las direcciones $16F5-$176C. A partir de la dirección $176D
encontramos también punteros.
Ya con esto tenemos las direcciones de cada uno de los niveles que componen el juego y
podemos modificarlos a nuestro gusto.
También podríamos averiguar donde está el fragmento de código que carga los punteros
poniendo un breakpoint cuando se lea la dirección $00:16F5 donde están los punteros
podemos obtener la dirección $00:0836 donde se para el emulador:
ROM0:082A AF
ROM0:082B FA 28 C3
xor a
ld a,(C328)
ROM0:082E 87
add a
ROM0:082F 5F
ld e,a
ROM0:0830 16 00
ROM0:0832 21 F5 16
ld d,00
ld hl,16F5
ROM0:0835 19
add hl,de
ROM0:0836 2A
ldi a,(hl)
ROM0:0837 66
ld h,(hl)
ROM0:0838 6F
ld l,a
ROM0:0839 11 00 98
ld de,9800
ROM0:083C CD AE 1F
call 1FAE
Aquí si vemos el código tenemos como se carga en el registro “a” el valor que hay en la
dirección $C328=Nivel. En el registro “hl” se carga la dirección $00:16F5 donde están los
punteros y en el registro “de” se pone la dirección $9800=Map Address donde empieza a
dibujar el nivel en pantalla (que corresponde a la parte superior izquierda).
KESAMARU
Este juego tiene también una serie de bloques 2*2 que pone en la pantalla para dibujar el nivel
tal y como se ve en la captura correspondiente al nivel 1:
El primer recuadro que aparece en la parte superior izquierda se forma por los tiles 2C 2D 3C
3D.
Podríamos optar por buscar con un editor hexadecimal estos valores y que encontraremos en
2 direcciones de la ROM o poner un breakpoint cuando se escriba en la dirección $9946=Map
Address el valor $2C para poner el primer tile del bloque superior izquierdo para dibujar el
nivel.
La segunda opción es mejor aunque en caso de que no se encontrara lo buscado podríamos
utilizar la primera.
Al poner el breakpoint el emulador se para en la dirección $00:2AD0 donde encontramos:
ROM0:2ABA 21 77 13
ld hl,1377
ROM0:2ABD CB 27
sla a
ROM0:2ABF CB 27
sla a
ROM0:2AC1 5F
ROM0:2AC2 16 00
ROM0:2AC4 19
ROM0:2AC5 11 40 C6
ld e,a
ld d,00
add hl,de
ld de,C640
ROM0:2AC8 1A
ld a,(de)
ROM0:2AC9 F5
push af
ROM0:2ACA 13
inc de
ROM0:2ACB 1A
ld a,(de)
ROM0:2ACC 57
ld d,a
ROM0:2ACD F1
pop af
…
En la primera línea se carga en el registro “hl” la dirección $00:1377 que es donde empiezas los
datos para dibujar los objetos 2*2 que se ponen en el nivel.
Realizando la búsqueda con el editor hexadecimal encontramos los valores 2C 2D 3C 3D en las
direcciones $139B-$139E y $4ADD-$4AE0.
Las primeras son las que buscabamos pero no se sabía exactamente donde empezaban los
datos de los objetos que con el breakpoint hemos encontrado de forma fácil.
Bloque tipo $00: 02 03 12 13 ($1377-$137A)
Bloque tipo $01: 04 05 14 15
Bloque tipo $02: 06 07 16 17
Bloque tipo $03: 0A 0B 1A 1B
Bloque tipo $04: 0C 0D 1C 1D
Bloque tipo $05: 0E 0F 1E 1F
Bloque tipo $06: 20 21 30 31
Bloque tipo $07: 22 23 32 33
Bloque tipo $08: 24 25 34 35
Bloque tipo $09: 2C 2D 3C 3D
Bloque tipo $0A: 2E 2F 3E 3F
Bloque tipo $0B: 68 69 78 79
Bloque tipo $0C: 6A 6B 7A 7B
Bloque tipo $0D: 6C 6D 7C 7D
Bloque tipo $0E: 6E 6F 7E 7F
Bloque tipo $0F: B0 B2 B1 B3
Si buscáramos los datos de la primera línea según los tipos de bloque que aparecen podría ser
09 09 09 04 04 0F 04 04 04 04
Aunque si buscamos estos bytes no los encontraremos por lo que deben estar almacenados los
niveles de otra forma.
En el fragmento anterior de código cuando se para el emulador en la dirección $00:2AD0 y el
registro “bc” tiene el valor $0F62 que podría corresponder a una dirección.
Si nos vamos con el editor hexadecimal en la dirección $0F62-$0F66 encontramos lo siguiente:
99 94 4F 44 44
Se parece bastante a lo que buscábamos (09 09 09 04 04 0F 04 04 04 04).
Es exactamente lo mismo pero en lugar de utilizarse 1 byte para cada bloque se utiliza 1 byte
para dibujar 2 bloques con lo que se necesitan la mitad de bytes para cada línea.
Cada línea son 10 bloques que se ponen con 5 bytes sólo y hay 8 líneas con lo que cada nivel
ocupa 40 bytes.
Con esto ya hemos encontrado los datos para dibujar el primer nivel y podemos comprobarlo
cambiando el byte en $0F62 que tenía el valor $99 (2 bloques seguidos de tipo $09).
Si ponemos el valor $44 pondríamos 2 bloques seguidos tipo $04 en la parte superior izquierda
y que corresponde al bloque blanco como los que hay en la primera línea antes y después del
bloque con una roca quedando el nivel modificado como se ve en la captura:
Como el nivel 1 empieza en la dirección $00:0F62, el nivel 2 debe empezar $28 (40 bytes) más
adelante es decir en $00:0F8A, el nivel 3 debería empezar en $00:0FB2 (aunque no ocurre),…
Si buscamos el primer valor 620F con el editor hexadecimal HxD lo localizamos en la dirección
$AC0 ($00:0AC0) y comprobamos como a continuación también aparece 8A0F que
corresponde al puntero al segundo nivel aunque el siguiente puntero DA0F ($00:0FDA) no es el
que debería corresponder al nivel 3 pero es porque están los niveles algo desordenados.
Pero con los punteros podemos conocer donde están los datos de cada nivel:
($00:0AC0):
Nivel 1: 62 0F ($00:0F62)
Nivel 2: 8A 0F ($00:0F8A)
Nivel 3: DA 0F ($00:0FDA)
Nivel 4: 6A 11 ($00:116A)
…
Ya tenemos aquí lo que buscábamos los punteros a los datos del nivel, los datos del nivel y los
datos para dibujar los objetos 2*2 que se ponen en pantalla.
Si pusiéramos un breakpoint cuando se lee la dirección $00:0AC0 donde están los punteros el
emulador se para en la dirección $00:0068 que es una rutina de uso general de carga de
punteros.
Este caso ha costado un poco más pero al final también lo hemos localizado.
BOOMER IN ASMIK WORLD 2
En este juego también tenemos objetos 2*2 para dibujar el nivel y vamos a intentar localizar
donde están estos objetos en la ROM.
Si nos fijamos en el objeto que hay en la parte superior de la pantalla a la derecha podemos
comprobar que se forma con los tiles AC AD BC BD pero si buscamos estos valores no lo
encontramos en el juego.
Vamos a poner un breakpoint cuando se escriba el valor $AC en la dirección $9810=Map
Address donde se pone en pantalla.
El juego se para en la dirección $00:0574 donde encontramos lo siguiente:
ROM0:0566 C5
push bc
ROM0:0567 E5
push hl
ROM0:0568 FB
ei
ROM0:0569 F3
di
ROM0:056A F0 41
ld a,(ff00+41)
ROM0:056C E6 03
and a,03
ROM0:056E FE 02
cp a,02
ROM0:0570 D2 68 05
jp nc,0568
ROM0:0573 1A
ld a,(de)
ROM0:0574 77
ld (hl),a
Aquí carga en el registro “a” el valor que hay en la dirección contenida en el registro “de” (que
tiene el valor $CBC1) y lo pone en la dirección contenida en el registro “hl” (que tiene el valor
$9810). La dirección $CBC1 tiene el valor $AC que es el que pone en pantalla pero esto se trata
de dirección de la RAM y no es de donde están los datos de los objetos para dibujar el nivel.
Vamos a poner un breakpoint ahora en la dirección $CBC1 cuando se escriba el valor $AC para
comprobar cuando ocurre para lo cual reinicio el juego para que comience otra vez de nuevo
ya que esto ocurrirá antes.
El emulador ahora se para en la dirección $00:2407 donde encontramos:
ROM0:2403 1A
ld a,(de)
ROM0:2404 13
inc de
ROM0:2405 EE 80
xor a,80
ROM0:2407 22
ldi (hl),a
ROM0:2408 1A
ld a,(de)
ROM0:2409 13
inc de
ROM0:240A EE 80
xor a,80
ROM0:240C 22
ldi (hl),a
ROM0:240D A7
and a
ROM0:240E C9
ret
Aquí encontramos como carga en el registro “a” el valor que hay en la dirección contenida en
el registro “de” (que tiene el valor $7516 y rom=1 con lo que la dirección es $01:7516).
En $00:2405 aparece la instrucción “xor a,80” que si utilizamos la calculadora con un valor
tiene el siguiente efecto:
$20 xor $80=$A0 con lo que le suma al valor que hay en el registro “a” el valor $80.
Por lo que si nos vamos a la dirección $01:7516 en el juego encontramos el valor $2D y en
$01:7515 el valor $2C que si le sumamos $80 nos da $AC que es el primer valor del objeto que
buscábamos.
Con esto ya hemos encontrado donde están los datos de este objeto en el juego y es
$01:7515-$01:7518 (7515-7518).
Si nos vamos a esta dirección en un editor hexadecimal podemos encontrar un poco antes lo
siguiente:
Hasta la dirección $74E0 inclusive encontramos el valor $FF y a continuación encontramos
valores distintos a esto.
A partir de la dirección $01:74E1 o a partir de $01:74E5 podrían estar los datos donde
comienzan los datos para dibujar el nivel.
Si tomamos la dirección $01:74E1 donde está el bloque tipo $00 podemos tener:
Bloque tipo $00: 00 00 00 00
Bloque tipo $01: 60 61 70 71
Bloque tipo $02: 68 61 70 71
Bloque tipo $03: 60 61 78 71
Bloque tipo $04: 62 63 72 73
Bloque tipo $05: 62 63 7A 73
Bloque tipo $06: 62 63 72 7B
Bloque tipo $07: 64 65 74 75
Bloque tipo $08: 64 69 74 75
Bloque tipo $09: 64 65 74 79
Bloque tipo $0A: 66 67 76 77
Bloque tipo $0B: 6A 67 76 77
Bloque tipo $0C: 66 6B 76 77
Bloque tipo $0D: 2C 2D 3C 3D
Bloque tipo $0E: 2E 2F 3E 3F
Bloque tipo $0F: 8E 8F 9E 9F
Bloque tipo $10: 85 85 95 95
Bloque tipo $11: 9C 9D 9C 9D
Bloque tipo $12: 82 85 9C 94
Bloque tipo $13: 85 86 93 9D
Bloque tipo $14: 9C 84 92 95
Bloque tipo $15: 83 9D 95 96
Bloque tipo $16: 8A 8A 9A 9A
Bloque tipo $17: 8C 8D 8C 8D
Bloque tipo $18: 87 8A 8C 99
Bloque tipo $19: 8A 8B 98 8D
Bloque tipo $1A: 8C 89 97 9A
Bloque tipo $1B: 88 8D 9A 9B
Bloque tipo $1C: 11 11 11 11
Bloque tipo $1D: 01 01 01 01
Bloque tipo $1E: 10 10 10 10
…
Si nos fijamos en la primera línea del nivel nos encontramos según estos datos los valores:
1E 1E 1E 1E 1E 1E 1E 1E 0D 1E
Encontramos sólo 2 tipos de bloques ($1E y $0D) para dibujar esta línea.
Cada línea tiene 10 tiles y hay 8 líneas para dibujar el nivel lo que hace un total de 80 bytes o
$50 en hexadecimal.
Estos datos de la primera línea del nivel la encontramos en las direcciones $02:4C2F-$02:4C38
($8C2F-$8C38).
El nivel 1 se encuentra en $02:4C2F-$02:4C7E y los del nivel 2 deben estar en $02:4C7F$02:4CCE por lo que podemos buscar el valor 2F4C7F4C en el juego con el editor hexadecimal.
Esto lo encontramos en las direcciones $02:4945-$02:4948 ($8945-$8948) donde están los
punteros pero antes parece que hay punteros que empiezan en $893F.
Con esto ya hemos localizado lo que buscabamos.
MR. DO!
En este juego debemos coger las cerezas que hay en el nivel para pasar al siguiente. En el
primer nivel encontramos 5 bloques de 8 cerezas cada uno que se muestran en la pantalla.
Para dibujar la cereza se utilizan los tiles 08 09 0A 0B.
Poniendo un breakpoint en la dirección $9840 cuando se escribe el valor $08 que corresponde
a la primera cereza de la parte superior izquierda comprobamos como se para el emulador en
la dirección $00:2C1A:
ROM0:2C10 C5
push bc
ROM0:2C11 D5
push de
ROM0:2C12 6B
ld l,e
ROM0:2C13 62
ld h,d
ROM0:2C14 CD E6 2D
ROM0:2C17 06 08
call 2DE6
ld b,08
ROM0:2C19 0A
ld a,(bc)
ROM0:2C1A 22
ldi (hl),a
ROM0:2C1B 0C
inc c
ROM0:2C1C 0A
ld a,(bc)
ROM0:2C1D 22
ldi (hl),a
ROM0:2C1E 0C
inc c
ROM0:2C1F 7D
ROM0:2C20 C6 1E
ld a,l
add a,1E
…
En el registro “bc” se carga la dirección donde están los datos para dibujar la cereza y se ponen
los datos en la dirección contenida en el registro “hl” (Map Address donde se pone en
pantalla).
El registro “bc” contiene la dirección $00:0840 que es donde están los datos para dibujar la
cereza.
Sin embargo, si ponemos un breakpoint cuando se lee en la dirección $00:0840 no
encontramos la dirección donde pueden estar los datos del nivel.
Para lo cual, vamos a intentar hacerlo de otra forma buscando la dirección de la RAM donde se
guarda el número de nivel en la RAM.
Para hacerlo algo más fácil podemos buscar primero el número de cerezas que quedan en el
nivel que inicialmente son 40 y que se guarda en la dirección $D0AE.
Podemos introducir un cheat poniendo el valor $01 en la dirección $D0AE con lo que
quedarían sólo 1 cereza por coger para pasar de nivel independientemente de las que
realmente queden.
Activamos el cheat y lo desactivamos porque sino no disminuyen las cerezas que quedan en el
nivel. Cuando cojamos una cereza pasaremos al siguiente nivel.
Haciendo la búsqueda encontramos que en la dirección $D0AA se guarda en la RAM el número
de nivel ($00: MAP 1, $01: MAP 2,…).
Si introducimos un cheat poniendo el valor $05 en la dirección $D0AA empezaremos en el nivel
6 o MAP 06 aunque en pantalla ponga otro valor sin embargo la ubicación de las cerezas y los
elementos del nivel son los del 6 aunque ponga 1.
Valiéndonos del cheat para pasar de nivel podemos empezar en el primer nivel y una vez
iniciado activar y desactivar el cheat del número de cerezas poniendo $01.
Ahora ponemos un breakpoint cuando se lea la dirección $D0AA (Nivel) con lo que se parará el
emulador cuando se lea dicho valor.
El hacer esto es debido a que normalmente en los juegos de Gameboy se suele leer el valor del
número de nivel para según su valor cargar unos datos u otros
Por ejemplo, si estamos en el nivel 1 debe cargar los datos del nivel 1 y si es el 2 los datos del
nivel 2.
El emulador se para en la dirección $00:2BC0 donde se lee el número de nivel ($D0AA):
ROM0:2BB5 CD 72 2B
ROM0:2BB8 AF
ROM0:2BB9 21 00 CC
ROM0:2BBC 22
call 2B72
xor a
ld hl,CC00
ldi (hl),a
ROM0:2BBD 05
ROM0:2BBE 20 FC
ROM0:2BC0 FA AA D0
ROM0:2BC3 87
ROM0:2BC4 C6 B6
ROM0:2BC6 6F
ROM0:2BC7 26 08
dec b
jr nz,2BBC
ld a,(D0AA)
add a
add a,B6
ld l,a
ld h,08
ROM0:2BC9 2A
ldi a,(hl)
ROM0:2BCA 66
ld h,(hl)
ROM0:2BCB 6F
ld l,a
ROM0:2BCC 56
ld d,(hl)
ROM0:2BCD 23
inc hl
ROM0:2BCE 5E
ld e,(hl)
ROM0:2BCF 23
inc hl
ROM0:2BD0 CD 2F 2C
call 2C2F
ROM0:2BD3 CD 5C 2C
call 2C5C
…
A partir de $00:2BC0 vemos como se lee el número de nivel que en el caso del nivel 1 tiene el
valor $00 con lo que el registro “a”=$00.
La siguiente instrucción “add a” (add a,a) equivale a “a=a+a=$00 para el nivel 1 y en la
siguiente instrucción se le suma al valor que hay en el registro “a” el valor $B6 con lo que
“a=$B6” para el nivel 1.
En la siguiente instrucción “ld l,a” esto equivale a “l=a=$B6) y a continuación “ld h,08” que
equivale a “h=$08” por lo que el registro “hl” tiene el valor $00:08B6.
Las siguientes instrucciones sirven para cargar los datos que hay en la dirección $00:08B6 en
adelante que debe corresponder a un puntero.
Esto lo que hace a continuación es poner en el registro “de” la dirección o puntero que hay a
partir de la dirección $00:08B6.
En $00:08B6-$00:08B7 encontramos el valor 35 10 que corresponde a la dirección $00:1035
donde están los datos para dibujar el primer nivel.
En $00:08B8-$00:08B9 encontramos el valor 8E 10 que corresponde a la dirección $00:108E
donde están los datos para dibujar el nivel 2 y así sucesivamente.
En $00:1035 hay datos aunque todavía tendré que analizarlo para averiguar de lo que se trata
(posiblemente cuando haya otra revisión de este documento).
Más delante de esta información tenemos los datos para dibujar las cerezas, las rocas que
caen en el nivel 1 y el objeto que aparece en mitad del nivel:
(Nivel 1. Cerezas):
ROM0:1068 0C
ROM0:1069 02 (Y=$02)
ROM0:106A 00 (X=$00)
ROM0:106B 06 (Y=$06)
ROM0:106C 00 (X=$00)
ROM0:106D 06 (Y=$06)
ROM0:106E 0C (X=$0C)
ROM0:106F 06 (Y=$06)
ROM0:1070 10 (X=$10)
ROM0:1071 08 (Y=$08)
ROM0:1072 06 (X=$06)
ROM0:1073 0C (Y=$0C)
ROM0:1074 06 (X=$06)
ROM0:1075 12 (Y=$12)
ROM0:1076 00 (X=$00)
ROM0:1077 12 (Y=$12)
ROM0:1078 04 (X=$04)
ROM0:1079 0E (Y=$0E)
ROM0:107A 10 (X=$10)
ROM0:107B 12 (Y=$12)
ROM0:107C 10 (X=$10)
ROM0:107D FF
-----------------------------(Nivel 1. Rocas):
ROM0:107E 02 (Y=$02)
ROM0:107F 04 (X=$04)
ROM0:1080 06 (Y=$06)
ROM0:1081 08 (X=$08)
ROM0:1082 0C (Y=$0C)
ROM0:1083 04 (X=$04)
ROM0:1084 0A (Y=$04)
ROM0:1085 10 (X=$10)
ROM0:1086 08 (Y=$08)
ROM0:1087 14 (X=$14)
ROM0:1088 12 (Y=$12)
ROM0:1089 0E (X=$0E)
ROM0:108A FF
----------------------------------------(Nivel 1. Objeto):
ROM0:108B 00
ROM0:108C 0E (Y=$0E)
ROM0:108D 0A (X=$0A)
Como vemos se ponen las coordenadas de la parte superior de un bloque de cerezas, o la
parte superior de la roca o el objeto.
El valor $0C que aparece en $00:1068 todavía no se su significado.
Estas coordenadas corresponden al primer tile del grupo de 4 cerezas y que son las que
aparecen en la “BGB RAM Viewer”.
Si cambiamos estas coordenadas podemos conseguir que los grupos de cereza estén en
diferente posición en la pantalla.
Las coordenadas de las rocas y del objeto son similares que las cerezas sólo que en lugar de
dibujar 4 objetos sólo dibuja un elemento.
Por ejemplo, el grupo superior de la parte izquierda está en las coordenadas X,Y: 0,2
ROM0:1069 02 (Y=$02)
ROM0:106A 00 (X=$00)
Si cambiamos la coordenada X que aparece en la dirección $00:106A por $18 el nivel 1
quedaría tal y como se ve en la captura:
Igualmente podemos hacer con las rocas y el objeto que hay en el nivel.
Ya tenemos los punteros donde hay datos para dibujar cada uno de los niveles para que
podamos modificarlos.
Este juego tiene una forma diferente para poner objetos en la pantalla del juego a través de
coordenadas y los agrupa en una parte los datos de las cerezas, en otro las rocas y en otro el
objeto central.
BOMB JACK
En este juego los niveles están llenos de bombas y una serie de plataformas sobre las que
puedes ponerte evitando que te cojan los enemigos.
Podemos buscar donde se encuentra la información para poner cada una de las bombas que
hay en cada nivel y también para ubicar las plataformas que aparecen.
Poniendo un breakpoint en la dirección $9802 cuando se escribe el valor $C6 que corresponde
al tile de la parte superior de la bomba que está en la izquierda en lo más alto del nivel 1
podemos comprobar como el emulador se para en la dirección $00:13A4.
ROM0:13A1 E1
ROM0:13A2 F0 A8
pop hl
ld a,(ff00+A8)
ROM0:13A4 77
ld (hl),a
ROM0:13A5 C1
pop bc
ROM0:13A6 C9
ret
Aquí se carga el valor que hay en la dirección $FFA8 que tiene el valor $C6 y se pone en la
dirección contenida en “hl” que tiene el valor $9802.
Poniendo un breakpoint cuando se escriba el valor $C6 en la dirección $FFA8 se para el
emulador un poco más arriba en la dirección $00:1391.
ROM0:138E F0 A9
ROM0:1390 81
ROM0:1391 E0 A8
ld a,(ff00+A9)
add c
ld (ff00+A8),a
Poniendo un breakpoint en la dirección $FFA9 cuando se escriba en esta dirección.
Podemos ponerlo cuando estamos en la pantalla de menú y se para antes de cargar el nivel 1
con lo que se para en la dirección $00:136F pero un poco más arriba encontramos lo que
buscamos y que hace referencia a la dirección $00:1334 donde se pone en el registro “de” la
dirección $00:2231.
ROM0:1327 F0 A3
ld a,(ff00+A3)
ROM0:1329 26 00
ld h,00
ROM0:132B 6F
ld l,a
ROM0:132C 29
add hl,hl
ROM0:132D 29
add hl,hl
ROM0:132E 29
add hl,hl
ROM0:132F 29
add hl,hl
ROM0:1330 54
ld d,h
ROM0:1331 5D
ld e,l
ROM0:1332 29
add hl,hl
ROM0:1333 19
add hl,de
ROM0:1334 11 31 22
ld de,2231
ROM0:1337 19
add hl,de
ROM0:1338 54
ld d,h
ROM0:1339 5D
ld e,l
ROM0:133A 3E 18
ld a,18
ROM0:133C E0 A4
ld (ff00+A4),a
ROM0:133E E0 A5
ld (ff00+A5),a
En esta dirección $00:2231 están las coordenadas para situar cada una de las bombas del nivel
1.
Otra forma con la que podríamos haberlo hecho es buscando la dirección donde se pone el
número de bombas que quedan del nivel y que corresponde a la dirección de la RAM $FFA5.
Inicialmente el nivel 1 tiene 24 bombas o $18 hexadecimal con lo que si ponemos un
breakpoint cuando se escriba el valor $18 en la dirección $FFA5 el emulador se para en la
dirección $00:133E y en $00:133A encontramos como en esta instrucción se pone el número
de bombas del nivel. Un poco más arriba en $00:1334 encontramos lo que buscamos:
ROM0:1334 11 31 22
ld de,2231
Esta forma puede ser más rápida que la anterior y más sencilla para localizar esta dirección.
Para el nivel 1 los datos de cada una de las 24 bombas son los siguientes:
(Datos situación bombas en los niveles):
(Nivel 1):
00:2231
0C 0A (Y=$0C, X=$0A)
00:2233
0C 0C (Y=$0C, X=$0C)
00:2235
0C 0E (Y=$0C, X=$0E)
00:2237
00 06 (Y=$00, X=$06)
00:2239
00 04 (Y=$00, X=$04)
00:223B
00 02 (Y=$00, X=$02)
00:223D
0C 00 (Y=$0C, X=$00)
00:223F
0A 00 (Y=$0A, X=$00)
00:2241
08 00 (Y=$08, X=$00)
00:2243
06 00 (Y=$06, X=$00)
00:2245
0C 10 (Y=$0C, X=$10)
00:2247
0A 10 (Y=$0A, X=$10)
00:2249
08 10 (Y=$08, X=$10)
00:224B
06 10 (Y=$06, X=$10)
00:224D
05 0E (Y=$05, X=$0E)
00:224F
05 0C (Y=$05, X=$0C)
00:2251
05 0A (Y=$05, X=$0A)
00:2253
05 08 (Y=$05, X=$08)
00:2255
00 10 (Y=$00, X=$10)
00:2257
00 0E (Y=$00, X=$0E)
00:2259
00 0C (Y=$00, X=$0C)
00:225B
10 02 (Y=$10, X=$02)
00:225D
10 04 (Y=$10, X=$04)
00:225F
10 06 (Y=$10, X=$06)
La primera bomba que aparece en la parte superior izquierda corresponde a los datos:
00:223B
00 02 (Y=$00, X=$02)
Si cambiamos el byte que hay en $00:223B poniendo $03 en lugar de $00 conseguimos que
cambie la posición de esta bomba tal y como se ve en la captura:
En $00:2261 comienzan los datos para situar las bombas del nivel 2. Cada nivel tiene 24
bombas lo que hace un total de 48 bytes para las coordenadas de las bombas o $30.
Por tanto las direcciones de cada uno de los niveles son:
00:2231 (Nivel 1)
00:2261 (Nivel 2)
00:2291 (Nivel 3)
00:22C1 (Nivel 4)
00:22F1 (Nivel 5)
00:2321 (Nivel 6)
00:2351 (Nivel 7)
00:2381 (Nivel 8)
00:23B1 (Nivel 9)
00:23E1 (Nivel 10)
00:2411 (Nivel 11)
00:2441 (Nivel 12)
Si nos situamos en la dirección $00:2471 encontramos unos punteros a datos:
(Punteros):
00:2471
89 24 (Address $00:2489: Datos rectángulos nivel 1)
00:2473
B3 24 (Address $00:24B3: Datos rectángulos nivel 2)
00:2475
E0 24 (Address $00:24E0: Datos rectángulos nivel 3)
00:2477
04 25 (Address $00:2504: Datos rectángulos nivel 4)
00:2479
21 25 (Address $00:2521: Datos rectángulos nivel 5. No tiene este nivel)
00:247B
22 25 (Address $00:2522: Datos rectángulos nivel 6)
00:247D
51 25 (Address $00:2551: Datos rectángulos nivel 7)
00:247F
80 25 (Address $00:2580: Datos rectángulos nivel 8)
00:2481
BD 25 (Address $00:25BD: Datos rectángulos nivel 9)
00:2483
FC 25 (Address $00:25FC: Datos rectángulos nivel 10. No tiene este nivel)
00:2485
FD 25 (Address $00:25FD: Datos rectángulos nivel 11)
00:2487
34 26 (Address $00:2634: Datos rectángulos nivel 12)
Si nos ponemos en la dirección $00:2489 encontramos los datos:
(Datos dibujar rectángulos donde el personaje puede posarse en el nivel 1):
00:2489
04 09 (Y=$04, X=$09)
00:248B
00
00:248C
95 96 96 96 97 (Tiles a dibujar)
00:2491
00
00:2492
06 03 (Y=$06, X=$03)
00:2494
00
00:2495
95 96 97
00:2498
00
00:2499
0A 07 (Y=$0A, X=$07)
00:249B
00
00:249C
95 96 96 96 97 (Tiles a dibujar)
00:24A1
00
00:24A2
0C 02 (Y=$0C, X=$02)
00:24A4
00
00:24A5
95 96 97
00:24A8
00
00:24A9
0E 0A (Y=$0E, X=$0A)
00:24AB
00
00:24AC
95 96 96 96 97 (Tiles a dibujar)
00:24B1
00
(Tiles a dibujar)
(Tiles a dibujar)
00:24B2
FF
(Fin de los datos)
Las primeras coordenadas corresponden a la plataforma donde puede posarse “Bomb Jack” y
que está en la parte superior del nivel.
También podríamos buscar donde están los datos para dibujar el fondo de la pirámide del nivel
1 para lo cual podemos poner un breakpoint en la dirección $98E4 cuando se escribe el valor
$01 con lo que el emulador se para en la dirección $00:13D9.
ROM0:13D1 11 00 98
ld de,9800
ROM0:13D4 06 12
ld b,12
ROM0:13D6 0E 11
ld c,11
ROM0:13D8 2A
ldi a,(hl)
ROM0:13D9 12
ld (de),a
ROM0:13DA 1C
inc e
ROM0:13DB 0D
dec c
ROM0:13DC 20 FA
ROM0:13DE 7B
ROM0:13DF C6 0F
jr nz,13D8
ld a,e
add a,0F
ROM0:13E1 5F
ld e,a
ROM0:13E2 8A
adc d
ROM0:13E3 93
sub e
ROM0:13E4 57
ld d,a
ROM0:13E5 05
dec b
ROM0:13E6 20 EE
ROM0:13E8 C9
jr nz,13D6
ret
Aquí encontramos que el registro “hl” tiene el valor $2B89 cuando se para el emulador.
ROM0:13D4 06 12
ld b,12
ROM0:13D6 0E 11
ld c,11
En el registro “b” se pone el valor $12=18 líneas y en “c” el valor$11=17=Número de tiles de
cada línea.
La pantalla tiene líneas de 20 tiles pero los 3 últimos tiles de cada línea se utilizan para poner la
barra vertical con el número de vidas y puntuación.
ROM0:13DF C6 0F
add a,0F
Se utiliza para incrementar la dirección de la Map Address para pasar a la siguiente línea. Por
ejemplo, en la primera línea acaba en $9810 pero se incrementará hasta $9811 y al sumarle
$0F nos da la dirección $9820=Map Address donde empieza a dibujar en la segunda línea y así
igual con las otras.
En “hl” estaba la dirección $00:2B89 (en $00:2B88 está el valor $01 que lee y pone en la
dirección $98E4). Sin embargo, la pantalla empieza en $9800 a dibujarse poniendo el valor $00
con lo que si ponemos un breakpoint en la dirección $9800 cuando se pone el valor $00
podemos comprobar cuando se para.
Para hacer esto reiniciamos el juego para hacer la búsqueda. El emulador se puede parar varias
veces pero la que nos interesa ocurre después de mostrar la pantalla del menú principal una
vez que le damos para empezar el nivel.
La dirección que nos interesa es la misma que antes $00:13D9 que es donde se paraba antes
pero ahora en “hl” tenemos $00:2B0E (que restando 1 byte nos da $00:2B0D que es donde
empiezan los datos para dibujar el fondo).
(Fondo pirámide):
00:2B0D
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
(Línea 1)
00:2B1E
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
(Línea 2)
00:2B2F
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
(Línea 3)
00:2B40
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
(Línea 4)
00:2B51
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
(Línea 5)
00:2B62
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
(Línea 6)
00:2B73
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
(Línea 7)
00:2B84
00 00 00 00 01 02 00 00 00 00 00 00 00 00 00 00 00
(Línea 8)
00:2B95
00 00 00 03 04 05 02 00 00 00 00 00 00 06 07 00 00
(Línea 9)
00:2BA6
00 00 08 09 0A 0B 0C 0D 00 00 00 0E 0F 10 11 02 00
(Línea 10)
00:2BB7
00 12 13 14 15 16 17 18 19 00 1A 1B 1C 1D 1E 1F 20
(Línea 11)
00:2BC8
21 22 23 24 25 26 27 28 29 19 2A 2B 2C 2D 2E 2F 30
(Línea 12)
00:2BD9
31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 41
(Línea 13)
00:2BEA
42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52
(Línea 14)
00:2BFB
53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63
(Línea 15)
00:2C0C
64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 62 62 72
(Línea 16)
00:2C1D
73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F 80 81 82 83
(Línea 17)
00:2C2E
84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90 91 92 93 94
(Línea 18)
Con el editor hexadecimal podemos encontrar estos datos para dibujar el fondo entero.
Los datos para dibujar el fondo del nivel 2 se encuentra en $00:2EC7-$00:2FF8 ($132=306
bytes=18 líneas*17 tiles por línea), los del nivel 3 (castillo) se encuentra en $00:33E9-$00:351A
, los de los niveles 5 y 10 en $00:3C7D-$00:3DAE.
Si buscamos la dirección donde está el fondo del nivel 1 ($2B0D) en un editor hexademal
(0D2B) lo encontramos en la dirección $00:1E63 donde están los punteros a los diferentes
fondos:
00:1E63 0D 2B ($00:2B0D)
00:1E65 65 26 ($00:2665)
00:1E67 C7 2E ($00:2EC7)
00:1E69 3F 2C ($00:2C3F)
00:1E6B E9 33 ($00:33E9)
00:1E6D F9 2F ($00:2FF9)
00:1E6F 1B 38 ($00:381B)
00:1E71 1B 35 ($00:351B)
00:1E73 7D 3C ($00:3C7D)
00:1E75 4D 39 ($00:394D)
Igualmente podemos encontrar los datos para dibujar la barra vertical donde aparecen las
vidas y la puntuación por lo que podemos poner un breakpoint en la dirección $9811 cuando
se escribe el valor $DC con lo que el emulador se para en la dirección $00:12DB.
ROM0:12CE 21 8F 3F
ld hl,3F8F
ROM0:12D1 11 11 98
ld de,9811
ROM0:12D4 06 12
ld b,12
ROM0:12D6 0E 03
ld c,03
ROM0:12D8 2A
ROM0:12D9 C6 DC
ldi a,(hl)
add a,DC
ROM0:12DB 12
ld (de),a
ROM0:12DC 1C
inc e
ROM0:12DD 0D
dec c
ROM0:12DE 20 F8
jr nz,12D8
ROM0:12E0 7B
ROM0:12E1 C6 1D
ld a,e
add a,1D
ROM0:12E3 5F
ld e,a
ROM0:12E4 8A
adc d
ROM0:12E5 93
sub e
ROM0:12E6 57
ld d,a
ROM0:12E7 05
dec b
ROM0:12E8 20 EC
ROM0:12EA C9
jr nz,12D6
ret
En el registro “hl” se pone la dirección $00:3F8F donde están los datos para dibujar esta línea ,
en “de” la dirección $9811=Map Address donde empieza a dibujar en pantalla, en “b” el valor
$12=18 líneas y en “c” el valor $03=Nº de tiles por línea.
ROM0:12D9 C6 DC
add a,DC
Se añade al registro “a” el valor $DC para poner el tile correspondiente.
(Datos dibujar parte derecha en el juego con vidas, puntuación,...):
00:3F8F
00 01 02
(Línea 1)
00:3F92
03 04 05
(Línea 2)
00:3F95
06 07 08
(Línea 3)
00:3F98
09 0A 0B
(Línea 4)
00:3F9B
0C 0D 0E
(Línea 5)
00:3F9E
0F 10 11
(Línea 6)
00:3FA1
12 13 14
(Línea 7)
00:3FA4
15 16 17
(Línea 8)
00:3FA7
18 19 1A
(Línea 9)
00:3FAA
1B 1C 1D
(Línea 10)
00:3FAD
0C 0D 0E
(Línea 11)
00:3FB0
09 0A 0B
(Línea 12)
00:3FB3
09 0A 0B
(Línea 13)
00:3FB6
09 0A 0B
(Línea 14)
00:3FB9
09 0A 0B
(Línea 15)
00:3FBC
09 0A 0B
(Línea 16)
00:3FBF
09 0A 0B
(Línea 17)
00:3FC2
09 0A 0B
(Línea 18)
Así la línea 1 tiene los valores:
00:3F8F00 01 02
(Línea 1)
Que corresponden a los tiles DC DD DE (se le suma $DC para poner el tile correspondiente en
pantalla).
PAC-ATTACK
Este juego tiene la opción de “PUZZLE MODE” donde podemos jugar a 100 niveles del juego y
que empiezan a partir de la dirección $03:400E o $C00E (nivel 0 sin nada en el nivel).
El primer nivel del juego empieza en $03:405C o $C05C, el nivel 2 en $C0AA, nivel 3 en
$C0F8,…
Cada nivel está formado por 13 líneas de 6 tiles cada uno lo que hace un total de 78 bytes o
$4E.
Como el nivel 0 empieza en la dirección $C05C podemos hallar una fórmula para obtener
donde empiezan estos datos que sería:
$C00E+$4E*Nº de nivel
Donde el número de nivel va a valer 1-100.
En los datos del nivel encontramos varios valores para los objetos que aparecen en el nivel:
$13: Ladrillo del fondo o sin ladrillo
$80: Fantasma
$BC: Ladrillo normal
$BD:
$BF: Ladrillo sólido
El tile $13 corresponde al ladrillo del fondo y el tile $80-$B7 corresponden a varios tiles del
fantasma para hacer la animación de éste.
Por ejemplo, para el nivel 1 los datos son:
(Nivel 1):
03:405C 13 13 13 13 13 13
03:4062 13 13 13 13 13 13
03:4068 13 13 13 13 13 13
03:406E 13 13 13 13 13 13
03:4074 13 13 13 13 13 13
03:407A 13 13 13 13 13 13
03:4080 13 13 13 13 13 13
03:4086 13 13 13 13 13 13
03:408C 13 BC BC BC 13 13
03:4092 80 80 80 80 13 13
03:4098 80 BC BC BC BC 13
03:409E 80 BC BC BC BC BC
03:40A4 80 BC BC BC BC BC
Si cambiáramos los bytes en $03:408D-$03:408F que tienen el valor $BC por $BF cambiamos el
ladrillo normal de esta línea por un ladrillo sólido tal y como se ve en la imagen:
Hace tiempo hice una utilidad escrita en Visual Basic 6 que permite leer cada uno de los 100
niveles y mostrarlos aunque no permite modificarlo. Esto es una tarea pendiente que se podría
hacer para tener nuestros propios niveles del juego.
OBTENCIÓN INFORMACIÓN HACKS
Una forma de obtener información en juegos también puede ser la ingeniería inversa o a
través de hacks creados tales como se pueden encontrar en la página “Romhacking.net” donde
hay una gran cantidad de ficheros .ips (parches) de traducciones de juegos, modificaciones
gráficas, de los niveles,…
Podemos utilizar por ejemplo, la traducción de un juego del japonés al inglés para a partir de
éste traducirlo a español de una forma más fácil.
Esto es una buena forma si no conocemos el japonés pero debemos tener en cuenta que
cualquier mala traducción que se haya hecho por parte del otro traductor nos influirá en
nuestra traducción.
La captura que aparece es una traducción del juego “DOUBLE DRAGON II” de Gameboy
realizada por Tanero y la del juego original.
Tal y como se ve se han cambiado los textos y el nombre del juego se ha traducido también.
Si no nos convenciera algún texto se podría cambiar como la última parte “AGAIN BILLY AND
JIMMY WERE READY TO TRAIN THEIR MARTIAL ARTS.”.
Podríamos poner “DE NUEVO BILLY Y JIMMY ESTAN PREPARADOS PARA ENTRENARSE EN
ARTES MARCIALES.”
Si no conociéramos donde está localizado el texto podríamos coger el parche .IPS y abrirlo con
la utilidad “IPS PEEK” para ver información de éste.
Como se ve en la captura el fichero del parche ocupa 2153 bytes de los cuales 1748 bytes
corresponden a bytes que se modifican en el juego.
Hay 102 parches y el que aparece marcado es el 91 de 104. La primera fila corresponde al ID y
la última EOF corresponde al final del fichero con lo que quedan 102 filas.
En azul viene marcado la dirección inicial donde está el texto ($01BE1A que corresponde a
$07:7E1A) y la final del texto ($01BE2C que corresponde a $07:7E2C).
El texto ocupa 19 bytes y el tamaño del texto en el parche es de 24 bytes.
Si abrimos el fichero .IPS con un editor hexadecimal antes del texto vemos antes del texto 3
bytes con la dirección donde se va a escribir el texto (01 BE 1A) y a continuación 2 bytes con el
número de bytes a escribir (00 13 que es $0013=19 decimal).
Con esto ya localizamos fácilmente los textos sin tener que complicarnos mucho aunque en
este caso el texto se encuentra fácil ya que están guardados tal y como aparecen en la pantalla
en el juego.
El texto que pretendíamos poner tiene el problema que es demasiado largo y deberíamos
localizar como se cargan los textos y si hay punteros que carguen los textos.
Es por eso que para la traducción “Tanero” ha utilizado textos con la misma longitud que los
originales.
Habría que analizar el juego con detenimiento pero ello requiere tiempo y no lo he tenido a la
hora de hacerlo por lo que esto se lo dejo como trabajo si fuera posible ya que tal vez requiera
buscar espacio adicional para el texto.
En este caso ya existía la traducción al español del juego pero muchas veces no lo estará y tal
vez lo encontremos en inglés.
OBTENCIÓN DE INFORMACIÓN CON EDITORES DE NIVEL
Otra forma de obtener información es con editores de niveles que haya de un determinado
juego aunque esto es solo para entenderlo ya que no será necesario para modificarlos
manualmente ya que contamos con estas herramientas para hacerlo.
Por ejemplo, el juego “Snoopy Magic Show” cuenta con un editor para modificar cada uno de
los niveles del juego llamado “c”.
Si cambiamos la posición de la bola que en el juego es (4,0) tal y como se ve en el editor (“Ball
1´s Start position (in tiles)”) por (5,1) podemos buscar con la ayuda del editor hexadecimal HxD
valores que hayan cambiado con respecto al original para lo cual previamente he hecho un
fichero copia del original para poder compararlo utilizando “Análisis”-“Comparar archivos”“Comparar…”.
Como se observa ha cambiado el valor en $7901 y en $7902 ($01:7901-$01:7902) que
corresponden a las coordenadas iniciales del nivel 1 de la bola.
El byte en $7903 tiene el valor $00 pero si se selecciona en el editor “2Balls” en lugar de
“1Ball” cambia a $60 por lo que $00: 1Ball y $60:2Balls.
Los bytes $7904 (Coordenada X) y $7905 (Coordenada Y) son las coordenadas de esa segunda
bola.
Los bytes $7907 (Coordenada X) y $7908 (Coordenada Y) de Snoopy en el nivel 1.
El byte $7909 tiene el valor $23 (35) que corresponde a “In-Level Music”.
El byte $790B (Coordenada X) y $790C (Coordenada Y) del powerup en el nivel 1 que
inicialmente está en (4,4).
Cada nivel tiene $0E bytes para poner las coordenadas de la bola, Snoopy,.. que va desde la
dirección $7900-$7C47 ($348=840 bytes).
Igual podemos hacer cambiando los tiles que aparecen en el nivel para cambiar el nivel 1 que
como se ve en la captura está formado por 8 líneas con 9 tiles cada una que hacen un total de
72 tiles que se almacenan en 72 bytes.
Con el botón izquierdo podemos cambiar el tile y con el botón derecho del ratón obtenemos
información de cada uno de los tiles que forman el nivel.
Con lo que podemos ver que el nivel 1 está formado por los datos siguientes:
60AD ($01:60AD):10 00 00 00 00 00 00 00 10
60B6 ($01:60B6):00 21 00 00 00 00 00 21 00
60BF ($01:60BF):00 00 00 00 22 00 00 00 00
60C8 ($01:60C8):00 00 00 0A 22 00 00 00 00
60D1 ($01:60D1):00 00 00 00 22 08 00 00 00
60DA ($01:60DA):00 00 00 00 22 00 00 00 00
60E3 ($01:60E3):00 21 00 00 00 00 00 21 00
60EC ($01:60EC):10 00 00 00 00 00 00 00 10
Con esto ya hemos averiguado donde se encuentran los datos del nivel 1. A continuación
encontramos los datos del nivel 2 que se encuentran en $60F5-$613C.
Los 60 niveles van desde la dirección $60AD-$718C ($10E0=4320 bytes).
Si buscamos el valor $AD60 con el editor hexadecimal lo encontramos en $4BAA-$4BAB donde
encontramos lo siguiente:
ROM1:4BA0 FA 6E C3
ld a,(C36E)
ROM1:4BA3 FE 3C
cp a,3C
ROM1:4BA5 38 02
jr c,4BA9
ROM1:4BA7 D6 3C
sub a,3C
ROM1:4BA9 21 AD 60
ld hl,60AD
ROM1:4BAC FE 00
cp a,00
ROM1:4BAE 28 0B
jr z,4BBB
ROM1:4BB0 47
ld b,a
ROM1:4BB1 11 48 00
ld de,0048
ROM1:4BB4 19
add hl,de
ROM1:4BB5 05
dec b
ROM1:4BB6 78
ld a,b
ROM1:4BB7 FE 00
cp a,00
ROM1:4BB9 20 F6
jr nz,4BB1
ROM1:4BBB 7C
ld a,h
ROM1:4BBC 57
ld d,a
ROM1:4BBD 7D
ld a,l
ROM1:4BBE 5F
ld e,a
ROM1:4BBF C9
ret
Con la instrucción “ld hl,60AD” se pone en el registro “hl” la dirección donde empiezan los
datos de los niveles y en el registro “de” el valor $0048 (72) que es el número de bytes que
ocupa cada nivel.
En el registro “a” se carga el valor que hay en la dirección $C36E que contiene el número de
nivel con lo que según el nivel se calcula la dirección donde están los datos.
Se compara el valor del nivel con $3C (60) ya que el juego tiene 60 niveles desde $00 (Nivel 1)
hasta $3B ($3B).
Los datos para dibujar los objetos 2*2 que dibujan en nivel se encuentran en la dirección
$3CFE-$3DA9 para los objetos $00-$26 (0-42).
El editor muestra tiles 43-55 pero que no corresponden a objetos sino a código fuente. Estos
no se pueden poner en el nivel.
Esto puede ser un fallo del editor y no debemos utilizarlos para modificar los niveles del juego.
Los tiles $43-$55 empezarían en $00:3DAA pero ahí encontramos:
ROM0:3DAA CD 22 26
ROM0:3DAD AF
call 2622
xor a
ROM0:3DAE EA 9F C3
….
COMPRESIÓN DE DATOS
Introducción
ld (C39F),a
La compresión de los datos que aparecen en juegos es uno de los aspectos que nos pueden
complicar la vida a la hora de modificarlos ya que a veces no hay herramientas específicas para
ese tipo de compresión y tendremos que averiguar como funciona
Una vez averiguado deberemos descomprimirlos los datos, modificarlos y volver a
comprimirlos con la consecuente dificultad que ello conlleva. Además también puede ocurrir
que los datos modificados al comprimirlos ocupen más o menos bytes que los originales.
También va a depender de la forma como se carguen los datos si está en forma de punteros a
estos y con el tamaño comprimido,…
También podríamos crear una rutina propia para descomprimir datos con algún algoritmo
creado por nosotros para insertar en algún juego de Gameboy aunque esto ya es complicado
por sí ya que requiere conocer en profundidad el juego que se trate.
Compresiones simples
Una compresión simple se puede basar en el número de elementos diferentes de que se
disponga. Por ejemplo, si tenemos las letras del abecedario, más el espacio en blanco:
ABCDEFGHIJKLMNÑOPQRSTUVWXYZ
Aquí tenemos 28 símbolos distintos aunque podríamos ampliarlos hasta 32 por ejemplo.
Utilizando 1 byte por ejemplo para representar los datos que está formado por 8 bits
podríamos comprimir un texto con caracteres que se repiten:
00000000: Corresponde a $00 hexadecimal (1 byte).
Si a cada letra le asignamos un valor desde 00 a 27. Por ejemplo:
A=0, B=1, C=2,…, Espacio en blanco=27.
La letra “C” tiene el valor 2 que en binario es 10. Para escribir el máximo valor que es 31
(11111 en binario) se necesitan 5 bits y un byte tiene 8 con lo que nos quedan 3 bits.
Podemos utilizar 3 bits para indicar el número de veces que se repite la letra y 5 bits para
indicar la letra. En nuestro caso hemos utilizado una cantidad fija para representar cada letra
pero en algunos algoritmos más avanzados utilizan longitud variable de cada carácter
utilizando para ello la frecuencia con la que se repiten los valores utilizando menos bits para
los valores que se repiten más con la consecuente mayor compresión de los datos.
Si tenemos el texto:
“CCCCCDDD FG” podríamos codificarlo en binario como:
(100 00010) (000 00011) (000 11011) (000 00101) (000 00110)
Se utilizarán 5 bytes para codificar el texto que tiene 11 bytes con lo que se ha reducido
bastante. Esto es debido a que se repiten elementos pero si no se repitieran muchos datos no
se conseguiría mucha compresión.
El primer byte comprimido: 100 00010 equivale a 4 los 3 primeros bits que podemos
incrementar en una unidad para tener 5. Los siguientes 5 bits corresponden a la letra que tiene
el valor 2 en decimal que corresponde a “C” con lo que se ponen 5 veces la letra “C”.
El incrementar una unidad el valor de los 3 primeros bits es que 000 (0) correspondería a 0
veces la letra lo que no tiene sentido pero sí lo tendría si correspondiese a 1.
Podríamos crear un algoritmo que lea estos datos, los descomprima y ponga el texto en
pantalla.
Con este tipo de compresión se utilizaría más o menos bits dependiendo del número de
elementos distintos que haya.
Por ejemplo, podría haber compresión de este tipo 3+5 bits, 2+6 bits, 1+7 bits, 4+4 bits,…
Otra utilidad puede ser para comprimir los datos de los niveles para que ocupen menos. Por
ejemplo, en el juego “Battle Bull” que vimos había una serie de bloques para dibujar el nivel y
es límitado la cantidad de bloques distintos.
Suponiendo que hay menos de 16 bloques distintos se necesitan 4 bits para cada bloque desde
0-15 (0000=0, 1111=15).
Podríamos utilizar 4 bits para los bloques y otros 4 para el número de veces que aparece el
bloque.
La primera línea del nivel 1 es: 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
16 veces el bloque 1 lo que se puede codificar como 1111 0001 (15+1 veces el bloque 1) con lo
que habríamos utilizado 1 byte para poner 16 bloques con el consiguiente ahorro de bytes.
La segunda línea del nivel 1 es: 01 09 09 09 09 09 09 09 09 09 09 00 09 09 09 01
Si se hubiesen utilizado 5 bits para representar el número de bloques se podría haber cogido el
primer bloque de la segunda línea que vale también 1 con lo que se pondría 17 veces el bloque
1 (16 en la primera línea y 1 al inicio de la segunda línea).
La segunda línea se codificaría como: (0000 0001) (1001 1001) (0000 0000) (0010 1001) (0000
0001)
Utilizaríamos 5 bytes para representar los 16 bloques del nivel con lo que se ha comprimido
también bastante.
Sería 1 vez el bloque tipo 1, 10 veces el bloque tipo 9, 1 vez el bloque tipo 0, 3 veces el bloque
tipo 9 y 1 vez el bloque tipo 1.
El algoritmo de compresión debe ser más o menos rápido o se ralentaría algunos procesos que
tienen lugar en el juego. Por ejemplo, si los tiles del juego están comprimidos si se utilizara un
algoritmo muy complejo y que requiere muchos ciclos del procesador de la Gameboy se
tardaría en descomprimirlos y en mostrar los gráficos del juego,…
También se tiene que tener en cuenta si merece la pena la compresión porque puede ser
mínima y requiera un algoritmo complejo. Si hay espacio suficiente en la ROM no hace falta
comprimir datos.
Datos consecutivos
A veces a la hora de dibujar objetos en pantalla cuando los tiles que aparecen son consecutivos
se puede utilizar una rutina para ello con lo que no hace falta cargar datos para ello.
Por ejemplo: Supongamos, que dibujamos un personaje que está formado por 5 líneas de 4
tiles con los valores a partir de la dirección $99A0:
Línea 1: 5D, 5E, 5F, 60
Línea 2: 61, 62, 63, 64
Línea 3: 65, 66, 67, 68
Línea 4: 69, 6A, 6B, 6C
Línea 5: 6D, 6E, 6F, 70
Una forma podría ser poner estos datos en el juego y cargarlos o bien utilizar una rutina que
dibuje el objeto sin tener que aparecer ningún dato.
Podríamos utilizar la siguiente rutina para dibujar dicho objeto en pantalla:
3E 5D
ld a,5D
11 1C 00
ld de,001C
21 A0 99
ld hl,99A0
06 05
ld b,05
Salto:
0E 04
ld c,04
Salto1:
22
ldi (hl),a
3C
inc a
0D
dec c
20 FB
jr nz,Salto1
19
add hl,de
05
dec b
20 F5
jr nz,Salto
Esta rutina pone en el registro “a” el valor inicial que en este caso es $5D, en el registro “de” se
pone la cantidad que hay que sumar a la Map Address que se pone en el registro “hl” para
pasar a la siguiente línea.
En el registro “b” se pone el número de líneas y en “c” el número de tiles que tiene cada línea.
Por regla, como entre línea y línea en pantalla hay $20 se debe cumplir que el valor que hay en
el registro “de” ($001C) más el que hay en el registro “c” ($04) debe ser igual a esa cantidad
($20).
La instrucción “ldi (hl),a” se encarga de poner el tile en pantalla e incrementar la dirección que
hay en el registro “hl”, “inc a” incrementa el tile a poner en la siguiente dirección en pantalla,
“dec b” y “dec c” disminuyen b y c para disminuir el número de líneas que quedan y el número
de tiles que quedan de la línea actual.
“add hl,de” añade a la dirección que hay contenida en “hl” la dirección contenida en “de” para
pasar a la siguiente línea.
Este fragmento de código ocupa ($15=21 bytes) que es menos de los 20 bytes+fragmento de
código que ocuparía una rutina de carga de datos desde la ROM para ponerla en pantalla.
Una rutina de este tipo podemos encontrarla en el juego “Kid Dracula” para dibujar el logotipo
de KONAMI a partir de la dirección $9943 (3 líneas de 7 tiles cada una):
ROM0:166F 21 43 99
ld hl,9943
ROM0:1672 3E 01
ld a,01
ROM0:1674 0E 03
ld c,03
ROM0:1676 11 12 00
ROM0:1679 06 07
ROM0:167B CD CE 24
ld de,0012
ld b,07
call 24CE
ROM0:167E 22
ldi (hl),a
ROM0:167F 3C
inc a
ROM0:1680 22
ldi (hl),a
ROM0:1681 3C
inc a
ROM0:1682 05
dec b
ROM0:1683 20 F6
jr nz,167B
ROM0:1685 19
add hl,de
ROM0:1686 0D
dec c
ROM0:1687 20 F0
jr nz,1679
Datos repetidos
Algunos juegos de Gameboy utilizan para comprimir tiles escribir 2 veces cada valor como
ocurre en los que corresponden a letras.
1 tile de Gameboy ocupa 16 bytes y utilizando esta compresión se utilizarían sólo 8 bytes
ocupando la mitad de bytes.
En el juego “Flintstones, The - King Rock Treasure Island” tiene tiles comprimidos que
corresponden a las letras y números. Por ejemplo el tile $80 que corresponde al número “0” se
escribe como:
00 00 3C 3C 66 66 66 66 66 66 66 66 3C 3C 00 00
En el juego este tile está almacenado como:
07:6772
38 4C C6 C6 C6 64 38 00
Esto es utilizable sólo para algunos tiles y no siempre se puede hacer (sólo cuando se repitan 2
veces todos los bytes que componen el tile).
Si se pone en "de" la dirección donde están los datos de los tiles en la ROM, en "hl" la dirección
donde se escribe el tile (Tile Address), en "c" el valor $08 (número de bytes que ocupa el tile en
la ROM que se ampliará a los 16 que ocupa un tile).
La rutina que carga los tiles se encuentra en:
ROM0:0263 06 01
ld b,01
ROM0:0265 0E 08
ld c,08
ROM0:0267 1A
ld a,(de)
ROM0:0268 13
inc de
ROM0:0269 22
ldi (hl),a
ROM0:026A 22
ldi (hl),a
ROM0:026B 0D
dec c
ROM0:026C 20 F9
ROM0:026E 05
jr nz,0267
dec b
ROM0:026F 20 F4
ROM0:0271 C9
jr nz,0265
ret
Las 2 instrucciones “ldi (hl),a” lo que hacen es precisamente escribir 2 veces el valor en la
dirección de la Tile Address.
Otros juegos que utilizan esta compresión para tiles en Gameboy son:
-
Altered Space
-
Amazin Penguin
-
Astérix
-
Castlevania II: Belmont´s Revenge
En el juego “Boomer´s Adventure in Asmik World” los tiles de las letras está comprimidos. Los
datos de los tiles están en la dirección $02:4F80 y se ponen en la dirección $9500=Tile Address.
En el registro “bc” se pone el valor $0180 que es el número de bytes que ocupan los tiles
(número de bytes a leer/escribir).
Por ejemplo el tile $50 que corresponde al número “0” está formado por los bytes:
3C 00 66 00 66 00 6E 00 76 00 66 00 3C 00 00 00
Y en el juego (ROM) está almacenado como: 3C 66 66 6E 76 66 3C 00
Escribe el valor que aparece en la ROM y a continuación añade el valor $00 hasta completar los
16 bytes.
La rutina que se encarga de cargar los tiles comprimdos y ponerlos a partir de la dirección
$9500 es:
ROM0:01E2 21 00 95
ld hl,9500
ROM0:01E5 11 80 4F
ld de,4F80
ROM0:01E8 01 80 01
ld bc,0180
ROM0:01EB 1A
ld a,(de)
ROM0:01EC 22
ldi (hl),a
ROM0:01ED AF
xor a
ROM0:01EE 22
ldi (hl),a
ROM0:01EF 13
inc de
ROM0:01F0 0B
dec bc
ROM0:01F1 78
ld a,b
ROM0:01F2 B1
or c
ROM0:01F3 20 F6
jr nz,01EB
La instrucción “xor a” equivale a “a=$00” que es el valor que añade para completar los 16
bytes del tile.
“dec bc”: Disminuye bc=Nº de bytes que quedan por leer/escribir.
La rutina se repite hasta que queden bytes saltando a la dirección $00:01EB para leer/escribir
más bytes.
Compresión RLE
Este tipo de compresión utiliza códigos para indicar que se repite un determinado byte una
cantidad de veces y dependiendo del juego puede utilizar un determinado valor u otro para
ello.
Por ejemplo, el juego “3 Choume no Tama - Tama and Friends - 3 Choume Obake Panic!!”
utiliza este tipo de compresión para tiles de las letras en las que se repiten los valores.
El tile $00 que corresponde al número “0” está almacenado en $9000 como:
00 00 38 38 44 44 44 44 44 44 44 44 44 44 38 38 (16 bytes)
En el juego dichos valores están almacenados como:
05:5608
00 00 38 38 DD 44 0A 38 38 (9 bytes)
Los 4 primeros bytes aparecen tal como están en la ROM. Antes de estos bytes aparece el valor
$EE (que no se si realmente es algún código).
A continuación aparece el valor $DD que es un código de control que indica que el byte
siguiente se repite un número de veces.
44 0A es lo que aparece a continuación, con lo que se repite $0A (10 veces) el valor $44.
Luego viene 38 38 que se pone tal y como aparece.
El motivo por el que no se utiliza el código $DD para repetir los valores 00 00 y 38 38 es porque
ocuparía más ya que 00 00 (2 bytes) se pondría como DD 00 02 (3 bytes) y 38 38 (2 bytes)
como DD 38 02 (3 bytes).
Hay valores que están comprimidos y otros que no lo están.
Si ponemos un breakpoint en la dirección $9000 cuando se escribe el valor $00 para poner los
datos de los tiles que corresponden a las letras y que están comprimidos veremos como el
emulador se para en la dirección $00:10EF y con los valores de los registros: rom=5, “hl=5609”
(05:5609 aunque en realidad es $05:5608 donde empiezan los datos para el tile $00 (“0”),
“de=$9000” (Tile Address donde se guardan los tiles).
Aquí parece que se encuentra la rutina que se encarga de leer los datos de los tiles y
descomprimirlos en la Tile Address:
ROM0:108A 2A
ldi a,(hl)
ROM0:108B FE BB
cp a,BB
ROM0:108D 28 18
jr z,10A7
ROM0:108F FE CC
cp a,CC
ROM0:1091 28 18
jr z,10AB
ROM0:1093 FE DD
cp a,DD
ROM0:1095 28 19
jr z,10B0
ROM0:1097 FE AA
cp a,AA
ROM0:1099 28 1F
jr z,10BA
ROM0:109B FE EE
cp a,EE
ROM0:109D C8
ret z
ROM0:109E FE 99
cp a,99
ROM0:10A0 20 01
jr nz,10A3
ROM0:10A2 2A
ldi a,(hl)
ROM0:10A3 12
ld (de),a
ROM0:10A4 13
inc de
ROM0:10A5 18 E3
jr 108A
ROM0:10A7 AF
xor a
ROM0:10A8 4E
ld c,(hl)
ROM0:10A9 18 07
jr 10B2
ROM0:10AB 3E FF
ld a,FF
ROM0:10AD 4E
ROM0:10AE 18 02
ld c,(hl)
jr 10B2
ROM0:10B0 2A
ldi a,(hl)
ROM0:10B1 4E
ld c,(hl)
ROM0:10B2 12
ld (de),a
ROM0:10B3 13
inc de
ROM0:10B4 0D
dec c
ROM0:10B5 20 FB
ROM0:10B7 23
ROM0:10B8 18 D0
jr nz,10B2
inc hl
jr 108A
ROM0:10BA 2A
ldi a,(hl)
ROM0:10BB 4F
ld c,a
ROM0:10BC 2A
ldi a,(hl)
ROM0:10BD E0 92
ld (ff00+92),a
ROM0:10BF 2A
ldi a,(hl)
ROM0:10C0 47
ld b,a
ROM0:10C1 F0 92
ld a,(ff00+92)
ROM0:10C3 12
ld (de),a
ROM0:10C4 13
inc de
ROM0:10C5 78
ld a,b
ROM0:10C6 12
ld (de),a
ROM0:10C7 13
inc de
ROM0:10C8 0D
dec c
ROM0:10C9 20 F6
jr nz,10C1
ROM0:10CB 18 BD
jr 108A
ROM0:10CD 2A
ldi a,(hl)
ROM0:10CE FE BB
cp a,BB
ROM0:10D0 28 22
jr z,10F4
ROM0:10D2 FE CC
cp a,CC
ROM0:10D4 28 22
jr z,10F8
ROM0:10D6 FE DD
cp a,DD
ROM0:10D8 28 23
jr z,10FD
ROM0:10DA FE AA
cp a,AA
ROM0:10DC 28 33
jr z,1111
ROM0:10DE FE EE
cp a,EE
ROM0:10E0 C8
ret z
ROM0:10E1 FE 99
cp a,99
ROM0:10E3 20 01
jr nz,10E6
ROM0:10E5 2A
ldi a,(hl)
ROM0:10E6 47
ld b,a
ROM0:10E7 F3
di
ROM0:10E8 F0 41
ld a,(ff00+41)
ROM0:10EA E6 03
and a,03
ROM0:10EC 20 FA
jr nz,10E8
ROM0:10EE 78
ld a,b
ROM0:10EF 12
ld (de),a
ROM0:10F0 FB
ei
ROM0:10F1 13
inc de
ROM0:10F2 18 D9
jr 10CD
ROM0:10F4 AF
xor a
ROM0:10F5 4E
ld c,(hl)
ROM0:10F6 18 07
jr 10FF
ROM0:10F8 3E FF
ld a,FF
ROM0:10FA 4E
ROM0:10FB 18 02
ld c,(hl)
jr 10FF
ROM0:10FD 2A
ldi a,(hl)
ROM0:10FE 4E
ld c,(hl)
ROM0:10FF 47
ld b,a
ROM0:1100 F3
di
ROM0:1101 F0 41
ld a,(ff00+41)
ROM0:1103 E6 03
and a,03
ROM0:1105 20 FA
jr nz,1101
ROM0:1107 78
ld a,b
ROM0:1108 12
ld (de),a
ROM0:1109 FB
ei
ROM0:110A 13
inc de
ROM0:110B 0D
dec c
ROM0:110C 20 F1
ROM0:110E 23
ROM0:110F 18 BC
jr nz,10FF
inc hl
jr 10CD
ROM0:1111 2A
ldi a,(hl)
ROM0:1112 4F
ld c,a
ROM0:1113 2A
ldi a,(hl)
ROM0:1114 E0 92
ROM0:1116 2A
ROM0:1117 E0 93
ROM0:1119 F3
ROM0:111A F0 92
ROM0:111C 47
ld (ff00+92),a
ldi a,(hl)
ld (ff00+93),a
di
ld a,(ff00+92)
ld b,a
ROM0:111D F0 41
ld a,(ff00+41)
ROM0:111F E6 03
and a,03
ROM0:1121 20 FA
jr nz,111D
ROM0:1123 78
ld a,b
ROM0:1124 12
ld (de),a
ROM0:1125 FB
ei
ROM0:1126 13
inc de
ROM0:1127 F0 93
ROM0:1129 47
ld a,(ff00+93)
ld b,a
ROM0:112A F3
di
ROM0:112B F0 41
ld a,(ff00+41)
ROM0:112D E6 03
and a,03
ROM0:112F 20 FA
jr nz,112B
ROM0:1131 78
ld a,b
ROM0:1132 12
ld (de),a
ROM0:1133 FB
ei
ROM0:1134 13
inc de
ROM0:1135 0D
dec c
ROM0:1136 20 E1
jr nz,1119
ROM0:1138 18 93
jr 10CD
…
Según esto los valores que podrían ser de control son: 99 AA BB CC DD EE.
El código $DD ya lo hemos visto que repite el tile siguiente x veces.
El código $BB escribe x veces el valor $00.
Los tiles $00 y el $01 que aparecen en la pantalla de título se representan por los bytes:
00 00 00 00 0F 0F 1F 1F 1F 1F 1F 1F 0F 0F 00 00 (Tile $00)
00 00 00 00 80 80 E0 E0 FC FC FE FE FF FF 3F 3F (Tile $01)
Estos dos tiles se encuentran en la ROM como:
05:403C ($1403C) BB 04 0F 0F DD 1F 06 0F 0F BB 06 80 80 E0 E0 FC FC FE FE FF FF DD 3F 03
BB 04 (Escribe $04 veces el valor $00) 0F 0F
DD 1F 06 (Escribe $06 veces el valor $1F) 0F 0F
BB 06 (Escribe $06 veces el valor $00: 2 últimos bytes del tile $00 y 4 primeros bytes del tile
$01) 80 80 E0 E0 FC FC FE FE FF FF
DD 3F 03 (Escribe $03 veces el valor $3F: 2 últimos bytes del tile $01 y primer byte del tile
$02).
En este juego si buscamos la primera dirección donde encontrábamos tiles $05:5608 (0856) en
un editor hexadecimal lo encontramos en $05:4012-$05:4013 ($14012-$14013) y si buscamos
la dirección $05:403C donde están otros tiles del juego lo encontramos en $05:4000-$05:4001
($14000-$14001) por lo que aquí encontramos los punteros a tiles comprimidos del juego
(aunque no he comprobado si hay algú puntero a otro tipo de datos).
05:4000 ($14000): 3C 40
($05:403C)
05:4002 ($14002): FF 45
($05:45FF)
05:4004 ($14004): 4E 4F
($05:4F4E)
05:4006 ($14006): B5 4C
($05:4CB5)
05:4008 ($14008): 32 40
($05:4032)
05:400A ($1400A): 48 53
($05:5348)
05:400C ($1400C): DC 53
($05:53DC)
05:400E ($1400E): D6 51
($05:51D6)
05:4010 ($14010): B2 54
($05:54B2)
05:4012 ($14012): 08 56
($05:5608)
05:4014 ($14014): 6B 5C
($05:5C6B)
05:4016 ($14016): 55 63
($05:6355)
05:4018 ($14018): 54 65
($05:6554)
05:401A ($1401A): 28 66
($05:6628)
05:401C ($1401C): 2B 67
($05:672B)
05:401E ($1401E): 4E 68
($05:684E)
05:4020 ($14020): 77 6B
($05:6B77)
05:4022 ($14022): FE 6C
($05:6CFE)
05:4024 ($14024): FA 6D
($05:6DFA)
05:4026 ($14026): 5F 72
($05:725F)
05:4028 ($14028): 7A 73
($05:737A)
05:402A ($1402A): EA 79
($05:79EA)
05:402C ($1402C): FA 7A
($05:7AFA)
05:402E ($1402E): 59 7B
($05:7B59)
05:4030 ($14030): 26 7C
($05:7C26)
Compresión de textos DTE y MTE
Antes cuando hemos buscado textos en juegos de Gameboy nos hemos encontrado que un tile
corresponde a una letra pero no siempre ocurre así.
Podemos encontrar compresión DTE (Dual Tile Encoding) en algunos juegos que consiste en
que un byte corresponde a 2 tiles.
Por ejemplo, el valor $A0 puede corresponder al texto "GA", el valor $A1 puede corresponder
al texto "ME", el valor $A2 puede corresponder al texto "BO" y el valor $A3 corresponder al
texto "Y." con lo que la secuencia de bytes: A0 A1 A2 A2 corresponde al texto "GAMEBOY."
Se puede dar el caso de que algunos valores correspondan a 2 tiles y otros sólo a un tile.
La compresión MTE (Multi Tile Encoding) consiste en asignar a un byte una serie de tiles que
puede ser variable.
Por ejemplo: Si el valor $A0 corresponde al texto "GAMEBOY ", $A1 corresponde al texto
"ROMHACKING.".
La secuencia de bytes: A0 A1 corresponde al texto "GAMEBOY ROMHACKING.".
Esta compresión DTE y MTE no es específico de Gameboy sino que también se utiliza en otras
consolas.
Compresión RNC
Este tipo de compresión conocida como RNC o Rob Northen debido a su autor se encuentra en
algunos juegos de la consola Gameboy aunque también la podemos encontrar en otras
consolas tales como Atari Lynx, Megadrive, Super Nintendo y también para PC.
Para localizar datos comprimidos en un juego de Gameboy basta con buscar la cadena de texto
“RNC” tal y como aparece (con todo en mayúsculas).
Puede haber algún juego que por casualidad aparezca esta cadena pero para asegurarte que
utiliza la compresión deberá aparecer varias veces ya que los juegos que utilizan compresión
RNC suelen tener varios fragmentos de datos comprimidos.
Algunos de los juegos de Gameboy que utlizan esta compresión son:
-
Addams Family: Pugsley´s Scavenger Hunt
-
Alien Olimpics 2044 AD
-
Amazing Spider-Man 3, The - Invasion of the Spider-Slayers
-
Batman Forever
-
Bubsy 2
-
Bust-A-Move 4
-
Dennis, the Menace
-
Gremlins Unleashed (GBC)
-
Inspector Gadget - Operation Madkactus (GBC)
-
Jelly Boy
-
Jurassic Park 2: The Chaos Continues
-
Last Action Hero
-
Lucky Luke
-
Mortal Kombat
-
Mortal Kombat II
-
Mortal Kombat 3
-
Mr. Nutz
-
(The) Smurfs
-
(The) Smurfs 2
-
(The) Smurfs 3
-
Spirou
-
Tintin in Tibet
Los datos comprimidos en formato RNC tienen una cabecera de 18 bytes con la siguiente
estructura:
-
Texto “RNC” (3 bytes)
-
Método de compresión (1 byte): 1 o 2
-
Tamaño datos descomprimidos (4 bytes)
-
Tamaño datos comprimidos (4 bytes)
-
CRC datos descomprimidos (2 bytes): Checksum de los datos originales
-
CRC datos comprimidos (2 bytes): Checksum de los datos comprimidos
-
Leeway (1 byte): Diferencia entre datos comprimidos y descomprimidos en pack chunk
largos
-
Pack Chunks (1 byte): Cantidad de pack chunks.
Por ejemplo, en el juego “Jurassic Park 2: The Chaos Continues” encontramos datos
comprimidos en la direcciones:
$10000
$10201
$18843
$18F65
$1906D
$1922A
$192CC
$19AA8
$19C08
$19FA6
$1AEF1
$1B0A4
$1B601
$1B68E
$1B726
$1B7B8
$1B84F
$1B8E8
$1B981
$1BA0E
$1BA95
$1BB1D
$1BBA5
$1BC2C
$1BCB4
$1BD3C
$1BDC4
$24000
….
En la dirección $10000 (04:4000) encontramos:
$04:4000 52 4E 43
$04:4003 02
(“RNC”)
(Método de compresión 2)
$04:4004 00 00 03 00 (Tamaño datos descomprimidos: $00000300)
$04:4008 00 00 01 EF (Tamaño datos comprimidos: $000001EF)
$04:400C F9 3B (CRC Datos descomprimidos)
$04:400E 94 A0 (CRC Datos comprimidos)
$04:4010 04
$04:4011 01
(Pack chunk=$01)
Esto nos dice que los datos comprimidos ocupan $1EF bytes con lo que estarán desde la
dirección $04:4012-$04:4200.
Existe un programa de MS-DOS que permite descomprimir los datos en formato RNC por lo
que podemos coger los datos desde $04:4000-$04:4200 y guardarlos en un fichero (ej:
“jp2.rnc”).
El problema que surge al descomprimir es que necesitamos un ordenador con Windows XP o
utilizar el emulador DOSBOX para ejecutarlo en Windows 7 y posteriores pero no es
complicado.
Yo en mi caso tengo un ordenador con Windows 8 y el emulador DOSBOX que utilizo para
ejecutar aplicaciones antiguas de MS-DOS.
Tengo un fichero .bat que realiza el proceso y que contiene sólo una línea para descomprimir
los ficheros comprimidos en formato RNC:
PPIBM u jp2.rnc
Esto lo que hace es ejecutar el programa PPIBM con el modificador “u” para descomprimir los
datos y “jp2.rnc” es el fichero que vamos a descomprimir.
Para utilizar con el DOSBOX basta con situar en la misma carpeta el fichero .bat, los datos
comprimidos en formato RNC y los ficheros que contiene el “RNC_ProPack” que contiene los
programas para descomprimir (que incluyo junto a este manual).
Hay que arrastrar el fichero .bat al acceso directo del programa “DOSBOX” para que se ejecute
y nos aparecerá la siguiente pantalla:
Aquí nos aparece la información de los datos que ocupan 513 bytes ($201) comprimidos y 768
bytes ($300) descomprimidos. Los datos comprimidos ocupan $201=$1EF+$12 ya que hemos
incluido la cabecera de los datos.
Como se ve en la captura los datos están comprimidos al 33.20%.
Si no cogemos la cabecera nos dice el programa que no es un fichero empaquetado con RNC.
Si abrimos el fichero obtenido de 768 bytes con el TilEd 2002 podemos comprobar que se trata
de tiles del juego que están comprimidos en formato RNC.
Si quisiéramos modificar los tiles del juego tendríamos que descomprimirlos tal y como hemos
hecho. Una vez descomprimidos podemos modificarlos y volver a comprimirlos.
Tendríamos que buscar si hay punteros a los datos comprimidos por si fuera necesario
modificarlos en caso de que los nuevos datos ocupen más y tengamos que situarlo en otra
dirección de la ROM.
Modificar save states emulador BGB
El emulador BGB permite guardar hasta 10 save states o partidas que podemos recuperar en
cualquier momento y continuar el juego desde ese punto.
Aunque se pueden utilizar los cheats para tener ventajas otra opción que se puede hacer es
modificar los savestates del emulador para por ejemplo, tener más vidas de las que teníamos
cuando lo guardemos.
Esto también es aplicable para save states de otros emuladores siempre y cuando dichos save
states tengan el mismo tamaño. Hay emuladores en los que conforme avanzamos en el juego
el save state va aumentando de tamaño y no tiene un tamaño fijo.
También hay otros en los que el save state está comprimido y al descomprimirlo el fichero
obtenido es del mismo tamaño por lo que tendríamos que realizar la búsqueda en este fichero
descomprimido, modificar el valor y luego volver a comprimirlo utilizando para ello el
compresor 7zip.
En la versión 1.5 que es la que he utilizado del emulador BGB el save state ocupa 17.1 Kb
(17604 bytes).
Para el ejemplo, vamos a utilizar el juego “Batman” de Gameboy guardando 2 save states uno
con 3 vidas y otro con 2 vidas.
Para buscar en el juego vamos a utilizar un programa propio “File Search” para encontrar
donde se guardan las vidas en el save state tal y como se ve en la imagen:
El programa es sencillo, basta con abrir los 2 save states y poner los valores a buscar. El botón
“Inicilizar” sirve para iniciar una nueva búsqueda o si nos equivocamos al seleccionar alguno de
los ficheros.
Como se ve en la captura hemos encontrado 2 valores por lo que podemos comprobar cual de
los 2 es el correcto para lo cual abrimos uno de los save states y modificamos estos bytes para
ver cuál es.
Si tienen conocimientos de programación pueden hacerse un programa similar aunque éste
cumple con el objetivo buscado u optimizarlo para un emulador en concreto o una consola
específica distinta de Gameboy.
Simplemente tienen que hacer un programa que busque para el caso visto antes en un fichero
el valor $03 y en el otro el valor $02 pero en la misma posición.
Para optimizar el buscador para este emulador podríamos buscar sólo en la parte del fichero
donde se guardan los datos de la RAM ya que además cuenta con otra información que no nos
va a interesar y que según el emulador para el juego es de 8319 posibles valores lo que haría el
proceso más rápido.
Como el editor hexadecimal (HxD) o cualquier otro empieza en $0000 debemos irnos al byte
1128 decimal o $0468 hexadecimal donde en el fichero “Batman (JU) [!].sn2” (2º savestate)
encontramos el valor $02.
Este byte es donde se guardan las vidas del juego ya que si lo modificamos poniendo el valor
$09 tendremos 9 vidas.
Si cargamos el save state seguirá apareciendo 2 vidas pero cuando nos quiten esta vida
veremos como tendremos 8 vidas ahora con lo que comprobamos que funciona.
Igual que con las vidas podemos hacer con la energía,…
Esta modificación afecta al save state (partida guardada) y no al juego (ROM) que sigue sin
cambiar.
En la RAM el número de vidas se guarda en la dirección $C0C7 de la RAM, la energía en $C0C8,
la puntuación en $C0DE-$C0E0.
Podemos calcular también la relación que existe entre la RAM y el save state.
En el save state hemos modificado el byte $0468 para las vidas y en la RAM está en $C0C7 (que
quitándole $C000 nos da $C7).
Si a $0468 le quitamos $C7 nos da $3A1 hexadecimal o 929.
Save State=RAM+$03A1
Save State=RAM+929
Sin embargo ésta no es realmente la fórmula porque esta funciona para este juego pero para
otros no irá.
El problema que tiene el save state del emulador BGB es que almacena en dicho fichero el
nombre del juego de Gameboy con lo que dependiendo de que el nombre del juego sea más o
menos largo va a empezar la RAM en una posición u otra del fichero.
Si observan en $0032 está situado el nombre del fichero del juego y un poco antes en $002E
tiene el valor $12 hexadecimal o 18 decimal que corresponde al número de bytes que ocupa
dicho nombre.
Si a $3A1 le restamos $12 que es el tamaño del nombre del fichero nos da $38F con lo que nos
daría las fórmulas:
Save State BGB=RAM+$038F+tamaño nombre fichero
Save State BGB=RAM+911+tamaño nombre fichero
Esta relación existirá en los juegos de Gameboy aunque habría que ver si para juegos de Super
Gameboy o Gameboy Color se mantiene la relación ya que puede que el emulador guarde
información adicional y comience la RAM en el save state en otra posición del fichero.
Podemos hacer una modificiación interesante cambiando el nivel que se encuentra en RAM en
$C0C2 y que se encontrará en $0463 en el fichero de save state.
Si hacemos esto poniendo el valor $03 en lugar de 1 que corresponde al nivel 1-1
empezaremos en el nivel “CHEMICAL FACTORY” (1-3) pero el efecto del cambio de nivel no lo
veremos hasta que no nos quiten una vida.
Para comprobar la relación que existe entre RAM y save state podemos probar con otro juego
y ver si se cumple.
Por ejemplo, en el juego “Batle Bull” las vidas se almacenan en la dirección de la RAM $C82E.
Por lo que en el save state estará en:
Save State=RAM+$038F+tamaño nombre fichero
Save State=$82E+$038F+$16=$BD3
Modificando el valor que aparece por $09 por ejemplo, conseguiremos tener 9 vidas en el
juego “Battle Bull”.
Podríamos crear un programa para modificar los save states de un determinado juego en el
emulador BGB de forma automática sin tener que utilizar un editor hexadecimal.
Esta imagen es de un editor de save states propio escrito en Visual Basic 6 que permite
modificar “Vidas” y “Enemigos que quedan” en el nivel aunque se puede ampliar a otras
opciones tales como puntuación y el nivel en el que estamos. También le quedaría modificar el
icono que hay en la parte izquierda superior por algún otro relacionado con el juego del editor.
También podríamos hacerlo multiemulador, es decir que no sólo modificara save states del
emulador BGB sino también de otros o incluso uno que permita modificar los de varios juegos
de Gameboy.
Por ejemplo, para el juego “Battle Bull” en el emulador “gambatte_qt_win32-r550” de
Gameboy el número de vidas se guarda en el byte 26289 que en el editor hexadecimal es
26288 decimal o $66B0 hexadecimal.
Como se guardaban las vidas en la RAM en la dirección $C82E podemos obtener la relación
entre RAM y save state del emulador Gambatte restándole a $66B0 el valor $82E lo que nos da
$5E82.
Save State Gambatte=RAM+$5E82
Save State Gambatte=RAM+24194
Si cambiamos el byte en $5F49 del save state de Gambatte en el juego “Batman” conseguimos
cambiar el número de vidas en este juego con lo que se cumple la relación anterior y no
tenemos que tener en cuenta el tamaño del nombre del juego ya que esta información no la
guarda el emulador en los save states.
Igual podemos hacer con otros emuladores que tienen save states de tamaño fijo.
Para el emulador Gest 1.1.1 el número de vidas del juego “Battle Bull” se guarda en el byte
18840 que en el editor hexadecimal es 18839 o $4997 hexadecimal. Como se guardaba las
vidas en la RAM en $C82E obtenemos:
$4997-$82E=$4169
Save State Gest=RAM+$4169
Save State Gest=RAM+16745
Documentación adicional
Otros documentos que son interesantes para comprender la consola Gameboy son:
-
GBCribSheet (Otaku No Gameboy) donde aparece un resumen de las direcciones,
registros, instrucciones,…
-
Gameboy Programing Manual (290 páginas)
-
Gameboy CPU Tutorial (139 páginas)
Webs interesantes de romhacking en general
-
www.Romhacking.net
-
http://wahackforo.com/
-
http://datacrystal.romhacking.net/
-
https://tcrf.net/ (The Cutting Room Floor)
-
http://acmlm.kafuka.org/board/
-
http://foro.romhackhispano.org/

Documentos relacionados