You are on page 1of 89

Building Emulators

@miguelpedroso

O que um emulador?

Um emulador um software (ou hardware) que duplica o funcionamento de outro sistema informtico, de forma a que o comportamento seja igual ou parecido.

Para emular-mos uma plataforma, precisamos de saber tudo acerca dela. Por esse motivo, vamos primeiro estudar o funcionamento geral dos computadores.

Como que um computador trabalha?

Os computadores tm processadores (CPUs)

Um processador uma unidade de hardware que tem como objectivo executar instrues.

As instrues so executadas step-by-step, apesar dos processadores modernos estarem optimizados e tentarem executar vrias instrues paralelamente, desde que o seu resultado seja compatvel.

As instrues de um processador permitem fazer vrios tipos de operaes, como operaes bsicas de aritmtica, operaes lgicas e de I/O.

Processadores diferentes e de famlias diferentes podem ter instruction-sets diferentes.

O Intel 4004 foi o primeiro microprocessador a estar disponvel comercialmente

O 6502 foi um CPU de 8 bits de baixo custo e extremamente popular.

NES (Nintendo Enterteinment System)

O 6502 foi utilizado em imensas plataformas populares dos anos 80

Apple II

O 6502 foi utilizado em imensas plataformas populares dos anos 80

O 6502 foi utilizado em imensas plataformas populares dos anos 80


Commodore 64

At o Exterminador Implacvel funcionava a 6502!

O Z80 da Zilog foi tambm um CPU muito utilizado durante os anos 80 (em consolas como o prprio GameBoy, que usava uma variante do Z80).

Compilador de C

Assembler

i386 Programa em C ARM

i386 bin ARM bin bin

Cdigo assembly especfico de um CPU

binrio para correr num tipo de CPUs.

Um programa compilado, em formato binrio, no nada mais do que uma sequncia de nmeros (ou de bytes), que o processador vai ler e executar.

Antes de emular-mos um processador, temos de saber tudo o que h a saber acerca dessa arquitectura.

Neste workshop vamos emular um CPU que eu criei um o objectivo de ser didctico, o MP08E.

Antes de comear-mos, vamos rever alguns conceitos elementares

Bit a nidade bsica da informao na rea da computao. Um bit pode ter o valor de 0, ou 1 Nibble Conjunto de 4 bits (pode valer entre 0 e 15) Byte Conjunto de 8 bits (pode valer entre 0 e 255) Word Conjunto de 16 bits (pode valer entre 0 e 2^16 - 1)

Sistema de numerao binrio: Como temos apenas bits (0s e 1s) como forma de guardar informao, representamos os nmeros em base 2, e no em base 10.

Binrio 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111

Decimal 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Hexadecimal 0 1 2 3 4 5 6 7 8 9 A B C D E F

Alguns operadores
Shift esquerda 0001 << 2 = 0100 Shift direita 1001 >> 3 = 0001 Negao ~0110 = 1001 And 1001 & 0001 = 0001 Or 1001 | 0101 = 1101 Xor 1001 ^ 0101 = 0100

O MP08 um processador de 8 bits. Tem um acumulador e 8 registos de dados de 8-bits

Um registo de um CPU uma rea dentro do processador que permite armazenar uma pequena quantidade de memria.

Registo de 4 bits, permite armazenar 4 bits, ou seja 2^4 combinaes de 0s ou 1s ____


0010 1111

I/O

R0

R1

Status

Accumulator R2 R3

Program Counter Memory RE RF

Um opcode de um processador um cdigo de referncia que especifica uma determinada operao a realizar

Os opcodes do MP08E tm 4 bits de tamanho. Como tal, podemos ter at 2^4 = 16 instrues distintias!

