
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 | +-----+-----+-----+
Você pode testar isso com alguma linguagem comREPL ou usandobash
:
echo$((0^0))0echo$((0^1))1echo$((1^1))0
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
A instrução dejump
(0x1NNN
) faz o código pular até a instrução de memóriaNNN
.
0b11010 | 0b1001011A | 25JUMP | 0xA250x1 | A25
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
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.
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 ║╚═══╩═══╩═══╩═══╝
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();
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;}
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;}
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)
For further actions, you may consider blocking this person and/orreporting abuse