Nota: Han llegado un par de respuestas. Considere también votar a favor de nuevas respuestas.
- Lisp común de happy5214
- C de luser droog
- Java de NeatMonster
- Javascript desde crempp
- C de Mike C
- C ++ de Darius Goad
- Postdata de luser droog
- C ++ de JoeFish
- Javascript desde enteramente subjetivo
- C de RichTX
- C ++ de Dave C
- Haskell de JB
- Python de ja
El 8086 es el primer microprocesador x86 de Intel. Su tarea es escribir un emulador para ello. Como esto es relativamente avanzado, quiero limitarlo un poco:
- Solo se deben implementar los siguientes códigos de operación:
- mov, empujar, pop, xchg
- add, adc, sub, sbb, cmp y, o, xor
- inc, dec
- llamada, ret, jmp
- jb, jz, jbe, js, jnb, jnz, jnbe, jns
- stc, clc
- hlt, nop
- Como resultado de esto, solo necesita calcular las marcas de acarreo, cero y signos
- No implemente segmentos. Asumir
cs = ds = ss = 0
. - Sin prefijos
- No hay tipos de interrupciones o puertos IO
- Sin funciones de cadena
- No hay códigos de operación de dos bytes (0F ..)
- Sin aritmética de coma flotante
- (obviamente) no hay cosas de 32 bits, sse, mmx, ... lo que aún no se ha inventado en 1979
- No tiene que contar ciclos ni hacer ningún tiempo
Comience con ip = 0
y sp = 100h
.
Entrada: su emulador debe tomar un programa binario en cualquier tipo de formato que le guste como entrada (leer del archivo, matriz predefinida, ...) y cargarlo en la memoria en la dirección 0.
Salida: la RAM de video comienza en la dirección 8000h, cada byte es un carácter (ASCII-). Emule una pantalla de 80x25 a la consola. Tratar cero bytes como espacios.
Ejemplo:
08000 2E 2E 2E 2E 2E 2E 2E 2E 2E 00 00 00 00 00 00 00 ................
08010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08050 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 00 00 00 Hello,.world!...
Nota: Esto es muy similar al modo de video real, que generalmente está en 0xB8000 y tiene otro byte por carácter para los colores.
Criterios ganadores:
- Todas las instrucciones mencionadas deben implementarse
Hice un programa de prueba sin comentarios ( enlace , fuente nasm ) que debería ejecutarse correctamente. Sale
......... Hello, world! 0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ################################################################################ ## ## ## 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 ## ## ## ## 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 ## ## ## ## 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ################################################################################
No estoy muy seguro de si esto debería ser codegolf; es una tarea difícil, por lo que cualquier presentación ganará muchos votos a favor de todos modos. Por favor comenta.
Aquí hay algunos enlaces para ayudarlo en esta tarea:
- formato de instrucciones , más
- tabla de código de operación
- descripciones de código de operación
- Decodificación de bytes R / M mod de 16 bits
- registros , banderas registro
- Manual de 1979
Esta es mi primera entrada a esta plataforma. Si hay algún error, indíquelo; Si me perdí un detalle, simplemente pregunte.
fuente
Respuestas:
Siéntase libre de bifurcar y jugar golf: https://github.com/julienaubert/py8086
También incluí un depurador interactivo.
Hay tres archivos: emu8086.py (requerido) console.py (opcional para salida de pantalla), disasm.py (opcional, para obtener una lista de los asm en el codegolf).
Para ejecutar con la pantalla (la nota usa maldiciones):
Para ejecutar con depurador interactivo:
Para ejecutar con "depurador" no interactivo:
El programa " codegolf " debe estar en el mismo directorio.
emu8086.py
console.py
disasm.py
En github
fuente
Haskell,
256234196 líneasHe tenido este trabajo en progreso durante algún tiempo, tenía la intención de pulirlo un poco más antes de publicarlo, pero ahora la diversión comenzó oficialmente, ya no tiene mucho sentido mantenerlo oculto. Al extraerlo, noté que tiene exactamente 256 líneas de largo, así que supongo que está en un punto "notable" de su existencia.
Qué hay dentro: apenas suficiente del conjunto de instrucciones 8086 para ejecutar el binario de ejemplo sin problemas. El código de auto-modificación es compatible. (captación previa: cero bytes)
Irónicamente, las primeras iteraciones suficientes del código fueron más largas y admitieron menos espacio del código de operación. La refactorización resultó beneficiosa tanto para la longitud del código como para la cobertura del código operativo.
Lo que está fuera: obviamente, segmentos, prefijos y códigos de operación multibyte, interrupciones, puertos de E / S, operaciones de cadena y FP. Inicialmente seguí el
PUSH SP
comportamiento original , pero tuve que abandonarlo después de algunas iteraciones.Los resultados del indicador de acarreo probablemente estén muy desordenados en algunos casos de
ADC
/SBB
.De todos modos, aquí está el código:
La salida del binario de muestra proporcionado coincide perfectamente con la especificación. Pruébelo utilizando una invocación como:
La mayoría de las operaciones no implementadas simplemente resultarán en una falla de coincidencia de patrones.
Todavía tengo la intención de factorizar un poco más e implementar la salida en vivo real con maldiciones.
Actualización 1: lo bajó a 234 líneas. Organizó mejor el código por funcionalidad, realineó lo que podría ser, trató de atenerse a 80 columnas. Y refactorizó la ALU varias veces.
Actualización 2: han pasado cinco años, pensé que una actualización para que se compilara perfectamente en el último GHC podría estar en orden. Por el camino:
<$>
y<*>
en el preludio.Como dicen los comentarios del código, 5 líneas (la importación Data.Char, las asignaciones de registro de 8 bits y el volcado de pantalla) están fuera de especificación, por lo que le invitamos a descontarlas si se siente tan inclinado :-)
fuente
.|.
? / 10charC - 7143 líneas (CPU en sí 3162 líneas)
EDITAR: La compilación de Windows ahora tiene menús desplegables para cambiar los discos virtuales.
He escrito un emulador de PC 80186 / V20 completo (con CGA / MCGA / VGA, sound blaster, adlib, mouse, etc.), no es una cosa trivial emular un 8086 de ninguna manera. Tomó muchos meses ser completamente exacto. Aquí está el módulo de la CPU solo fuera de mi emulador.
http://sourceforge.net/p/fake86/code/ci/master/tree/src/fake86/cpu.c
Seré el primero en admitir que uso muchas variables globales en este emulador. Empecé a escribir esto cuando todavía era bastante nuevo en C, y se nota. Necesito limpiar algo de eso uno de estos días. La mayoría de los otros archivos fuente no se ven tan feos.
Puede ver todo el código (y algunas capturas de pantalla, una a continuación) a través de aquí: http://sourceforge.net/p/fake86
Me encantaría ayudar a cualquier otra persona que quiera escribir la suya, porque es muy divertido, ¡y aprendes MUCHO sobre la CPU! Descargo de responsabilidad: no agregué la emulación 8080 del V20 ya que casi nunca se usó en un programa de PC. Parece mucho trabajo sin ganancia.
fuente
Postdata (
130200367517531222246 líneas)Sigue siendo un trabajo en progreso, peroQuería mostrar algo de código en un esfuerzo por alentar a otros a mostrar algo de código .El conjunto de registros se representa como una cadena, por lo que los diversos registros de bytes y de tamaño de palabra pueden superponerse naturalmente al referirse a las subcadenas. Las subcadenas se utilizan como punteros en todas partes, de modo que un registro y una ubicación de memoria (subcadena de la cadena de memoria) se pueden tratar de manera uniforme en las funciones del operador.
Luego hay un puñado de palabras para obtener y almacenar datos (byte o palabra) desde un "puntero", desde la memoria, desde mem [(IP)] (IP en aumento). Luego hay algunas funciones para buscar el byte MOD-REG-R / M y establecer las variables REG y R / M y MOD, y decodificarlas usando tablas. Luego, el operador funciona, tecleado al byte del código de operación. Entonces el ciclo de ejecución es simple
fetchb load exec
.Solo he implementado un puñado de códigos de operación, pero gHacer que la decodificación del operando se sintiera como un hito tan importante que quería compartirlo.editar: palabras agregadas para firmar-extender números negativos. Más códigos de operación. Corrección de errores en las tareas de registro. Comentarios Todavía trabajando en banderas y llenando los operadores. La salida presenta algunas opciones: salida de texto a salida estándar en la terminación, salida continua usando códigos vt100, salida a la ventana de imagen usando la fuente CP437.
editar: Terminada la escritura, comenzó la depuración. ¡Obtiene los primeros cuatro puntos de salida! Entonces el acarreo sale mal. Soñoliento.
editar: Creo que tengo la bandera de transporte ordenada. Parte de la historia sucedió en comp.lang.postscript . Agregué algunos aparatos de depuración, y la salida se dirige a la ventana de gráficos (usando mi fuente Code-Page 437 Type-3 previamente escrita ), por lo que la salida de texto puede estar llena de trazas y volcados. Escribe "¡Hola Mundo!" y luego está esa preocupación sospechosa. Entonces un montón de nada. :( Llegaremos allí. ¡Gracias por todo el aliento!
editar: ejecuta la prueba hasta su finalización. Los últimos errores fueron: XCHG haciendo 2 {read store} repite lo que, por supuesto, copia en lugar de intercambios, Y no establece banderas, (FE) INC intenta obtener una palabra de un puntero de byte.
editar: Reescritura total desde cero utilizando la tabla concisa del manual (¡se volvió una nueva página! ). Estoy empezando a pensar que descifrar la tienda de los códigos de operación era una mala idea, pero ayudó a mantener el optab bonito. No hay captura de pantalla esta vez. Agregué un contador de instrucciones y un mod-trigger para volcar la memoria de video, por lo que se intercala fácilmente con la información de depuración.
editar: ejecuta el programa de prueba, de nuevo! Los últimos errores para la reescritura más corta fueron descuidar firmar-extender el byte inmediato en los códigos de operación 83 (el grupo "Inmediato") y EB (JMP corto). El aumento de 24 líneas cubre rutinas de depuración adicionales necesarias para rastrear esos errores finales.
Y la salida (con el final de la salida de depuración abreviada).
fuente
Javascript
Estoy escribiendo un emulador 486 en javascript inspirado en jslinux. Si hubiera sabido cuánto trabajo sería, probablemente nunca hubiera comenzado, pero ahora quiero terminarlo.
Luego me encontré con su desafío y me sentí muy feliz de tener un programa 8086 para probar.
Puede "verlo" en vivo aquí: http://codinguncut.com/jsmachine/
Tuve un problema al imprimir el búfer de gráficos. Donde debería haber espacios, la memoria contiene elementos "00". ¿Es correcto interpretar "0x00" como espacio o tengo un error en mi emulador?
Salud,
Johannes
fuente
C ++
Me gustaría enviar nuestra entrada para este desafío de código. Fue escrito en c ++ y ejecuta el programa de prueba perfectamente. Hemos implementado el 90% de los códigos de operación de un byte y la segmentación básica (algunos desactivados porque no funciona con el programa de prueba).
Programa de redacción: http://davecarruth.com/index.php/2012/04/15/creating-an-8086-emulator
Puede encontrar el código en un archivo zip al final de la publicación del blog.
Captura de pantalla que ejecuta el programa de prueba:
Esto tomó bastante tiempo ... si tiene alguna pregunta o comentario, no dude en enviarme un mensaje. Sin duda fue un gran ejercicio en la programación de socios.
fuente
ret imm
instrucciones son incorrectas (vea aquí ) y se está perdiendo el0xff
grupo. Sin embargo, me gustan sus mensajes de error: arroje "El valor inmediato no puede almacenar un valor, retardo";cs
registroC
Gran desafío y el primero. Creé una cuenta solo porque el desafío me intrigaba mucho. La desventaja es que no podía dejar de pensar en el desafío cuando tenía trabajo real, de pago y de programación que hacer.
Me siento obligado a ejecutar una emulación 8086 completa, pero ese es otro desafío ;-)
El código está escrito en ANSI-C, así que solo compile / vincule los archivos .c, pase el código binario codegolf y listo.
fuente comprimida
fuente
C ++ 1064 líneas
Fantástico proyecto. Hice un emulador de Intellivision hace muchos años, así que fue genial flexionar mis músculos de golpe de nuevo.
Después de aproximadamente una semana de trabajo, no podría haber estado más emocionado cuando esto sucedió:
Un poco de depuración más tarde y ... ¡SHAZAM!
Además, reconstruí el programa de prueba original sin las extensiones 80386, ya que quería construir mi emulador fiel al 8086 y no falsificar ninguna instrucción adicional. Enlace directo al código aquí: archivo Zip .
Ok, me estoy divirtiendo mucho con esto. Rompí la gestión de memoria y pantalla, y ahora la pantalla se actualiza cuando se escribe en el búfer de pantalla. Hice un video :)
http://www.youtube.com/watch?v=qnAssaTpmnA
Actualizaciones: está en el primer paso de la segmentación. Muy pocas instrucciones se implementan realmente, pero lo probé moviendo CS / DS y SS, y todo sigue funcionando bien.
También se agregó el manejo rudimentario de interrupciones. Muy rudimentario. Pero implementé int 21h para imprimir una cadena. Se agregaron algunas líneas a la fuente de prueba y también se subieron.
Si alguien tiene un código de ensamblaje bastante simple que pruebe los segmentos, me encantaría jugar con él.
Estoy tratando de averiguar qué tan lejos quiero llevar esto. Emulación de CPU completa? Modo VGA? Ahora estoy escribiendo DOSBox.
6/12: ¡Compruébalo, modo VGA!
fuente
C ++ - 4455 líneas
Y no, no solo hice los requisitos de la pregunta. Hice TODO 8086, incluidos 16 códigos de operación CONOCIDOS nunca antes conocidos. Reenigne ayudó a descifrar esos códigos de operación.
https://github.com/Alegend45/IBM5150
fuente
#include "cpu.h"
es difícil de ver.Javascript - 4,404 líneas
Me topé con esta publicación cuando busqué información para mi propio emulador. Esta publicación de Codegolf ha sido absolutamente invaluable para mí. El programa de ejemplo y el ensamblaje asociado permitieron depurar y ver fácilmente lo que estaba sucediendo.
¡¡¡Gracias!!!
Y aquí está la primera versión de mi emulador Javascript 8086.
caracteristicas:
Manifestación
Tengo una demostración en línea, siéntase libre de jugar con ella y avíseme si encuentra errores :)
http://js86emu.chadrempp.com/
Para ejecutar el programa codegolf
1) haga clic en el botón de configuración
2) luego simplemente haga clic en cargar (puede jugar con las opciones de depuración aquí, como pasar por el programa). El programa codegolf es el único disponible en este momento, estoy trabajando para obtener más en línea.
Fuente
Fuente completa aquí. https://github.com/crempp/js86emu
Traté de pegar las entrañas de la emulación 8086 aquí (como lo sugiere el pomo de la puerta) pero superó el límite de caracteres ("El cuerpo está limitado a 30000 caracteres; ingresó 158,272").
Aquí hay un enlace rápido al código que iba a pegar aquí: https://github.com/crempp/js86emu/blob/39dbcb7106a0aaf59e003cd7f722acb4b6923d87/src/js/emu/cpus/8086.js
*Edit - updated for new demo and repo location
fuente
Java
Había querido hacer este desafío durante tanto tiempo, y finalmente me tomé el tiempo para hacerlo. Ha sido una experiencia maravillosa hasta ahora y me enorgullece anunciar que finalmente la he completado.
Fuente
El código fuente está disponible en GitHub en NeatMonster / Intel8086 . He tratado de documentar casi todo, con la ayuda del Manual del usuario de la familia holly 8086 .
Tengo la intención de implementar todos los códigos de operación y características que faltan, por lo que es posible que desee consultar el lanzamiento 1.0 para una versión con solo los necesarios para este desafío.
Muchas gracias a @copy!
fuente
Common Lisp - 580 loc (442 sin líneas en blanco y comentarios)
Usé este desafío como una excusa para aprender Common Lisp. Aquí está el resultado:
Aquí está la salida en Emacs:
Quiero destacar tres características principales. Este código hace uso intensivo de las macros en la aplicación de instrucciones, como
mov
,xchg
y las operaciones artithmetic. Cada instrucción incluye unadisasm-instr
llamada macro. Esto implementa el desensamblaje junto con el código real usando un if sobre un conjunto de variables globales en tiempo de ejecución. Estoy particularmente orgulloso del enfoque agnóstico de destino utilizado para escribir valores en registros y direcciones indirectas. Las macros que implementan las instrucciones no se preocupan por el destino, ya que los formularios empalmados para cualquier posibilidad funcionarán con lasetf
macro genérica Common Lisp.El código se puede encontrar en mi repositorio de GitHub . Busque la rama "codegolf", ya que ya he comenzado a implementar otras características del 8086 en master. Ya he implementado los indicadores de desbordamiento y paridad, junto con el registro FLAGS.
Hay tres operaciones en esto, no en el 8086, las versiones
0x82
y0x83
de los operadores lógicos. Esto fue atrapado muy tarde, y sería bastante complicado eliminar esas operaciones.Me gustaría agradecer a @ja por su versión de Python, que me inspiró al principio de esta aventura.
fuente
C -
319348 líneasEsta es una traducción más o menos directa de mi programa Postscript a C. Por supuesto, el uso de la pila se reemplaza con variables explícitas. Los campos de una instrucción se dividen en las variables
o
: byte de código de operación de la instrucción,d
campo de dirección,w
campo de ancho. Si se trata de una instrucción "mod-reg-r / m", se lee el byte mr-rmstruct rm r
. La decodificación de los campos reg y r / m se realiza en dos pasos: calculando el puntero a los datos y cargando los datos, reutilizando la misma variable. Entonces, para algo asíADD AX,BX
, primero x es un puntero a ax e y es un puntero a bx, luego x es el contenido (ax) e y es el contenido (bx). Se requiere mucha conversión para reutilizar la variable para diferentes tipos como este.El byte opcode se decodifica con una tabla de punteros de función. Cada cuerpo de función está compuesto por macros para piezas reutilizables. La
DW
macro está presente en todas las funciones del código de operación y decodifica las variablesd
yw
delo
byte del código de operación. LaRMP
macro realiza la primera etapa de decodificación del byte "mr-rm" yLDXY
realiza la segunda etapa. Los códigos de operación que almacenan un resultado usan lap
variable para mantener el puntero a la ubicación del resultado y laz
variable para mantener el valor del resultado. Las banderas se calculan después de calcular elz
valor. Las operacionesINC
yDEC
guardan el indicador de acarreo antes de usar laMATHFLAGS
función genérica (como parte deADD
oSUB
submacro) y restaurarlo después de palabras, para preservar el Carry.Editar: errores corregidos!
Editar: expandido y comentado. Cuando
trace==0
ahora genera un comando ANSI move-to-0,0 al volcar el video. Por lo tanto, simula mejor una pantalla real. LaBIGENDIAN
cosa (que ni siquiera funcionó) ha sido eliminada. Se basa en algunos lugares en el orden de bytes little-endian, pero planeo arreglar esto en la próxima revisión. Básicamente, todo el acceso al puntero debe pasar por las funcionesget_
yput_
que explícitamente (des) componen los bytes en el orden LE.El uso de macros para las etapas de las diversas operaciones hace una coincidencia semántica muy cercana a la forma en que el código postscript funciona de manera puramente secuencial. Por ejemplo, los primeros cuatro códigos de operación, 0x00-0x03, son todas instrucciones de AGREGAR con dirección variable (REG -> REG / MOD, REG <- REG / MOD) y tamaños de bytes / palabras, por lo que se representan exactamente igual en la tabla de funciones .
La tabla de funciones se instancia con esta macro:
que se aplica
OPF()
a cada representación de código de operación.OPF()
Se define como:Entonces, los primeros cuatro códigos de operación se expanden (una vez) a:
Estas funciones se distinguen por el resultado de la
DW
macro que determina la dirección y los bits de byte / palabra directamente desde el byte del código de operación. Expandir el cuerpo de una de estas funciones (una vez) produce:Donde el bucle principal ya ha establecido la
o
variable:Expandir una vez más da toda la "carne" del código de operación:
Y la función totalmente preprocesada, pasó a través de
indent
:No es el mejor estilo C para uso diario, pero usar macros de esta manera parece bastante perfecto para hacer que la implementación aquí sea muy corta y muy directa.
Probar la salida del programa, con la cola de la salida de rastreo:
Compartí algunas versiones anteriores en comp.lang.c pero no estaban muy interesados.
fuente
indent
ed 5810 líneas.