Pregunta ¿Cómo funcionan los emuladores y cómo están escritos? [cerrado]


¿Cómo funcionan los emuladores? Cuando veo emuladores NES / SNES o C64, me sorprende.

http://www.tommowalker.co.uk/snemzelda.png

¿Tiene que emular el procesador de esas máquinas interpretando sus instrucciones particulares de ensamblaje? ¿Qué más entra? ¿Cómo están típicamente diseñados?

¿Puede dar algún consejo para alguien interesado en escribir un emulador (particularmente un sistema de juego)?


969
2018-01-15 22:10


origen


Respuestas:


La emulación es un área multifacética. Aquí están las ideas básicas y los componentes funcionales. Voy a dividirlo en pedazos y luego completar los detalles a través de ediciones. Muchas de las cosas que voy a describir requerirán el conocimiento del funcionamiento interno de los procesadores: el conocimiento de ensamblaje es necesario. Si soy demasiado vago en ciertas cosas, por favor haga preguntas para poder continuar mejorando esta respuesta.

Idea básica:

La emulación funciona manejando el comportamiento del procesador y los componentes individuales. Usted construye cada pieza individual del sistema y luego conecta las piezas como lo hacen los cables en el hardware.

Emulación del procesador:

Hay tres formas de manejar la emulación del procesador:

  • Interpretación
  • Recompilación dinámica
  • Recompilación estática

Con todas estas rutas, tiene el mismo objetivo general: ejecutar una pieza de código para modificar el estado del procesador e interactuar con 'hardware'. El estado del procesador es un conglomerado de registros de procesador, controladores de interrupción, etc. para un objetivo de procesador determinado. Para el 6502, tendría un número de enteros de 8 bits que representan los registros: A, X, Y, Py S; también tendrías un 16 bits PC registro.

Con la interpretación, comienzas en el IP (puntero de instrucción - también llamado PC, contador de programa) y lee las instrucciones de la memoria. Su código analiza estas instrucciones y utiliza esta información para alterar el estado del procesador según lo especificado por su procesador. El problema central con la interpretación es que es muy lento; cada vez que maneja una instrucción dada, debe decodificarla y realizar la operación requerida.

Con la recompilación dinámica, itera sobre el código al igual que la interpretación, pero en lugar de simplemente ejecutar códigos de operación, crea una lista de operaciones. Una vez que llegue a una instrucción de bifurcación, compilará esta lista de operaciones para el código de máquina para su plataforma de host, luego almacenará en caché este código compilado y lo ejecutará. Luego, cuando vuelves a golpear un grupo de instrucciones dado, solo tienes que ejecutar el código desde la memoria caché. (Por cierto, la mayoría de las personas no hace una lista de instrucciones, sino que las compila al código de máquina sobre la marcha, esto hace que sea más difícil de optimizar, pero eso está fuera del alcance de esta respuesta, a menos que haya suficiente gente interesada)

Con la recompilación estática, haces lo mismo que en la recompilación dinámica, pero sigues las ramas. Terminas construyendo una porción de código que representa todo el código en el programa, que luego puede ejecutarse sin más interferencias. Este sería un gran mecanismo si no fuera por los siguientes problemas:

  • El código que no está en el programa para empezar (por ejemplo, comprimido, encriptado, generado / modificado en tiempo de ejecución, etc.) no se volverá a compilar, por lo que no se ejecutará.
  • Se ha demostrado que encontrar todo el código en un binario dado es equivalente a Detener el problema

Estos se combinan para hacer la recompilación estática totalmente inviable en el 99% de los casos. Para obtener más información, Michael Steil ha realizado una gran investigación sobre la recompilación estática, la mejor que he visto.

La emulación del lado opuesto al procesador es la forma en que usted interactúa con el hardware. Esto realmente tiene dos lados:

  • Tiempo del procesador
  • Manejo de interrupciones

Tiempo del procesador:

Ciertas plataformas, especialmente consolas antiguas como NES, SNES, etc., requieren que su emulador tenga un tiempo estricto para ser completamente compatible. Con el NES, tiene el PPU (unidad de procesamiento de píxeles) que requiere que la CPU ponga píxeles en su memoria en momentos precisos. Si usa la interpretación, puede contar ciclos fácilmente y emular el tiempo apropiado; con la recompilación dinámica / estática, las cosas son mucho / más complejas.

Manejo de interrupción:

Las interrupciones son el mecanismo primario que la CPU comunica con el hardware. En general, sus componentes de hardware le dirán a la CPU qué interrupciones le importan. Esto es bastante sencillo: cuando su código arroja una interrupción dada, observa la tabla del manejador de interrupciones y llama a la devolución de llamada adecuada.

Emulación de hardware:

Hay dos lados para emular un dispositivo de hardware dado:

  • Emulando la funcionalidad del dispositivo
  • Emular las interfaces reales del dispositivo

Tome el caso de un disco duro. La funcionalidad se emula al crear el almacenamiento de respaldo, las rutinas de lectura / escritura / formato, etc. Esta parte generalmente es muy sencilla.

