Comentar que ya he dado el salto de libdragon a
libn64.
Me topé con bugs imposibles de corregir, por la forma en que libdragon gestiona las cosas, como no conseguir de ninguna manera cargar 2KB de texturas en 4bits (64x64), creo que estaba perdiendo el tiempo.
Qué tiene de bueno libn64?
- Es muy rápida, CEN64 en mi portátil con ejemplos compilados en libdragon a duras penas alcanza los 30-40fps, los tests compilados con libn64 llegan a correr a 80fps, gran parte de la librería está escrita en ASM y hace exactamente lo que pide la consola.
- Multi hilo, en libdragon si tienes 10 enemigos hay que gestionarlos con 1 solo bucle, en libn64 puedes crear hilos independientes y de distinta prioridad, como darle prioridad a la música por encima de otras cosas para que no se entrecorte, sabéis de esos juegos que se cuelgan pero sigue la música? Algo así
- Soporta RSP, gestión transparente de cache, etc.
- N64 tool chain, no hace falta montar un entorno de trabajo, bajas los binarios de mips64, la librería, usas el cmd de windows mismamente, make en la carpeta de libn64, luego en los ejemplos, profit.
- Soporte, el autor de ésta librería es el autor de CEN64, ayer mismo hablé con él, más gente de la scene está interesada en ésta librería y no demasiado en libdragon.
Así que de momento estoy traduciendo todo lo que he hecho en el RDP para dárselo a libn64, la cual no tiene funciones de RDP.
Una de las primeras cosas que he creado es un controlador de comandos, el RDP empieza a leer en la dirección de memoria 0xA0100000 de la DRAM, el chip es lo suficientemente inteligente para concatenar comandos, así que no hay que indicarle cuando empiezan y cuando acaban.
Veréis que hay un salto de 4 en 4, esto es porque estamos formando comandos de 32bits, la memoria de N64 es de 9bits (1 reservado), así que la lectura es 32 / 8, hay que tener cuidado porque en la dirección 0xA0200000 empieza el framebuffer y no queremos pisar memoria, para ello habría que enviar 131K comandos por frame, cosa bastante improbable, esa última parte del código de igual forma es una chapuza, pero lo que vamos a hacer es NO usar la DRAM de la consola, se usará el RSP y la DMEM cuando esté todo traducido.
void rdp_command( uint32_t data )
{
uint32_t mem_address = 0xA0100000 | memory_pos;
*(uintptr_t *)mem_address = data;
memory_pos += 4; // 32 bit / 8
memory_pos = memory_pos % 131072; // TOP LIMIT
}
En libdragon hay muchos más pasos para enviarle comandos a la consola:
static void __rdp_ringbuffer_send( void )
{
/* Don't send nothingness */
if( __rdp_ringbuffer_size() == 0 ) { return; }
/* Ensure the cache is fixed up */
data_cache_hit_writeback(&rdp_ringbuffer[rdp_start >> 2], __rdp_ringbuffer_size());
/* Best effort to be sure we can write once we disable interrupts */
while( (((volatile uint32_t *)0xA4100000)[3] & 0x600) ) ;
/* Make sure another thread doesn't attempt to render */
disable_interrupts();
/* Clear XBUS/Flush/Freeze */
((uint32_t *)0xA4100000)[3] = 0x15;
MEMORY_BARRIER();
/* Don't saturate the RDP command buffer. Another command could have been written
* since we checked before disabling interrupts, but it is unlikely, so we probably
* won't stall in this critical section long. */
while( (((volatile uint32_t *)0xA4100000)[3] & 0x600) ) ;
/* Send start and end of buffer location to kick off the command transfer */
MEMORY_BARRIER();
((volatile uint32_t *)0xA4100000)[0] = ((uint32_t)rdp_ringbuffer | 0xA0000000) + rdp_start;
MEMORY_BARRIER();
((volatile uint32_t *)0xA4100000)[1] = ((uint32_t)rdp_ringbuffer | 0xA0000000) + rdp_end;
MEMORY_BARRIER();
/* We are good now */
enable_interrupts();
/* Commands themselves can't wrap around */
if( rdp_end > (RINGBUFFER_SIZE - RINGBUFFER_SLACK) )
{
/* Wrap around before a command can be split */
rdp_start = 0;
rdp_end = 0;
}
else
{
/* Advance the start to not allow clobbering current command */
rdp_start = rdp_end;
}
}
Otro detalle interesante que se preguntó en el hilo es si el programa se cargaba en memoria o se leía directamente del cartucho, el programa entero se carga en memoria, el SO de N64 reserva automáticamente el primer MB de RAM, en otras palabras, sería recomendable que un programa de N64 no ocupara más de 1MB en código, pues tras esa cifra la gestión se vuelve manual.
Un test donde cada rectángulo tiene su propio bucle y hilo.