Emulator Shell

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:

Let's go through these individually.

← Prev: disassembler-pt-2   Next: arithmetic-group →