La interfaz real del dispositivo es un poco más compleja. Por lo general, esta es una combinación de registros mapeados en memoria (por ejemplo, partes de la memoria que el dispositivo vigila para ver si hay cambios) e interrumpe. Para un disco duro, es posible que tenga un área de memoria asignada donde coloque comandos de lectura, escritura, etc., luego lea estos datos nuevamente.

Voy a entrar en más detalles, pero hay un millón de formas en que puede hacerlo. Si tiene alguna pregunta específica aquí, siéntase libre de preguntar y agregaré la información.

Recursos:

Creo que he dado una introducción bastante buena aquí, pero hay una tonelada de áreas adicionales. Estoy más que feliz de ayudar con cualquier pregunta; He sido muy vago en la mayoría de esto simplemente debido a la inmensa complejidad.

Obligatorio Wikipedia enlaces:

Recursos de emulación generales:

  • Zophar  - Aquí es donde comencé con la emulación, primero descargando emuladores y finalmente saqueando sus inmensos archivos de documentación. Este es el mejor recurso posible que puedas tener.
  • NGEmu - No hay muchos recursos directos, pero sus foros son inmejorables.
  • RomHacking.net- La sección de documentos contiene recursos relacionados con la arquitectura de la máquina para consolas populares

Proyectos de emulador a referenciar:

  • IronBabel - Esta es una plataforma de emulación para .NET, escrita en Nemerle y recompila el código para C # sobre la marcha. Descargo de responsabilidad: Este es mi proyecto, así que perdone el enchufe sin vergüenza.
  • BSnes - Un asombroso emulador de SNES con el objetivo de una precisión de ciclo perfecto.
  • MAME - los emulador de arcade Gran referencia.
  • 6502asm.com  - Este es un emulador JavaScript 6502 con un pequeño y genial foro.
  • dynarec'd 6502asm - Este es un pequeño truco que hice durante uno o dos días. Tomé el emulador existente de 6502asm.com y lo cambié para recompilar dinámicamente el código a JavaScript para incrementos masivos de velocidad.

Referencias de recompilación del procesador:

  • La investigación sobre recompilación estática realizada por Michael Steil (referencia arriba) culminó en este papel y puedes encontrar la fuente y tal aquí.

Apéndice:

Ha pasado más de un año desde que se envió esta respuesta y con toda la atención que ha estado recibiendo, pensé que era hora de actualizar algunas cosas.

Tal vez lo más emocionante en la emulación en este momento es libcpu, iniciado por el ya mencionado Michael Steil. Es una biblioteca pensada para admitir una gran cantidad de núcleos de CPU, que utilizan LLVM para la recompilación (¡estática y dinámica!). Tiene un gran potencial, y creo que hará grandes cosas para la emulación.

emu-docs también me llamó la atención, que alberga un gran repositorio de documentación del sistema, que es muy útil para fines de emulación. No pasé mucho tiempo allí, pero parece que tienen muchos recursos geniales.

Me alegro de que esta publicación haya sido útil, y espero poder sacarme el culo y terminar mi libro sobre el tema antes de fin de año / principios del próximo año.


1127
2018-01-15 22:13



Un tipo llamado Víctor Moya del Barrio escribió su tesis sobre este tema. Mucha buena información en 152 páginas. Puedes descargar el PDF aquí.

Si no quieres registrarte con garabatear, puedes buscar el título de PDF en google "Estudio de las técnicas para la programación de emulación". Hay un par de fuentes diferentes para el PDF.


126
2018-02-02 17:27



La emulación puede parecer desalentadora, pero en realidad es bastante más fácil que simular.

Cualquier procesador generalmente tiene una especificación bien escrita que describe estados, interacciones, etc.

Si no le importaba el rendimiento en absoluto, entonces podría emular fácilmente a la mayoría de los procesadores más antiguos utilizando programas orientados a objetos muy elegantes. Por ejemplo, un procesador X86 necesitaría algo para mantener el estado de los registros (fácil), algo para mantener el estado de la memoria (fácil) y algo que tomaría cada comando entrante y lo aplicaría al estado actual de la máquina. Si realmente deseaba precisión, también emularía las traducciones de memoria, el almacenamiento en caché, etc., pero eso es factible.

De hecho, muchos fabricantes de microchips y CPU prueban programas contra un emulador del chip y luego contra el chip mismo, lo que les ayuda a averiguar si hay problemas en las especificaciones del chip o en la implementación real del chip en el hardware. Por ejemplo, es posible escribir una especificación de chip que daría lugar a interbloqueos, y cuando se produce una fecha límite en el hardware, es importante ver si se puede reproducir en la especificación, ya que eso indica un problema mayor que algo en la implementación del chip.

Por supuesto, los emuladores de videojuegos normalmente se preocupan por el rendimiento, por lo que no usan implementaciones ingenuas, y también incluyen código que interactúa con el sistema operativo del sistema host, por ejemplo, para usar dibujo y sonido.

