emulation son ¿Cómo funcionan los emuladores y cómo se escriben?




youtube romualdfons (13)

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

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

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


El emulador de dispositivo de fuente compartida contiene código fuente compilable para un emulador de PocketPC / Smartphone (requiere Visual Studio, se ejecuta en Windows). Trabajé en V1 y V2 del lanzamiento binario.

Aborda muchos problemas de emulación: - traducción eficiente de direcciones de virtual invitado a físico de huésped a virtual de host - compilación JIT de código de huésped - simulación de dispositivos periféricos como adaptadores de red, pantalla táctil y audio - integración de IU, para teclado y mouse host - guardar Restauración de estado, para simulación de reanudación desde modo de bajo consumo.


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


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

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


Habiendo creado mi propio emulador de la BBC Microcomputadora de los años 80 (escriba VBeeb en Google), hay una serie de cosas que debe saber.

  • No estás emulando la cosa real como tal, eso sería una réplica. En cambio, estás emulando al estado . Un buen ejemplo es una calculadora, la cosa real tiene botones, pantalla, estuche, etc. Pero para emular una calculadora solo necesita emular si los botones están arriba o abajo, qué segmentos de LCD están encendidos, etc. Básicamente, un conjunto de números Representa todas las combinaciones posibles de cosas que pueden cambiar en una calculadora.
  • Solo necesita la interfaz del emulador para aparecer y comportarse como si fuera real. 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 un mapeo mental que ocurre entre el sistema real, es decir, chips, pantallas, teclados, placas de circuitos y el código de computadora abstracto.
  • Para emular un sistema informático, es más fácil dividirlo en trozos más pequeños y emular esos trozos individualmente. Luego ensarte todo el lote para el producto terminado. Al igual que un conjunto de cajas negras con entradas y salidas, que se presta a la programación orientada a objetos. Puede subdividir aún más estos trozos para hacer la vida más fácil.

En términos prácticos, generalmente se busca escribir para obtener velocidad y fidelidad de emulación. Esto se debe a que el software en el sistema de destino se ejecutará (puede) más lentamente que el hardware original en el sistema de origen. Eso puede restringir la elección del lenguaje de programación, compiladores, sistema de destino, etc.
Además, tiene que circunscribir lo que está preparado para emular, por ejemplo, no es necesario emular el estado de voltaje de los transistores en un microprocesador, pero es probable que sea necesario emular el estado del conjunto de registros del microprocesador.
En general, cuanto menor sea el nivel de detalle de la emulación, mayor será la fidelidad que obtendrá con el 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, diferenciar otro buen emulador que alguien más ha escrito!


La emulación es un área multifacética. Aquí están las ideas básicas y 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 al manejar 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
  • Recompilacion dinamica
  • Recompilacion estatica

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

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

