Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Construindo o seu próprio emulador, parte 1
Maurício Antunes
Maurício Antunes

Posted on • Edited on

     

Construindo o seu próprio emulador, parte 1

O que é um emulador?

Um emulador emula hardware em software. É uma técnica que habilita um computadorimitar as características de outro hardware, como por exemplo, um video game. Existem emuladores famosos como oProject64 e oZSNES, usados para emular os video games Nintendo 64 e Super Nintendo respectivamente.

Além de emular, um emulador pode ir adiante, providenciando uma maior performance, maior qualidade de vídeo e melhor gerenciamento de recursos como CPU e memória.

Por que aprender sobre emuladores?

Boa pergunta! Entender o básico de emuladores vai te proporcionar uma boa ideia de como computadores funcionam.

Emuladores são compostos de vários componentes de um computador, como uma memória, uma CPU, um teclado e um display. Cada um desses componentes tem suas características. O desenvolvimento de um emulador vai te ajudar a entender como um programa é carregado na memória (ROM,read-only memory), ou como as instruções do programa são interpretadas e executadas e como as informações são mostradas na tela.

Alguns requisitos são necessários para entender e construir um, como:

  • Conhecer alguma linguagem de programação;
  • Operações lógicas;
  • Deslocamento de bits (bit shifting).

Se você conhece uma linguagem de programação mas não entende sobre operações lógicas e operações de bit, não se preocupe, eu vou colocar referências de estudo e explicar as partes do código que falam sobre isso em detalhes.

Ohello world! dos emuladores é ochip-8, e é ele que vamos construir.

Um pouco de base

Antes de começarmos a falar sobre o emulador que vamos construir, vamos relembrar ou aprender um pouco sobre dois assuntos que precisamos compreender para seguir este guia.

Binário e hexadecimal

Nosso sistema número mais comum é o sistema decimal. Isso quer dizer que temos do 0 ao 9 para formarmos outros números, como o 2, o 321, o 9825, etc. Depois do 9, para adicionarmos um número passamos o 9 para 0 e colocamos 1 à esquerda e teremos o 10. Computadores usam outros sistemas de números: binário e hexadecimal.

Osistema binário é constituído de 2 números, o 0 e o 1.Zero é 0,um é 1 e o 2 troca o 1 pra 0 e adiciona um 1 à esquerda. Contando em binário: 0, 1, 10, 11, 100, 101, 110, 111, 1000 (0, 1, 2, 3, 4, 5, 6, 7, 8).

Osistema hexadecimal conta com 16 símbolos. Isso provê uma maior compactação na representação de números maiores. Por exemplo o 15 é representado pela letraF. Quanto maior o número, mais isso fica perceptível.

Todas as instruções do chip-8 são analisadas de forma hexadecimal. A instrução de limpar a tela começa com0x0 e a operação de desenhar é0xD.

Lógica binária

Lógica binária opera em bits. Usamos para manipular valores e fazer comparações.

Algumas dessas operações você já pode conhecer, mas uma em especial é importante conhecer, oXOR. O XOR devolve 1 bit sempre que a quantidade de números 1 for ímpar:

 |  V1 |  V2 |  R  | +-----+-----+-----+ |  0  |  0  |  0  | +-----+-----+-----+ |  1  |  0  |  1  | +-----+-----+-----+ |  0  |  1  |  1  | +-----+-----+-----+ |  1  |  1  |  0  | +-----+-----+-----+
Enter fullscreen modeExit fullscreen mode

Você pode testar isso com alguma linguagem comREPL ou usandobash:

echo$((0^0))0echo$((0^1))1echo$((1^1))0
Enter fullscreen modeExit fullscreen mode

Deslocamento de bits

Uma das razões do deslocamento de bits existir é a possibilidade de codificar informações importantes usando menos dados. O receptor da mensagem pode decodificar a mensagem, extraindo valores com significado.

As instruções do chip-8, por exemplo, são codificadas de 2 em 2 bytes, e cada byte pode ser dividido em 2nibbles usando deslocamento de bits. Veremos isso em mais detalhes no próximo post, quando vamos aprender a decodificar as instruções da ROM.

