2 puntos por GN⁺ 2024-06-09 | 1 comentarios | Compartir por WhatsApp
  • La multiplicación de punto flotante de la VU de la PS2 tiene un error de operación de 1 bit, por lo que, con ciertos valores, 1 * X puede terminar siendo distinto de X
  • Según el manual para desarrolladores de la VU, se garantiza la exactitud de X * 1, pero no existe la misma garantía para 1 * X, y esa diferencia se convierte en una señal de detección de emuladores
  • El ejemplo usa 129.5f, uno de los valores problemáticos encontrados por fuerza bruta, para comprobar la diferencia de comportamiento entre una PS2 real y los emuladores
  • La implementación tiene una estructura simple: en modo macro de VU0 multiplica 129.5f por 1 y luego solo compara si el resultado difiere de la entrada original
  • PCSX2, Play!, DobieStation y hps2x64 actualmente no emulan este comportamiento, y la dificultad de detección se evalúa en 1/5

Error de 1 bit en la multiplicación de la VU de PS2

  • Este método es el segundo elemento de la serie sobre detección de emuladores de PS2, y puede usarse en VU1, en modo micro de VU0 y en modo macro de VU0
  • El ejemplo usa modo macro de VU0 para simplificar la implementación
    • Como VU0 se usa como un coprocesador, puede ejecutarse directamente desde la CPU EE
    • No hace falta manejar un programa VU separado
  • En las instrucciones de multiplicación como MUL y MULi del manual para desarrolladores de la VU hay una nota que indica que existe un error de operación de 1 bit
    • 1 * X puede diferir del valor original X
    • Si se usa VF[fs] como multiplicando, se garantiza la exactitud del resultado en la forma X * 1
  • No se ha confirmado con exactitud por qué se pierde el bit

Valor de detección y forma de implementación

  • Para detectar este error se necesita un número que provoque el problema, y la forma más sencilla de buscarlo es por fuerza bruta
  • El autor creó en el pasado una lista de los primeros 250 números que provocan el problema en incrementos de 0.5, y publicó esa lista en un gist
  • El código de ejemplo usa 129.5f como número objetivo de detección
    • Con QMTC2 configura 129.5f en VF1
    • Con VADDw crea 1 en VF2
    • Con VMUL calcula VF1 = 1 * 129.5f
    • Con QMFC2 trae el resultado al lado de EE y lo compara con la entrada
  • El valor de retorno es in[0] != out[0], y si el valor original y el resultado de la multiplicación difieren, se determina que existe el error de multiplicación de la VU

Impacto por emulador

  • Actualmente PCSX2, Play!, DobieStation y hps2x64 no emulan este comportamiento de multiplicación de la VU de PS2
  • Como solo hay que multiplicar un número por 1 y revisar el resultado, la dificultad de este método de detección es de nivel 1/5

