Está en la página 1de 18

ASM PRÁCTICO 1: Modificación de una Rutina de carga de Punteros

V 1.1
13 Mayo 2004

Por Dark-N ( hernaldog@gmail.com )

Requerimientos: Haberse leído y entendido Los Capítulos 1 2 y 3 de mi Guía de ASM de


SNES.

NOTA: Esta es una modificación SENCILLA usando algo de asm, no esperen salir haciendo
modificaciones a su gusto, ya que falta para eso. Recomiendo leer los documentos que están
en la página de Magno para los que quieres saber mucho más.

Índice:

Introducción:...............................................................................................................................2
Encontrando la Rutina.................................................................................................................2
Empezando..............................................................................................................................3
En Memoria.............................................................................................................................4
Entendiendo cada línea:...........................................................................................................5
Editar el Puntero: Tres Métodos Probados................................................................................11
Agradecimientos........................................................................................................................18
2

Introducción:
Este documento lo hice hace tiempo, pero debía primero mostrar los fundamentos con los
capítulos 1 2 y 3.

Ya estamos capacitados para ver algún código y entender algo. En este capitulo se verá un
caso concreto, modificar un puntero de una rom conocida usando algo de asm y herramientas,
hablo del FF6. Dada una rutina la analizaremos completamente, descubriremos que es una
rutina que carga los punteros y la modificaremos las usando herramientas necesarias.

Nuevo en V1.1: Agregue el método de modificación de rutinas usando ASM DEV.

Encontrando la Rutina
Lo primero que haremos será encontrar la rutina que deseamos cambiar para usaré como
ejemplo el rom de Final Fantasy 6 (J), aunque con la versión US igual se puede. Lo abrimos
con el snes9x1.42 que hace Trace, también puede ser con el clásico Trace de Lord Tech, y
cuando este a punto de empezar el Intro (cuando uno no presiona ningún botón) presionamos
la tecla DIVISION (/) del teclado numérico, apenas se acabe la frase, presiónalo de nuevo ya
que solo nos interés esta primera frase. Ahora ve a ver el archivo que se generó (un .log) en el
directorio donde tienes el juego.
Lo abrimos con el EDITPAD y tenemos muchos códigos, dentro de todo ellos, el trozo de
código que nos interesa está aquí:

1. $C0/7FBF A9 CD LDA #$CD A:0001


2. $C0/7FC1 85 CB STA $CB [$00:00CB] A:00CD
3. $C0/7FC3 C2 20 REP #$20 A:00CD
4. $C0/7FC5 A5 D0 LDA $D0 [$00:00D0] A:00CD
5. $C0/7FC7 0A ASL A A:0006
6. $C0/7FC8 AA TAX A:000C
7. $C0/7FC9 BF 02 E6 CC LDA $CCE602,x [$CC:E60E] A:000C
8. $C0/7FCD 85 C9 STA $C9 [$00:00C9] A:0174
9. $C0/7FCF A5 D0 LDA $D0 [$00:00D0] A:0174
10. $C0/7FD1 CF 00 E6 CC CMP $CCE600 [$CC:E600] A:0006
11. $C0/7FD5 90 05 BCC $05 [$7FDC] A:0006
12. $C0/7FDC 7B TDC A:0006
13. $C0/7FDD E2 20 SEP #$20 A:0000
14. $C0/7FDF A9 01 LDA #$01 A:0000
15. $C0/7FE1 8D 68 05 STA $0568 [$00:0568] A:0001
16. $C0/7FE4 60 RTS A:0001

Si quieres lo compruebas con tu archivo, puede que no lo encuentres a la primera, busca por
“LDA #$CD” y deberá aparecer. Muchos se preguntarán como yo sé que esta rutina nos sirve,
3

y la verdad es que este trozo de código yo no la descubrí, en una guía en francés que bajé
solo daban la rutina y decían que aquí se cargaban los punteros, yo la investigue y pregunté a
Magno y a otros por mail y foro, al final la entendí casi completamente he hice esta guía.

Empezando

Primero que todo debes saber donde está el texto. El texto del intro está en localizado en la
posición D0200 (lo vi en una guía en Internet). Es Obvio que si lo vez con un editor no verás
nada ya que no tenemos una tabla:

Esto el texto de la Intro vista en el Transhextion.

Ahora debemos convertir la dirección Hexadecimal del Intro (D0200) en Dirección de SNES,
para esto usamos el programa Hex2Snes, escogemos Hi-Rom, ya que FF6 es Hi-Rom, escribe
0D0200 (6 dígitos) y dale a Convert!, te debería salir $CD:0000

Ocupando el Hex2Snes
4

El texto entonces está en el “Banco” CD, pero ¿que es un banco?. Banco es una zona de
memoria de la Snes. En los Hi-Rom el banco comienza siempre en C0, y en 80 en las Lo-
Rom.

En Memoria

Como cualquier lenguaje asm, las rutinas se pueden representar en la memoria misma, así
cada instrucción y operandos ocupan ciertos “casillas” de memoria en cierta dirección. El
trace.log que genera el Snes9X_Trace indica todo lo necesario para representarla
gráficamente:

A la izquierda está la rutina en la memoria como bytes, a la derecha la rutina en asm. A la


derecha abajo está el proceso para convertir una dirección de SNES a un Offset para ser visto
desde un Editor Hex gracias al programa Hex2snes.
El mapa de memoria puede que no lo entiendas, ya que esta hecho para gente que sabe como
trabaja un mapa de memoria. Pero traté de explicarlo para todos lo entiendan incluso los
novatos .
5

¿Por qué el mapa de memoria no guarda Opcodes (LDA,REP, etc)? Ya que la maquina solo
trabaja con hexadecimales y no caracteres. Un LDA es A9, STA es 85 y así, cada opcode tiene
su equivalente en hex.
Si quieres saber mas acerca del mapa de memoria de una Hi-rom y una Lo-rom lee mi
próxima guía que haré solo acerca del mapa de memoria.

Entendiendo cada línea:

No es necesario que entiendas cada línea, pero es necesario para que ya te acostumbres a las
instrucciones de asm de la snes. Si ya conoces las instrucciones y quieres saber de que sirve
tener la rutina que di mas arriba, ve más abajo donde dice “Editando el Puntero”

1. LDA #$CD
Transferimos el banco del texto al acumulador A. Este es el banco del comienzo del texto.

2. STA $CB [$00:00CB]


Se almacena el banco de TEXTO en la dirección de memoria $CB, del banco 00 pero como
este banco tiene su Shadow en el banco 7E (ver mapa de memoria de SNES para entender
esto), entonces guardamos CD en 7E:00CB.

