Registrarse

[ASM] [FR]¡Asignar Scripts a Teclas!

MetalKaktus

A la luz del quinto día, mira al este
Miembro insignia
[FR]¡ASIGNAR SCRIPTS A TECLAS!​

En este tutorial os traigo un sistema para poder asignar un script a una tecla. Antes de dar la rutina para insertarla y dar las instrucciones para usarla quiero explicar como he conseguido implementar este sistema, por si alguien está interesado en ello. Aclaro que la parte de la explicación no es necesario y si solo quieres implementar el sistema o no tienes conocimientos de ASM saltatela.

Antes de nada quiero aclarar la forma que usa el juego de leer las teclas. Antes de haber mirado podría ser de dos maneras, por interrupción o por polling. Una interrupción es que cuando pulsamos una tecla se ejecuta una función especifica de la ROM. El polling o sondeo mira el estado de las teclas cada x tiempo, si no me equivoco en este caso a cada frame, y actuá en función de ello. Pues visto lo visto el sistema que se usa es el polling. Una vez que se lee el estado de las teclas se almacena ese dato en la memoria, concretamente en la I/O Registers. La estructura es la siguiente:

Código:
  Bit   Expl.
  0     Button A        (0=Pressed, 1=Released)
  1     Button B        (etc.)
  2     Select          (etc.)
  3     Start           (etc.)
  4     Right           (etc.)
  5     Left            (etc.)
  6     Up              (etc.)
  7     Down            (etc.)
  8     Button R        (etc.)
  9     Button L        (etc.)
  10-15 Not used
Es decir que en una half word se almacena el estado de las teclas, cabe destacar que una vez que se leen las teclas permanecerá así durante todo el frame. Si no hay nada pulsado el valor de la half word es 0x03FF. Si presionamos cualquier tecla su bit se pondrá a 0 mientras lo estemos pulsando.

Nuestro objetivo es detectar ese cambio de 1 a 0, entonces, a la hora de hacer la rutina tendremos que tener eso en cuenta.

Ahora queda encontrar la rutina que lea de las teclas. No me ha costado mucho encotrarla. En el debugger he puesto un break point cuando se lea de esa dirección y encontré la rutina que está en el offset 0x080005E8.

Yo he hecho un hook en 0x080005EC. Si os fijáis al hacer el hook r0 = offset del estado de las teclas. De modo que lo usaremos a nuestro favor. La rutina que he hecho es la siguiente:

Código:
//hook en 0x5ec
//r0 = offset de la memoria que almacena el estado de las teclas durante el frame actual

.text
.align 2
.thumb

main:
    push    {r0-r2}
    mov    r2, r0			;salvar el contenido de r0 en r2, r0 lo necesitamos para otra cosa
    ldr    r0, check_var_ID		;variable que se encarga de activar el sistema
    ldr    r1, =(0x0806E454 +1)		;var finder
    push    {r2}
    bl    linker
    pop    {r2}
    ldrh    r0, [r0]
    cmp    r0, #1
    bne    noCrash			;salta si la variable no está en 1
    ldrh    r0, [r2]			;cargar en r0 el estado de las teclas
    ldr   r1, =(valor decodificado del bit de la tecla, se explica más adelante)
    and    r0, r1			;poner el resto de bits a 0 y dejar el del boton deseado como esta
   ldrb    r2, position
     lsr    r0, r2
    mov    r2, r0
    ldr    r0, state_var_ID		;variable que guarda el estado de la tecla en el frame anterior
    ldr    r1, =(0x0806E454 +1)
    push    {r2}
    bl    linker
    pop    {r2}
    ldrh    r0, [r0]
    cmp    r2, r0
    blo    execute_script		;detectar flanco de bajada en la tecla (es decir, que se pulsa)
    b    store_state

execute_script:
    push    {r2}
    ldr     r0, =(0x08[offset del script])
    bl     script_routine
    pop    {r2}
    b    store_state

script_routine:
    ldr     r1, =(0x08069AE4 +1)	;rutina interna de la ROM que se encarga de ejecutar scripts
    bx    r1

store_state:
    ldr    r0, state_var_ID
    ldr    r1, =(0x0806E454 +1)
    push    {r2}
    bl    linker
    pop    {r2}
    strh    r2, [r0]			;guardar el estado actual para el próximo ciclo
    b    noCrash

linker:
    bx    r1

noCrash:
    pop    {r0-r2}
    ldrh    r1, [r0]
    ldr     r2, =(0x3FF)
    mov     r0, r2
    mov     r3, r0
    ldr    r2, =(0x080005F4 +1)    
    bx    r2				;volver a la rutina original

.align 2
    state_var_ID:    .word 0x(variable para ir guardando el estado de las teclas en el frame anterior)
    check_var_ID:    .word 0x(numero de la variable a usar como flag para activar el sistema)
    position:              .byte 0x(posicion de la tecla, se especifica en una tabla que dejaré abajo)
Quiero aclarar primero el valor que hay que guardar en r1 antes de la and lógica. Antes he explicado que cada tecla tiene un bit asociado. A mi solo me interesa una de las teclas, independientemente de como estén las demás, por tanto tendré que aislarla. Como el bit de la tecla B es el de la posición uno (empezando de cero) debemos cargar en r1 el valor de la posición decodificado, es decir, para 2 => 10, para 5 => 10000. Es decir, dejar todos los bits a cero excepto el bit que corresponde a la posición de la tecla, que lo dejaremos a uno.

Y con esto estaría todo. Todo lo que debéis configurar para vuestro uso es el valor decodificado, las variables y el offset del script.

No os olvidéis del hook.

La rutina que hay que insertar es la siguiente:

