Ok, pues conceptos básicos entonces.
Llamadas al sistemaLa PSP viene más que con un firmware, con un sistema operativo en toda regla. Posiblemente fuera la primera consola en tener uno propiamente dicho. Esto significa que los juegos (y las aplicaciones, como el VSH) no tienen acceso directo al hardware (en todo caso sólo a partes del hardware).
Por tanto el SO debe ofrecer alguna forma para que este software (el llamado código
modo usuario) pueda realizar operaciones sobre el hardware a través de unas determinadas funciones (que se ejecutan en el famoso
modo kernel). Esto permite al SO controlar qué se puede y qué no se puede hacer desde el modo usuario. Estas funciones son las famosas
sceLoQueFuere(), que habrás usado hasta la saciedad. Estas funciones se denominan
llamadas al sistema (
system calls in inglis).
El código de las llamadas al sistema reside en el SO de la PSP. Esto quiere decir que cuando tú usas por ejemplo
sceIoOpen() en tu homebrew (en el software oficial es exactamente igual), el código de
sceIoOpen() no estará en tu homebrew, sino que el SO lo
enlazará a la hora de ejecutar tu homebrew. ¿Y esto cómo se come? Pues usando
importes.
Importes y stubsLos ejecutables y las librerías de PSP (y PS2, PS3, PSV...) están en formato ELF, que es básicamente un estándar (salvo para Microshit). En concreto en PSP es un ELF especial denominado PRX. Los PRX están divididos en secciones (al igual que todos los ELF) que definen por ejemplo dónde está el código dentro del fichero, así como una serie de parámetros adicionales que permiten al SO cargar el PRX correctamente. A la hora de compilar tu homebrew, el
enlazador (
linker, y en caso del PSPSDK es concretamente
psp-ld) se encarga de generar estas secciones y entre éstas están las que definen los importes. ¿Cómo funciona esto?
Por ejemplo, supongamos que usas
sceIoOpen() en tu homebrew. A la hora de enlazarlo, en vez de hacer una llamada "real" a
sceIoOpen() donde tú la haces en tu código, lo que hace el enlazador es algo diferente:
- Pone sceIoOpen() en una entrada de los stubs (una sección del ELF)
- Rellena la información referente a sceIoOpen() para que el SO pueda resolverla
- Hace que todas las llamadas a sceIoOpen() en tu PRX apunten a este stub en concreto
En ensamblador, tu PRX quedaría así al compilarlo:
Código: Seleccionar todo
[...]
jal sceIoOpen #Llamada a sceIoOpen
nop
[...]
jal sceIoOpen #Llamada a sceIoOpen
nop
[...]
jal sceIoOpen #Llamada a sceIoOpen
nop
[...]
sceIoOpen_stub:
jr $ra
nop
Pero como puedes ver, el stub de
sceIoOpen no hace absolutamente nada salvo un
return. Esto es porque, como he comentado antes, el código de sceIoOpen está en el SO, por tanto es el SO quien tiene que enlazarlo a la hora de cargar el PRX. Para ello, las llamadas al sistema se codifican como librería + NID. Un NID es simplemente un identificador de 32 bits para la llamada al sistema. En caso de
sceIoOpen estaría referenciada como IoFileMgrForUser y 0x109F50BC.
Pues bien (y ya estamos acabando...) a la hora de cargar el PRX en RAM para ser ejecutado (o usado como librería), el SO tiene una tabla que tiene qué número de llamada al sistema corresponde con cada importe librería + NID. Supongamos que
sceIoOpen corresponde con 0x2000. Entonces lo que hace el SO es reemplazar el stub de
sceIoOpen con dicha llamada al sistema, que quedaría tal que:
Al ejecutar la instrucción
syscall, la ejecución pasa al manejador de excepciones del kernel (ya que syscall genera una excepción MIPS), que tiene una tabla que relaciona este número de llamadas al sistema con una dirección de memoria modo kernel, que es donde se encuentra cargada la función
sceIoOpen, a la que, finalmente, hace un salto para que se ejecute.
Parchear stubsEntonces con
parchear los stubs nos referimos a modificar los stubs de un determinado PRX cargado en memoria para desviar las llamadas al sistema de este PRX a otro código. En tu caso, serían los stubs del VSH. Cuando se esté ejecutando tu plugin, deberías anular los stubs de las llamadas al sistema de recogida de pulsaciones. Por ejemplo, el stub de
sceCtrlPeekBufferPositive (sceCtrl + 0x3A622550) estaría por ejemplo como:
y tendrías que modificarlo para dejarlo como
mientras tu menú está activo. Obviamente tendrías que volver a dejarlo como estaba cuando salgas de tu menú, o el VSH no reconocería las pulsaciones de teclas.
Cualquier duda, me preguntas