Professional Documents
Culture Documents
@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.
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.
Apple II
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
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.
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
Um registo de um CPU uma rea dentro do processador que permite armazenar uma pequena quantidade de memria.
I/O
R0
R1
Status
Accumulator R2 R3
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
Em cada posio, podem guardar um byte. Aqui temos o valor 5 guardado no endereo 9.
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
Program Counter =9
Program Counter =9
Program Counter =9
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!
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
Programa Em C
int a = 5; int b = 3; int c; c = a; a = b; b = 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
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
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.
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.
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
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
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.
switch(opcode) { case 0x0: /* NOP */ break; case 0x1: /* LOADLIT byte*/ This.AC = memory[++this.PC]; break; } 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
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.
Preparei uma pequena biblioteca que contm uma simples interface grfica para o CPU que tero de emular.
Abre um programa
Corre um programa
I/O
R0
R1
Status
Accumulator R2 R3
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