Remarque: quelques réponses sont arrivées. Pensez également à augmenter le nombre de réponses plus récentes.
- Common Lisp de happy5214
- C de luser droog
- Java de NeatMonster
- Javascript de crempp
- C de Mike C
- C ++ de Darius Goad
- Postscript de luser droog
- C ++ de JoeFish
- Javascript entièrement sujet
- C de RichTX
- C ++ de Dave C
- Haskell de JB
- Python de ja
Le 8086 est le premier microprocesseur x86 d’Intel. Votre tâche consiste à écrire un émulateur pour cela. Comme cela est relativement avancé, je veux le limiter un peu:
- Seuls les codes d'opération suivants doivent être implémentés:
- mov, push, pop, xchg
- add, adc, sous, sbb, cmp, et, ou, xor
- inc, dec
- appeler, ret, jmp
- jb, jz, jbe, js, jnb, jnz, jnbe, jns
- stc, clc
- hlt, nop
- En conséquence, il vous suffit de calculer les indicateurs de retenue, de remise à zéro et de signature
- Ne pas implémenter les segments. Supposer
cs = ds = ss = 0
. - Pas de préfixes
- Aucun type d'interruption ou de port IO
- Aucune fonction de chaîne
- Pas d'opcode sur deux octets (0F ..)
- Aucune arithmétique en virgule flottante
- (évidemment) pas de choses 32 bits, sse, mmx, ... tout ce qui n'a pas encore été inventé en 1979
- Vous n'êtes pas obligé de compter les cycles ou de faire un chronométrage
Commencez avec ip = 0
et sp = 100h
.
Entrée: votre émulateur doit prendre en entrée un programme binaire dans le format de votre choix (lire dans un fichier, un tableau prédéfini, ...) et le charger en mémoire à l'adresse 0.
Sortie: La RAM vidéo commence à l'adresse 8000h, chaque octet est composé d'un caractère (ASCII-). Émuler un écran 80x25 à la console. Traiter zéro octets comme des espaces.
Exemple:
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!...
Remarque: Ceci est très similaire au mode vidéo réel, qui est généralement à 0xB8000 et a un autre octet par caractère pour les couleurs.
Critères gagnants:
- Toutes les instructions mentionnées doivent être mises en œuvre
J'ai créé un programme de test non commenté ( lien , source nasm ) qui devrait fonctionner correctement. Il sort
......... 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 ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ################################################################################
Je ne suis pas tout à fait sûr si cela devrait être codegolf; C'est un peu une tâche difficile, donc toute soumission gagnera de nombreux votes positifs. Commentez s'il vous plaît.
Voici quelques liens pour vous aider dans cette tâche:
- format d'instruction , plus
- table d'opcode
- description des codes d'opération
- Décodage d'octets mod R / M 16 bits
- registres , registre des drapeaux
- Manuel de 1979
Ceci est ma première entrée sur cette plateforme. S'il y a des erreurs, veuillez les signaler; si j'ai raté un détail, demandez simplement.
la source
Réponses:
Sentez-vous libre de fourcher et de jouer au golf: https://github.com/julienaubert/py8086
J'ai également inclus un débogueur interactif.
Il existe trois fichiers: emu8086.py (requis) console.py (facultatif pour l'affichage), disasm.py (facultatif, pour obtenir une liste des asm dans le codegolf).
Pour exécuter avec l'affichage (note utilise curses):
Pour exécuter avec le débogueur interactif:
Pour exécuter avec un "débogueur" non interactif:
Le programme " codegolf " devrait être dans le même répertoire.
emu8086.py
console.py
Disasm.py
Sur github
la source
Haskell,
256234196 lignesCela fait un certain temps déjà que je travaille sur ce travail en cours, je voulais le peaufiner un peu plus avant la publication, mais maintenant que le divertissement a officiellement commencé, il n’est plus utile de le cacher. Lors de l'extraction, j'ai remarqué que sa longueur est exactement de 256 lignes. Je suppose donc qu'il est à un stade "remarquable" de son existence.
Contenu: à peine assez de jeu d’instructions 8086 pour exécuter l’exemple binaire sans faille. Le code à modification automatique est pris en charge. (prefetch: zero bytes)
Paradoxalement, les premières itérations suffisantes du code étaient plus longues et prenaient moins en charge la plage de code opération. Le refactoring a été bénéfique à la fois pour la longueur du code et pour la couverture par opcode.
Éléments à exclure: évidemment, les segments, les préfixes et les opcodes multi-octets, les interruptions, les ports d'E / S, les opérations sur les chaînes et les FP. Au départ, j'ai suivi le
PUSH SP
comportement initial , mais j'ai dû le laisser tomber après quelques itérations.Les résultats de l'indicateur de portage sont probablement très perturbés dans quelques cas de
ADC
/SBB
.Quoi qu'il en soit, voici le code:
La sortie de l'exemple de binaire fourni correspond parfaitement à la spécification. Essayez-le en utilisant une invocation telle que:
La plupart des opérations non implémentées entraîneront simplement un échec de correspondance de modèle.
J'ai toujours l'intention de prendre un peu plus de temps et d'implémenter la sortie en direct réelle avec des sorts.
Mise à jour 1: résumé à 234 lignes. Mieux organiser le code par fonctionnalité, ré-aligner ce qui pourrait être, essayer de s'en tenir à 80 colonnes. Et refactored l'ALU plusieurs fois.
Mise à jour 2: Cela fait cinq ans, j'ai pensé qu'une mise à jour lui permettant de compiler parfaitement le dernier GHC pourrait être utile. Le long du chemin:
<$>
et<*>
dans le prélude.Comme l'indiquent les commentaires de code, 5 lignes (l'importation Data.Char, les mappages de registre 8 bits et la copie d'écran) sont hors spécifications. Vous pouvez donc les actualiser si vous vous sentez si enclin :-)
la source
.|.
? / 10charC - 7143 lignes (la CPU elle-même 3162 lignes)
EDIT: La construction de Windows a maintenant des menus déroulants pour changer les disques virtuels.
J'ai écrit un émulateur de PC 80186 / V20 complet (avec CGA / MCGA / VGA, sound blaster, adlib, souris, etc.), ce n'est pas une mince affaire que de simuler un 8086 de quelque façon que ce soit. Il a fallu plusieurs mois pour être parfaitement précis. Voici le module de processeur que de mon émulateur.
http://sourceforge.net/p/fake86/code/ci/master/tree/src/fake86/cpu.c
Je serai le premier à admettre que j'utilise beaucoup trop de variables globales dans cet émulateur. J'ai commencé à écrire cela alors que j'étais encore nouveau dans C, et ça se voit. Je dois en nettoyer un peu un de ces jours. La plupart des autres fichiers source n’ont pas l’air si moche.
Vous pouvez voir tout le code (et quelques captures d'écran, l'une d'entre elles est ci-dessous) à l' adresse suivante : http://sourceforge.net/p/fake86
Je serais très très heureux d'aider quiconque voudrait écrire le leur, car c'est très amusant et vous en apprenez beaucoup sur le processeur! Clause de non-responsabilité: je n'ai pas ajouté l'émulation 8080 du V20 car elle n'a presque jamais été utilisée dans un programme PC. On dirait beaucoup de travail sans gain.
la source
Post-scriptum (
130200367517531222246 lignes)Encore un travail en cours, maisJe voulais montrer du code pour encourager les autres à en montrer .Le jeu de registres étant représenté par une chaîne, les différents registres de la taille en octets et en mots peuvent naturellement se chevaucher en faisant référence à des sous-chaînes. Les sous-chaînes sont utilisées comme pointeurs, de sorte qu'un registre et un emplacement de mémoire (sous-chaîne de la chaîne de mémoire) puissent être traités de manière uniforme dans les fonctions de l'opérateur.
Ensuite, il existe une poignée de mots pour obtenir et stocker des données (octet ou mot) à partir d'un "pointeur", à partir de la mémoire, à partir de mem [(IP)] (incrémentation d'adresse IP). Ensuite, quelques fonctions permettent d'extraire l'octet MOD-REG-R / M, de définir les variables REG, R / M et MOD et de les décoder à l'aide de tables. Ensuite, les fonctions de l'opérateur, associées à l'octet de code d'opération. Donc, la boucle d'exécution est simplement
fetchb load exec
.Je n'ai qu'une poignée d'opcodes implémentés, mais gObtenir le décodage de l'opérande semblait être une étape si importante que je voulais le partager.modifier: mots ajoutés pour signer-étendre les nombres négatifs. Plus d'opcodes. Correction de bug dans les assignations de registre. Commentaires. Travaille toujours sur les drapeaux et remplit les opérateurs. La sortie présente quelques choix: sortie du texte sur stdout à la fin, sortie continue à l'aide de codes vt100, sortie à la fenêtre d'image à l'aide de la police CP437.
edit: écriture terminée, débogage commencé. Il obtient les quatre premiers points de sortie! Ensuite, le portage va mal. Somnolent.
edit: Je pense avoir trié le drapeau Carry. Une partie de l'histoire s'est déroulée sur comp.lang.postscript . J'ai ajouté quelques appareils de débogage et la sortie est affichée dans la fenêtre graphique (à l'aide de la police Code-Type 43 précédemment écrite ), de sorte que la sortie de texte peut être pleine de traces et de vidages. Il écrit "Bonjour le monde!" et puis il y a ce signe suspect. Puis tout un tas rien. :( Nous y arriverons. Merci pour tous les encouragements!
edit: exécute le test jusqu'à la fin. Les derniers bogues étaient: XCHG faisant 2 {read store} répète lequel, bien sûr, copie plutôt que d'échanger, ET ne positionne pas d'indicateurs, (FE) INC essayant d'obtenir un mot à partir d'un pointeur d'octet.
edit: Réécriture totale à partir de zéro en utilisant le tableau concis du manuel (a tourné une nouvelle page! ). Je commence à penser que le factorisation du magasin à partir des codes d'opération était une mauvaise idée, mais cela a permis de garder l'optab beau. Aucune capture d'écran cette fois. J'ai ajouté un compteur d'instructions et un déclencheur de mod pour vider la mémoire vidéo, de sorte qu'il s'entrelace facilement avec les informations de débogage.
edit: Lance le programme de test, encore! Les derniers bogues pour la réécriture plus courte ont été omis de signer-étendre l'octet immédiat dans les opcodes 83 (le groupe "Immediate") et EB (short JMP). L'augmentation de 24 lignes couvre les routines de débogage supplémentaires nécessaires pour retrouver ces derniers bogues.
Et la sortie (avec la fin de la sortie de débogage abrégée).
la source
Javascript
J'écris un émulateur 486 en javascript inspiré par jslinux. Si j'avais su combien de travail cela ferait, je n'aurais probablement jamais commencé, mais maintenant je veux le finir.
Ensuite, j’ai relevé votre défi et étais très heureux de pouvoir tester un programme 8086.
Vous pouvez "voir" le programme en direct ici: http://codinguncut.com/jsmachine/
J'ai eu un problème lors de l'impression du tampon graphique. Là où il devrait y avoir des espaces, la mémoire contient "00" éléments. Est-il correct d'interpréter "0x00" comme espace ou ai-je un bogue dans mon émulateur?
À votre santé,
Johannes
la source
C ++
Je voudrais soumettre notre entrée pour ce défi de code. Il a été écrit en c ++ et exécute parfaitement le programme de test. Nous avons implémenté 90% des codes opérationnels à un octet et de la segmentation de base (certains sont désactivés car ils ne fonctionnent pas avec le programme de test).
Programme d'écriture: http://davecarruth.com/index.php/2012/04/15/creating-an-8086-emulator
Vous pouvez trouver le code dans un fichier zip à la fin du blog.
Capture d'écran exécutant le programme de test:
Cela a pris un peu de temps ... si vous avez des questions ou des commentaires, n'hésitez pas à m'envoyer un message. Ce fut certainement un excellent exercice de programmation pour les partenaires.
la source
ret imm
instruction est erronée (voir ici ) et que le0xff
groupe vous manque . J'aime vos messages d'erreur cependant: "La valeur immédiate ne peut pas stocker une valeur, retarder";cs
registreC
Grand défi et mon premier. J'ai créé un compte simplement parce que le défi m'intriguait tellement. L'inconvénient est que je ne pouvais pas m'empêcher de penser au défi lorsque j'avais du travail réel, payant et programmant à faire.
Je me sens obligé de faire fonctionner une émulation 8086 terminée, mais c'est un autre défi ;-)
Le code est écrit en ANSI-C, il suffit donc de compiler / lier les fichiers .c ensemble, de passer le fichier binaire codegolf et de partir.
source zippée
la source
C ++ 1064 lignes
Projet fantastique. J'ai utilisé un émulateur Intellivision il y a de nombreuses années. C'était donc génial de pouvoir à nouveau faire travailler mes muscles.
Après environ une semaine de travail, je n'aurais pas pu être plus excité lorsque cela s'est produit:
Un peu de débogage plus tard et ... SHAZAM!
De plus, j'ai reconstruit le programme de test original sans les extensions 80386, car je voulais construire mon émulateur fidèle au 8086 et ne pas fudge dans des instructions supplémentaires. Lien direct vers le code ici: fichier Zip .
Ok je m'amuse trop avec ça. J'ai éclaté la gestion de la mémoire et de l'écran, et maintenant l'écran est mis à jour lorsque le tampon d'écran est écrit. J'ai fait une vidéo :)
http://www.youtube.com/watch?v=qnAssaTpmnA
Mises à jour: Le premier passage de la segmentation est terminé. Très peu d'instructions sont réellement implémentées, mais je l'ai testée en déplaçant le CS / DS et le SS, et tout fonctionne toujours bien.
Également ajouté une gestion rudimentaire des interruptions. Très rudimentaire. Mais j'ai implémenté int 21h pour imprimer une chaîne. Ajout de quelques lignes à la source de test et de le télécharger également.
Si quelqu'un a un code d'assemblage assez simple pour tester les segments, j'aimerais jouer avec.
J'essaie de savoir jusqu'où je veux aller. Emulation complète du processeur? Mode VGA? Maintenant j'écris DOSBox.
12/6: Check it out, mode VGA!
la source
C ++ - 4455 lignes
Et non, je n'ai pas fait que répondre aux exigences de la question. J'ai fait le ENTIRE 8086, y compris 16 opcodes jamais connus. Reenigne a aidé à comprendre ces opcodes.
https://github.com/Alegend45/IBM5150
la source
#include "cpu.h"
est difficile de voir.Javascript - 4 404 lignes
Je suis tombé sur ce post lors de la recherche d'informations pour mon propre émulateur. Ce post Codegolf a été une aide précieuse pour moi. L'exemple de programme et l'assemblage associé ont permis de déboguer facilement et de voir ce qui se passait.
Je vous remercie!!!
Et voici la première version de mon émulateur Javascript 8086.
Fonctionnalités:
Démo
J'ai une démo en ligne, n'hésitez pas à y jouer et laissez-moi savoir si vous trouvez des bugs :)
http://js86emu.chadrempp.com/
Pour exécuter le programme codegolf
1) cliquez sur le bouton des paramètres
2) puis cliquez simplement sur load (vous pouvez jouer avec les options de débogage ici, comme passer par le programme). Le programme codegolf est le seul disponible pour le moment, je suis en train d’obtenir plus d’informations en ligne.
La source
Source complète ici. https://github.com/crempp/js86emu
J'ai essayé de coller ici les entrailles de l'émulation 8086 (comme suggéré par la poignée de porte), mais celle-ci dépassait la limite de caractères ("Corps limité à 30000 caractères; vous avez entré 158 272").
Voici un lien rapide vers le code que j'allais coller ici: https://github.com/crempp/js86emu/blob/39dbcb7106a0aaf59e003cd7f722acb4b6923d87/src/js/emu/cu/cpus/8086.js
*Edit - updated for new demo and repo location
la source
Java
J'avais envie de relever ce défi depuis si longtemps et j'ai finalement pris le temps de le faire. Cela a été une expérience merveilleuse jusqu'à présent et je suis fier d'annoncer que je l'ai enfin terminée.
La source
Le code source est disponible sur GitHub à l' adresse NeatMonster / Intel8086 . J'ai essayé de documenter à peu près tout, à l'aide du manuel d'utilisation holly 8086 Family .
J'ai l'intention d'implémenter tous les opcodes et fonctionnalités manquants. Vous voudrez peut-être consulter la version 1.0 pour une version ne contenant que celles requises pour ce défi.
Merci beaucoup à @copy!
la source
Common Lisp - 580 loc (442 sans lignes vierges ni commentaires)
J'ai utilisé ce défi comme excuse pour apprendre Common Lisp. Voici le résultat:
Voici la sortie dans Emacs:
Je veux souligner trois caractéristiques principales. Ce code utilise beaucoup de macros lors de l' application des instructions, telles que
mov
,xchg
et les opérations artithmetic. Chaque instruction comprend undisasm-instr
appel de macro. Cela implémente le désassemblage à côté du code réel en utilisant un if sur une variable globale définie au moment de l'exécution. Je suis particulièrement fier de l’approche agnostique à la destination utilisée pour écrire des valeurs dans des registres et des adresses indirectes. Les macros implémentant les instructions ne se soucient pas de la destination, car les formulaires fusionnés pour l'une ou l'autre possibilité fonctionneront avec lasetf
macro générique Common Lisp.Le code peut être trouvé sur mon dépôt GitHub . Recherchez la branche "codegolf", car j'ai déjà commencé à implémenter d'autres fonctionnalités du 8086 en maître. J'ai déjà implémenté les indicateurs de débordement et de parité, ainsi que le registre FLAGS.
Il y a trois opérations en cela, pas dans 8086, les versions
0x82
et0x83
des opérateurs logiques. Cela a été pris très tard et il serait assez désordonné de supprimer ces opérations.Je voudrais remercier @ja pour sa version Python, qui m'a inspiré très tôt dans cette aventure.
la source
C -
319348 lignesC’est une traduction plus ou moins directe de mon programme PostScript en C. Bien entendu, l’utilisation de la pile est remplacée par des variables explicites. Les champs d'une instruction sont divisés en variables
o
- octet d'instruction d'instruction,d
- champ de direction,w
- champ de largeur. S'il s'agit d'une instruction "mod-reg-r / m", l' octet mr-rm est lustruct rm r
. Le décodage des champs reg et r / m se déroule en deux étapes: calcul du pointeur sur les données et chargement des données, réutilisation de la même variable. Donc, pour quelque chose commeADD AX,BX
, d'abord x est un pointeur sur ax et y est un pointeur sur bx, alors x est le contenu (ax) et y est le contenu (bx). Il faut beaucoup de transtypage pour réutiliser la variable pour différents types comme celui-ci.L'octet de code d'opération est décodé avec une table de pointeurs de fonction. Chaque corps de fonction est composé à l’aide de macros pour les morceaux réutilisables. La
DW
macro est présente dans toutes les fonctions d'opcode et décode les variablesd
etw
de l'o
octet d'opcode. LaRMP
macro effectue la première étape de décodage de l'octet "mr-rm" etLDXY
exécute la deuxième étape. Les opcodes qui stockent un résultat utilisent lap
variable pour maintenir le pointeur sur l'emplacement du résultat et laz
variable pour conserver la valeur du résultat. Les indicateurs sont calculés une fois laz
valeur calculée. Les opérationsINC
etDEC
sauvegardent l’indicateur de port avant d’utiliser laMATHFLAGS
fonction générique (dans le cadre duADD
ouSUB
submacro) et le restaure après, afin de préserver le Carry.Edit: bugs corrigés!
Edit: développé et commenté. Quand
trace==0
il sort maintenant une commande ANSI move-to-0,0 lors du vidage de la vidéo. Donc, cela simule mieux un affichage réel. LaBIGENDIAN
chose (qui n'a même pas fonctionné) a été supprimée. À certains endroits, il se base sur l'ordre des octets little-endian, mais je prévois de résoudre ce problème lors de la prochaine révision. Fondamentalement, tout accès par pointeur doit passer par les fonctionsget_
andput_
qui composent (de) explicitement la composition des octets dans l’ordre du LE.L'utilisation de macros pour les étapes des différentes opérations permet une correspondance sémantique très proche de la manière dont le code postscript fonctionne de manière purement séquentielle. Par exemple, les quatre premiers opcodes, 0x00-0x03, sont toutes des instructions ADD avec des directions différentes (REG -> REG / MOD, REG <- REG / MOD) et des tailles en octets / mots, de sorte qu'elles sont exactement identiques dans le tableau des fonctions. .
La table de fonction est instanciée avec cette macro:
qui s'applique
OPF()
à chaque représentation d'opcode.OPF()
est défini comme:Ainsi, les quatre premiers opcodes se développent (une fois) pour:
Ces fonctions se distinguent par le résultat de la
DW
macro qui détermine la direction et les bits octet / mot directement à partir de l'octet. L'élargissement du corps d'une de ces fonctions (une fois) produit:Où la boucle principale a déjà défini la
o
variable:Agrandir encore une fois donne toute la "viande" de l'opcode:
Et la fonction entièrement prétraité, passée à travers
indent
:Pas le meilleur style C pour un usage quotidien, mais l’utilisation de macros de cette manière semble être un choix idéal pour rendre la mise en œuvre très courte et très directe.
Sortie du programme de test, avec la fin de la sortie de trace:
J'ai partagé certaines versions antérieures de comp.lang.c mais elles n'étaient pas très intéressées.
la source
indent
ed 5810 lignes.