You should have all the background you need to start to build the 8080 CPU emulator.
I am writing this code to hopefully be as clear as possible, with each opcode implemented separately. When you get comfortable with it, you might want to rewrite this code for better performance or more code reuse.
First I'm going to set up a data structure that contains fields for the everything I noticed I'd need when I wrote the disassembler. There will also be a place for a memory buffer that will represent the RAM.
typedef struct ConditionCodes { uint8_t z:1; uint8_t s:1; uint8_t p:1; uint8_t cy:1; uint8_t ac:1; uint8_t pad:3; } ConditionCodes; typedef struct State8080 { uint8_t a; uint8_t b; uint8_t c; uint8_t d; uint8_t e; uint8_t h; uint8_t l; uint16_t sp; uint16_t pc; uint8_t *memory; struct ConditionCodes cc; uint8_t int_enable; } State8080;
Now, make a routine with a error call for every opcode which will quit the program with an error. It will look something like this:
void UnimplementedInstruction(State8080* state) { //pc will have advanced one, so undo that printf ("Error: Unimplemented instruction\n"); exit(1); } int Emulate8080Op(State8080* state) { unsigned char *opcode = &state->memory[state->pc]; switch(*opcode) { case 0x00: UnimplementedInstruction(state); break; case 0x01: UnimplementedInstruction(state); break; case 0x02: UnimplementedInstruction(state); break; case 0x03: UnimplementedInstruction(state); break; case 0x04: UnimplementedInstruction(state); break; /*....*/ case 0xfe: UnimplementedInstruction(state); break; case 0xff: UnimplementedInstruction(state); break; } state->pc+=1; //for the opcode }
Let's implement some opcodes.
void Emulate8080Op(State8080* state) { unsigned char *opcode = &state->memory[state->pc]; switch(*opcode) { case 0x00: break; //NOP is easy! case 0x01: //LXI B,word state->c = opcode[1]; state->b = opcode[2]; state->pc += 2; //Advance 2 more bytes break; /*....*/ case 0x41: state->b = state->c; break; //MOV B,C case 0x42: state->b = state->d; break; //MOV B,D case 0x43: state->b = state->e; break; //MOV B,E } state->pc+=1; }
Like that. For each opcode, we modify the state and memory with what that instruction would do if it was actually executing on the real 8080.
The 8080 has about 7 types, depending on how you classify them:
Data Transfer
Arithmetic
Logical
Branch
Stack
I/O
Special
Let's go through these individually.
← Prev: disassembler-pt-2 Next: arithmetic-group →Post questions or comments on Twitter @realemulator101, or if you find issues in the code, file them on the github repository.