Con la recompilación dinámica, se itera sobre el código de manera muy similar a la interpretación, pero en lugar de simplemente ejecutar opcodes, se crea una lista de operaciones. Una vez que llega a una instrucción de bifurcación, compila esta lista de operaciones en código de máquina para su plataforma de host, luego almacena en caché este código compilado y lo ejecuta. Luego, cuando vuelvas a golpear un grupo de instrucciones dado, solo tienes que ejecutar el código desde el caché. (Por cierto, la mayoría de las personas en realidad no hacen una lista de instrucciones, sino que las compilan en 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 comenzar (por ejemplo, comprimido, cifrado, generado / modificado en tiempo de ejecución, etc.) no se volverá a compilar, por lo que no se ejecutará
  • Se ha comprobado que encontrar todo el código en un binario dado es equivalente al problema de Detener

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

El otro lado de la emulación del procesador es la forma en que interactúa con el hardware. Esto realmente tiene dos lados:

  • Tiempo del procesador
  • Manejo de interrupciones

Sincronización del procesador:

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

Manejo de interrupciones:

Las interrupciones son el mecanismo principal que la CPU se comunica con el hardware. En general, los componentes de su hardware le dirán a la CPU qué interrupciones le preocupan. Esto es bastante sencillo: cuando su código produce una interrupción determinada, 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.
  • Emulando las interfaces reales del dispositivo

Tomemos 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 es generalmente muy sencilla.

La interfaz real del dispositivo es un poco más compleja. En general, esta es una combinación de registros asignados en la memoria (por ejemplo, partes de la memoria que el dispositivo observa para hacer cambios en la señalización) e interrumpe. Para un disco duro, es posible que tenga un área asignada en la memoria donde coloque comandos de lectura, escrituras, etc., y luego vuelva a leer estos datos.

Me gustaría entrar en más detalles, pero hay un millón de formas en que puedes hacerlo. Si tiene alguna pregunta específica aquí, no dude en preguntar y agregaré la información.

Recursos:

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

Enlaces obligatorios de Wikipedia:

Recursos generales de emulación:

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

Emulador de proyectos a referencia:

  • IronBabel : esta es una plataforma de emulación para .NET, escrita en Nemerle y que recompila el código a C # sobre la marcha. Descargo de responsabilidad: este es mi proyecto, así que perdón por el descarado enchufe.
  • BSnes : un increíble emulador de SNES con el objetivo de lograr una precisión de ciclo perfecta.
  • MAME - El emulador de arcade. Gran referencia.
  • 6502asm.com - Este es un emulador de JavaScript 6502 con un pequeño foro genial.
  • 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 aumentos masivos de velocidad.

Referencias de la compilación del procesador:

  • La investigación sobre la recompilación estática realizada por Michael Steil (mencionada anteriormente) culminó en este documento y puede encontrar la fuente y tal here .

Apéndice:

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

Quizás lo más emocionante en la emulación en este momento sea libcpu , iniciado por el mencionado Michael Steil. Es una biblioteca diseñada para admitir una gran cantidad de núcleos de CPU, que utilizan LLVM para la recompilación (¡estático y dinámico!). Tiene un gran potencial, y creo que hará grandes cosas para la emulación.

También me han llamado la atención emu-docs , que alberga un gran depósito de documentación del sistema, que es muy útil para fines de emulación. No he pasado mucho tiempo allí, pero parece que tienen muchos recursos excelentes.

Me alegro de que esta publicación haya sido útil, y espero poder salir de mi trasero y terminar mi libro sobre el tema para finales de año / principios del próximo año.


¿Consejos para emular un sistema real o lo tuyo? Puedo decir que los emuladores funcionan emulando TODO el hardware. Tal vez no hacia el circuito (como mover los bits como lo haría el HW. Mover el byte es el resultado final, por lo que copiar el byte está bien). Los emuladores son muy difíciles de crear, ya que hay muchos hacks (como en efectos inusuales), problemas de tiempo, etc. que necesitas simular. Si una pieza (entrada) es incorrecta, todo el sistema puede fallar o, en el mejor de los casos, tener un error / falla.


Para agregar la respuesta provista por @Cody Brocious
En el contexto de la virtualización donde está emulando un nuevo sistema (CPU, I / O, etc.) a una máquina virtual, podemos ver las siguientes categorías de emuladores.

Interpretación: bochs es un ejemplo de intérprete, es un emulador de PC x86, toma cada instrucción del sistema invitado y la traduce en otro conjunto de instrucciones (de la ISA del host) para producir el efecto deseado. Sí, es muy lento, no No almacene en caché nada para que cada instrucción pase por el mismo ciclo.

Emalador dinámico: Qemu es un emulador dinámico. La traducción instantánea de la instrucción de invitado también almacena los resultados en caché. La mejor parte es que ejecuta tantas instrucciones como sea posible directamente en el sistema host para que la emulación sea más rápida. También como lo mencionó Cody, divide el código en bloques (1 único flujo de ejecución).

Emulador estático: Hasta donde sé, no hay un emulador estático que pueda ser útil en la virtualización.


Nunca he hecho nada tan sofisticado como para emular una consola de juegos, pero una vez hice un curso en el que la tarea era escribir un emulador para la máquina descrita en Andrew Tanenbaums Structured Computer Organization . Eso fue divertido y me dio muchos momentos aha. Es posible que desee recoger ese libro antes de sumergirse en escribir un emulador real.


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

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

Debería comenzar a escribir emuladores para los sistemas Simpe como el viejo Game Boy (que utiliza un procesador Z80, no me equivoco) O para C64.


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

Cualquier procesador normalmente 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 la mayoría de los procesadores antiguos utilizando programas muy elegantes orientados a objetos. 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 deseara 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 los programas contra un emulador del chip y luego contra el propio chip, lo que les ayuda a descubrir 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 podría resultar en puntos muertos, y cuando se produce una fecha límite en el hardware, es importante ver si podría reproducirse en la especificación, ya que eso indica un problema mayor que algo en la implementación del chip.

Por supuesto, los emuladores para videojuegos generalmente 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 videojuegos antiguos (NES / SNES, etc.), la emulación es bastante sencilla en los sistemas modernos. De hecho, es aún más sorprendente que solo puedas descargar un juego de todos los juegos SNES de la historia o cualquier juego Atari 2600, considerando que cuando estos sistemas eran populares, tener acceso gratuito a todos los cartuchos hubiera sido un sueño hecho realidad.


Los emuladores son muy difíciles de crear, ya que hay muchos hacks (como en efectos inusuales), problemas de tiempo, etc. que necesitas simular.

Para ver un ejemplo de esto, consulte http://queue.acm.org/detail.cfm?id=1755886 .

Eso también le mostrará por qué 'necesita' una CPU de varios GHz para emular una de 1MHz.



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 los 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 jamás creados, emuló los juegos comerciales de Nintendo 64 (con un rendimiento decente en las computadoras domésticas) en un momento en el que se consideraba imposible hacerlo. De hecho, ¡Nintendo todavía estaba produciendo nuevos títulos para la Nintendo 64 cuando se creó UltraHLE!

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

El concepto de UltraHLE era hacer posible lo imposible mediante la emulación de llamadas a la biblioteca C en lugar de llamadas a nivel de máquina.