Como funcionam os emuladores e como eles são escritos? [fechadas]


968

Como os emuladores funcionam? Quando vejo emuladores NES / SNES ou C64, isso me surpreende.

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

Você precisa emular o processador dessas máquinas interpretando suas instruções de montagem específicas? O que mais há nele? Como eles são tipicamente projetados?

Você pode dar algum conselho para alguém interessado em escrever um emulador (particularmente um sistema de jogo)?


15
A coisa mais importante que você precisa encontrar é o "manual do programador" para esse sistema, pois detalha o "contrato" entre o fornecedor de HW e os programadores e oculta detalhes que não são relevantes e podem mudar. Suas chances dependem da popularidade do sistema.
214 Uri

155
Boa escolha de jogo.
Cristián Romo


16
Para quem se pergunta Emulação vs Simulação
Lazer

8
Desde a primeira vez que joguei esse jogo, eu sempre me perguntei por Hyrule está repleta de pedras "8-Ball" :-)
Vivian Rio

Respostas:


1124

A emulação é uma área multifacetada. Aqui estão as idéias básicas e os componentes funcionais. Vou dividi-lo em pedaços e depois preencher os detalhes através de edições. Muitas das coisas que vou descrever exigirão conhecimento do funcionamento interno dos processadores - é necessário conhecimento de montagem. Se eu sou um pouco vago em certas coisas, faça perguntas para que eu possa continuar a melhorar esta resposta.

Ideia básica:

A emulação funciona manipulando o comportamento do processador e dos componentes individuais. Você constrói cada peça individual do sistema e as conecta da mesma forma que os fios do hardware.

Emulação do processador:

Existem três maneiras de lidar com a emulação de processador:

  • Interpretação
  • Recompilação dinâmica
  • Recompilação estática

Com todos esses caminhos, você tem o mesmo objetivo geral: executar um pedaço de código para modificar o estado do processador e interagir com o 'hardware'. O estado do processador é uma conglomeração dos registros do processador, manipuladores de interrupção etc. para um determinado destino do processador. Para o 6502, você teria um número de inteiros de 8 bits que representam registros: A, X, Y, P, e S; você também teria um PCregistro de 16 bits .

Com a interpretação, você começa no IP(ponteiro da instrução - também chamado de PCcontador de programa) e lê a instrução da memória. Seu código analisa essas instruções e usa essas informações para alterar o estado do processador, conforme especificado pelo seu processador. O principal problema com a interpretação é que é muito lento; toda vez que você lida com uma determinada instrução, é necessário decodificá-la e executar a operação necessária.

Com a recompilação dinâmica, você repete o código da mesma forma que a interpretação, mas, em vez de apenas executar opcodes, cria uma lista de operações. Depois de chegar a uma instrução de ramificação, você compila essa lista de operações para codificar a máquina para sua plataforma host, depois armazena em cache esse código compilado e o executa. Então, quando você atingir um determinado grupo de instruções novamente, precisará executar o código apenas do cache. (Na verdade, a maioria das pessoas não faz uma lista de instruções, mas as compila para o código da máquina em tempo real - isso torna mais difícil a otimização, mas está fora do escopo desta resposta, a menos que haja um número suficiente de pessoas interessadas)

Com a recompilação estática, você faz o mesmo que na recompilação dinâmica, mas segue as ramificações. Você acaba criando um pedaço de código que representa todo o código do programa, que pode ser executado sem mais interferências. Esse seria um ótimo mecanismo se não fosse pelos seguintes problemas:

  • O código que não está no programa para começar (por exemplo, compactado, criptografado, gerado / modificado em tempo de execução, etc.) não será recompilado e, portanto, não será executado
  • Está provado que encontrar todo o código em um dado binário é equivalente ao problema da parada

Eles se combinam para tornar a recompilação estática completamente inviável em 99% dos casos. Para mais informações, Michael Steil fez uma grande pesquisa sobre recompilação estática - a melhor que já vi.

O outro lado da emulação de processador é a maneira como você interage com o hardware. Isso realmente tem dois lados:

  • Tempo do processador
  • Manuseio de interrupção

Tempo do processador:

Certas plataformas - especialmente os consoles mais antigos, como o NES, SNES etc. - exigem que seu emulador tenha um tempo estrito para ser completamente compatível. Com o NES, você possui a PPU (unidade de processamento de pixels), que exige que a CPU coloque pixels na memória em momentos precisos. Se você usa a interpretação, pode contar facilmente ciclos e emular o tempo adequado; com a recompilação dinâmica / estática, as coisas são muito mais complexas.

Manuseio de interrupção:

Interrupções são o principal mecanismo que a CPU se comunica com o hardware. Geralmente, seus componentes de hardware informam à CPU o que a interrompe. Isso é bem direto - quando seu código lança uma determinada interrupção, você olha para a tabela do manipulador de interrupções e chama o retorno de chamada apropriado.

Emulação de hardware:

Existem dois lados para emular um determinado dispositivo de hardware:

  • Emulando a funcionalidade do dispositivo
  • Emulando as interfaces reais do dispositivo

Veja o caso de um disco rígido. A funcionalidade é emulada através da criação de armazenamento de backup, rotinas de leitura / gravação / formato, etc. Essa parte geralmente é muito simples.

A interface real do dispositivo é um pouco mais complexa. Geralmente, é uma combinação de registros mapeados na memória (por exemplo, partes da memória que o dispositivo observa para alterações na sinalização) e interrompe. Para um disco rígido, você pode ter uma área mapeada na memória onde coloca comandos de leitura, gravações etc., e depois lê esses dados novamente.

Eu entraria em mais detalhes, mas há um milhão de maneiras pelas quais você pode seguir com isso. Se você tiver alguma dúvida específica aqui, não hesite em perguntar e eu adicionarei as informações.

Recursos:

Acho que dei uma introdução muito boa aqui, mas há várias áreas adicionais. Estou mais do que feliz em ajudar com qualquer dúvida; Eu tenho sido muito vago na maior parte disso simplesmente devido à imensa complexidade.

Links obrigatórios da Wikipedia:

Recursos gerais de emulação:

  • Zophar - Foi aqui que comecei com a emulação, primeiro baixando emuladores e, eventualmente, pilhando seus imensos arquivos de documentação. Este é o melhor recurso absoluto que você pode ter.
  • NGEmu - Não há muitos recursos diretos, mas seus fóruns são imbatíveis.
  • RomHacking.net - A seção de documentos contém recursos sobre arquitetura de máquina para consoles populares

Projetos de emulador para referência:

  • IronBabel - Esta é uma plataforma de emulação para .NET, escrita em Nemerle e recompila o código para C # rapidamente . Disclaimer: Este é o meu projeto, então perdoe o plugue sem vergonha.
  • BSnes - Um emulador SNES incrível com o objetivo de precisão de ciclo perfeito.
  • MAME - O emulador de arcade. Ótima referência.
  • 6502asm.com - Este é um emulador JavaScript 6502 com um pequeno fórum interessante.
  • dynarec'd 6502asm - Este é um pequeno truque que fiz ao longo de um dia ou dois. Peguei o emulador existente no 6502asm.com e mudei-o para recompilar dinamicamente o código para JavaScript para obter grandes aumentos de velocidade.

Referências de recompilação do processador:

  • A pesquisa sobre recompilação estática realizada por Michael Steil (referenciada acima) culminou neste artigo e você pode encontrar fontes e outras aqui .

Termo aditivo:

Já faz mais de um ano que essa resposta foi enviada e com toda a atenção que recebi, achei que era hora de atualizar algumas coisas.

Talvez a coisa mais emocionante na emulação agora seja o libcpu , iniciado pelo mencionado Michael Steil. É uma biblioteca destinada a suportar um grande número de núcleos de CPU, que usam o LLVM para recompilação (estática e dinâmica!). Ele tem um enorme potencial e acho que fará grandes coisas para emular.

O emu-docs também foi trazido à minha atenção, que abriga um ótimo repositório de documentação do sistema, que é muito útil para fins de emulação. Não passei muito tempo lá, mas parece que eles têm muitos recursos excelentes.

Fico feliz que este post tenha sido útil e espero poder sair do meu lugar e terminar meu livro sobre o assunto até o final do ano / início do próximo ano.


37
Isso já está se preparando para ser uma resposta épica. Se você puder me indicar algum recurso, no final, ele será apreciado. Eu estou olhando para possivelmente o sistema SNES ou NES para emular e torná-lo meu projeto semestral.
mmcdole

8
Certamente. Vou montar uma boa lista de recursos. Se vocês tiverem algum pedido específico, farei o possível para preenchê-lo.
Cody Brocious

