Como os emuladores funcionam? Quando vejo emuladores NES / SNES ou C64, isso me surpreende.
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)?
Respostas:
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:
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
, eS
; você também teria umPC
registro de 16 bits .Com a interpretação, você começa no
IP
(ponteiro da instrução - também chamado dePC
contador 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:
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:
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:
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:
Projetos de emulador para referência:
Referências de recompilação do processador:
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.
fonte
Um sujeito chamado Victor Moya del Barrio escreveu sua tese sobre esse assunto. Muita informação boa em 152 páginas. Você pode baixar o PDF aqui .
Se você não deseja se registrar no scribd , pode pesquisar no google pelo título em PDF "Estudo das técnicas para programação de emulação" . Existem algumas fontes diferentes para o PDF.
fonte
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.
fonte
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.
fonte
Algo que vale a pena dar uma olhada é a tentativa de Imran Nazar de escrever um emulador Gameboy em JavaScript.
fonte
Tendo criado meu próprio emulador do microcomputador da BBC dos anos 80 (digite VBeeb no Google), há várias coisas a saber.
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!
fonte
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.
fonte
... --- ...
- 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?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.
fonte
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.
fonte
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.
fonte
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.
fonte
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.
fonte
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
fonte
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.
fonte
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
fonte
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.
fonte