Clase Número 3
Se recomienda leer las clases anteriores, disponibles en el final del post.
Se recomienda leer las clases anteriores, disponibles en el final del post.
¡Que tal gente! :awesome:
Retomemos dónde nos habíamos quedado anteriormente, que fue en los Registros del Procesador.
Recordemos:
- 16 registros de 32 Bytes cada uno (Hasta 0xFFFFFFFF).
- R0 a R7 Lo-Registers (Registros Bajos)
- R8 a R12 Hi-Registers (Registros Altos)
- R13 Stack Pointer (SP)
- R14 Link Register (LR)
- R15 Program Counter (PC)
Los conceptos de cada uno de ellos están en la clase anterior y entenderlos es bastante importante, aunque seguramente deberán (deberemos) ir a la clase anterior para releer varias cosas allí explicadas.
También tengamos en cuenta ésta notación:
Rd = Registro Destino.
Rm/Rn = Registro Normal
#0xZZZZZZ = Numero fijo
Empecemos con ésta clase:
Instrucciones Principales: Push y Pop
¿Recuerdan el Concepto de Pila? - Pues más les vale que lo hagan xD
Push:
Uso: Push - o empujar - almacena en una pila los valores de los registros dentro de los corchetes {}, y los deposita en el Stack - o pila - de nuestro procesador. Se usa en el 90% de los casos al hacer una rutina, y es lo primero que se escribe de la misma.
Sintaxis: Push { Lista de Registros, LR}
Ejemplo: Push {R0, R1, R2, R3, LR} ; Push {R0-R3,LR}
*Nota: Ambos ejemplos son equivalentes e igualmente válidos.
¿Para qué?: Para que al terminar nuestra rutina, con otra instrucción, podamos volver todo a "como estaba antes"
Pop:
Uso: Pop - o sacar - saca de la pila los valores almacenados de los registros dentro de los corchetes {}, y los carga en memoria. Se usa en el 90% de los casos al hacer una rutina, y es lo último que se escribe de la misma. (Siempre que haya un Push deberá haber un Pop).
Sintaxis: Pop { Lista de Registros, PC}
¿Para qué?: Para que al terminar nuestra rutina volvamos todo a "como estaba antes". (Cargamos lo que almacenó el Push, así puede seguir adelante la Rom desde donde estaba)