3
@thenonhacker, o projeto IronBabel mencionado na minha seção de recursos é meu. (O tampão sem vergonha é marcada;))
Cody Brocious

1
"Está provado que encontrar todo o código em um dado binário é equivalente ao problema da parada" - Referência, por favor? Ou deveria ser "Está provado que encontrar todo o código em qualquer binário é equivalente ao problema da parada"? Também não consigo acessar o trabalho de Steil :-(
squelart 19/01/2009

4
Você menciona que está escrevendo um livro; você pode nos dar uma atualização sobre isso? Eu, por exemplo, estaria interessado em lê-lo.
Alex


43

A emulação pode parecer assustadora, mas na verdade é bem mais fácil do que simular.

Qualquer processador geralmente possui uma especificação bem escrita que descreve estados, interações etc.

Se você não se importava com o desempenho, poderia facilmente emular a maioria dos processadores mais antigos usando programas orientados a objetos muito elegantes. Por exemplo, um processador X86 precisaria de algo para manter o estado dos registros (fácil), algo para manter o estado da memória (fácil) e algo que usaria cada comando recebido e o aplicaria ao estado atual da máquina. Se você realmente quisesse precisão, também emularia traduções de memória, cache, etc., mas isso é possível.

De fato, muitos fabricantes de microchips e CPU testam programas contra um emulador do chip e depois contra o próprio chip, o que os ajuda a descobrir se há problemas nas especificações do chip ou na implementação real do chip no hardware. Por exemplo, é possível escrever uma especificação de chip que resultaria em deadlocks e, quando ocorrer um prazo no hardware, é importante verificar se ele pode ser reproduzido na especificação, pois isso indica um problema maior do que algo na implementação do chip.

Obviamente, os emuladores de videogame geralmente se preocupam com o desempenho para não usar implementações ingênuas e também incluem código que faz interface com o sistema operacional do sistema host, por exemplo, para usar desenho e som.

Considerando o desempenho muito lento dos videogames antigos (NES / SNES etc.), a emulação é bastante fácil nos sistemas modernos. De fato, é ainda mais surpreendente que você possa simplesmente baixar um conjunto de todos os jogos SNES de todos os tempos ou de qualquer jogo Atari 2600 de todos os tempos, considerando que, quando esses sistemas eram populares, ter acesso livre a todos os cartuchos seria um sonho tornado realidade.


1
Quais são as diferenças entre emulação e simulação?
Wei Hu

2
@ Wei: De um modo geral, um emulador deve se comportar "externamente" como o sistema que emula, mas não há nada a dizer que deve ser implementado de maneira semelhante. Um simulador é implementado de uma maneira que imita o sistema simulado e, como resultado, se comporta como ele.
Uri

Quando você vê "Simulator", pensa que é semelhante enquanto um emulador "emula"
mP.


29

Sei que essa pergunta é um pouco antiga, mas gostaria de acrescentar algo à discussão. A maioria das respostas aqui se concentra em emuladores interpretando as instruções da máquina dos sistemas que eles imitam.

No entanto, há uma exceção muito conhecida a isso chamada "UltraHLE" ( artigo da WIKIpedia ). O UltraHLE, um dos emuladores mais famosos já criados, emulava jogos comerciais do Nintendo 64 (com desempenho decente em computadores domésticos) em um momento em que era amplamente considerado impossível fazê-lo. De fato, a Nintendo ainda estava produzindo novos títulos para o Nintendo 64 quando o UltraHLE foi criado!

Pela primeira vez, vi artigos sobre emuladores em revistas impressas, onde antes, eu só os havia discutido na web.

O conceito do UltraHLE era tornar possível o impossível emulando chamadas da biblioteca C em vez de chamadas no nível da máquina.


22

Algo que vale a pena dar uma olhada é a tentativa de Imran Nazar de escrever um emulador Gameboy em JavaScript.


1
Como obtemos as instruções brutas do opcode para o jogo Gameboy?
Pacerier

Existem vários dispositivos disponíveis para venda no 'mercado cinza'. Você não os encontrará em nenhuma loja importante do mundo desenvolvido. Esses dispositivos são capazes de copiar as instruções do cartucho de jogo para arquivos que geralmente são chamados de "ROMs". Google "Gameboy Roms", mas cuidado com links perigosos e sites de ataque!
Vivian rio

18

Tendo criado meu próprio emulador do microcomputador da BBC dos anos 80 (digite VBeeb no Google), há várias coisas a saber.

  • Você não está imitando a coisa real como tal, isso seria uma réplica. Em vez disso, você está emulando State . Um bom exemplo é uma calculadora, a coisa real tem botões, tela, estojo etc. Mas para emular uma calculadora, você só precisa emular se os botões estão para cima ou para baixo, em quais segmentos do LCD estão etc. Basicamente, um conjunto de números representando todas as combinações possíveis de coisas que podem mudar em uma calculadora.
  • Você só precisa que a interface do emulador apareça e se comporte como a coisa real. Quanto mais convincente for esse, mais próxima será a emulação. O que acontece nos bastidores pode ser o que você quiser. Mas, para facilitar a escrita de um emulador, existe um mapeamento mental que ocorre entre o sistema real, ou seja, chips, monitores, teclados, placas de circuito e o código abstrato do computador.
  • Para emular um sistema de computador, é mais fácil dividi-lo em pedaços menores e emular esses pedaços individualmente. Em seguida, junte todo o lote para o produto acabado. Muito parecido com um conjunto de caixas pretas com entradas e saídas, que se presta muito bem à programação orientada a objetos. Você pode subdividir ainda mais esses pedaços para facilitar a vida.

Na prática, você geralmente procura escrever sobre velocidade e fidelidade da emulação. Isso ocorre porque o software no sistema de destino (pode) roda mais lentamente do que o hardware original no sistema de origem. Isso pode restringir a escolha da linguagem de programação, compiladores, sistema de destino etc.
Além disso, você precisa circunscrever o que está preparado para emular, por exemplo, não é necessário simular o estado de tensão dos transistores em um microprocessador, mas provavelmente é necessário para emular o estado do conjunto de registros do microprocessador.
De um modo geral, quanto menor o nível de detalhe da emulação, maior a fidelidade do sistema original.
Finalmente, as informações para sistemas mais antigos podem ser incompletas ou inexistentes. Portanto, é essencial adquirir o equipamento original, ou pelo menos separar outro bom emulador que outra pessoa tenha escrito!


17

Sim, você deve interpretar toda a bagunça binária do código da máquina "manualmente". Além disso, na maioria das vezes você também precisa simular algum hardware exótico que não tem um equivalente na máquina de destino.

A abordagem simples é interpretar as instruções uma por uma. Isso funciona bem, mas é lento. Uma abordagem mais rápida é a recompilação - traduzindo o código da máquina de origem para o código da máquina de destino. Isso é mais complicado, pois a maioria das instruções não é mapeada individualmente. Em vez disso, você terá que elaborar soluções alternativas que envolvam código adicional. Mas no final é muito mais rápido. A maioria dos emuladores modernos faz isso.


1
O pior de tudo é a falta de documentação. É quando você descobre que o núcleo Z80 modificado no GameBoy Color possui operações de sinalização não documentadas que o jogo que você está testando utiliza que você realmente começa a perder a fé.
Callum Rogers

1
Pet peeve: É código de máquina (singular), não códigos de máquina (plural); assim como o Código Morse, não os códigos Morse .
Lawrence Dol

1
@ Vilx: Na verdade não - o termo "código da máquina", referente ao conjunto de instruções para uma CPU, está em uso desde o início do software e não é plural. Refere-se ao " conjunto de instruções ", uma forma singular, não à forma "instruções" do plural. O mesmo que código de programa, código Morse, etc. O uso da forma plural surgiu do uso indevido, geralmente por aqueles que falam inglês como segunda língua.
Lawrence Dol

1
@ Monkey Software - Mas não posso usar a palavra "código" para me referir a um único item do conjunto? Por exemplo: " ... --- ...- esses três códigos Morse representam as três letras S, O, S." Porque ...é um código que representa a letra "S". Não?
Vilx-

1
Não, o código é substantivo incontável, não tem forma plural como a água ou areia ..
Ivan

15

Ao desenvolver um emulador, você está interpretando o conjunto do processador no qual o sistema está trabalhando (Z80, 8080, CPU PS, etc.).

Você também precisa emular todos os periféricos que o sistema possui (saída de vídeo, controlador).

Você deve começar a escrever emuladores para os sistemas simpe, como o bom e velho Game Boy (que usa um processador Z80, não estou me enganando) OU para o C64.


9
C64 um sistema "simples"? Enquanto o 6510 é relativamente simples (depois de cobrir os códigos de operação não listados), os chips de som (SID) e vídeo (VIC) são tudo menos simples. Para alcançar um nível decente de compatibilidade, você precisa emulá-los - bugs de hardware e tudo.
moobaa

10

É muito difícil criar um emulador, pois existem muitos hacks (como em efeitos incomuns), problemas de tempo, etc., que você precisa simular.

Para um exemplo disso, consulte http://queue.acm.org/detail.cfm?id=1755886 .

Isso também mostrará por que você 'precisa' de uma CPU multi-GHz para emular uma CPU de 1 MHz.


9

Verifique também o Emulators.com de Darek Mihocka para obter ótimos conselhos sobre otimização em nível de instrução para JITs e muitas outras vantagens em criar emuladores eficientes.


7

Eu nunca fiz nada tão chique quanto emular um console de jogos, mas fiz um curso uma vez em que a tarefa era escrever um emulador para a máquina descrita em Andrew Tanenbaums Structured Computer Organization . Isso foi divertido e me deu muitos momentos aha. Você pode pegar esse livro antes de escrever um emulador real.


4

Conselhos sobre como emular um sistema real ou como você próprio? Posso dizer que os emuladores funcionam emulando o hardware INTEIRO. Talvez não no circuito (como mover bits como o HW faria. Mover o byte é o resultado final, portanto, copiar o byte é bom). É muito difícil criar um emulador, pois existem muitos hacks (como em efeitos incomuns), problemas de tempo, etc., que você precisa simular. Se uma peça (de entrada) estiver errada, todo o sistema poderá ser desativado ou, na melhor das hipóteses, apresentar um bug / falha.


4

O emulador de dispositivo de origem compartilhada contém código-fonte que pode ser construído em um emulador de PocketPC / Smartphone (requer o Visual Studio, é executado no Windows). Eu trabalhei na V1 e V2 da versão binária.

Ele lida com muitos problemas de emulação: - tradução eficiente de endereços de convidado virtual para físico convidado para host virtual - compilação JIT de código convidado - simulação de dispositivos periféricos, como adaptadores de rede, tela sensível ao toque e áudio - integração de interface do usuário para teclado e mouse host - salvar / restauração de estado, para simulação de currículo a partir do modo de baixo consumo de energia


1

Para adicionar a resposta fornecida por @Cody Brocious
No contexto da virtualização em que você está emulando um novo sistema (CPU, E / S, etc) em uma máquina virtual, podemos ver as seguintes categorias de emuladores.

Interpretação: bochs é um exemplo de intérprete, é um emulador de PC x86, e cada instrução do sistema convidado converte-a em outro conjunto de instruções (do ISA host) para produzir o efeito pretendido. Sim, é muito lento, não é armazena em cache qualquer coisa para que todas as instruções passem pelo mesmo ciclo.

Emalator dinâmico: Qemu é um emulador dinâmico. A tradução instantânea da instrução de convidado também armazena em cache os resultados. A melhor parte é que executa o máximo de instruções possível diretamente no sistema host, para que a emulação seja mais rápida. Também como mencionado por Cody, ele divide o código em blocos (1 fluxo único de execução).

Emulador estático: Até onde eu sei, não há emulador estático que possa ser útil na virtualização.


1

Como eu começaria a emulação.

1.Obtenha livros baseados em programação de baixo nível, você precisará dele para o sistema operacional "fingir" do Nintendo ... game boy ...

2. Obtenha livros sobre emulação especificamente e talvez sobre desenvolvimento. (você não estará criando um sistema operacional, mas o mais próximo dele.

3. observe alguns emuladores de código aberto, especialmente os do sistema para o qual você deseja criar um emulador.

Copie trechos do código mais complexo para o seu IDE / complementador. Isso poupará você a escrever códigos longos. Isto é o que eu faço para o desenvolvimento, uso um distrito de linux


1

Escrevi um artigo sobre a emulação do sistema Chip-8 em JavaScript .

É um ótimo lugar para começar, pois o sistema não é muito complicado, mas você ainda aprende como funcionam os códigos de operação, a pilha, os registros etc.

Em breve estarei escrevendo um guia mais longo para o NES.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.