3. REP $20
Con REP $20 hacemos que el acumulador esté en modo de 16 bits aunque por ahora solo
almacene un valor de 8 bits (#$CD).

4. LDA $D0 [$00:00D0]


Cargamos el valor que está dentro de la dirección $D0. Dentro de $D0 está el 06, valor que le
dice al juego que frase mostrar por pantalla, es decir que aquí va puntero a cargar. Es como
decir “cargar puntero numero 06”. Ese 06 se carga al inicio del juego por lo que esta allí de
6

antes. Recuerda en la RAM solo hay DATOS y en la ROM está la instrucción LDA $D0 que
la accede.

5. ASL A
Se multiplica A=06 por 2, quedando A=C, ya que cada puntero es de 2 bytes. Ya veras para
que se hace esto.

6. TAX
Este nuevo valor se pone en X para…

7. LDA $CCE602, X [$CC:E60E]


Leer la tabla de punteros. Guardamos en A el valor que hay almacenado en ROM en la
dirección $CC:E602 + el valor del índice X. Es decir lo que esta dentro de CC:E60E. En la
dirección $CCE602 esta el primer puntero del juego ya que si X = 0 se accede a la dirección
E602 + 0 =E602. Entonces a partir de CC:E602 empieza la tabla de punteros y gracias a la X
uno puede buscar el puntero de la siguiente sentencia. A terminara con valor 0174 después de
esta instrucción, esto quiere decir que lo que hace la Snes es: “el primero puntero que se carga
es el numero 06 con valor 0174”. En memoria claro se almacena 0174 (de 16 bits o 2 Bytes)
al revés, es decir 74 (byte) y luego 01 (otro bytes), veamos como queda:
7

8. STA $C9 [$00:00C9]


El puntero que está en A (puntero es de 16 bits) se guarda en la dirección $C9 y $CA de la
Ram. Mas adelante, se supone que alguna rutina fuera de estas 16 instrucciones, se ocupará el
puntero $C9, ya que es la frase que se mostrará.
8

9. LDA $D0 [$00:00D0]


Recargamos A con el número del puntero que se ha de leer. A: 0006.

10. CMP $CCE600 [$CC:E600]


Y compara A con lo que hay en $CC:E600 para saber si el número de puntero (recuerda el 06
almacenado en 7E:00D0) que queremos mostrar es mayor o no que el número de punteros que
hay en total almacenados a partir de la dirección CC:E602 (le puse X a este numero ya que lo
desconozco)
9

11. BCC $05 [$7FDC]


Aquí salta a la dirección 7FDC si el carry esta en 0, pero no es así, así que NO salta. Recuerda
que BCC= brach carry clear. El carry se determina en la comparación de arriba. Si [numero
del puntero guardado en A] < [cantidad de punteros] no te sales de la tabla de punteros y por
tanto, sigues con la rutina yendo al paso 12. Si es mayor, hace un salto condicional se a la
dirección $XX:7FDC, donde XX es el valor del banco actual del que se estan leyendo las
instrucciones. Por eso, los saltos condicionales sólo se pueden hacer dentro del mismo banco
de instrucciones. Pero lo que está claro es que ya no leerá de esa tabla de ROM porque el
número de vector (puntero) que había almacenado en $D0 haría que leyeras el puntero fuera
de la tabla, con lo que no sería válido.

12. TDC
Entonces seguimos y ejecutamos TDC - Transfer Direct Page to Accumulator, es decir se
transfiere lo que estaba en un registro llamado Direct Page (DP) al acumulador. Ve mi guía de
Referencia para saber más del DP. Al terminar esta instrucción A vale 0, lo que quiere decir
que los que estaba en DP es 0.

13. SEP $20


Deja el acumulador en modo de 8-bits.

14. LDA #$01


A vale 01.

15. STA $0568 [$00:0568]


Y lo coloca en la dirección $0568. Esto es para otros fines que desconozco.

16. RTS
Vuelve de la subrutina al código que la llamó y ejecuta la instrucción siguiente a la subrutina.
Recuerda que casi siempre una instrucción de salto (como JSR) llama a una subrutina. Pero
queda una duda:
10

¿Qué pasa si un salto JSR que llamó a toda esta rutina de los 16 pasos, y ese JSR era la ultima
instrucción de otra subrutina? Ejemplo:

$C0/A497 8D 64 05 STA $0564 [$00:0564]


$C0/A49A 20 BF 7F JSR $7FBF

$C0/7FBF A9 CD LDA $CD -> paso 1


$C0/7FC1 85 CB STA $CB -> paso 2
... ...
$C0/7FE4 60 RTS -> paso 16

Lo que pasa aquí es que al ejecutar RTS vuelve a la


siguiente instrucción después del JSR que lo llamó (JSR $7FBF), que en este caso sería la
instrucción que hubiera en $C0:A49D marcada con círculo rojo.
Hay que saber que cuando se hace un JSR se introduce la dirección siguiente (la de C0:A49D)
al tope de la PILA y cuando se hace un RTS se llama desde arriba de la pila. Si hay varias JSR
se meten varias instrucciones a la pila y luego según los RTS se van sacando de la pila según
orden que se pusieron. Recuerda que la Pila es LIFO, es decir “la ultima instrucción que se
mete es la primera en salir”, claro, ya que queda arriba.
11

Editar el Puntero: Tres Métodos Probados


Ahora que ya he explicado como funciona mas o menos la rutina que carga los punteros.
Podemos preguntarnos: ¿Qué hay que hacer si queremos que muestre otra frase en la Intro del
juego? Podemos editar o insertar rutinas, esta parte de la guía solo explica la Edición una
instrucción, si quieres agregar toda una rutina tuya, esto no te servirá mucho, hay otros
métodos que los veré en otra guía.

Hay 3 formas de editar una instrucción asm (por lo menos las que conozco yo):

a) Usando Trace.log generado por el snes9x_trace y viendo que dirección hexadecimal