Geralmente a primeira instrução de uma ROM é limpar a tela, a00E0. Em binário isso seria00000000 (00) e11100000 (E0).

A tela é limpa quando oOpCode é0 e o quartonibble é0. Vamos testar usando o REPL do Python:

>>>(0b00000000>>4)&0xF//OpCode0>>>0b11100000&0xF//quartonibblechamadodeN0
Enter fullscreen modeExit fullscreen mode

A instrução dejump (0x1NNN) faz o código pular até a instrução de memóriaNNN.

0b11010 | 0b1001011A      | 25JUMP | 0xA250x1  | A25
Enter fullscreen modeExit fullscreen mode

0x1 é a operação de pular eA25 forma oNNN, o quarto, terceiro e segundo nibble dos dois bytes da instrução.

Veremos esse assunto em detalhes na parte 2.

O que é o chip-8?

O chip-8 é uma linguagem interpretada e, também, uma máquina virtual criada por Joe Weisbecker em 1977 para rodar noCOSMAC VIP

foto de um computador COSMAC VIP

A ideia do Joe era rodar pequenos programas e jogos com a ajuda de um teclado de hexadecimal. Em vez de usar linguagem de máquina, o teclado hexadecimal era usado para digitar instruções que seriam interpretadas.

Interpretadas? O correto não seriaemuladas? Não. Nesse guia não vamos construir um emulador exatamente, mas sim uminterpretador de instruções chip-8. Nosso interpretador vai ler instruções de uma ROM e executá-las uma a uma, em um loop deler, decodificar e executar as instruções carregadas.

Mas qual a diferença entreinterpretador eemulador nesse caso? O chip-8 é um programa que rodava em um computador. Um emulador imita hardware. Simular o chip-8 significa que vamos escrever uma máquina virtual que interpreta comandos via uma linguagem hexadecimal, porém, trazendo vários conceitos vistos em emulação e arquitetura de computadores como PC (program counter), stack, timers, RAM, ROM, etc.

O chip-8 é formado por diversos componentes. Abaixo vamos falar de cada um dos componentes de maneira breve.

Memória

O espaço de memória deve ser de 4kB (4096 bytes). Toda essa memória é volátil (RAM) e pode ser modificável.

Uma ROM deve começar começar a ser carregada a partir do endereço0x200 (512 em decimal). Os endereços0x000 até o0x1FF são reservados para o chip-8, porém vamos construir ele usando nosso computador, então não precisamos nos preocupar com isso.

Registradores

Existem dois registradores:dados e deendereço.
Eles são usados para gerenciar dados no chip-8. Há várias operações como adição, subtração, leitura de valores, etc. Essas operações se tornam possíveis quando você tem registradores.

O registrador de dados é um array com 16 posições de valor inteiro de 8 bits sem sinal (u8). O endereço0xF é normalmente usado para configurar umaflag (0 ou1), que pode ser usado para indicar se houve colisão ao desenhar no display.

O registrador de endereço não é um array, mas sim uma espécie de ponteiro. Ele é chamado deindex e é usado para ler e escrever na memória. O registrador de endereços, chamado deI, também é usado para desenhar as fontes no display, que serão configuradas através de uma das instruções contidas na ROM.

Display

Com as dimensões de64 pixels de largura e32 pixels (64x32) de altura, o display é usado para renderizar na tela toda atualização identificada pela instrução dedraw (DXYN) que veremos adiante.

No chip-8, os pixels são valores booleanos,0 ou1,on ouoff,0x0 ou0x1. Os displays eram monocromáticos (preto e branco), porém, quando chegar a hora você vai poder usar diferentes cores para os pixels. O display começa com todos os pixels off.

Em desenvolvimento você pode, em vez de escrever na tela usando algumaengine de gráficos, escrever noSTDOUT para conferir se o programa está desenhado da maneira correta.

Teclado

O teclado do COSMAC VIP tem 16 teclas, por isso é chamado dehex keypad. Não vamos construir o teclado, mas precisamos saber que ele existe e que algumas instruções do chip-8 esperam por uma tecla a ser pressionada.

teclado com 16 teclas do COSMAC VIP