Código:
07 B4 02 1C 19 48 1B 49 04 B4 00 F0 24 F8 04 BC 00 88 01 28 20 D1 10 88 17 49 08 40 14 4A D0 40 02 1C 11 48 13 49 04 B4 00 F0 15 F8 04 BC 00 88 82 42 00 D3 07 E0 04 B4 10 48 00 F0 02 F8 04 BC 01 E0 0F 49 08 47 08 48 0A 49 04 B4 00 F0 03 F8 04 BC 02 80 00 E0 08 47 07 BC 01 88 09 4A 10 1C 03 1C 09 4A 10 47 C0 46 ZZ ZZ 00 00 VV VV 00 00 QQ 00 00 00 55 E4 06 08 XX XX 00 00 YY YY YY 08 E5 9A 06 08 FF 03 00 00 F5 05 00 08
QQ: Byte de la posición del bit de la tecla, mirar tabla de abajo.

XX XX: Es la tecla que queremos detectar, hay que seguir la tabla que dejo mas abajo. Permutado

YY YY YY: offset de vuestro script permutado. NO sumar 1, repito, no suméis 1.

ZZ ZZ: Usad una variable segura y mientras el sistema esté activado NO tocar esta variable, hay que permutar la variable

VV VV: Variable para activar el sistema. Para activarla setear la var a 1, para desactivarla sirve cualquier otro valor, por buenos habitos, recomiendo setearlo a 0. Hay que permutar la variable.

Para acabar debo explicar como hacer el script que queramos ejecutar cuando pulsamos una tecla. La estructura de TODOS los scripts de este sistema es el siguiente:

Código:
#dynamic 0x800000
#org @start
setvar 0x(variable que habéis puesto en VV VV, sin permutar claro está) 0x0

…
Aquí iría todo vuestro script
…

setvar 0x(variable que habéis puesto en VV VV, sin permutar claro está) 0x1
end
El script para activar el sistema debería ser algo así:

Código:
#dynamic 0x800000

#org @start
…
setvar 0x( variable que habéis puesto en VV VV, sin permutar claro está) 0x1
setvar 0x( variable que habeis puesto en ZZ ZZ, sin permutar claro está) 0x1
...
Para desactivarlo:

Código:
#dynamic 0x800000

#org @start
…
setvar 0x( variable que habéis puesto en VV VV, sin permutar claro está) 0x1
...
La razón de desactivar el sistema mientras el script esté en ejecución es muy simple. Para que no se inicié el script mientras se esté ejecutando esperaremos a que acabe el script para poder volver a ejecutarlo.

Tabla para las teclas:
Código:
Botón A		0x1
Botón B		0x2
Select		0x4
Start		0x8
Derecha		0x10
Izquierda	0x20
Arriba		0x40
Abajo		0x80
Botón R		0x100
Botón L		0x200
Tabla para posiciones:

Código:
 0     Button A        
  1     Button B      
  2     Select         
  3     Start           
  4     Right           
  5     Left            
  6     Up              
  7     Down     
  8     Button R  
  9     Button L
Ahora solo falta ir a la dirección 0x5EC y pegar 00 4A 10 47 XX XX XX 08. Siendo XX XX XX el offset donde hayáis insertado la rutina +1 y permutado.

La variable que hayáis usado para activar y desactivar el sistema no podéis usarla para nada más. La otra variable podéis usarla sin problema siempre que el sistema no esté activado. Recomiendo usar variables seguras, de las de 0x40FF abajo.

Créditos a @Annatar (yo vamos) por la rutina.

Y esto es todo por ahora, espero que le deis un buen uso. Ante cualquier duda preguntad sin miedo :)
 
Última edición:

Nachoius

Caminante del cielo
A ver no se que decir, serán ... meses intentando con esto? Gorka de verdad me saco el sombrero. Demasiado buen aporte, con un poco de moficaciones puedes asignar la tecla que quieras. Además tiene una infinidad de usos, es cosa de ser creativo. Creo que le vendrá bien a muchísimos que quieran hacer cosas novedosas en el RH. Felicidades, gran aporte!
 

Dragonite

But where's your heart
Esto también es infinitamente útil para los que hacemos Decomp, básicamente es el mismo ROM y la misma consola, nada más que retocado de distintas formas. ¡Gracias!
 

MetalKaktus

A la luz del quinto día, mira al este
Miembro insignia
Debo decir que al hacer la rutina la hice para la tecla B y a la hora de hacerlo más general para cualquier otra tecla se me pasó una cosa. Ahora la rutina está bien y sirve absolutamente para cualquier tecla. Tanto la rutina sin compilar como compilada están modíficados y ahora están plenamente operativos.

Si alguien implemento el sistema ayer lo siento (17/03/2020)
 

Gold

Porrero a tiempo parcial
Miembro insignia
Usando la rutina he encontrado un bug. Pasa que si en batalla tienes activado un script en la tecla L o R y las presionas al terminar la batalla el script asignado se ejecuta de inmediato. Quizás no pareciera grave, pero lo es cuando estás en medio de la ejecución de un script de nivel/de gatillo y al acabar un trainerbattle el script asignado a la tecla interrumpe el script normal y te deja a medias tu script. No sé si pueda repararse eso.
 

MetalKaktus

A la luz del quinto día, mira al este
Miembro insignia
De primeras se me ocurre que como puedes activar y desactivar el sistema a voluntad tengas en cuenta en tus scripts de nivel o de gatillo que hay que desactivar ese sistema. De todos modos intentaré echarle un ojo.
 

KERBEROS

Usuario de Platino
a mi me funciono a la perfeccion, lo use para crear dos menus extras uno para R para usar mi sistema de monturas, y L para el enu de ropa del personaje.. hasta los momentos, 0 bugs
 
Arriba