L'émulation est un domaine à multiples facettes. Voici les idées de base et les composants fonctionnels. Je vais le diviser en morceaux, puis remplir les détails via des modifications. Beaucoup de choses que je vais décrire nécessiteront une connaissance du fonctionnement interne des processeurs - la connaissance de l'assemblage est nécessaire. Si je suis un peu trop vague sur certaines choses, veuillez poser des questions afin que je puisse continuer à améliorer cette réponse.
Idée basique:
L'émulation fonctionne en gérant le comportement du processeur et des composants individuels. Vous construisez chaque pièce individuelle du système, puis connectez les pièces comme le font les fils dans le matériel.
Émulation de processeur:
Il existe trois façons de gérer l'émulation du processeur:
- Interprétation
- Recompilation dynamique
- Recompilation statique
Avec tous ces chemins, vous avez le même objectif global: exécuter un morceau de code pour modifier l'état du processeur et interagir avec le «matériel». L'état du processeur est une conglomération des registres du processeur, des gestionnaires d'interruption, etc. pour une cible de processeur donnée. Pour le 6502, vous auriez un certain nombre d'entiers 8 bits représentant des registres: A
, X
, Y
, P
et S
; vous auriez également un PC
registre 16 bits .
Avec l'interprétation, vous commencez au IP
(pointeur d'instruction - également appelé PC
, compteur de programme) et lisez l'instruction dans la mémoire. Votre code analyse cette instruction et utilise ces informations pour modifier l'état du processeur comme spécifié par votre processeur. Le problème central de l'interprétation est qu'elle est très lente; chaque fois que vous manipulez une instruction donnée, vous devez la décoder et effectuer l'opération requise.
Avec la recompilation dynamique, vous parcourez le code un peu comme l'interprétation, mais au lieu d'exécuter simplement des opcodes, vous créez une liste d'opérations. Une fois que vous avez atteint une instruction de branche, vous compilez cette liste d'opérations sur le code machine de votre plate-forme hôte, puis vous mettez en cache ce code compilé et l'exécutez. Ensuite, lorsque vous frappez à nouveau un groupe d'instructions donné, vous n'avez qu'à exécuter le code à partir du cache. (BTW, la plupart des gens ne font pas réellement une liste d'instructions mais les compilent à la machine à la volée - cela le rend plus difficile à optimiser, mais cela sort du cadre de cette réponse, sauf si suffisamment de personnes sont intéressées)
Avec la recompilation statique, vous faites la même chose qu'en recompilation dynamique, mais vous suivez les branches. Vous finissez par construire un morceau de code qui représente tout le code du programme, qui peut ensuite être exécuté sans autre interférence. Ce serait un excellent mécanisme s'il n'y avait pas les problèmes suivants:
- Le code qui n'est pas dans le programme pour commencer (par exemple compressé, chiffré, généré / modifié au moment de l'exécution, etc.) ne sera pas recompilé, il ne s'exécutera donc pas
- Il a été prouvé que trouver tout le code dans un binaire donné équivaut au problème d'arrêt
Celles-ci se combinent pour rendre la recompilation statique complètement irréalisable dans 99% des cas. Pour plus d'informations, Michael Steil a fait de grandes recherches sur la recompilation statique - le meilleur que j'ai vu.
L'autre côté de l'émulation de processeur est la manière dont vous interagissez avec le matériel. Cela a vraiment deux côtés:
- Synchronisation du processeur
- Interruption de la manipulation
Calendrier du processeur:
Certaines plates-formes - en particulier les consoles plus anciennes comme NES, SNES, etc. - nécessitent que votre émulateur ait un timing strict pour être complètement compatible. Avec le NES, vous avez le PPU (unité de traitement des pixels) qui nécessite que le CPU place des pixels dans sa mémoire à des moments précis. Si vous utilisez l'interprétation, vous pouvez facilement compter les cycles et émuler le bon timing; avec la recompilation dynamique / statique, les choses sont / beaucoup / plus complexes.
Interruption de la manipulation:
Les interruptions sont le principal mécanisme que la CPU communique avec le matériel. Généralement, vos composants matériels indiqueront au CPU les interruptions dont il se soucie. C'est assez simple - lorsque votre code lève une interruption donnée, vous regardez la table du gestionnaire d'interruption et appelez le rappel approprié.
Émulation matérielle:
L'émulation d'un périphérique matériel donné a deux côtés:
- Émuler la fonctionnalité de l'appareil
- Émulation des interfaces de périphérique réelles
Prenons le cas d'un disque dur. La fonctionnalité est émulée en créant le stockage de sauvegarde, les routines de lecture / écriture / formatage, etc. Cette partie est généralement très simple.
L'interface réelle de l'appareil est un peu plus complexe. Il s'agit généralement d'une combinaison de registres mappés en mémoire (par exemple, des parties de la mémoire que le périphérique surveille pour les modifications à effectuer pour la signalisation) et des interruptions. Pour un disque dur, vous pouvez avoir une zone mappée en mémoire où vous placez des commandes de lecture, des écritures, etc., puis relisez ces données.
J'irais plus en détail, mais il y a un million de façons de procéder. Si vous avez des questions spécifiques ici, n'hésitez pas à me les poser et j'ajouterai les informations.
Ressources:
Je pense que j'ai donné une assez bonne introduction ici, mais il y a une tonne de domaines supplémentaires. Je suis plus qu'heureux de répondre à toutes vos questions; J'ai été très vague dans la plupart de cela simplement en raison de l'immense complexité.
Liens Wikipédia obligatoires:
Ressources d'émulation générales:
- Zophar - C'est là que j'ai commencé mon émulation, en téléchargeant d'abord des émulateurs et en pillant finalement leurs immenses archives de documentation. C'est la meilleure ressource absolue que vous puissiez avoir.
- NGEmu - Pas beaucoup de ressources directes, mais leurs forums sont imbattables.
- RomHacking.net - La section des documents contient des ressources concernant l'architecture de la machine pour les consoles populaires
Projets d'émulation à référencer:
- IronBabel - Il s'agit d'une plate-forme d'émulation pour .NET, écrite en Nemerle et recompile le code en C # à la volée. Avertissement: Ceci est mon projet, alors pardonnez la fiche sans vergogne.
- BSnes - Un émulateur SNES génial dans le but d'une précision parfaite du cycle.
- MAME - L' émulateur d'arcade. Grande référence.
- 6502asm.com - Il s'agit d'un émulateur JavaScript 6502 avec un petit forum sympa.
- dynarec'd 6502asm - C'est un petit hack que j'ai fait pendant un jour ou deux. J'ai pris l'émulateur existant de 6502asm.com et l'ai changé pour recompiler dynamiquement le code en JavaScript pour des augmentations de vitesse massives.
Références de recompilation du processeur:
- La recherche sur la recompilation statique effectuée par Michael Steil (référencé ci-dessus) a abouti à cet article et vous pouvez trouver la source et autres ici .
Addenda:
Cela fait bien plus d'un an que cette réponse a été soumise et avec toute l'attention qu'elle suscite, j'ai pensé qu'il était temps de mettre à jour certaines choses.
La chose la plus excitante dans l'émulation en ce moment est peut-être libcpu , démarré par Michael Steil susmentionné. Il s'agit d'une bibliothèque destinée à prendre en charge un grand nombre de cœurs de processeur, qui utilisent LLVM pour la recompilation (statique et dynamique!). Son potentiel est énorme et je pense que cela fera de grandes choses pour l'émulation.
emu-docs a également été porté à mon attention, qui abrite un grand référentiel de documentation système, qui est très utile à des fins d'émulation. Je n'y ai pas passé beaucoup de temps, mais on dirait qu'ils ont beaucoup de bonnes ressources.
Je suis content que ce post ait été utile, et j'espère que je pourrai me défouler et terminer mon livre sur le sujet d'ici la fin de l'année / au début de l'année prochaine.