hay que cambiar.

Este método es sencillo, solo hay que tener:


-Un editor Hexadecimal, yo usaré el Translhextion 1.6c.
-El Hex2snes 1.3 para convertir direcciones de Snes a Offset para verlos en el Editor
Hexadecimal.
-Emulador y fabricador de log, el Snes9x_trace.

Para empezar supongo que ya tienes el log. Ahora modificaremos la instrucción del paso
4:

4. $C0/7FC5 A5 D0 LDA $D0 [$00:00D0]

Esta instrucción carga en A el numero del puntero a mostrar por pantalla, ese numero esta
guardado en la dirección D0, no se carga en A el puntero en si. Con ese número se accede
a la Tabla de puntero en los pasos siguientes y se saca. Recuerda que cada instrucción
(LDA, BCC, TXA, etc..) tiene un equivalente en bytes, así LDA es A5, entonces LDA
$D0 es el par A5 D0. Recuerdalo.

Lo primero es apuntar la dirección snes donde se ejecuta LDA $D0, es decir $C0:F7C5,
esta dirección la usamos en el Hex2snes, marcamos Hi-Rom como se muestra abajo y le
damos a Convert!
12

Nos entregó el Offset 008C5, recuérdala ya que tenemos ahora que buscarla en el Editor
Hexadecimal.
Abrimos el Translhextion y vamos donde dice Offset->Jump To y adentro escribimos
x81c5 (la ‘x’ porque accederemos a una dirección hexadecimal):

Y nos mostrará lo siguiente:


13

y mira lo que encontró…cha chan! A5 D0, o sea que allí hace LDA $DO, es decir que allí
carga el numero del puntero a mostrar por pantalla. Te dije que lo recordarás! Entonces es
fácil darse cuenta que 0A es ASL A, y así sucesivamente.

Ahora editemos ese valor (que esta en la posición 0x81C5), pongámosle en vez de A5 D0,
A5 00. Quedaría así:

Salva y ve a ver al emulador que pasó, je je je:

Antes Ahora

¡¡¡Bravo!!!, merezco un aplauso ¿no? El puntero que se cargo fue el otro por lo que mostró
otro en pantalla. Bueno, con esto ya pueden aprender más por cuenta suya y experimentar
con otras roms.
Ahora veremos una forma mas profesional de hacerlo, es decir ocupando el compilador
X86.
14

b) Realizando/Creando una rutina (.asm) luego compilarla con el assembler X86,


generar el binario y ese insertarlo en la rom.

Requerimientos:
-Un editor Hexadecimal, yo usaré el Translhextion 1.6c.
-El programa Bin Inserter 1.0 (encuéntralo en la pagina de Los Sayans) es para insertar
bytes en una rom dado una dirección.
-El compilador X86 para hacer nuestro archivo.bin, para luego insertarlo en la rom.
-El Hex2snes 1.3 para convertir direcciones de Snes a Offset para verlos en el Editor
Hexadecimal.
-Emulador y fabricador de log, el Snes9x_trace.