Teniendo en cuenta el rendimiento muy lento de los viejos videojuegos (NES / SNES, etc.), la emulación es bastante fácil en los sistemas modernos. De hecho, es aún más sorprendente que solo puedas descargar un juego de cada juego de SNES o cualquier juego de Atari 2600, teniendo en cuenta que cuando estos sistemas eran populares, tener acceso gratuito a cada cartucho habría sido un sueño hecho realidad.


43
2018-01-15 22:43



Sé que esta pregunta es un poco vieja, pero me gustaría agregar algo a la discusión. La mayoría de las respuestas aquí se centran en emuladores que interpretan las instrucciones de la máquina de los sistemas que emulan.

Sin embargo, hay una excepción muy conocida a esto llamada "UltraHLE" (Artículo de WIKIpedia) UltraHLE, uno de los emuladores más famosos que se haya creado, emulaba los juegos comerciales de Nintendo 64 (con un rendimiento decente en las computadoras hogareñas) en un momento en que se consideraba que era imposible hacerlo. ¡De hecho, Nintendo todavía estaba produciendo nuevos títulos para Nintendo 64 cuando se creó UltraHLE!

Por primera vez, vi artículos sobre emuladores en revistas impresas donde antes, solo los había visto discutidos en la web.

El concepto de UltraHLE era hacer posible lo imposible al emular llamadas de biblioteca C en lugar de llamadas a nivel de máquina.


29
2017-07-07 18:17



Algo que vale la pena mirar es el intento de Imran Nazar de escribir un Gameboy emulador en JavaScript.


22
2017-11-11 10:19



Después de haber creado mi propio emulador de la microcomputadora BBC de los 80 (escriba VBeeb en Google), hay una serie de cosas que debe saber.

  • No estás emulando lo real como tal, eso sería una réplica. En cambio, estás emulando Estado. Un buen ejemplo es una calculadora, la verdadera tiene botones, pantalla, estuche, etc. Pero para emular una calculadora solo necesita emular si los botones están arriba o abajo, qué segmentos de la pantalla LCD están encendidos, etc. Básicamente, un conjunto de números representando todas las posibles combinaciones de cosas que pueden cambiar en una calculadora.
  • Solo necesita que la interfaz del emulador aparezca y se comporte como la realidad. Cuanto más convincente es esto, más cerca está la emulación. Lo que sucede detrás de escena puede ser lo que quieras. Pero, para facilitar la escritura de un emulador, existe una asignación mental que ocurre entre el sistema real, es decir, chips, pantallas, teclados, placas de circuitos y el código abstracto de la computadora.
  • Para emular un sistema de computadora, es más fácil dividirlo en pedazos más pequeños y emular esos fragmentos individualmente. Luego, junten todo el lote para el producto terminado. Al igual que un conjunto de cajas negras con entradas y salidas, que se presta maravillosamente a la programación orientada a objetos. Puede subdividir estos trozos para facilitar la vida.

Hablando en términos prácticos, generalmente estás buscando escribir para la velocidad y la fidelidad de la emulación. Esto se debe a que el software en el sistema de destino se ejecutará (más lentamente) que el hardware original en el sistema de origen. Eso puede limitar la elección del lenguaje de programación, los compiladores, el sistema de destino, etc.
Además de eso, debe circunscribir lo que está preparado para emular, por ejemplo, no es necesario emular el estado de voltaje de los transistores en un microprocesador, pero probablemente sea necesario emular el estado del conjunto de registros del microprocesador.
En términos generales, cuanto menor es el nivel de detalle de la emulación, más fidelidad obtendrás del sistema original.
Finalmente, la información para sistemas más antiguos puede ser incompleta o inexistente. ¡Así que conseguir el equipo original es esencial, o al menos destacar otro buen emulador que alguien más ha escrito!


18
2017-08-05 15:36



Sí, tiene que interpretar todo el código del código binario de la máquina "a mano". No solo eso, la mayoría de las veces también tienes que simular algún hardware exótico que no tenga un equivalente en la máquina de destino.

El enfoque simple es interpretar las instrucciones una a una. Eso funciona bien, pero es lento. Un enfoque más rápido es la recompilación: traducir el código máquina de origen al código máquina objetivo. Esto es más complicado, ya que la mayoría de las instrucciones no se correlacionarán uno a uno. En su lugar, tendrá que realizar elaboradas soluciones que impliquen código adicional. Pero al final es mucho más rápido. La mayoría de los emuladores modernos hacen esto.


17
2018-01-15 22:17



Cuando desarrolla un emulador, está interpretando el ensamblaje del procesador en el que está trabajando el sistema (Z80, 8080, CPU PS, etc.).

También necesita emular todos los periféricos que tiene el sistema (salida de video, controlador).

Deberías comenzar a escribir emuladores para los sistemas simpe como el viejo y bueno Game Boy (que use un procesador Z80, no estoy confundiendo) O para C64.


15
2018-01-15 22:21