byte - 8-bit value reg - data register (nibble - 4 bits, there are 16 data registers) addr - 8-bit value Opcode 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 Hex 0 1 2 3 4 5 6 7 8 9 A B C D E Assembly NOP LOADLIT byte LOADREG reg STORE reg INC DEC IN OUT COMPLIT byte COMPREG reg JE addr JG addr JL addr JMP addr ADD reg Description No operation Loads 8-bit immediate into the accumulator Loads 8-bit value from register into the accumulator Stores the accumulator into a data register Increments the accumulator Decrements the accumulator Reads the value of the I/O latch into the accumulator Stores the value of the accumulator into the I/O latch Compares the value of the accumulator with the immediate value (affects flags) Compares the value of the accumulator with a data register PC = addr, if Equal flag is set PC = addr, if Greater flag is set PC = addr, if Lower flag is set PC = addr AC = AC + reg

Imaginem a RAM como sendo uma enorme array de bytes.

Em cada posio, podem guardar um byte. Aqui temos o valor 5 guardado no endereo 9.

Todas as posies na memria tm um endereo preenchido (1 byte), um valor entre 0 e 255

Estes valores podem corresponder a variveis, mas no s!

Na memria guardado o programa que est a ser executado!

Os processadores tm um registo chamado de Program Counter (PC). Esse registo aponta para a posio de memria onde est guardado o opcode da prxima instruo que o CPU vai executar

Program Counter =9

A prxima instruo a executar pelo CPU est guardada no endereo de memria 9.

Program Counter =9

No endereo de memria 9 est guardado o valor 5.

Program Counter =9

O CPU vai executar o opcode 5, que ir ter algum efeito no processador.

Program Counter =9

Depois de executar a instruo de opcode 5, o PC vai ser incrementado.

Program Counter =9

Para executar a instruo seguinte, o CPU vai ler o opcode e mandar execut-lo.

Um CPU , portanto, nada mais do que um processador de nmeros. De acordo com o nmero que l (opcode) vai executar uma determinada instruo!

Vamos, portanto, analisar a tabela de opcodes do MP08E.

byte - 8-bit value reg - data register (nibble - 4 bits, there are 16 data registers) addr - 8-bit value Opcode 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 Hex 0 1 2 3 4 5 6 7 8 9 A B C D E Assembly NOP LOADLIT byte LOADREG reg STORE reg INC DEC IN OUT COMPLIT byte COMPREG reg JE addr JG addr JL addr JMP addr ADD reg Description No operation Loads 8-bit immediate into the accumulator Loads 8-bit value from register into the accumulator Stores the accumulator into a data register Increments the accumulator Decrements the accumulator Reads the value of the I/O latch into the accumulator Stores the value of the accumulator into the I/O latch Compares the value of the accumulator with the immediate value (affects flags) Compares the value of the accumulator with a data register PC = addr, if Equal flag is set PC = addr, if Greater flag is set PC = addr, if Lower flag is set PC = addr AC = AC + reg

Compilador de C

Assembler

i386 Programa em C ARM

i386 bin ARM bin bin

Cdigo assembly especfico de um CPU

binrio para correr num tipo de CPUs.

Programa Em C
int a = 5; int b = 3; int c; c = a; a = b; b = c;

parte do programa swap.c

Ficheiro assembly que vai se gerado para correr este programa no processador MP08E.
int a = 5; int b = 3; int c; c = a; a = b; b = c;
LOADLIT STORE LOADLIT STORE LOADREG STORE LOADREG STORE LOADREG STORE 5 R0 3 R1 R0 R2 R1 R0 R2 R1

end: JMP end

O ficheiro assembly vai ser convertido para cdigo de mquina executvel pelo CPU.
LOADLIT STORE LOADLIT STORE LOADREG STORE LOADREG STORE LOADREG STORE 5 R0 3 R1 R0 R2 R1 R0 R2 R1
1005 30 1003 31 20 32 21 30 22 31 D0 0C

end: JMP end

Como emular uma plataforma?

Para emular um chip, podemos ter duas abordagens

Emular o circuito eletrnico do chip

Emular a lgica e o modo de comportamento do chip

Se optar-mos por emular o circuito eletrnico, estaremos a fazer uma emulao muito lenta, mas incrivelmente precisa e fiel. Nesta abordagem teremos de descer ao nvel dos transstores e emular voltagens e correntes.

o caso do projeto Visual 6502

Como estamos a emular um circuito eletrnico, conseguimos obter uma emulao perfeita do sistema. O problema que conseguimos uma velocidade de funcionamento muito baixa, pois emularemos o CPU a velocidades de clock de apenas alguns Hertz.

Portanto esta abordagem no nos serve para muito!

Vamos, deste modo, emular o comportamento do processador.

Como j vimos, a memria principal de um CPU no nada mais que uma array de bytes!
Ento vamos implementar a memria como um array de bytes! byte[] memory = new byte[0x100];

Os registo so caixas individuais onde podemos guardar informao. Ento vamos implement-los como variveis.
byte AC = 0x00; // Accumulator byte PC = 0x00; // Program Counter

O CPU trabalha step-by-step, ou seja, uma instruo executada de cada vez. Ento vamos criar uma funo Step que consiga executar uma qualquer instruo que lhe seja passada.

public void Step() { int opcode = memory[PC]; switch(opcode) { case 0x0: /* NOP*/ break; case 0x1: /* Faz qualquer coisa */ break; } PC++;

byte - 4-bit value reg - data register (nibble - 4 bits, there are 16 data registers) addr - 8-bit value Opcode 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 Hex 0 1 2 3 4 5 6 7 8 9 A B C D E Assembly NOP LOADLIT byte LOADREG reg STORE reg INC DEC IN OUT COMPLIT byte COMPREG reg JE addr JG addr JL addr JMP addr ADD reg Description No operation Loads 8-bit immediate into the accumulator Loads 8-bit value from register into the accumulator Stores the accumulator into a data register Increments the accumulator Decrements the accumulator Reads the value of the I/O latch into the accumulator Stores the value of the accumulator into the I/O latch Compares the value of the accumulator with the immediate value (affects flags) Compares the value of the accumulator with a data register PC = addr, if Equal flag is set PC = addr, if Greater flag is set PC = addr, if Lower flag is set PC = addr AC = AC + reg

byte - 4-bit value reg - data register (nibble - 4 bits, there are 16 data registers) addr - 8-bit value Opcode 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 Hex 0 1 2 3 4 5 6 7 8 9 A B C D E Assembly NOP LOADLIT byte LOADREG reg STORE reg INC DEC IN OUT COMPLIT byte COMPREG reg JE addr JG addr JL addr JMP addr ADD reg Description

Vamos escolher 2 opcodes e ver como implementa-los!

No operation Loads 8-bit immediate into the accumulator Loads 8-bit value from register into the accumulator Stores the accumulator into a data register Increments the accumulator Decrements the accumulator Reads the value of the I/O latch into the accumulator Stores the value of the accumulator into the I/O latch Compares the value of the accumulator with the immediate value (affects flags) Compares the value of the accumulator with a data register PC = addr, if Equal flag is set PC = addr, if Greater flag is set PC = addr, if Lower flag is set PC = addr AC = AC + reg

public void Step() { int opcode = memory[PC];

switch(opcode) { case 0x0: /* NOP */ break; case 0x1: /* Faz qualquer coisa */ break; } PC++;

O opcode 0x0 um NOP. Um NOP no faz nada, portanto temos apenas de incrementar o PC e esperar pelo opcode seguinte.

public void Step() { int opcode = memory[PC];

O opcode 0x1 guarda o immediate value no acumulador.

switch(opcode) { case 0x0: /* NOP */ break; case 0x1: /* LOADLIT byte*/ This.AC = memory[++this.PC]; break; } PC++;

public void Step() { int opcode = memory[PC];

switch(opcode) { case 0x0: /* NOP */ break; case 0x1: /* LOADLIT byte*/ This.AC = memory[++this.PC]; break; } PC++;

O immediate value o valor de 8-bits que est na posio de memria exactamente a seguir ao opcode

public void Step() { int opcode = memory[PC];

1002 10 Opcode 02- Immediate

switch(opcode) { case 0x0: /* NOP */ break; case 0x1: /* LOADLIT byte*/ this.AC = memory[++this.PC]; break; }

PC++;
}

At agora vimos apenas uma pequena parte de tudo o que h a saber acerca de emulao.

As tcnicas que vimos para emular um CPU, apesar de funcionarem extremamente bem em sistema antigos, como a NES ou o GameBoy, so extremamente lentas quando emulamos CPUs modernos a funcionar a clocks elevados.

Se quisermos emular plataformas mais avanadas, precisamos de recorrer a outras tcnicas, como a de recompilao dinmica.

Neste caso, em vez de estar-mos a interpretar o cdigo diretamente num switch, vamos recompilar o programa para o processador em o que queremos emular.

Por exemplo, se pretender-mos emular uma Nintendo 64 num PC, ao ler-mos cada opcode, iremos recompilar essa instruo para o processador i386.

Desta forma, s da primeira vez que teremos o trabalho de descodificar os opcodes. Das vezes seguintes, bastar mandar executar o cdigo que j recompilmos.

Apresento-vos o meu emulador de NES (Nintendo Enterntainment System), tambm conhecida como Famicom, no Japo.

A NES trabalha com um processador costum-made, chamado Ricoh 2A03, que consite numa cpia do 6502 (sem modo BCD) e num pseudo-processador de udio (pAPU), tudo embutido num s chip.

O 6502 tm 151 opcodes (oficiais) e mais umas dezenas de opcodes no oficiais, que podem ser utilizados porque a descodificao de opcodes na electrnica do chip no foi feita de forma completa.

O facto de poder-mos optimizar o software com opcodes no oficiais, permitiu usar truques para aumentar a velocidade dos programas e/out diminuir o tamanho dos mesmos.

O Apple II foi um exemplo de um computador que excedeu todas as expectativas iniciais, do que se podia inicialmente fazer com ele.

Para emular a NES, no basta apenas emular o CPU, tambm preciso emular o PPU (o processador de imagens) que gera um sinal de televiso NTSC e interage com o CPU.

O objectivo deste workshop foi apenas de introduzir os bare-bones da emulao. A emulao das reas mais avanadas da informtica e requer muito tempo de estudo acerca da plataforma a emular.

Atividade Prtica: Emular o MP08E usando C#

Preparei uma pequena biblioteca que contm uma simples interface grfica para o CPU que tero de emular.

Abre um programa

Corre um programa

Executa uma instruo Faz reset ao CPU

Contedo da memria (RAM)

Estado dos registos do CPU

Para isso disponibilizei uma biblioteca dinmica (.dll) e um template de projecto em C#

I/O

R0

R1

Status

Accumulator R2 R3

Program Counter Memory RE RF

byte - 8-bit value reg - data register (nibble - 4 bits, there are 16 data registers) addr - 8-bit value Opcode 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 Hex 0 1 2 3 4 5 6 7 8 9 A B C D E Assembly NOP LOADLIT byte LOADREG reg STORE reg INC DEC IN OUT COMPLIT byte COMPREG reg JE addr JG addr JL addr JMP addr ADD reg Description No operation Loads 8-bit immediate into the accumulator Loads 8-bit value from register into the accumulator Stores the accumulator into a data register Increments the accumulator Decrements the accumulator Reads the value of the I/O latch into the accumulator Stores the value of the accumulator into the I/O latch Compares the value of the accumulator with the immediate value (affects flags) Compares the value of the accumulator with a data register PC = addr, if Equal flag is set PC = addr, if Greater flag is set PC = addr, if Lower flag is set PC = addr AC = AC + reg

Obrigado!

@miguelpedroso

You might also like