Ao construir seu chip-8 você não precisa seguir esse layout. O layout que usei é como mostrado abaixo:

╔═══╦═══╦═══╦═══╗║ 1 ║ 2 ║ 3 ║ 4 ║╠═══╬═══╬═══╬═══╣║ Q ║ W ║ E ║ R ║╠═══╬═══╬═══╬═══╣║ A ║ S ║ D ║ F ║╠═══╬═══╬═══╬═══╣║ Z ║ X ║ C ║ V ║╚═══╩═══╩═══╩═══╝
Enter fullscreen modeExit fullscreen mode

Se o seu teclado não é no layoutQWERTY, tudo bem, você pode mapear outras teclas ou até deixar isso configurável.

Stack

A stack é uma área de memória relacionada a sub-rotinas que podem ser chamadas durante a execução da ROM.
Como o próprio nome já diz, isso é uma stack. Se a sua linguagem de programação suporta o uso de stacks, use-a para maior legibilidade.

Os valores guardados nessa stack são de 16 bits.

Em Rust, os vectors possuem as funçõespush epop:

letmutstack:Vec<u16>=Vec::new();stack.push(0xF);stack.pop();
Enter fullscreen modeExit fullscreen mode

Existe uma limitação no chip-8 de 12 a 16 endereços de memória, porém, não precisamos nos preocupar com isso, deixando o tamanho da stack ilimitada.

Fontes

O chip-8 vem com fontes pré-determinadas. Isso significa que existe na memória do chip-8 sprites que são basicamente números e letras no intervalo hexadecimal:0 aF. Cada uma dessas representações tem 4 pixels de largura e 5 pixels de altura.

Guarde essas fontes em uma área de memória antes do0x200 que falamos acima. Por algum motivo ficou popular gravar a partir do0x50. É possível definir e carregar as fontes usando um código similar ao debaixo:

staticFONTS:[u8;80]=[0xF0,0x90,0x90,0x90,0xF0,// 00x20,0x60,0x20,0x20,0x70,// 10xF0,0x10,0xF0,0x80,0xF0,// 20xF0,0x10,0xF0,0x10,0xF0,// 30x90,0x90,0xF0,0x10,0x10,// 40xF0,0x80,0xF0,0x10,0xF0,// 50xF0,0x80,0xF0,0x90,0xF0,// 60xF0,0x10,0x20,0x40,0x40,// 70xF0,0x90,0xF0,0x90,0xF0,// 80xF0,0x90,0xF0,0x10,0xF0,// 90xF0,0x90,0xF0,0x90,0x90,// A0xE0,0x90,0xE0,0x90,0xE0,// B0xF0,0x80,0x80,0x80,0xF0,// C0xE0,0x90,0x90,0x90,0xE0,// D0xF0,0x80,0xF0,0x80,0xF0,// E0xF0,0x80,0xF0,0x80,0x80,// F];for(n,font)inFONTS.iter().enumerate(){machine.memory[0x50+n]=*font;}
Enter fullscreen modeExit fullscreen mode

Existe uma instrução (FX29) que guarda o endereço da fonte a ser desenhada. Então, a instrução de desenhar vai usar esse valor de memória para escrever a fonte na tela.

Timers

O chip-8 conta com 2 timers (temporizadores):delay timer esound timer.

Ambos timers são decrementados numa taxa de60khz até chegar a zero. Ao chegar em zero, cada um terá seu evento interrompido e a contagem recomeça.

O delay timer é usado para sincronizar eventos.
O sound timer vai tocar um som (beep) enquanto o valor for maior que zero.

Os dois timers podem ser inicializados como um inteiro de 8 bits sem sinal (u8).

ifself.delay_timer>0{self.delay_timer-=1;}ifself.sound_timer>0{self.sound_timer-=1;}
Enter fullscreen modeExit fullscreen mode

No próximo post vamos abordar, em detalhes, a codificação do chip-8 usando a linguagem Rust, entendendo como decodificar as instruções e executar as operações.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I enjoy coding, video streaming and programming languages
  • Location
    Brazil
  • Work
    Software Developer @Globo
  • Joined

Trending onDEV CommunityHot

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp