What's new

Intel 8080 emulator: need help finding bug(s)

miker00lz

New member
As the title says, I think I need some help tracking down a bug in an 8080 emulator that I've been writing in C over the last couple of days. I wrote it from scratch based on Intel documentation. It mostly seems to work, but there is clearly some issue with it. If I run the "8080 instruction exerciser" in the emulator, here are my results:

Code:
8080 instruction exerciser (KR580VM80A CPU)
dad <b,d,h,sp>................  ERROR **** crc expected:14474ba6 found:33b6a681
aluop nn......................  ERROR **** crc expected:9e922f9e found:284aecbe
aluop <b,c,d,e,h,l,m,a>.......  ERROR **** crc expected:cf762c86 found:0c2144eb
<daa,cma,stc,cmc>.............  ERROR **** crc expected:bb3f030c found:797efb2b
<inr,dcr> a...................  ERROR **** crc expected:adb6460e found:0e5332db
<inr,dcr> b...................  ERROR **** crc expected:83ed1345 found:9fc94769
<inx,dcx> b...................  ERROR **** crc expected:f79287cd found:eb7d1a2b
<inr,dcr> c...................  ERROR **** crc expected:e5f6721b found:1695262e
<inr,dcr> d...................  ERROR **** crc expected:15b5579a found:d594aa97
<inx,dcx> d...................  ERROR **** crc expected:7f4e2501 found:63a1b8e7
<inr,dcr> e...................  ERROR **** crc expected:cf2ab396 found:2b639ce1
<inr,dcr> h...................  ERROR **** crc expected:12b2952c found:9bee5682
<inx,dcx> h...................  ERROR **** crc expected:9f2b23c0 found:83c4be26
<inr,dcr> l...................  ERROR **** crc expected:ff57d356 found:1d476ec3
<inr,dcr> m...................  ERROR **** crc expected:92e963bd found:28f7092b
<inx,dcx> sp..................  ERROR **** crc expected:d5702fab found:c99fb24d
lhld nnnn.....................  ERROR **** crc expected:a9c3d5cb found:6d1eeb35
shld nnnn.....................  ERROR **** crc expected:e8864f26 found:2c5b71d8
lxi <b,d,h,sp>,nnnn...........  OK
ldax <b,d>....................  ERROR **** crc expected:2b821d5f found:06cfc034
mvi <b,c,d,e,h,l,m,a>,nn......  OK
mov <bcdehla>,<bcdehla>.......  ERROR **** crc expected:10b58cee found:dfe39de0
sta nnnn / lda nnnn...........  ERROR **** crc expected:ed57af72 found:c01a7219
<rlc,rrc,ral,rar>.............  ERROR **** crc expected:e0d89235 found:4bae6af9
stax <b,d>....................  ERROR **** crc expected:2b0471e9 found:b726a433
Tests complete

The fact that there are apparently errors in SO MANY opcodes leads me to believe that the bug has something to do with a common piece of code used by some or many of them. Perhaps something like decoding register fields or the like. Although, I've already tried to find issues there, and I'm not able to see one. Either that, or it's a problem in an opcode that the test software uses to calculate the CRC of the results. Unfortunately, I can't find the source code to this test software. Or maybe it's an issue with flag calculation(s)?? Could be so many things, that's the hard part about writing CPU emulators, the debugging!

Here is the source code: https://pastebin.com/mweNgfae (Note: It doesn't have cycle timing implemented yet)

It DOES run Space Invaders, but there is a bug there where rows of enemies don't get cleared from the screen at the top as they move down the play field as you can see below. Maybe this will help someone. Thanks for any help you guys can provide.

k74TFt6.png
 
Last edited:

Iconoclast

New member
This may not be related to your bugs in either Space Invaders or 8080 Instruction Exerciser.

Briefly looking at your source, I can see that your implementation of the HLT op-code is unreachable.
Code:
...
        else if (opcode == 0x76) { //HLT - halt processor
            reg_PC--;
        }
