diff --git a/include/mos6502.enums.h b/include/mos6502.enums.h index 8d7a5c7..5678e7f 100644 --- a/include/mos6502.enums.h +++ b/include/mos6502.enums.h @@ -26,6 +26,12 @@ enum status_flags { MOS_NEGATIVE = 128, }; +#define MOS_NVZ (MOS_NEGATIVE | MOS_OVERFLOW | MOS_ZERO) +#define MOS_NVZC (MOS_NEGATIVE | MOS_OVERFLOW | MOS_ZERO | MOS_CARRY) +#define MOS_NZ (MOS_NEGATIVE | MOS_ZERO) +#define MOS_NZC (MOS_NEGATIVE | MOS_ZERO | MOS_CARRY) +#define MOS_ZC (MOS_ZERO | MOS_CARRY) + /* * Here we define the various address modes that are possible. These do * not map to any significant numbers that are documented for the 6502 diff --git a/include/mos6502.h b/include/mos6502.h index a38e816..95bba8c 100644 --- a/include/mos6502.h +++ b/include/mos6502.h @@ -36,6 +36,9 @@ vm_8bit carry = 0; \ if (cpu->P & MOS_CARRY) carry = 1 +#define SET_RESULT(op) \ + int result = op + /* * A uniform way of declaring resolve functions for address modes, which * is useful in the event that we need to change the function signature. @@ -138,7 +141,7 @@ extern vm_8bit mos6502_get(mos6502 *, size_t); extern void mos6502_execute(mos6502 *); extern void mos6502_free(mos6502 *); extern void mos6502_last_executed(mos6502 *, vm_8bit *, vm_8bit *, vm_16bit *); -extern void mos6502_modify_status(mos6502 *, vm_8bit, vm_8bit); +extern void mos6502_modify_status(mos6502 *, vm_8bit, int, int); extern void mos6502_push_stack(mos6502 *, vm_16bit); extern void mos6502_set(mos6502 *, size_t, vm_8bit); extern void mos6502_set16(mos6502 *, size_t, vm_16bit); diff --git a/src/apple2.dbuf.c b/src/apple2.dbuf.c index bde7c98..b6a3cc8 100644 --- a/src/apple2.dbuf.c +++ b/src/apple2.dbuf.c @@ -9,6 +9,12 @@ static size_t switch_reads[] = { 0xC01B, 0xC01E, 0xC01F, + 0xC050, + 0xC051, + 0xC052, + 0xC053, + 0xC05E, + 0xC05F, 0xC07E, 0xC07F, }; @@ -167,6 +173,40 @@ SEGMENT_READER(apple2_dbuf_switch_read) return mach->display_mode & DISPLAY_DHIRES ? 0x80 : 0x00; + + // As in apple2.mem.c, the following switch cases are duplicated + // from the switch_write function because the Apple II expects + // both reads and writes to have the same effect at these + // addresses. + case 0xC050: + apple2_set_display(mach, + mach->display_mode | DISPLAY_TEXT); + break; + + case 0xC051: + apple2_set_display(mach, + mach->display_mode & ~DISPLAY_TEXT); + break; + + case 0xC052: + apple2_set_display(mach, + mach->display_mode | DISPLAY_MIXED); + break; + + case 0xC053: + apple2_set_display(mach, + mach->display_mode & ~DISPLAY_MIXED); + break; + + case 0xC05E: + apple2_set_display(mach, + mach->display_mode | DISPLAY_DHIRES); + break; + + case 0xC05F: + apple2_set_display(mach, + mach->display_mode & ~DISPLAY_DHIRES); + break; } // ??? diff --git a/src/apple2.mem.c b/src/apple2.mem.c index f0f65fe..f8ad095 100644 --- a/src/apple2.mem.c +++ b/src/apple2.mem.c @@ -16,6 +16,11 @@ static size_t switch_reads[] = { 0xC018, 0xC01C, 0xC01D, + 0xC054, + 0xC055, + 0xC056, + 0xC057, + 0xC059, }; static size_t switch_writes[] = { @@ -183,6 +188,30 @@ SEGMENT_READER(apple2_mem_switch_read) return mach->memory_mode & MEMORY_HIRES ? 0x80 : 0x00; + + // Note the following are intentionally duplicated from the + // write switch function. Apple II allows both reads and writes + // to have the same behavior here. + case 0xC055: + apple2_set_memory_mode(mach, + mach->memory_mode | MEMORY_PAGE2); + break; + + case 0xC054: + apple2_set_memory_mode(mach, + mach->memory_mode & ~MEMORY_PAGE2); + break; + + case 0xC059: + case 0xC057: + apple2_set_memory_mode(mach, + mach->memory_mode | MEMORY_HIRES); + break; + + case 0xC056: + apple2_set_memory_mode(mach, + mach->memory_mode & ~MEMORY_HIRES); + break; } // ??? diff --git a/src/mos6502.arith.c b/src/mos6502.arith.c index da12a65..1323902 100644 --- a/src/mos6502.arith.c +++ b/src/mos6502.arith.c @@ -16,9 +16,11 @@ DEFINE_INST(adc) { MOS_CARRY_BIT(); - cpu->A += oper + carry; + SET_RESULT(cpu->A + oper + carry); - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_OVERFLOW | MOS_CARRY | MOS_ZERO, cpu->A); + mos6502_modify_status(cpu, MOS_NVZC, cpu->A, result); + + cpu->A = result & 0xff; } /* @@ -30,7 +32,7 @@ DEFINE_INST(adc) */ DEFINE_INST(cmp) { - mos6502_modify_status(cpu, MOS_ZERO | MOS_NEGATIVE | MOS_CARRY, cpu->A - oper); + mos6502_modify_status(cpu, MOS_NZC, cpu->A, cpu->A - oper); } /* @@ -39,7 +41,7 @@ DEFINE_INST(cmp) */ DEFINE_INST(cpx) { - mos6502_modify_status(cpu, MOS_ZERO | MOS_NEGATIVE | MOS_CARRY, cpu->X - oper); + mos6502_modify_status(cpu, MOS_NZC, cpu->X, cpu->X - oper); } /* @@ -48,7 +50,7 @@ DEFINE_INST(cpx) */ DEFINE_INST(cpy) { - mos6502_modify_status(cpu, MOS_ZERO | MOS_NEGATIVE | MOS_CARRY, cpu->Y - oper); + mos6502_modify_status(cpu, MOS_NZC, cpu->Y, cpu->Y - oper); } /* @@ -60,8 +62,8 @@ DEFINE_INST(cpy) DEFINE_INST(dec) { if (cpu->eff_addr) { + mos6502_modify_status(cpu, MOS_NZ, oper, oper - 1); mos6502_set(cpu, cpu->eff_addr, oper - 1); - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_ZERO, oper - 1); } } @@ -70,8 +72,8 @@ DEFINE_INST(dec) */ DEFINE_INST(dex) { + mos6502_modify_status(cpu, MOS_NZ, cpu->X, cpu->X - 1); cpu->X--; - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_ZERO, cpu->X); } /* @@ -79,8 +81,8 @@ DEFINE_INST(dex) */ DEFINE_INST(dey) { + mos6502_modify_status(cpu, MOS_NZ, cpu->Y, cpu->Y - 1); cpu->Y--; - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_ZERO, cpu->Y); } /* @@ -91,8 +93,8 @@ DEFINE_INST(dey) DEFINE_INST(inc) { if (cpu->eff_addr) { + mos6502_modify_status(cpu, MOS_NZ, oper, oper + 1); mos6502_set(cpu, cpu->eff_addr, oper + 1); - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_ZERO, oper + 1); } } @@ -101,8 +103,8 @@ DEFINE_INST(inc) */ DEFINE_INST(inx) { + mos6502_modify_status(cpu, MOS_NZ, cpu->X, cpu->X + 1); cpu->X++; - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_ZERO, cpu->X); } /* @@ -110,8 +112,8 @@ DEFINE_INST(inx) */ DEFINE_INST(iny) { + mos6502_modify_status(cpu, MOS_NZ, cpu->Y, cpu->Y + 1); cpu->Y++; - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_ZERO, cpu->Y); } /* @@ -123,7 +125,8 @@ DEFINE_INST(iny) DEFINE_INST(sbc) { MOS_CARRY_BIT(); - cpu->A = cpu->A - oper - carry; + SET_RESULT(cpu->A - oper - carry); - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_OVERFLOW | MOS_CARRY | MOS_ZERO, cpu->A); + mos6502_modify_status(cpu, MOS_NVZC, cpu->A, result); + cpu->A = result & 0xff; } diff --git a/src/mos6502.bits.c b/src/mos6502.bits.c index d0f29d7..8df4317 100644 --- a/src/mos6502.bits.c +++ b/src/mos6502.bits.c @@ -14,8 +14,10 @@ */ DEFINE_INST(and) { - cpu->A &= oper; - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_ZERO, cpu->A); + SET_RESULT(cpu->A & oper); + + mos6502_modify_status(cpu, MOS_NZ, cpu->A, result); + cpu->A = result & 0xff; } /* @@ -31,20 +33,15 @@ DEFINE_INST(and) */ DEFINE_INST(asl) { - cpu->P &= ~MOS_CARRY; - if (oper & 0x80) { - cpu->P |= MOS_CARRY; - } + SET_RESULT(oper << 1); - oper <<= 1; + mos6502_modify_status(cpu, MOS_NZC, oper, result); if (cpu->eff_addr) { - mos6502_set(cpu, cpu->eff_addr, oper); + mos6502_set(cpu, cpu->eff_addr, result & 0xff); } else { - cpu->A = oper; + cpu->A = result & 0xff; } - - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_CARRY | MOS_ZERO, oper); } /* @@ -54,21 +51,7 @@ DEFINE_INST(asl) */ DEFINE_INST(bit) { - cpu->P &= ~MOS_NEGATIVE; - if (oper & MOS_NEGATIVE) { - cpu->P |= MOS_NEGATIVE; - } - - cpu->P &= ~MOS_OVERFLOW; - if (oper & MOS_OVERFLOW) { - cpu->P |= MOS_OVERFLOW; - } - - if (oper & cpu->A) { - cpu->P &= ~MOS_ZERO; - } else { - cpu->P |= MOS_ZERO; - } + mos6502_modify_status(cpu, MOS_NVZ, oper, oper); } /* @@ -77,8 +60,10 @@ DEFINE_INST(bit) */ DEFINE_INST(eor) { - cpu->A ^= oper; - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_ZERO, cpu->A); + SET_RESULT(cpu->A ^ oper); + + mos6502_modify_status(cpu, MOS_NZ, cpu->A, result); + cpu->A = result & 0xff; } /* @@ -90,21 +75,16 @@ DEFINE_INST(eor) */ DEFINE_INST(lsr) { - cpu->P &= ~MOS_CARRY; - if (oper & 0x01) { - cpu->P |= MOS_CARRY; - } - - oper >>= 1; - - if (cpu->eff_addr) { - mos6502_set(cpu, cpu->eff_addr, oper); - } else { - cpu->A = oper; - } + SET_RESULT(oper >> 1); // MOS_NEGATIVE is intentionally not included here. - mos6502_modify_status(cpu, MOS_CARRY | MOS_ZERO, oper); + mos6502_modify_status(cpu, MOS_ZC, oper, result); + + if (cpu->eff_addr) { + mos6502_set(cpu, cpu->eff_addr, result & 0xff); + } else { + cpu->A = result & 0xff; + } } /* @@ -113,8 +93,10 @@ DEFINE_INST(lsr) */ DEFINE_INST(ora) { - cpu->A |= oper; - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_ZERO, cpu->A); + SET_RESULT(cpu->A | oper); + + mos6502_modify_status(cpu, MOS_NZ, cpu->A, result); + cpu->A = result & 0xff; } /* @@ -125,24 +107,23 @@ DEFINE_INST(ora) DEFINE_INST(rol) { MOS_CARRY_BIT(); + SET_RESULT(oper << 1); if (oper & 0x80) { carry = 1; } - oper <<= 1; - if (carry) { - oper |= 0x01; + result |= 0x01; } + mos6502_modify_status(cpu, MOS_NZC, oper, result); + if (cpu->eff_addr) { - mos6502_set(cpu, cpu->eff_addr, oper); + mos6502_set(cpu, cpu->eff_addr, result & 0xff); } else { - cpu->A = oper; + cpu->A = result & 0xff; } - - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_CARRY | MOS_ZERO, oper); } /* @@ -152,22 +133,21 @@ DEFINE_INST(rol) DEFINE_INST(ror) { MOS_CARRY_BIT(); + SET_RESULT(oper >> 1); if (oper & 0x01) { carry = 1; } - oper >>= 1; - if (carry) { - oper |= 0x80; + result |= 0x80; } + mos6502_modify_status(cpu, MOS_NZC, oper, result); + if (cpu->eff_addr) { - mos6502_set(cpu, cpu->eff_addr, oper); + mos6502_set(cpu, cpu->eff_addr, result & 0xff); } else { - cpu->A = oper; + cpu->A = result & 0xff; } - - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_CARRY | MOS_ZERO, oper); } diff --git a/src/mos6502.c b/src/mos6502.c index 3a0839d..eab4620 100644 --- a/src/mos6502.c +++ b/src/mos6502.c @@ -236,32 +236,42 @@ mos6502_set_status(mos6502 *cpu, vm_8bit status) * bits are the negative, overflow, carry, and zero flags. */ void -mos6502_modify_status(mos6502 *cpu, vm_8bit status, vm_8bit oper) +mos6502_modify_status(mos6502 *cpu, vm_8bit status, int orig, int result) { if (status & MOS_NEGATIVE) { cpu->P &= ~MOS_NEGATIVE; - if (oper & 0x80) { + if (result & 0x80) { cpu->P |= MOS_NEGATIVE; } } if (status & MOS_OVERFLOW) { cpu->P &= ~MOS_OVERFLOW; - if (oper & MOS_OVERFLOW) { + + // If the result of the operation is such that the sign bit, + // that is to say bit 7, changes, then we have overflowed. E.g.: + // 90 + 40 = 130, but that's actually -124 in two's complement. + // So if you are paying attention to the sign, you have + // overflowed from a positive into a negative result. + if (orig < 0x80 && result >= 0x80) { cpu->P |= MOS_OVERFLOW; } } if (status & MOS_CARRY) { cpu->P &= ~MOS_CARRY; - if (oper > 0) { + + // The result of the operation requires 9 bits to hold, but we + // can only hold 8 bits; rather than lose that bit's value, it + // is held in the carry bit of the P register. + if (result > 0xFF) { cpu->P |= MOS_CARRY; } } if (status & MOS_ZERO) { cpu->P &= ~MOS_ZERO; - if (oper == 0) { + if (result == 0) { cpu->P |= MOS_ZERO; } } diff --git a/src/mos6502.loadstor.c b/src/mos6502.loadstor.c index 248fb59..827b9d7 100644 --- a/src/mos6502.loadstor.c +++ b/src/mos6502.loadstor.c @@ -14,7 +14,7 @@ */ DEFINE_INST(lda) { - mos6502_modify_status(cpu, MOS_ZERO | MOS_NEGATIVE, oper); + mos6502_modify_status(cpu, MOS_NZ, cpu->A, oper); cpu->A = oper; } @@ -23,7 +23,7 @@ DEFINE_INST(lda) */ DEFINE_INST(ldx) { - mos6502_modify_status(cpu, MOS_ZERO | MOS_NEGATIVE, oper); + mos6502_modify_status(cpu, MOS_NZ, cpu->X, oper); cpu->X = oper; } @@ -32,7 +32,7 @@ DEFINE_INST(ldx) */ DEFINE_INST(ldy) { - mos6502_modify_status(cpu, MOS_ZERO | MOS_NEGATIVE, oper); + mos6502_modify_status(cpu, MOS_NZ, cpu->Y, oper); cpu->Y = oper; } @@ -57,8 +57,10 @@ DEFINE_INST(php) */ DEFINE_INST(pla) { - cpu->A = mos6502_pop_stack(cpu); - mos6502_modify_status(cpu, MOS_NEGATIVE | MOS_ZERO, cpu->A); + SET_RESULT(mos6502_pop_stack(cpu)); + + mos6502_modify_status(cpu, MOS_NZ, cpu->A, result); + cpu->A = result; } /* @@ -100,7 +102,7 @@ DEFINE_INST(sty) */ DEFINE_INST(tax) { - mos6502_modify_status(cpu, MOS_ZERO | MOS_NEGATIVE, cpu->A); + mos6502_modify_status(cpu, MOS_NZ, cpu->X, cpu->A); cpu->X = cpu->A; } @@ -109,7 +111,7 @@ DEFINE_INST(tax) */ DEFINE_INST(tay) { - mos6502_modify_status(cpu, MOS_ZERO | MOS_NEGATIVE, cpu->A); + mos6502_modify_status(cpu, MOS_NZ, cpu->Y, cpu->A); cpu->Y = cpu->A; } @@ -118,7 +120,7 @@ DEFINE_INST(tay) */ DEFINE_INST(tsx) { - mos6502_modify_status(cpu, MOS_ZERO | MOS_NEGATIVE, cpu->S); + mos6502_modify_status(cpu, MOS_NZ, cpu->X, cpu->S); cpu->X = cpu->S; } @@ -127,7 +129,7 @@ DEFINE_INST(tsx) */ DEFINE_INST(txa) { - mos6502_modify_status(cpu, MOS_ZERO | MOS_NEGATIVE, cpu->X); + mos6502_modify_status(cpu, MOS_NZ, cpu->A, cpu->X); cpu->A = cpu->X; } @@ -136,7 +138,7 @@ DEFINE_INST(txa) */ DEFINE_INST(txs) { - mos6502_modify_status(cpu, MOS_ZERO | MOS_NEGATIVE, cpu->X); + mos6502_modify_status(cpu, MOS_NZ, cpu->S, cpu->X); cpu->S = cpu->X; } @@ -145,6 +147,6 @@ DEFINE_INST(txs) */ DEFINE_INST(tya) { - mos6502_modify_status(cpu, MOS_ZERO | MOS_NEGATIVE, cpu->Y); + mos6502_modify_status(cpu, MOS_NZ, cpu->A, cpu->Y); cpu->A = cpu->Y; } diff --git a/src/vm_screen.c b/src/vm_screen.c index 160ee55..a03cff2 100644 --- a/src/vm_screen.c +++ b/src/vm_screen.c @@ -168,7 +168,7 @@ vm_screen_active(vm_screen *screen) { SDL_Event event; char ch; - static int counter = 2000; + static int counter = 500; // There may be _many_ events in the queue; for example, you may be // facerolling on Zork because it feels good. And good for you if diff --git a/tests/mos6502.c b/tests/mos6502.c index 7db1d01..728f0c1 100644 --- a/tests/mos6502.c +++ b/tests/mos6502.c @@ -38,24 +38,24 @@ Test(mos6502, pop_stack) Test(mos6502, modify_status) { - mos6502_modify_status(cpu, MOS_NEGATIVE, 130); + mos6502_modify_status(cpu, MOS_NEGATIVE, 130, 130); cr_assert_eq(cpu->P & MOS_NEGATIVE, MOS_NEGATIVE); - mos6502_modify_status(cpu, MOS_NEGATIVE, 123); + mos6502_modify_status(cpu, MOS_NEGATIVE, 123, 123); cr_assert_neq(cpu->P & MOS_NEGATIVE, MOS_NEGATIVE); - mos6502_modify_status(cpu, MOS_OVERFLOW, 123); + mos6502_modify_status(cpu, MOS_OVERFLOW, 123, 133); cr_assert_eq(cpu->P & MOS_OVERFLOW, MOS_OVERFLOW); - mos6502_modify_status(cpu, MOS_OVERFLOW, 44); + mos6502_modify_status(cpu, MOS_OVERFLOW, 44, 44); cr_assert_neq(cpu->P & MOS_OVERFLOW, MOS_OVERFLOW); - mos6502_modify_status(cpu, MOS_CARRY, 23); + mos6502_modify_status(cpu, MOS_CARRY, 230, 230); cr_assert_eq(cpu->P & MOS_CARRY, MOS_CARRY); - mos6502_modify_status(cpu, MOS_CARRY, 0); + mos6502_modify_status(cpu, MOS_CARRY, 30, 190); cr_assert_neq(cpu->P & MOS_CARRY, MOS_CARRY); - mos6502_modify_status(cpu, MOS_ZERO, 0); + mos6502_modify_status(cpu, MOS_ZERO, 0, 0); cr_assert_eq(cpu->P & MOS_ZERO, MOS_ZERO); - mos6502_modify_status(cpu, MOS_ZERO, 1); + mos6502_modify_status(cpu, MOS_ZERO, 1, 1); cr_assert_neq(cpu->P & MOS_ZERO, MOS_ZERO); }