Para no perdernos, de nuevo cambiaremos de nuevo la instrucción:


4. $C0/7FC5 A5 D0 LDA $D0

Lo primero que haremos será crear nuestro archivo.bin, para esto abrimo un editor de texto
(usaré el EditPad) y escribimos lo que queremos insertar: LDA $00, lo salvamos como
ej1.asm:

Ahora le sacamos acceso directo al X86, y en línea de comandos agregamos -s -l ej1.asm,


como se ve en la figura de abajo:
15

Le sacamos el tick que dice “cerrar al salir” para ver la información una vez compilado el
archivo. Lo ejecutamos y mostrará:

Todo OK, lo compiló y creó un ej1.smc, un ej1.bin y un ej1.lst. Si tuviesemos un juego


entero escrito en el .asm nos serviría el .smc y estaría listo para correrlo en un emulador,
pero ahora nos interesa el archivo binario .bin. Así que ábrelo con un editor hex y mira lo
que muestra:
16

A5 y 00, ¿los recuerdas? Son los valores del Opcode LDA y del operando 05 pero su
equivalente en bytes. La raya __ es que es el fin del archivo según Translhextion. Ciérralo.
Ahora toca convertir la dirección de Snes que teníamos ($C0:7FC5) a dirección Offset,
dándonos 81C5, ahora tomamos el Bin Insert y lo ejecutamos.
Escribimos primero el archivo donde se insertaran los bytes, luego el archivo.bin que
creamos y finalmente la dirección en DECIMAL (mira en el editor hex y te das cuenta que
81C5 es 33221 decimal):

Aceptamos y listo, reemplazó los bytes que teníamos en la dirección 81C5 los del binario.
Para comprobarlo solo abre el editor hex y comprueba que así sea:

Ahora solo carga el juego en el emulador y verás lo mismo que con el método anterior:

Antes Ahora

¡Funciono!, ahora ya saben ocupar el compilador X86 y el Bin Insert y además,


modificaron el puntero.
17

c) New!. Utilizando el programa Snes Profesional ASM Development Kit

Este es el método MAS FACIL de todos. Primero tomamos la instrucción:

4. $C0/7FC5 A5 D0 LDA $D0 [$00:00D0] -> Recuerda $C0/7FC5

Esta instrucción carga en A el numero del puntero a mostrar por pantalla, ese numero esta
guardado en la dirección D0, no se carga en A el puntero en si. Luego abrimos Snes
Profesional ASM Development Kit y nos sale una ventana asi:

Vamos a File->New Proyect y escogemos la rom FF6(US).smc. Luego se nos abre una
ventana chica donde aparece la Rom, haces clic en el signo + para que se expanda y
aparece Code y Data.

Haz clic sobre Code y se abrira el Código asm del Juego!.


18

Ahora ve a la dirección $C0/7FC5 (la puse al principio) y aparecerá INX (ver imagen) ahora
SUBE 2 BYTES arriba y esta la dirección $C0/7FC3, y aquí nos encontramos con LDA
$DO.
Les suena esta instrucción? Ahora solo la modificamos haciendo un clic sobre ella (ver
imagen de arriba) y escribimos LDA $00.
Ve a guardar y carga el juego en el emulador y verás lo mismo que con los 2 métodos
anteriores:

Antes Ahora

KIAAA!!! Funciono ;)

Agradecimientos

 A Magno por si gran paciencia y ayuda que me dio.


 A Jonas por enseñarme mucho de lo que esta aquí escrito.

26 Junio 2004

Modificación de una Rutina de carga de Punteros


por Dark-N
naldo_go@hotmail.com

Cualquier duda o sugerencia mándeme mails!!


Pronto, el Capitulo 4.

También podría gustarte