Imagen

Cazadores de mitos(DreamWalker Alter Ego 2)

Responder
Avatar de Usuario
ackerman
Mensajes: 525
Registrado: 05 Feb 2019 21:32
Ubicación: Asturias
Contactar:

Cazadores de mitos(DreamWalker Alter Ego 2)

Mensaje por ackerman »

mythbusters.gif
mythbusters.gif (2.99 KiB) Visto 675 veces
Hace tiempo, en Cazadores de mitos (Aquaplane):

https://retrowiki.es/viewtopic.php?f=114&t=200039791

Se ha visto que para emular bordes de color en el juego, NO se necesita ni Accurate cycle (ciclo exacto) ni Contended memory (memoria en contienda). Además, la solución planteada, al no consumir CPU, ni memoria adicional, garantiza su inmediata implantación sin falta de tener que comprobar rendimientos. Juegos como el Beach Head II y UGH!, siguen la misma solución trivial.

Ahora le toca el turno al juego de 2014, DreamWalker (Alter Ego 2), que por muchos sitios figura que se requiere:
  • Cycle accurate (ciclo exacto)
  • Contended memory (memoria en contienda).
Estos requerimientos, supuestamente son debidos al uso del Nirvana Engine, que hace uso del multicolor, y que supuestamente requiere una gran exactitud de tiempos.

¿Es el zx 48K una máquina tan única, exacta y especial que puede superar la fumada del IBM5100 con la historia de John Titor?
https://es.wikipedia.org/wiki/John_Titor

Para no perder minutos de nuestra vida y hacer esto más corto, NO se necesita nada de lo antes comentado, es más, tampoco se necesita al actor tiempo.
 ! Mensaje de: ackerman
Alguno, pensará: ¿Como no se va a necesitar el tiempo, pero que me estás container?
Dado que en este foro, se respetan las leyes de la termodinámica, no se puede viajar en el tiempo. La mera existencia de un objeto, ya sea vivo o inerte, ya garantiza que el tiempo está avanzando, y eso es lo único que interesa, como veremos más adelante explicado.

Y ahora, toda la chapa:

Antes de empezar, aclaración de conceptos: En los enlaces está todo muy bien explicado (traductor), ahorrándonos escribir más hojas.

La solución basada en emuladores de ciclo exacto, es el equivalente a usar la fuerza bruta para sacar las claves de criptografía, es decir, es un opción, pero con una sobrecarga en la máquina host. Aunque se suele utilizar múltiples mecanismos que gestionan todos los conceptos arriba nombrados, al final se suele acabar con una resincronización que suma y resta tiempos, o bien muestreos para que las cosas cuadren entre el binomio ULACPU y la sincronización con la función de redibujado.
Cada escanline de los 192 posibles, consta de:
  • 48 tstates borde + 128 bitmap + 48 tstates borde.
  • 96 pixels borde + 256 pixels bitmap + 96 pixels borde.
Se suele hablar de resincronizaciones a nivel de:
  • 1 scanline completo (224 tstates).
  • 4 caracteres (32 pixels) de un scanline.
  • 8 pixels de un carácter en un scanline.
  • 4 pixels de un carácter de 8 en un scanline.
Usar emuladores de ciclo exacto e intentar recrear el comportamiento de la ULA a nivel de tstates nos limitará a usar unos concretos y el consumo de recursos será mayor.

Por eso , aquí se puede ver una manera alternativa:
La solución más eficiente es la simulación del engine NIRVANA, al estilo del engine BIFROST, como había comentado hace tiempo para localizar los btiles, ctiles y tilemap en memoria:
https://retrowiki.es/viewtopic.php?f=115&t=200038449#p200168173

Pero mientras tanto, dado que hay que implementar el 100% de la funcionalidad del engine, se puede llegar a un sistema intermedio (ni pa ti ni pa mi). Lo primero que necesitamos, un core de emulación que permita ejecutar el juego, como puede ser el LKF (LIN KE FONG) en el ESP32, aunque se vea mal. Esto es fundamental, porque si el core no carga un juego, aunque tenga defectos visuales, poco se puede hacer.
Podemos usar cualquier otro core que se nos ocurra (incluidos primitivos), ya sea de MAME, libretro, etc...