Instrucciones de Cálculo
Add
Uso: Sumar valores entre registros o sumar un registro con un valor.
Sintaxis:
- Add Rd, #0xZZ Donde 0xZZ tiene que estar entre 0x0 y 0xFF (Suma directa de un valor). Resultado: Rd = Rd + 0xZZ
- Add Rd, Rn (Suma entre dos registros, con asignacion del valor al primero). Resultado: Rd = Rd + Rn.
- Add Rd, Rn, #0xZZ Donde 0xZZ tiene que estar entre 0x0 y 0xFF (Suma entre un registro y un valor). Resultado: Rd = Rn + 0xZZ.
- Add Rd, Rn, Rm (Suma entre dos registros). Resultado: Rd = Rn + Rm.
Sub
Uso: Restar valores a los registros o restar valores entre registros.
Sintaxis:
- Sub Rd, #0xZZ Donde 0xZZ tiene que estar entre 0x0 y 0xFF (Resta directa de un valor). Resultado: Rd = Rd - 0xZZ
- Sub Rd, Rn (Resta entre dos registros, con asignacion del valor al primero). Resultado: Rd = Rd - Rn.
- Sub Rd, Rn, #0xZZ Donde 0xZZ tiene que estar entre 0x0 y 0xFF (Resta entre un registro y un valor). Resultado: Rd = Rn - 0xZZ.
- Sub Rd, Rn, Rm (Resta entre dos registros). Resultado: Rd = Rn - Rm.
Mul
Uso: Multiplica Registros.
Sintaxis: Mul Rd, Rn . Resultado: Rd = Rd * Rn
Instrucciones de Carga de Datos
Ldr (Load Register)
Uso: Carga un valor de 4 Bytes directamente en el registro (Desde 0x00000000 hasta 0xFFFFFFFF).
Sintaxis:
- Ldr Rd, [Rn] Resultado: Rd = Rn
- Ldr Rd, =(0xFFFFFFFF) Resultado: Rd = 0xFFFFFFFF
- Ldr Rd, [Rn, #0xFF] Resultado: Rd = Rn + 0xFF
Ldrh (Load Register Half-Word)
Uso: Carga un valor de 2 Bytes directamente en el registro (Desde 0x0000 hasta 0xFFFF).
Sintaxis:
- Ldrh Rd, [Rn] Resultado: Rd = Rn
- Ldrh Rd, =(0xFFFF) Resultado: Rd = 0xFFFF
- Ldrh Rd, [Rn, #0xFF] Resultado: Rd = Rn + 0xFF
Ldrb (Load Register Byte)
Uso: Carga un valor de 1 Byte directamente en el registro (Desde 0x00 hasta 0xFF).
Sintaxis:
- Ldrb Rd, [Rn] Resultado: Rd = Rn
- Ldrb Rd, =(0xFF) Resultado: Rd = 0xFF
- Ldrb Rd, [Rn, #0xFF] Resultado: Rd = Rn + 0xFF
_________________________________________
Str (Store Register)
Uso: Carga en una dirección 4 Bytes de data. La dirección es dada por el registro entre corchetes mientras que el otro es el que contiene la data.
Sintaxis:
- Str Rd, [Rn] Rn = Rd (Si Rn = 0x02303000, en ese offset se escribirá lo que contiene Rd)
- Str Rd, [Rn, #0xFF] [Rn + 0xFF] = Rd (Si Rn = 0x02303000, en ese offset + 0xFF (0x023030FF) se escribirá lo que contiene Rd)
Strh (Store Register Half-Word)
Uso: Carga en una dirección 2 Bytes de data. La dirección es dada por el registro entre corchetes mientras que el otro es el que contiene la data.
Sintaxis:
- Strh Rd, [Rn] Rn = Rd (Si Rn = 0x02303000, en ese offset se escribirá lo que contiene Rd)
- Strh Rd, [Rn, #0xFF] [Rn + 0xFF] = Rd (Si Rn = 0x02303000, en ese offset + 0xFF (0x023030FF) se escribirá lo que contiene Rd)
Strb (Store Register Half-Word)
Uso: Carga en una dirección 1 Bytes de data. La dirección es dada por el registro entre corchetes mientras que el otro es el que contiene la data.
Sintaxis:
- Str Rd, [Rn] Rn = Rd (Si Rn = 0x02303000, en ese offset se escribirá lo que contiene Rd)
- Str Rd, [Rn, #0xFF] [Rn + 0xFF] = Rd (Si Rn = 0x02303000, en ese offset + 0xFF (0x023030FF) se escribirá lo que contiene Rd)
Parte Práctica:
Inauguramos ésta sección en las clases/tutoriales realizando... ¡Nuestra primer rutina!
Vamos a usar el compilador de HackMew (Adjunto al final del tema) por lo que usaremos ésta plantilla por ahora:
Código:
.text
.align 2
.thumb
.thumb_func
.global Rutina_1
main:
@Aquí irá nuestra rutina
.align 2
@Aquí declararemos nuestras variables, en caso que necesitemos hacerlo
.align 2
.thumb
.thumb_func
Son instrucciones que le dicen al compilador que estamos trabajando en thumb y alinea los bytes para ésto.
.global Rutina_1
Es opcional, sólo para darle un nombre a nuestra rutina
main:
Es una etiqueta, puede ser main: ; pepino: ; aca_empieza_mi_rutina_xdxd:
siempre terminada por " : " .
Para empezar a escribir nuestra rutina, primero necesitamos una idea, ¿A que si? xD
Pues recientemente quise hacer un script en el cual no me dejen entrar a una cueva hasta que mi pokémon tenga cierto nivel, pero no existe algo como un "GetPokemonLevel", así que lo haremos nosotros.
Entonces tenemos el primer paso:
1.Tener la Idea
El segundo paso sería obtener la mayor información posible del tema que nos atañe (Más adelante habrá una clase dedicada al 100% a ésto), para así saber o tener idea de como hacerlo y también si es posible de alguna manera.
Éste es un ejemplo simple, ya que sabemos que el nivel del pokémon ya está ahí, es solo extraerlo, meterlo en una variable y jugar con él.
¿Pero dónde está? Aquí entra nuestra amiga: Bulbapedia !
De aquí sacamos los datos para nuestra base, siendo así:
FR: 0x02024284
EM: 0x020244EC
RY: 0x03004360
Y luego tenemos una muy bonita tabla a la derecha:

Ésta tabla, nos explica fácilmente que, a partir de la dirección dada para nuestra base, tenemos distintos "Offsets" para cada dato particular de nuestro pokémon. (Nota: Los "Offsets" en la tabla están en decimal)
A nosotros nos interesa el offset del nivel,por lo que haremos lo siguiente:
FR: 0x02024284 + 0x54 (84 en Hex)
EM: 0x020244EC + 0x54 (84 en Hex)
RY: 0x03004360 + 0x54 (84 en Hex)
Quedándonos con los offset:
0x20242D8 --> Level primer pokémon en la party
0x2024540 --> Level primer pokémon en la party
0x03004360 --> Level primer pokémon en la party
Anotemos también por ahí:
Data total que hace a un pokémon: 100 Bytes
Direccion de la variable 0x800D en la Ram: 0x020270B6 + (0x800D * 2) (Gracias HackMew)
Direccion de la variable 0x800D en la Ram: 0x020275D6 + (0x800D * 2) (Gracias HackMew)
Direccion de la variable 0x800D en la Ram: 0x0201E8C2 + (0x800D * 2) (Gracias HackMew)
Bueno, y ya tenemos todo lo que necesitamos, copiemos la plantilla:
(Yo la escribiré para FR, pero será cuestión de cambiar el valor de las variables)
Código:
.text
.align 2
.thumb
.thumb_func
.global Rutina_1
main:
push{r0-rx, LR} @Aún no sabemos cuántos registros usaremos
pop {r0-rx, PC}
.align 2
Código:
.text
.align 2
.thumb
.thumb_func
.global Rutina_1
main:
push{r0-rx, LR}
pop {r0-rx, PC}
.align 2
.VAR: @De ésta forma declaramos que cada
.word 0x020270B6 + (0x800D * 2) @ vez que escribamos "VAR" nos referimos
@a dicha dirección
.PokemonLevel:
.byte 0x20242D8
Código:
.text
.align 2
.thumb
.thumb_func
.global Rutina_1
main:
push{r0-rx, LR}
ldr r0, .PokemonLevel [MENTION=28468]car[/MENTION]go en r0 el valor de la variable "PokemonLevel" r0 = 0x020242D8
ldr r1, .VAR [MENTION=28468]car[/MENTION]go en r1 el valor de la variable "VAR" r1 = 0x020370D0
ldrb r0, [r0] [MENTION=28468]car[/MENTION]go en r0 el valor byte que contiene el offset que contiene r0 (El lvl del primer pokémon)
strb r0, [r1] [MENTION=28468]car[/MENTION]go en el offset que contiene r1 el valor byte que contiene r0
pop {r0-rx, PC}
.align 2
.VAR:
.word 0x020270B6 + (0x800D * 2)
.PokemonLevel:
.byte 0x020242D8
Código:
.text
.align 2
.thumb
.thumb_func
.global Rutina_1
main:
push {r0-r1, lr} @Arreglamos el push y el pop
ldr r0, .PokemonLevel
ldr r1, .VAR
ldrb r0, [r0]
strb r0, [r1]
pop {r0-r1, pc}
.align 2
.VAR:
.word 0x020270B6 + (0x800D * 2)
.PokemonLevel:
.byte 0x020242D8
Y listo, ya tenemos nuestra rutina lista para compilar y probar
Código:
#dynamic 0x800000
'---------------
#org @start
callasm 0x8900001
buffernumber 0x0 LASTRESULT
msgbox [MENTION=29127]String[/MENTION]1 MSG_FACE '"Nivel del 1er pokémon: [buffer1]"
end
'---------
' Strings
'---------
#org [MENTION=29127]String[/MENTION]1
= Nivel del 1er pokémon: [buffer1]
Tarea: - Hacer que ésta rutina sirva para cualquier pokémon X de la party
Pista: Se puede usar la var 0x800D para ingresar un valor.
_____________________________
Clases Anteriores:
Clase 0 - Comprendiendo lo muy muy básico
Clase 1 - Comprendiendo lo Básico
Clase 2 - Registros, conociendo al procesador.
Clase Número 3: Instrucciones Básicas
Última edición: