¿Cómo funcionan los emuladores y cómo se escriben? [cerrado]


968

¿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 de montaje particulares? ¿Qué más entra? ¿Cómo se diseñan típicamente?

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


15
Lo más importante que debe encontrar es el "manual del programador" para ese sistema, ya que detalla el "contrato" entre el proveedor de HW y los programadores, y oculta detalles que no son relevantes y podrían cambiar. Sus posibilidades dependen de la popularidad del sistema.
Uri el

155
Buena elección de juego.
Cristián Romo el


16
Para cualquiera que se pregunte Emulación vs Simulación
Lazer

8
Desde la primera vez que jugué ese juego, siempre me he preguntado por qué Hyrule está lleno de rocas "8-Ball" :-)
Vivian River

Respuestas:


1124

La emulación es un área multifacética. Aquí están las ideas básicas y los componentes funcionales. Voy a romperlo 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 del ensamblaje es necesario. Si soy demasiado vago en ciertas cosas, haga preguntas para que pueda 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 de manera muy similar a 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 un código para modificar el estado del procesador e interactuar con el 'hardware'. El estado del procesador es un conglomerado de registros del procesador, controladores de interrupciones, etc. para un objetivo de procesador dado. Para el 6502, tendría una serie de números enteros de 8 bits que representan registros: A, X, Y, P, y S; también tendrías un PCregistro de 16 bits .

Con la interpretación, comienza en el IP(puntero de instrucciones, también llamado PCcontador 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 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 de forma muy similar a la interpretación, pero en lugar de solo ejecutar códigos de operación, crea una lista de operaciones. Una vez que alcanza una instrucción de bifurcación, compila esta lista de operaciones en el código de máquina para su plataforma host, luego almacena en caché este código compilado y lo ejecuta. Luego, cuando golpeas un grupo de instrucciones dado nuevamente, 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 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 suficientes personas interesadas)

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

  • Para empezar, el código que no está en el programa (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 demostrado que encontrar todo el código en un binario dado es equivalente al problema de detención

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 gran investigación sobre la compilació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:

  • Sincronización 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 el 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 fácilmente los ciclos y emular el tiempo adecuado; con la compilación dinámica / estática, las cosas son / mucho / más complejas.

Manejo de interrupciones:

Las interrupciones son el mecanismo principal 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 determinada, mira la tabla de manejo 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

Tome el caso de un disco duro. La funcionalidad se emula creando 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, esto es una combinación de registros mapeados de memoria (por ejemplo, partes de la memoria que el dispositivo busca cambios para hacer señalización) e interrumpe. Para un disco duro, es posible que tenga un área asignada de 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 buena introducción 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ía 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, primero descargué emuladores y finalmente saqueé sus inmensos archivos de documentación. Este es el mejor recurso absoluto que puede tener.
  • NGEmu : no hay muchos recursos directos, pero sus foros son inmejorables.
  • RomHacking.net - La sección de documentos contiene recursos sobre arquitectura de máquinas para consolas populares

Proyectos de emulador de referencia:

  • IronBabel : esta es una plataforma de emulación para .NET, escrita en Nemerle y recompila el código a C # sobre la marcha. Descargo de responsabilidad: este es mi proyecto, así que perdón por el enchufe descarado.
  • BSnes : un increíble emulador de SNES con el objetivo de una precisión de ciclo perfecto.
  • MAME : el emulador de arcade. Gran referencia
  • 6502asm.com - Este es un emulador JavaScript 6502 con un pequeño foro genial.
  • dynarec'd 6502asm - Este es un pequeño truco que hice durante un día o dos. 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 compilación estática realizada por Michael Steil (mencionada anteriormente) culminó en este documento y puede 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.

Quizás lo más emocionante de la emulación en este momento es libcpu , iniciado por el ya mencionado Michael Steil. Es una biblioteca destinada a admitir una gran cantidad de núcleos de CPU, que utilizan LLVM para la compilació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 ha llamado la atención, 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 alegra que esta publicación haya sido útil, y espero poder sacarme de quicio y terminar mi libro sobre el tema para fin de año / principios del próximo año.


37
Esto ya se está preparando para ser una respuesta épica. Si me puede señalar algún recurso también al final, sería apreciado. Estoy mirando posiblemente el sistema SNES o NES para emular y convertirlo en mi proyecto de semestre.
mmcdole

8
Ciertamente. Voy a armar una buena lista de recursos. Si ustedes tienen alguna solicitud específica, haré todo lo posible para satisfacerla.
Cody Brocious

3
@thenonhacker, el proyecto IronBabel mencionado en mi sección de recursos es mío. (El enchufe descarado está marcado;))
Cody Brocious

1
"Se ha demostrado que encontrar todo el código en un binario dado es equivalente al problema de detención" - ¿Referencia por favor? ¿O debería ser "Se ha demostrado que encontrar todo el código en cualquier binario es equivalente al problema de detención"? Tampoco puedo acceder al artículo de Steil :-(
squelart

44
Mencionas que estás escribiendo un libro; ¿Puedes por favor darnos una actualización al respecto? Yo, por mi parte, estaría interesado en leerlo.
alex


43

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, podría emular fácilmente 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 quisiera precisión, también emularía traducciones de memoria, 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 en sí, 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 resultaría 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 de 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 fácil en los sistemas modernos. De hecho, es aún más sorprendente que 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.


1
¿Cuáles son las diferencias entre emulación y simulación?
Wei Hu

2
@Wei: En términos generales, se supone que un emulador se comporta "externamente" como el sistema que emula, pero no hay nada que decir que debe implementarse de manera similar. Un simulador se implementa de una manera que imita el sistema simulado y, como resultado, se comporta así.
Uri

Cuando vea "Simulador" piense que es similar mientras que un emulador "emula"
mP.


29

Sé que esta pregunta es un poco antigua, 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 jamás creados, emulaba juegos comerciales de Nintendo 64 (con un rendimiento decente en las computadoras domésticas) en un momento en que se consideraba 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 emulando las llamadas de la biblioteca C en lugar de las llamadas a nivel de máquina.


22

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


1
¿Cómo obtenemos las instrucciones de código de operación sin formato para el juego Gameboy?
Pacerier

Hay varios dispositivos disponibles para la venta en el 'mercado gris'. No los encontrará en ninguna tienda importante en el mundo desarrollado. Estos dispositivos son capaces de copiar las instrucciones del cartucho del juego a los archivos que generalmente se llaman "ROM". Google "Gameboy Roms", ¡pero ten cuidado con los enlaces inseguros para el trabajo y los sitios de ataque!
Vivian River

18

Después de haber creado mi propio emulador de la BBC Microcomputer de los años 80 (escriba VBeeb en Google), hay varias 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, lo 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 la pantalla LCD están activados, etc. Básicamente, un conjunto de números representando todas las combinaciones posibles de cosas que pueden cambiar en una calculadora.
  • Solo necesita que la interfaz del emulador aparezca y se comporte como si fuera real. Cuanto más convincente sea esto, más cerca estará la emulación. Lo que sucede detrás de escena puede ser lo que quieras. Pero, para facilitar la escritura de un emulador, hay 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 emularlos individualmente. Luego, unir 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 aún más estos fragmentos para hacer la vida más fácil.

En términos prácticos, generalmente buscas escribir para obtener velocidad y fidelidad de la emulación. Esto se debe a que el software en el sistema de destino (puede) ejecutarse 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, 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 para emular el estado del conjunto de registros del microprocesador.
En términos generales, cuanto menor sea el nivel de detalle de la emulación, mayor fidelidad obtendrá al sistema original.
Finalmente, la información para sistemas más antiguos puede ser incompleta o inexistente. Por lo tanto, obtener el equipo original es esencial, ¡o al menos separar otro buen emulador que alguien más ha escrito!


17

Sí, debe interpretar todo el desorden del código de máquina binario "a mano". No solo eso, la mayoría de las veces también tienes que 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 la máquina fuente al código de la máquina de destino. Esto es más complicado, ya que la mayoría de las instrucciones no se asignarán uno a uno. En su lugar, tendrá que hacer soluciones alternativas que involucren código adicional. Pero al final es mucho más rápido. La mayoría de los emuladores modernos hacen esto.


1
Lo peor es la falta de documentación con diferencia. Es cuando descubres que el núcleo Z80 modificado en GameBoy Color tiene operaciones de bandera indocumentadas que el juego que estás probando usa que realmente empiezas a perder la fe.
Callum Rogers

1
Manía: es código de máquina (singular), no códigos de máquina (plural); así como es el código Morse, no los códigos Morse .
Lawrence Dol

1
@Vilx: en realidad no, el término "código de máquina", que se refiere al conjunto de instrucciones para una CPU, se ha utilizado desde el inicio del software y no es plural. Se refiere al " conjunto de instrucciones ", una forma singular, no a la forma plural "instrucciones". Igual que el código del programa, el código Morse, etc. El uso de la forma plural se ha infiltrado por el mal uso, generalmente por aquellos que hablan inglés como segundo idioma.
Lawrence Dol

1
@Software Monkey: ¿pero no puedo usar la palabra "código" para referirme a un solo elemento del conjunto? Por ejemplo: " ... --- ...- estos tres códigos Morse representan las tres letras S, O, S." Porque ...es un código que representa la letra "S". ¿No?
Vilx-

1
No, el código es un sustantivo incontable, no tiene una forma plural como agua o arena ..
Ivan

15

Cuando desarrolla un emulador, está interpretando el ensamblaje del procesador en el que está trabajando el sistema (Z80, 8080, PS CPU, 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 usa un procesador Z80, no me confundo) O para C64.


99
C64 un sistema "simple"? Si bien el 6510 es relativamente simple (una vez que ha cubierto los códigos de operación no listados), los chips de sonido (SID) y video (VIC) son todo menos simples. Para lograr un nivel de compatibilidad decente, debe emularlos: errores de hardware y todo.
moobaa

10

El emulador es muy difícil de crear, ya que hay muchos hacks (como en efectos inusuales), problemas de tiempo, etc., que debes 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.


9

Visite también Emulators.com de Darek Mihocka para obtener excelentes consejos sobre la optimización del nivel de instrucción para JIT, y muchas otras ventajas en la construcción de emuladores eficientes.


7

Nunca he hecho algo tan elegante como emular una consola de juegos, pero una vez tomé un curso donde 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 de aha. Es posible que desee recoger ese libro antes de sumergirse en escribir un emulador real.


4

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


4

El emulador de dispositivo de fuente compartida contiene un código fuente edificable para un emulador de PocketPC / Smartphone (Requiere Visual Studio, se ejecuta en Windows). Trabajé en V1 y V2 de la versión binaria.

Aborda muchos problemas de emulación: - traducción eficiente de direcciones de virtual de invitado a físico de invitado a virtual de host - Compilación JIT de código de invitado - simulación de dispositivos periféricos como adaptadores de red, pantalla táctil y audio - Integración de interfaz de usuario, para teclado y mouse de host - guardar / restablecimiento de estado, para simulación de curriculum vitae desde modo de bajo consumo


1

Para agregar la respuesta proporcionada por @Cody Brocious
En el contexto de virtualización en el que está emulando un nuevo sistema (CPU, E / S, 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 (del host ISA) para producir el efecto deseado. Sí, es muy lento, no no almacene nada para que todas las instrucciones pasen por el mismo ciclo.

Emalator dinámico: Qemu es un emulador dinámico. La traducción instantánea de la instrucción del invitado también almacena en caché los resultados. 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 menciona Cody, divide el código en bloques (1 flujo único de ejecución).

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


1

Cómo comenzaría la emulación.

1. Obtenga libros basados ​​en programación de bajo nivel, lo necesitará para el sistema operativo "simulado" de Nintendo ... game boy ...

2. Obtenga libros sobre emulación específicamente, y quizás sobre desarrollo. (No harás un sistema operativo, sino el más cercano a él.

3. Mire algunos emuladores de código abierto, especialmente los del sistema para el que desea crear un emulador.

4. Copie fragmentos del código más complejo en su IDE / compilador. Esto le ahorrará escribir código largo. Esto es lo que hago para el desarrollo del sistema operativo, usar un distrito de Linux


1

Escribí un artículo sobre emular el sistema Chip-8 en JavaScript .

Es un gran lugar para comenzar, ya que el sistema no es muy complicado, pero aún así aprende cómo funcionan los códigos de operación, la pila, los registros, etc.

Pronto escribiré una guía más larga para la NES.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.