Tenemos que detectar el juego, que puede ser algo tan sencillo como a la hora de cargar el SNA (Snapshot) detectar la cadena en la zona de memoria:
detectarJuego.gif
detectarJuego.gif (8.83 KiB) Visto 675 veces
Lo siguiente es usar un algoritmo primitivo, como es el algoritmo del pintor, para guardar cada vez que se modifica en tiempo real un atributo de color en un array de atributos, así como un contador con el conteo actual. Se necesita pues, un array de 768 contadores de un byte.
Se requiere modificar la rutina de escritura a memoria del core de Z80, sólo para este juego, que permita controlar cuando se escribe en la zona de atributos, para que guarde su contador, y el atributo, que antes hacía algo tan básico como escribir en zonas de memoria:

Código: Seleccionar todo

void writebyte(uint16_t addr, uint8_t data)
{
  gb_real_write_ptr_ram[(addr>>14)][(addr & 0x3fff)] = data;
}
Pasa a detectar que es la zona de 768 bytes de atributos. En este caso se ha usado un array de 16 con los 768 atributos, y se ha dejado sin optimizar para que se vea la forma genérica y fácil:

Código: Seleccionar todo

unsigned char gb_atributos[768*16];
unsigned char gb_atributos_cont[768];

void writebyte(uint16_t addr, uint8_t data) 
{
  gb_real_write_ptr_ram[(addr>>14)][(addr & 0x3fff)] = data;

  if (addr>=0x5800 && addr<(0x5800+768))
  {
   unsigned short int idAtributo= addr-0x5800;
   unsigned char contador= gb_atributos_cont[idAtributo];
   if (contador<16)
   {    
    gb_atributos[(idAtributo*16)+contador]= data;    
    contador++;
    gb_atributos_cont[idAtributo]= contador;
   }   
}
Al inicio de cada loop de la emulación de la CPU de un frame (20 ms), se resetean los contadores, y cuando se dibuja en la rutina de volcado de video, se leen los contadores y se dibuja, sabiendo que cada escritura es lineal y secuencial en el tiempo y equivale a 4, 2 o 1 scanline, gracias a la información del conteo.
Por tanto, no necesitamos llevar la medición del tiempo, tal y como se comentó al principio del post, respetando las leyes de la termodinámica.
Hemos pasado del tiempo absolutamente, y sin embargo logramos el mismo resultado visual que si se usara micro segundos.
Al seguir teniendo una rutina de redibujado basada en una instantánea, además de los cambios de cada atributo en cada escritura, no hace falta tener un proceso de seguimiento, ni hilos paralelos, lo que permite portarlo cómodamente a múltiples plataformas.

Para este post, he añadido al emulador la posibilidad de procesar el modo normal (izquierda) y el NIRVANA (derecha) en tiempo real, con el core de LKF, pudiendo ver las diferencias.

La pantalla de portada, que es muy bonita no hace uso de NIRVANA ni multicolor, así que esta parte podríamos no procesarla, para optimizar:
00Portada.gif
00Portada.gif (12.55 KiB) Visto 675 veces
Al igual que el anterior, en esta pantalla tampoco se hace uso del NIRVANA, así que se puede procesar o no, lo que viene siendo un tanto da, que da lo mismo:
01Portada2.gif
01Portada2.gif (3.15 KiB) Visto 675 veces
Luego viene el logo de NIRVANA, que aquí, si hace uso del NIRVANA, y se puede ver la diferencia, entre una y otra.
02Nirvana.gif
02Nirvana.gif (1.5 KiB) Visto 675 veces
La siguiente pantalla también hace uso del NIRVANA:
03Start.gif
03Start.gif (2.83 KiB) Visto 675 veces
La pantalla del zx spectrum es de 32x24, donde cada carácter es de 8x8.
En este pantalla del modo nirvana, he añadido al emulador, que saque en tiempo real las estadísticas del conteo de los atributos que están cambiando, con el conteo del número de escrituras que han ocurrido en un frame completo de 20 ms:

Código: Seleccionar todo

(0,0)                                 (31,0)
      00000000000000000000000000000000
      04444444444444444444444444444440
      04444444444444444444444444444440
      04444444444444444444444444444440
      04444444444444444444444444444440
      04444444444444444444444444444440
      04444444444444444444444444444440
      04444444444444444444444444444440
      04444444444444444444444444444440
      04444444444444444444444444444440
      04444444444444444444444444444440
      04444444444444444444444444444440
      04444444444444444444444444444440
      04444444444444444444444444444440
      04444444444444444444444444444440
      04444444444444444444444444444440
      00000000000000000000000000000000
      00000000000000000000000000000000
      00000000000000000000000000000000
      00000000000000000000000000000000
      00000000000000000000000000000000
      00000000000000000000000000000000
      00000000000000000000000000000000
      00000000000000000000000000000000
(0,23)                                (31,23)
El 4 me está indicando que se han realizado 4 escrituras máximo en la zona de atributos por frame, es decir, que se estaría en modo de 15 colores por cada 2 scanlines. En realidad, tenemos que sólo se está usando la zona que tiene a 4 los contadores, es decir, 30x15 = 450 bytes x 3 = 1350 bytes que necesitamos realmente de RAM extra para el algoritmo del pintor para este juego. Sería 3, en lugar de 4, porque el último estado de los atributos ya se tiene en la propia memoria del zx spectrum emulado. Habría también que sumar los 30x15 = 450 bytes de contadores, que podrían quedar de manera empaquetada en 112 bytes, al necesitar sólo 2 bits para codificar.
Pero si no queremos exprimir mucho, y queremos un algoritmo genérico, son (3840 bytes):
  • 768x4 = 3072 bytes de atributos
  • 768 bytes de contadores
Y ahora pasamos a la pantalla de meter la clave, que tampoco hace uso de NIRVANA:
04Clave.gif
04Clave.gif (964 Bytes) Visto 675 veces
La pantalla previa al juego, si hace uso de NIRVANA. Ya de por si era bonita, pero los colores con el ENGINE le dan un toque más especial. ¿En una TV de los 80’s se vería exactamente los colores así? Lo dudo mucho, seguramente serían tan mate y apagados, como los primeros monitores VGA. Y si hablamos de salida de radiofrecuencia, recordando la época, le veo lagunas.
05AntesDeJugar.gif
05AntesDeJugar.gif (1.91 KiB) Visto 675 veces
Nivel 1, donde hace uso de NIRVANA:
06Nivel01.gif
06Nivel01.gif (2.58 KiB) Visto 675 veces
Nivel 2, con NIRVANA:
07Nivel02.gif
07Nivel02.gif (4.33 KiB) Visto 675 veces
Pasamos a analizar la pantalla:

La memoria de video para BITMAP en un spectrum se divide en 3 zonas:
  • Zona 1: 0x4000 - 0x47FF
  • Zona 2: 0x4800 - 0x4FFF
  • Zona 3: 0x5000 - 0x57FF
El carácter de 8x8 que estamos analizando es la D, es decir, la parte izquierda arriba de la D de DREAMWALKER:
  • columna 7:
  • fila: 1
prueba.gif
prueba.gif (1.52 KiB) Visto 675 veces
El sprite 8x8 de la D:
spriteD64x64.gif
spriteD64x64.gif (316 Bytes) Visto 675 veces
Al ser la primera zona de la pantalla, su correspondencia en memoria sería:

El valor BITMAP de las posiciones:

Código: Seleccionar todo

linea   Valor hex   Valor bin      Memoria   
0       0xE0        11100000       0x4127
l       0x38        00111000       0x4227
2       0x0C        00001100       0x4327
3       0x06        00000110       0x4427
4       0x06        00000110       0x4527
5       0x03        00000011       0x4627
6       0x03        00000011       0x4727
7       0x03        00000011       0x4827
La posición de atributos en los 768 bytes es (1x32)+7= 39:

El valor que hemos acumulado de las escrituras en las 4 llamadas ha sido:

Código: Seleccionar todo

iteración   Valor Hex    Brillo    Primer plano (ink)       Fondo(Paper)
0           0x41         SI        1(azul brillante)        0 negro
1           0x43         SI        3(magenta brillante)     0 negro
2           0x03         NO        3(magenta oscuro)        0 negro
3           0x4D         SI        5(cian  brillante)       1 azul brillante
Los atributos siguen el siguiente esquema:

Código: Seleccionar todo

Bit:         7        6        5        4        3        2        1        0
            Flash    Brillo     |---- Paper ----|          |------ Ink------|
Se habló de 4 llamadas, así que cada iteración son 2 scanlines en donde se aplican los colores y fondo que se ha descrito junto con el bitmap que hemos visto:

Código: Seleccionar todo

linea     iteración
0           0
1           0
2           1
3           1
4           2
5           2
6           3
7           3
Así que nuestra rutina de redibujado, hará lo mismo de siempre, es decir, pintar el carácter 8x8 con el atributo actual de la zona de atributos, siempre y cuando el contador sea 0. Pero, en el caso de encontrar un contador mayor de 0, aplicará a cada scanline el valor de cada atributo que tenemos guardado.

Nivel 3, que también tiene NIRVANA, lo mismo, sigue 4 llamadas en contadores:
08Nivel03.gif
08Nivel03.gif (3.41 KiB) Visto 675 veces
Con todo esto, conseguimos que se pueda añadir la funcionalidad del NIRVANA a cualquier core que no lo tenga, con un consumo muy bajo de CPU y RAM. Así mismo la funcionalidad, permite una gran escalabilidad y mantenibilidad, al ser modular, pudiendo ser aplicada de manera custom a un juego, sin falta de afectar al resto, evitando tener que hacer tests unitarios y repetir el día de la marmota.
Avatar de Usuario
ron
Mensajes: 22353
Registrado: 28 Oct 2010 14:20
Ubicación: retrocrypta

Re: Cazadores de mitos(DreamWalker Alter Ego 2)

Mensaje por ron »

-thumbup -thanks -good -snoozer_likelinux_man -yes
Avatar de Usuario
minter
Mensajes: 5106
Registrado: 22 Jul 2014 18:51

Re: Cazadores de mitos(DreamWalker Alter Ego 2)

Mensaje por minter »

ackerman escribió:Dado que en este foro, se respetan las leyes de la termodinámica, no se puede viajar en el tiempo.
homer.jpg
homer.jpg (18.83 KiB) Visto 632 veces
Avatar de Usuario
broderick77
Mensajes: 310
Registrado: 21 Jun 2022 12:46

Re: Cazadores de mitos(DreamWalker Alter Ego 2)

Mensaje por broderick77 »

"Leyes terrrmodinamica serr un coñazo, yo preferrir anarquismo cuantico, jaaaa"

Mister Planck dixit
--------------------------------------------------
A algunos les gusta el "Paku paku", y a los Commodores el "poke poke"
-------------------------------------------------
Avatar de Usuario
Eremus
Mensajes: 211
Registrado: 01 May 2022 18:10

Re: Cazadores de mitos(DreamWalker Alter Ego 2)

Mensaje por Eremus »

Hola!

Me parece interesante el análisis de Dreamwalker y el motor pero tengo serias dudas de que esto sea más rápido que ejecutarlo en una implementación bien afinada del comportamiento de la ULA con su contención y demás.

Una de las cosas que creo que haría perder el supuesto ahorro de rendimiento es que esta función:
ackerman escribió: Pasa a detectar que es la zona de 768 bytes de atributos. En este caso se ha usado un array de 16 con los 768 atributos, y se ha dejado sin optimizar para que se vea la forma genérica y fácil:

Código: Seleccionar todo

unsigned char gb_atributos[768*16];
unsigned char gb_atributos_cont[768];

void writebyte(uint16_t addr, uint8_t data) 
{
  gb_real_write_ptr_ram[(addr>>14)][(addr & 0x3fff)] = data;

  if (addr>=0x5800 && addr<(0x5800+768))
  {
   unsigned short int idAtributo= addr-0x5800;
   unsigned char contador= gb_atributos_cont[idAtributo];
   if (contador<16)
   {    
    gb_atributos[(idAtributo*16)+contador]= data;    
    contador++;
    gb_atributos_cont[idAtributo]= contador;
   }   
}
Afectaría muchísimo al rendimiento general del emulador al añadir complejidad a writebyte que es algo que se va a llamar una salvajada de veces durante el ciclo.

Es cierto que, después, el dibujado de la pantalla puede ser mas directo. Pero no tanto porque, además, hay que revisar y procesar adecuadamente toda la información recopilada en writebyte.

En resumen: molan mucho estas ideas y las leo con atención pero, en este caso, creo que no seria mejor que una buena implementación cycle accurate.

Saludos!
Avatar de Usuario
ackerman
Mensajes: 525
Registrado: 05 Feb 2019 21:32
Ubicación: Asturias
Contactar:

Re: Cazadores de mitos(DreamWalker Alter Ego 2)

Mensaje por ackerman »

Eremus escribió: Afectaría muchísimo al rendimiento general del emulador al añadir complejidad a writebyte que es algo que se va a llamar una salvajada de veces durante el ciclo.
Hace mucho tiempo, ya había comentado que siempre que se trata el tema del cycle accurate, de una manera genérica, es decir, sin especificar una plataforma hardware concreta, a mi (personalmente) me da la sensación, de que parece un campo en el que los temas a tratar aparentan o quieren dar la sensación de parecer muy difusos. Además, da la percepción de que se pretende reconducir, a una única forma de realizar las cosas.

La verdad que del NIRVANA apenas he hablado, sólo he rozado la punta del iceberg, y aún así ha quedado una chapa que cuesta leer, para que negarlo. Pero en ocasiones “hay que poner las cartas sobre la mesa”.

A diferencia de otros juegos, el NIRVANA consume todo en dibujar, de manera, que a nivel de emulación Z80 consume mucho, pero a nivel de HOST, consume muy poco. En concreto a nivel de Z80 real, no deja más de 5 fps a frame completo, frente a los 50 fps de cualquier otro juego, por eso, aunque es muy colorido, se usa para animaciones lentas.

Preocuparse por el consumo de este algoritmo, tan trivial, frente a un cycle accurate, sería el equivalente:
 ! Mensaje de: ackerman
Alguien se rompe un brazo y se hace un micro roce superficial en la piel durante la caída. Y lo primero que hacemos es poner el grito en el cielo por el roce, e ir corriendo a poner una tirita.
Como había comentado, el algoritmo lo he dejado para que se entendiera, es decir, no se ha optimizado en absoluto, pero como se puede ver, no es más que una simple pila, es decir, que puede quedar en 2 instrucciones. Se ha especificado como algoritmo del pintor, pero en ningún momento se trabaja con el área BITMAP de la memoria de video, como si se pintara por encima. Realmente se está trabajando con los atributos, y no se dibuja nada encima, sino que se procesa la pila uno detrás de otro, que cada uno equivale a los colores por scanline, no que se dibuje 4 u 8 veces por caracter encima. Los modos multicolor pueden llegar hasta 4 pixels, es decir, que podríamos llegar a nivel 16, pero el juego DreamWalker sólo llega a nivel 4, es decir, cada 2 scanlines tiene 1 nivel de colores.

La rutina de redibujado, el consumo adicional por tratar esto, es totalmente despreciable, dado que por cada carácter siempre se pinta por scanlines, y cada scanline va a ser un nivel, no necesita procesar una información que no este acotada, ni tiene que calcular nada complejo.

Cuando estamos jugando en el nivel 1 del juego, tenemos por cada frame (20 ms):
2882 escrituras a memoria writebyte del juego, de las cuales, 1800 son de los atributos.

Este 1800 es lo que en el post original comenté:

Código: Seleccionar todo

30 col  x 15 filas x 4 niveles = 1800
Pero es que NIRVANA para la escritura de atributos usa writeword, es decir, que serían 900 llamadas a writeword, en lugar de 1800 a writebyte, así que siempre salimos ganando.

Siempre que doy resultados, intento quedarme con el caso peor, mientras que la mayoría de la gente da resultados basándose en los casos mejores, de forma que muchas veces, no se corresponde con la realidad.

En cualquier caso, la función se puede activar o desactivar, dado que está totalmente separado del núcleo, no interfiriendo para nada en el resto de juegos. Además, como comenté, no se tiene en cuenta el temporizador virtual (tstates) ni el real (microsegundos), es decir, nos abstraemos y separamos, quedándonos con el anclaje sólo en writebyte o writeword.
No se tiene en cuenta nada de los ciclos durante la ejecución de un loop completo de 70000 tstates.

Un accurate cycle, está impregnado y mezclado el tiempo en todo, y no sólo en writebyte, sino en readbyte, readword, readport, writeport, etc.…

Pero vamos, que la pila de atributos, es lo de menos, porque si se va por el camino de simular NIRVANA, tal y como comenté de BIFROST, se consigue la máxima eficiencia.
Responder

Volver a “ESP32 TTGO VGA32”