1 comentarios

 
GN⁺ 2024-06-09
Comentarios de Hacker News
  • El truco más simple para detectar emulación ARM antigua, que creo que también se usaba en la protección anticopia de Game Boy Advance: guardar una instrucción trampa en la posición PC+4, es decir, justo en la siguiente instrucción
    En un ARM real, por el pipeline, mientras ejecuta desde PC y decodifica PC+4, lee PC+8, así que la instrucción recién guardada no debería tener efecto. Un emulador que no emule el pipeline de hardware terminaría ejecutando esa instrucción
    Un artículo que lo explica con más detalle junto con varias técnicas antiemulación de 2004: https://mgba.io//2014/12/28/classic-nes/
    • El procesador digital de señales TI320C40 de Texas Instruments tenía problemas de pipeline aún más raros: tenía slots de retardo de salto (https://en.wikipedia.org/wiki/Delay_slot), donde una o más instrucciones después de un salto se ejecutaban antes del salto real, y también slots de retardo de carga, en los que un valor guardado en un registro aparecía recién varias instrucciones después
      Supongo que durante algunos ciclos el valor del registro quedaba indefinido. Escribir código assembly ajustadísimo para optimizarlo en chips así era bastante horrible, como jugar un clon de Zachtronics con un gusto especialmente malo
    • En x86, la cola de prefetch producía un comportamiento parecido, pero cuando Intel decidió detectar código automodificable en CPUs posteriores al Pentium, modificar una instrucción que estaba por ejecutarse pasó a tener efecto siempre
      Mucho más tarde alguien encontró otro caso límite que no se detectaba: una instrucción repetitiva de cadenas que se sobrescribía a sí misma
      https://silviocesare.wordpress.com/2009/02/02/anti-debugging...
    • Algunas CPUs con pipeline mantienen compatibilidad con el código automodificable, así que si se sobrescribe una instrucción que ya está en el pipeline, lo detectan y vacían el pipeline
      x86 tiene ese mecanismo, aunque no sé bien si al final lo eliminaron en las variantes de 64 bits
    • ¿Será esta la razón por la que la ROM de Dragon Ball Z: The Legacy of Goku II a veces no funcionaba en VisualBoyAdvance?
  • Esta es precisamente la razón por la que la emulación que apunta al 100% de exactitud es un terreno de artesanía en la industria
    No solo hay que conocer todos los comportamientos peculiares del hardware y el software originales, sino reproducirlos tal cual, por más extraños que sean. Eso ya es difícil de por sí, pero además hay que considerar el impacto en el rendimiento
    • Los emuladores tienen que ser realistas respecto de la exactitud. Al emular sistemas más modernos, normalmente es imposible apuntar al mismo tiempo a una exactitud de hardware del 100% y a un rendimiento usable, así que se aceptan compromisos que son técnicamente distintos del hardware real, pero con diferencias observables en la práctica casi inexistentes
      Usar un recompilador JIT no puede ser perfectamente exacto ciclo por ciclo respecto del hardware original, pero por lo general no importa, salvo que el código del juego esté hecho intencionalmente para romper emuladores
      Dolphin también tuvo que manejar este equilibrio cuando algunos juegos comerciales de Wii incluyeron código antiemulador que abusaba de detalles del comportamiento de caché de la CPU real de Wii. En teoría se podría haber emulado la caché de la CPU real para ejecutar el juego sin problemas, pero el sobrecosto de rendimiento probablemente habría sido del orden de 10 veces más lento, volviéndolo injugable, así que se optó por un parche de evasión
      https://dolphin-emu.org/blog/2017/02/01/dolphin-progress-rep...
  • ¿Cómo se empieza en la emulación? Parece un nicho tremendamente difícil incluso dentro del software
    Da la impresión de que prácticamente hay que entender electrónica y magia profunda de programación
    • Como con cualquier otra cosa, se empieza de forma gradual
      Al comenzar, y en la mayoría de los casos incluso al terminar, no hace falta entender magia profunda. En general se mira la especificación y se implementa lo que dice. Sí hace falta capacidad para estructurar el código de forma que no se vuelva un desastre, pero hay patrones comunes, y después de hacer uno o dos emuladores se vuelve mucho más fácil
      Casi nunca hace falta entender electrónica. Lo que se emula es el comportamiento. Si se descubre un bug en el comportamiento del hardware original, normalmente basta con agregar un tratamiento especial en el emulador. Saber electrónica puede ayudar a entender por qué ocurre ese comportamiento, pero es más un interés histórico que algo práctico
      Tiene sus dificultades propias. Cuando aparece un problema, normalmente terminas depurando tres cosas al mismo tiempo: tu comprensión del hardware, la implementación del emulador y el juego que se está emulando. Puede ser difícil acotar la causa exacta. Aun así, recomiendo armar algo aproximado primero. No es elegante, pero todos los emuladores están llenos de tratamientos especiales para hacer funcionar de algún modo juegos populares. Si con unos hacks sucios el juego corre, haz eso. No hace falta implementar con exactitud el comportamiento del hardware original; hay que lograr que el juego funcione
    • Puedes empezar leyendo documentación de hardware; no hace falta entender la máquina a nivel de circuito electrónico. No es una simulación de circuitos digitales, así que no tiene por qué ser tan complejo
      Una CPU de 8 bits es una máquina de estados simple con unos pocos bytes de estado, es decir, registros. Lee el programa byte por byte y simula lo que hace la CPU después de leer ese byte. Son operaciones muy simples, como sumar y restar números, o leer y guardar bytes
      http://www.6502.org/users/obelisk/6502/registers.html
      http://www.6502.org/users/obelisk/6502/instructions.html
      Un emulador de CPU 6502 lee los siguientes bytes del programa, interpreta esos bytes como instrucciones y ejecuta la instrucción. En el proceso actualiza algunos registros o contadores de la CPU, realiza operaciones aritméticas o de bits y, si hace falta, lee o guarda un byte de datos de una ubicación a otra. Repite este proceso en un bucle infinito

Esto es una simulación del ciclo de búsqueda-decodificación-ejecución
https://en.wikipedia.org/wiki/Instruction_cycle

  • Depende de qué quieras decir con emulación
    Hace años porté de UNIX a Classic Macintosh un intérprete de 6502 para reproducir archivos de música SID. La precisión a nivel de ciclos de reloj no importaba, siempre que corriera lo suficientemente rápido
    Funcionaba llamando código C desde el intérprete
  • Cuando intenté meterme en esto hace mucho tiempo, la recomendación era empezar con algo muy simple y bien documentado, y desarrollar habilidad desde ahí
    Todavía me dan ganas de intentarlo, pero no tengo tiempo
  • Por hacer un poco de promoción: di una charla sobre este tema en el FOSDEM pasado: https://fosdem.org/2024/schedule/event/fosdem-2024-2146-how-...
    Por lo demás, el comentario hermano de @xcv123 está totalmente en lo cierto
  • Es un ejemplo interesante del tipo de cosas por las que probablemente no vale mucho la pena preocuparse en la emulación por software. Emular incluso ese bug la haría bastante más lenta
    Si algún día se vuelve posible clonar la PS2 con un FPGA, averiguar cómo ocurría este comportamiento sería un proyecto divertido para alguien
    • Muchas implementaciones en FPGA también se construyen a partir del código o la documentación de proyectos de emulación por software
      Que sea una versión FPGA de la PS2 no garantiza que no implemente el mismo bug, o uno parecido
    • Depende de si ese bug rompe juegos o no
    • Corregir este bug sería parte del trabajo de corregir varios otros bugs de coma flotante, más específicamente problemas de redondeo y saturación
      La coma flotante por software sería lenta, pero la solución habitual probablemente seguiría la del emulador de PS2 de la PS4: una lista blanca, juego por juego, de secciones de código donde se permite usar la ruta de coma flotante por software
    • ¿Por qué alguien querría emular una vieja y pésima CPU MIPS con un FPGA relativamente caro? La idea central de emular consolas antiguas es poder jugar juegos viejos en una computadora o un teléfono, de forma independiente del hardware
  • ¿A alguien más le pasó que, al ver el título, se confundió pensando por qué un mouse o un teclado tendrían que hacer matemáticas?
    Me tomó demasiado tiempo darme cuenta de que esto hablaba de la PlayStation 2 y no del puerto Personal System/2 para conectar mouse y teclado
    • Uno es PS2 y el otro es PS/2
    • A quienes votaron en contra: que para uno sea claro no significa que sea claro para todos
      Las siglas de tres letras pueden hacer muy difícil encontrar contexto, porque si pones solo la sigla en Google, con bastante frecuencia la mayoría de los resultados son casi irrelevantes