Hace unos días el emulador iba por la revisión 77 de subversion. Hoy la he dejado en la 99.

El estado actual en la revisión 99 es que funcionan unas cuantas demos gráficas. Unas pocas más que en la revisión 77, pero sin llegar a las de la versión que hice hace varios años. No tiene tampoco debugger gráfico.

Pero puedo afirmar que además de ser muchísimo mejor a nivel de código (aunque hay que refactorizar, limpiar y documentar muchas cosas). Además en este tiempo he aprendido un montón de cosas de D y técnicas de programación, con lo que he podido abarcar mejor muchos problemas típicos.

Con esta revisión, me quedo un poco más tranquilo. He estado unos cuantos días de “frenesí programantil” que llamo yo. He hecho una burrada de cosas en unos días que posiblemente habrían llevado semanas a varias personas. Igual es la primavera que la sangre altera, pero en cualquier caso la motivación hay que aprovecharla. Y necesitaba desconectar de todo un poco.

Seguiré con el emulador, pero ahora con más calma. Al menos entre semana :P

Respecto a las cosas que dije que iba a hacer, he hecho casi todo lo comentado.

Lo más interesante (y relativamente nuevo con respecto a la última vez) que he hecho, ha sido implementar threads y semaphores.

Cosas:

  • Actualmente he implementado input. Ahora las demos interactivas que usan como entrada la pulsación de botones de psp ya funcionan. Esta vez he implementado la entrada de botones creo que más acorde a cómo funciona en realidad. La última vez simplemente guardaba el último estado. Ahora de vez en cuando produzco lo que se llama aquí un “frame” del controlador usando un ringbuffer. El api permite leer el último o últimos frame/s de entrada.
  • Hice pruebas con SIMD (SSE). Inicialmente pensaba que el procesado de vértices y matrices, especialmente en el caso de los GU_SPRITES, tendría que hacerlo manualmente desde cpu. Así que me hice unas estructuras/clases Matrix y TVector(Type, int Size = 4) -> Vector!(float, 4). Por una parte para probar la creación de estructuras dinámicas mediante templates en D, y en otra par poder probar especialización de esas clases/estructuras. La especialización de Vector!(float, 4), hace que las operaciones vectoriales se hagan mediante SSE. Implementé operaciones internas y externas. Externas: opAdd/opSub/opMul /opDiv(float), Internas:opAdd/opSub/opMul /opDiv(Vector). Implementé la multiplicación de Matrix por Vector para poder hacer transformaciones. El caso es que posiblemente al final no hagan falta en la implementación de opengl que es la que estoy haciendo porque voy a usar Geometry Shaders para la creación de los dos vértices restantes (teselación). Era algo realmente nuevo que no había probado y es una buena ocasión para probar, además de que debería aumentar el rendimiento una barbaridad. En cualquier caso la creación de las clases Matrix y Vector nunca está de mal. Ha servido para hacer pruebas y puede ser por si alguna vez se hace implementación del GE por software (de hecho, ya tengo la clase prototipo ahí creada).
  • Empecé el soporte para audio. Ahora mismo funciona parcialmente. Genera la onda, pero no se reproduce bien. Hay muchas pausas y se oye mal, vaya. Pero en cualquier caso es una primera aproximación. Al principio no iba y eso que ya había implementado threads. No entendía por qué. Se ejecutaban los threads, pero no se generaba la onda. Luego vi que era por un problema en la implementación de sceKernelStartThread. Resulta que el sceKernelStartThread hace un switch al thread en cuestión inmediatamente. Y haciendo uso de esa característica se le pasaba al thread de audio de la demo que estaba probando un parámetro volátil. Un parámetro que estaba en la pila del thread padre y que además se estaba cambiando en un for. Pero aprovechando que hay unas cuantas instrucciones de margen, el thread de audio creado, lo primero que hace es copiar el parámetro pasado a una variable local. Mi implementación no ejecutaba el thread inmediatamente sino que lo dejaba en la cola, en espera de ser ejecutado por el ThreadManager, así que el thread se ejecutaba a posteriori y la variable que se le había pasado cambiaba de valor. En este caso resultaba que todos los threads creados se pensaban que estaban procesando el canal 4 en vez del 0, 1, 2, 3 respectivamente :P. Para los curiosos: pspsdk\src\audio\pspaudiolib.c
  • Implementé interrupts. Y con ello cambié el handling del vblank, que se hacía a lo chapuza. Ahora puse que se hiciese mediante un evento registrado cuando se produce una interrupción de VBLANK. Con eso se ha estabilizado bastante los fps. Antes en algún frame hacía alguna cosa rara.
  • Respecto a opengl, la implementación. Ahora hay swizzling de texturas y hay algún opcode más implementado. LOP por ejemplo. No le estoy dando mucha importancia a la GPU por ahora. Lo importante es la CPU que ya funciona mas o menos bien y las funciones del kernel, especialmente referente a threads y módulos que son las que más cosas rompen.
  • Añadí un ModuleManager. Hasta el momento los módulos HLE se cargaban de forma estática, e impedian testearse bien por una parte y por otra cargar nuevos binarios en tiempo de ejecución. O tener varias ejecuciones.
  • Embebí en el psplibdoc.xml en el ejecutable e hice que mostrase información sobre los módulos y NIDs no implementados.
  • Implementé una parte importante del módulo de gestión de archivos. Usando como base la utilidad VirtualFileSystem que creé que permite mapear directorios físicos, crear entradas proxy, remapear otras, o en general crear sistemas virtuales de archivos. Es algo que ya hice en la anterior versión, pero ahora lo he implementado bastante mejor. (Todavía no he portado la implementación de ISO para el VFS.). A partir de ese momento, algunos programas que hacían uso de archivos, han empezado a funcionar.  (SDL inclusive). Aunque esto lo implementé antes que los threads, y las demos de SDL cascaban como un demonio porque no llegaba ni a terminar el SDL_Init.
  • Creé una utilidad para detectar bucles infinitos en functiones que esperan a otros componentes. En principio este tipo de funciones se deben evitar en pro de callbacks. Pero como es más sencillo y hay que complicarlo poco a poco, por ahora hace su función. Especialmente en componentes que se ejecutan en threads diferentes que son THE PAIN.
  • Hice que el dump de debug (F5) además de mostrar registros y dump de las instrucciones colindantes al PC actual, mostrase los threads y los semaphores activos.
  • Cambié el sistema de generación de ejecutables. Antes eran .bats a pelo. Lo hice en PHP. El PHP además detecta las dependencias y compila los archivos necesarios sin tener que especificar manualmente. Este tipo de cosas ya la hacen algunas utilidades como BUD/REBUILD y DSSS. Posiblemente acabe usando DSSS, pero por ahora quería tener un control más fino y tener “alternativas”.
  • Hice bastantes refactorizaciones y simplificaciones del código. Pero todavía hay MUCHO que hacer al respecto. Hay cosas que piden una refactorización y limpieza a gritos.
  • Arreglé muchas más cosas, añadí un menú a la ventana, un iconcito y sobretodo añadí la posibilidad de cargar en caliente nuevos programas.

Algunos screenshots de las últimas versiones:

Cubo texturizado de uno de los tutoriales de NeHe portado a PSP. Usa swizzling de texturas y el cubo se rota manualmente. Saca unos FPS entre 120 y 200.

http://www.psp-programming.com/code/doku.php?id=c:pspgu-neheport-lesson6

Mini juego PSPONG.

Funciona perfectamente, pero muy lento. (5~10 fps)

Debe hacer un montón de operaciones en la cpu en vez de hacer rendering de gpu.

La implementación actual del emulador es interpretada, con lo que no es de extrañar.

En jpcsp esta demo funciona mas rápida, por la recompilación dinámica y posiblemente por la identificación de bloques funcionales y reemplazo por funciones nativas (memcpy, memset, etc.).

A mí por ahora la velocidad no me preocupa. Cuando esté funcionando bien en modo intérprete y tenga una base sólida de tests autoejecutables, ya me aventuraré a lo demás.

Por otra parte los juegos de verdad suelen hacer uso del GE, donde en general este emulador funciona mejor que los implementados en java y C#. (Y todavía está sin optimizar el GE).

Demo SDL que encontré en psp.scenebeta (http://psp.scenebeta.com/tutorial/tutorial-04-mostrar-un-archivo-bmp-en-pantalla).

Esta demo costó bastante en hacerla funcionar porque usa SDL. En la primera versión del emulador no funciona directamente. Aquí empezó a funcionar cuando implementé correctamente threads y sempahores.

La demo carga un archivo PNG usando SDL y SDL_Image.