web-dev-qa-db-fra.com

Comment fonctionnent les émulateurs et comment sont-ils écrits?

Comment fonctionnent les émulateurs? Quand je vois des émulateurs NES/SNES ou C64, cela me stupéfie.

http://www.tommowalker.co.uk/snemzelda.png

Devez-vous imiter le processeur de ces machines en interprétant ses instructions de montage particulières? Quoi d'autre va dedans? Comment sont-ils généralement conçus?

Pouvez-vous donner un conseil à une personne intéressée par l'écriture d'un émulateur (en particulier un système de jeu)?

968
mmcdole

L'émulation est une zone à multiples facettes. Voici les idées de base et les composants fonctionnels. Je vais le casser en morceaux, puis compléter les détails via des modifications. Bon nombre des choses que je vais décrire vont nécessiter 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, posez 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 composant du système, puis connectez les composants comme le font les câbles dans le matériel.

Emulation du 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 général: exécuter un morceau de code pour modifier l'état du processeur et interagir avec le "matériel". L'état du processeur est une agglomération des registres du processeur, des gestionnaires d'interruption, etc. pour une cible de processeur donnée. Pour le 6502, vous auriez un nombre entier de 8 bits représentant des registres: A, X, Y, P et S; vous auriez également un registre PC de 16 bits.

Avec interprétation, vous commencez par IP (pointeur d'instruction - également appelé PC, compteur de programme) et lisez l'instruction en mémoire. Votre code analyse cette instruction et utilise cette information pour modifier l'état du processeur tel que spécifié par votre processeur. Le problème principal de l'interprétation est que c'est very slow; 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 comme une interprétation, mais au lieu de simplement exécuter 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 en code machine pour 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é, il vous suffit d'exécuter le code à partir du cache. (Au fait, la plupart des gens ne font pas réellement une liste d'instructions mais les compilent en code machine à la volée - cela rend plus difficile l'optimisation, mais c'est hors du champ de cette réponse, sauf si suffisamment de personnes sont intéressées)

Avec la recompilation statique, vous procédez de la même manière que dans la recompilation dynamique, mais vous suivez les branches. Vous créez un bloc 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é, donc il ne s'exécutera pas.
  • Il a été prouvé que trouver tout le code dans un binaire donné équivaut à Halting problem

Celles-ci se combinent pour rendre la recompilation statique complètement irréalisable dans 99% des cas. Pour plus d’informations, Michael Steil a effectué d’excellentes recherches sur la recompilation statique - la meilleure que j’ai vue.

L’autre côté de l’émulation du processeur est la manière dont vous interagissez avec le matériel. Cela a vraiment deux côtés:

  • Temps processeur
  • Traitement des interruptions

Processeur:

Certaines plates-formes - notamment les anciennes consoles telles que la NES, la SNES, etc. - exigent que votre émulateur ait un minutage strict pour être totalement compatible. Avec le NES, vous avez le PPU (unité de traitement de pixels) qui exige que le CPU mette 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.

Traitement des interruptions:

Les interruptions constituent le mécanisme principal avec lequel la CPU communique avec le matériel. Généralement, vos composants matériels informeront le processeur des interruptions dont il se soucie. C’est assez simple: lorsque votre code génère une interruption donnée, vous consultez la table des gestionnaires d’interruptions et appelez le rappel approprié.

Emulation matérielle:

Il y a deux côtés pour émuler un périphérique matériel donné:

  • Emulation de 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 surveillées par le dispositif pour détecter les modifications à effectuer) et des interruptions. Pour un disque dur, vous pouvez avoir une zone mappée en mémoire dans laquelle vous placez des commandes de lecture, des écritures, etc., puis relisez ces données.

J'entrerais dans les détails, mais il y a un million de façons d'y aller. Si vous avez des questions spécifiques ici, n'hésitez pas à demander et je vais ajouter l'info.

Ressources:

Je pense avoir donné une assez bonne introduction, mais il y a une tonne de zones supplémentaires. Je suis plus qu'heureux d'aider avec toutes les questions; J'ai été très vague dans la plupart des cas simplement en raison de l'immense complexité.

Liens Wikipedia obligatoires:

Ressources d'émulation générale:

  • Zophar - C’est là que j’ai commencé avec l’émulation, le premier téléchargement d’émulateurs et le pillage de leurs immenses archives de documentation. C'est la meilleure ressource absolue que vous puissiez avoir.
  • NGEm - Peu de ressources directes, mais leurs forums sont imbattables.
  • RomHacking.net - La section documents contient des ressources concernant l'architecture des machines pour consoles populaires

Projets d'émulateur à référencer:

  • IronBabel - Il s'agit d'une plate-forme d'émulation pour .NET, écrite en Nemerle et recompilant le code en C # à la volée. Disclaimer: Ceci est mon projet, alors pardonnez-moi la fiche sans vergogne.
  • BSnes - Un émulateur SNES génial avec l'objectif d'une précision de cycle parfaite.
  • MAME - L'émulateur d'arcade . Excellente référence.
  • 6502asm.com - Ceci est un émulateur JavaScript 6502 avec un petit forum sympa.
  • dynarec'd 6502asm - C'est un petit bidouillage que j'ai fait pendant un jour ou deux. J'ai repris l'émulateur existant sur 6502asm.com et je l'ai modifié pour recompiler dynamiquement le code en JavaScript afin d'augmenter considérablement la vitesse.

Références de recompilation du processeur:

  • La recherche sur la recompilation statique effectuée par Michael Steil (référencée 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 mérite, j'ai pensé qu'il était temps de mettre à jour certaines choses.

La chose la plus excitante dans l’émulation en ce moment est probablement libcp , qui a été créée par Michael Steil. C'est une librairie destinée à supporter un grand nombre de cœurs de processeurs, qui utilisent LLVM pour la recompilation (statique et dynamique!). Son potentiel est énorme et je pense que cela produira de grandes choses pour l'émulation.

emu-docs a également été porté à mon attention, qui héberge un excellent référentiel de documentation système, très utile pour l’émulation. Je n'ai pas passé beaucoup de temps là-bas, mais il semble qu'ils disposent d'excellentes ressources.

Je suis heureux que ce message ait été utile et j'espère pouvoir enfin me sortir de mon cul et finir mon livre sur le sujet d'ici la fin de l'année/au début de l'année prochaine.

1126
Cody Brocious

Un certain Victor Moya del Barrio a rédigé sa thèse sur ce sujet. Beaucoup de bonnes informations sur 152 pages. Vous pouvez télécharger le PDF ici .

Si vous ne souhaitez pas vous inscrire avec scribd , vous pouvez rechercher Google pour le titre PDF, "Etude des techniques de programmation par émulation" . Il existe différentes sources pour le fichier PDF.

126
mdm

L’émulation peut sembler décourageante, mais elle est en réalité assez facile à simuler.

Tout processeur a généralement une spécification bien écrite décrivant les états, les interactions, etc.

Si vous ne vous souciez pas du tout de la performance, vous pouvez facilement émuler la plupart des processeurs plus anciens en utilisant des programmes orientés objet très élégants. Par exemple, un processeur X86 aurait besoin de quelque chose pour maintenir l'état des registres (easy), de maintenir l'état de mémoire (easy), et de quelque chose qui prendrait chaque commande entrante et l'appliquerait à l'état actuel de la machine. Si vous voulez vraiment de la précision, vous pouvez également émuler les traductions en mémoire, la mise en cache, etc., mais c'est faisable.

En fait, de nombreux fabricants de microprocesseurs et de processeurs testent des programmes sur un émulateur de puce, puis sur la puce elle-même, ce qui les aide à déterminer s’il existe des problèmes dans les spécifications de la puce ou dans l’implémentation réelle de la puce dans le matériel. Par exemple, il est possible d'écrire une spécification de puce qui entraînerait des blocages. Lorsqu'une échéance survient dans le matériel, il est important de voir si elle peut être reproduite dans la spécification car cela indique un problème plus grave que l'implémentation de la puce.

Bien sûr, les émulateurs pour les jeux vidéo se soucient généralement des performances, ils n'utilisent donc pas d'implémentations naïves. Ils incluent également du code qui s'interface avec le système d'exploitation du système hôte, par exemple pour utiliser le dessin et le son.

Compte tenu des performances très lentes des vieux jeux vidéo (NES/SNES, etc.), l’émulation est assez facile sur les systèmes modernes. En fait, il est encore plus étonnant de pouvoir télécharger un jeu de tous les jeux SNES ou Atari 2600, étant donné que lorsque ces systèmes étaient populaires, le libre accès à chaque cartouche aurait été un rêve devenu réalité.

43
Uri

Je sais que cette question est un peu ancienne, mais je voudrais ajouter quelque chose à la discussion. La plupart des réponses ici concernent des émulateurs interprétant les instructions machine des systèmes qu’ils émulent.

Cependant, il existe une exception très connue appelée "UltraHLE" ( article de WIKIpedia ). UltraHLE, l'un des émulateurs les plus célèbres jamais créés, a imité les jeux commerciaux Nintendo 64 (offrant des performances décentes sur les ordinateurs à la maison) à un moment où il était largement considéré impossible de le faire. En fait, Nintendo produisait encore de nouveaux titres pour la Nintendo 64 lors de la création d’UltraHLE!

Pour la première fois, j'ai vu des articles sur les émulateurs dans des magazines imprimés où je ne les avais jamais vus que discutés sur le Web.

Le concept d'UltraHLE consistait à rendre l'impossible possible en émulant des appels de bibliothèque C au lieu d'appels au niveau de la machine.

29

La tentative d'Imran Nazar d'écrire un émulateur Gameboy en JavaScript mérite d'être examinée.

22
Julio

Après avoir créé mon propre émulateur du micro-ordinateur BBC des années 80 (type VBeeb dans Google), il y a un certain nombre de choses à savoir.

  • Vous n'imitez pas la vraie chose en tant que telle, ce serait une réplique. Au lieu de cela, vous émulez State. Un bon exemple est une calculatrice, la vraie chose a des boutons, écran, cas, etc. Mais pour émuler une calculatrice, il vous suffit d’émuler si les boutons sont haut ou bas, quels segments de LCD sont allumés, etc. Fondamentalement, un ensemble de nombres représentant toutes les combinaisons possibles d'éléments pouvant changer dans une calculatrice.
  • Vous avez seulement besoin que l'interface de l'émulateur apparaisse et se comporte comme si de rien n'était. Plus c'est convaincant, plus l'émulation est proche. Ce qui se passe dans les coulisses peut être ce que vous voulez. Mais, pour faciliter l'écriture d'un émulateur, un mappage mental se produit entre le système réel, à savoir les puces, les écrans, les claviers, les cartes de circuits imprimés et le code informatique abstrait.
  • Pour émuler un système informatique, il est plus simple de le diviser en petits morceaux et de les émuler individuellement. Puis enchaînez le tout pour le produit fini. Cela ressemble beaucoup à un ensemble de boîtes noires avec des entrées et des sorties, qui se prêtent à merveille à la programmation orientée objet. Vous pouvez encore subdiviser ces morceaux pour vous rendre la vie plus facile.

En pratique, vous cherchez généralement à écrire pour la rapidité et la fidélité de l'émulation. En effet, le logiciel sur le système cible s'exécutera (peut-être) plus lentement que le matériel d'origine sur le système source. Cela peut limiter le choix du langage de programmation, des compilateurs, du système cible, etc.
De plus, vous devez limiter ce que vous êtes prêt à émuler. Par exemple, il n'est pas nécessaire d'émuler l'état de tension des transistors dans un microprocesseur, mais il est probablement nécessaire d'imiter l'état de l'ensemble de registres du microprocesseur.
En règle générale, plus le niveau de détail de l'émulation est petit, plus vous resterez fidèle au système d'origine.
Enfin, les informations pour les systèmes plus anciens peuvent être incomplètes ou inexistantes. Il est donc essentiel de mettre la main sur l’équipement d’origine, ou au moins de distinguer un autre bon émulateur écrit par quelqu'un d'autre!

18
Guillermo Phillips

Oui, vous devez interpréter tout le désordre binaire du code machine "à la main". De plus, la plupart du temps, vous devez également simuler un matériel exotique qui n'a pas d'équivalent sur la machine cible.

L’approche simple consiste à interpréter les instructions une par une. Cela fonctionne bien, mais c'est lent. La recompilation est une approche plus rapide: convertir le code machine source en code machine cible. C’est plus compliqué, car la plupart des instructions ne mapperont pas un à un. Au lieu de cela, vous devrez créer des solutions de rechange qui impliquent du code supplémentaire. Mais au final c'est beaucoup plus rapide. La plupart des émulateurs modernes le font.

17
Vilx-

Lorsque vous développez un émulateur, vous interprétez l’ensemble processeur sur lequel le système travaille (Z80, 8080, CPU PS, etc.).

Vous devez également émuler tous les périphériques du système (sortie vidéo, contrôleur).

Vous devriez commencer à écrire des émulateurs pour les systèmes simpe comme le bon vieux Game Boy (qui utilise un processeur Z80, je ne me trompe pas) OR pour C64.

15
Baget

Les émulateurs sont très difficiles à créer car il faut simuler de nombreux hacks (comme dans les effets inhabituels), des problèmes de timing, etc.

Pour un exemple, voir http://queue.acm.org/detail.cfm?id=1755886 .

Cela vous montrera également pourquoi vous "avez besoin" d’un processeur multi-GHz pour émuler un processeur à 1 MHz.

10
Someone

Consultez également Emulators.com de Darek Mihocka pour des conseils avisés sur l’optimisation des instructions pour les JIT, ainsi que de nombreux autres avantages pour la création d’émulateurs efficaces.

9
Barry Bond

Je n'ai jamais fait quelque chose d'assez sophistiqué pour imiter une console de jeu, mais j'ai suivi un cours une fois dans le but d'écrire un émulateur pour la machine décrite dans Andrew Tanenbaums Structured Computer Organization . C'était amusant et cela m'a donné beaucoup de moments aha. Vous voudrez peut-être prendre ce livre avant de plonger dans l'écriture d'un véritable émulateur.

7
oivvio

Des conseils pour imiter un vrai système ou votre propre chose? Je peux dire que les émulateurs fonctionnent en émulant tout le matériel. Peut-être pas dans le circuit (déplacer des bits comme le ferait le HW; déplacer l’octet est le résultat final, donc copier l’octet convient). Les émulateurs sont très difficiles à créer car il faut simuler de nombreux hacks (comme dans les effets inhabituels), des problèmes de timing, etc. Si un élément (entrée) est erroné, tout le système peut le faire ou, au mieux, avoir un bug/problème.

4
user34537

Émulateur de périphérique source partagé contient un code source pouvant être construit pour un émulateur PocketPC/Smartphone (nécessite Visual Studio, fonctionne sous Windows). J'ai travaillé sur les versions V1 et V2 de la version binaire.

Il aborde de nombreux problèmes d’émulation: - traduction efficace des adresses d’un invité virtuel à l’hôte virtuel - compilation JIT du code invité - simulation de périphériques tels que des adaptateurs réseau, des écrans tactiles et audio - intégration de l’UI, pour clavier et souris de l’hôte - sauvegarde/restauration d'état, pour la simulation de la reprise du mode basse consommation

4
Barry Bond

Pour ajouter la réponse fournie par @Cody Brocious
Dans le contexte de la virtualisation dans lequel vous émulez un nouveau système (CPU, E/S, etc.) sur une machine virtuelle, vous pouvez voir les catégories suivantes d’émulateurs.

Interprétation: bochs est un exemple d’interprète, c’est un émulateur de PC x86; chaque instruction du système invité doit être traduite dans un autre jeu d’instructions (de l’ISA hôte) pour produire l’effet souhaité. ne cache rien pour que chaque instruction répète le même cycle.

Emalator dynamique: Qemu est un émulateur dynamique. La traduction à la volée de l'instruction d'invité met également en cache les résultats. La meilleure partie est d'exécuter autant d'instructions que possible directement sur le système hôte afin que l'émulation soit plus rapide. De plus, comme l'a mentionné Cody, il divise le code en blocs (1 seul flux d'exécution).

Émulateur statique: Pour autant que je sache, aucun émulateur statique ne peut être utile pour la virtualisation.

1
Deepthought

J'ai écrit un article sur l'émulation de la système Chip-8 en JavaScript .

C'est un bon point de départ car le système n'est pas très compliqué, mais vous apprenez toujours comment fonctionnent les opcodes, la pile, les registres, etc.

J'écrirai bientôt un guide plus long pour la NDA.

1
alex

Comment je commencerais l'émulation.

1.Obtenez des livres basés sur la programmation de bas niveau, vous en aurez besoin pour le système d'exploitation "feint" de la console de jeu Nintendo ...

2.Obtenez des livres sur l'émulation spécifiquement, et peut-être le développement OS. (Vous ne ferez pas un os, mais le plus proche.

3. Regardez certains émulateurs open source, en particulier ceux du système pour lequel vous souhaitez créer un émulateur.

4.copiez des extraits du code plus complexe dans votre IDE/compliler. Cela vous évitera d'écrire un long code. C’est ce que je fais pour le développement os, utilise un district de linux

1
5Mixer