...
A problem here is that, way above this check at the top, you have this:
Code:
    while (cycles--) {
        opcode = i8080_read(reg_PC++);
 
        if ((opcode & 0xC0) == 0x40) { //MOV D,S - move register to register
            write_reg8((opcode >> 3) & 7, read_reg8(opcode & 7));
        }
Because of the order of if statements here, the former code is unreachable.
i.e., if your opcode variable is 0x76, the condition ((opcode & 0xC0) == 0x40) was already satisfied first, incorrectly interpreting the operation as a MOV instead of a HLT.



On a related note, I would seriously recommend not having a chain of if statements like that. You may get to have fun by documenting the individual bit-masking tricks with decoding each field of op-codes, but it's confusing to re-validate, read and maintain. (There is also a significant performance hit in your function from potentially almost always going down nearly the entire ladder of ifs until the correct branch is found.)

By using either an array of pointers to functions or a switch statement, you'll save a fair deal of overhead with program size as well as performance. You'll also render the outcome of duplicate or mis-ordered (because there is no dynamic ordering) op-code implementations an impossibility. Things will be smaller, faster, and static.

An ideal implementation with switch would go something like:
Code:
void
i8080_exec(int cycles)
{

    uint8_t opcode, temp8, reg;
    uint16_t temp16;

    uint32_t temp32;
 
    while (cycles--) {
        opcode = i8080_read(reg_PC++);
        switch (opcode & 0xFF) {
        case 0x76: /* HLT:  halt processor */
            reg_PC--;
            break;

        case 0x40:  case 0x41:  case 0x42:  case 0x43:
        case 0x44:  case 0x45:  case 0x46:  case 0x47:
        case 0x48:  case 0x49:  case 0x4A:  case 0x4B:
        case 0x4C:  case 0x4D:  case 0x4E:  case 0x4F:
        case 0x50:  case 0x51:  case 0x52:  case 0x53:
        case 0x54:  case 0x55:  case 0x56:  case 0x57:
        case 0x58:  case 0x59:  case 0x5A:  case 0x5B:
        case 0x5C:  case 0x5D:  case 0x5E:  case 0x5F:
        case 0x60:  case 0x61:  case 0x62:  case 0x63:
        case 0x64:  case 0x65:  case 0x66:  case 0x67:
        case 0x68:  case 0x69:  case 0x6A:  case 0x6B:
        case 0x6C:  case 0x6D:  case 0x6E:  case 0x6F:
        case 0x70:  case 0x71:  case 0x72:  case 0x73:
        case 0x74:  case 0x75:              case 0x77:
        case 0x78:  case 0x79:  case 0x7A:  case 0x7B:
        case 0x7C:  case 0x7D:  case 0x7E:  case 0x7F: /* MOV D,S */
            write_reg8((opcode >> 3) & 7, read_reg8(opcode & 7));
            break;

        case 0x06:  case 0x0E:
        case 0x16:  case 0x1E:
        case 0x26:  case 0x2E:
        case 0x36:  case 0x3E: /* MVI D,#:  move immediate to register */
            write_reg8((opcode >> 3) & 7, i8080_read(reg_PC++));
            break;

        /* ... and so on ... */

        default:
            printf("UNRECOGNIZED INSTRUCTION @ %04Xh: %02X\n", reg_PC - 1, opcode);
            exit(0);
        }
    }
}

I did not finish analyzing the entire function to see if HLT was the only bugged op-code affected by this design problem. To avoid distractions caused by design issues like these while hunting for bugs that may (or may not) turn out to be entirely unrelated, I would go with a switch.
 
OP
M

miker00lz

New member
Good points, and I did know the performance would be worse. My previous emulators (8086 and 6502) have all used a switch block or a function pointer array. I just changed this one to a switch block. Turns out the only conflict with the masking was with MOV and HLT at instruction 76h which you pointed out. Unfortunately, this did not fix any CPU bugs other than HLT not being usable, which I expected after I saw that was the only opcode conflict.

I'll just have to keep combing through the code and comparing with the documentation. Possibly a flags issue. Getting the flags right was hell on the 8086 emu, but the 8080 seems simpler in that regard.
 
OP
M

miker00lz

New member
I've gotten a lot closer. I was already forcing bit 1 of the flags register to appear set, but neglected to mask out bits 3 and 5. I think the instruction exerciser POPs the flags and inserts them into the calculation for the CRC, so this could have caused issues if the program manually set the PSW somewhere with bits 3 and/or 5 set.

Code:
8080 instruction exerciser (KR580VM80A CPU)
dad <b,d,h,sp>................  OK
aluop nn......................  ERROR **** crc expected:9e922f9e found:fd2bdf95
aluop <b,c,d,e,h,l,m,a>.......  ERROR **** crc expected:cf762c86 found:63dcfef6
<daa,cma,stc,cmc>.............  ERROR **** crc expected:bb3f030c found:02e11466
<inr,dcr> a...................  ERROR **** crc expected:adb6460e found:0f15bffe
<inr,dcr> b...................  ERROR **** crc expected:83ed1345 found:adcd6374
<inx,dcx> b...................  OK
<inr,dcr> c...................  ERROR **** crc expected:e5f6721b found:17d3ab0b
<inr,dcr> d...................  ERROR **** crc expected:15b5579a found:e7908e8a
<inx,dcx> d...................  OK
<inr,dcr> e...................  ERROR **** crc expected:cf2ab396 found:1967b8fc
<inr,dcr> h...................  ERROR **** crc expected:12b2952c found:a9ea729f
<inx,dcx> h...................  OK
<inr,dcr> l...................  ERROR **** crc expected:ff57d356 found:1c01e3e6
<inr,dcr> m...................  ERROR **** crc expected:92e963bd found:29b1840e
<inx,dcx> sp..................  OK
lhld nnnn.....................  OK
shld nnnn.....................  OK
lxi <b,d,h,sp>,nnnn...........  OK
ldax <b,d>....................  OK
mvi <b,c,d,e,h,l,m,a>,nn......  OK
mov <bcdehla>,<bcdehla>.......  OK
sta nnnn / lda nnnn...........  OK
<rlc,rrc,ral,rar>.............  OK
stax <b,d>....................  OK
Tests complete

Now it seems to just mainly be something going awry in the 8-bit ALU operations. This narrows my debugging scope by quite a bit!
 

Bluestorm

New member
I have a fully functional space invaders emulator but my 8080 core struggled to pass those tests.
Your post was inspirational and i really apreciate your comment. Cant belive i didnt think to mask out the not used bits on the F register. So i masked 0xD7 and | 0x02 on F and it just passed a bunch like you.

Code:
8080 instruction exerciser (KR580VM80A CPU)
dad <b,d,h,sp>................  OK
aluop nn......................  ERROR **** crc expected:9e922f9e found:8780bd0b
aluop <b,c,d,e,h,l,m,a>.......  ERROR **** crc expected:cf762c86 found:1fe7928c
<daa,cma,stc,cmc>.............  ERROR **** crc expected:bb3f030c found:9073202b
<inr,dcr> a...................  ERROR **** crc expected:adb6460e found:08b1943e
<inr,dcr> b...................  ERROR **** crc expected:83ed1345 found:382da4e5
<inx,dcx> b...................  OK
<inr,dcr> c...................  ERROR **** crc expected:e5f6721b found:803d08ed
<inr,dcr> d...................  ERROR **** crc expected:15b5579a found:707e2d6c
<inx,dcx> d...................  OK
<inr,dcr> e...................  ERROR **** crc expected:cf2ab396 found:ae34c0e5
<inr,dcr> h...................  ERROR **** crc expected:12b2952c found:3c0ab50e
<inx,dcx> h...................  OK
<inr,dcr> l...................  ERROR **** crc expected:ff57d356 found:5e89f4b3
<inr,dcr> m...................  ERROR **** crc expected:92e963bd found:bc51439f
<inx,dcx> sp..................  OK
lhld nnnn.....................  OK
shld nnnn.....................  OK
lxi <b,d,h,sp>,nnnn...........  OK
ldax <b,d>....................  OK
mvi <b,c,d,e,h,l,m,a>,nn......  OK
mov <bcdehla>,<bcdehla>.......  OK
sta nnnn / lda nnnn...........  OK
<rlc,rrc,ral,rar>.............  OK
stax <b,d>....................  OK
Tests complete

Thanks for the comment and i hope you got your space invaders emu working!!!
Even tho it works with space invaders i had a burn of why it failed almost all tests on 8080EX1

I struggled 3 weeks to finish it and was a very enjoying experience.
You can find mine on : https://github.com/BluestormDNA
/i8080-Space-Invaders/tree/master/i8080-Space-Invaders


Now... Do you pass other tests? have you tried cputest.com cpudiag.com test.com ?
Maybe is a timing thing?

Wish you look if you have not finished it yet!
 
Last edited:

Top