1
0
mirror of https://github.com/pevans/erc-c.git synced 2024-11-27 20:51:17 +00:00

Several core changes to status, soft switches

Regarding soft switches, we had several we should have been listening
for on both reads and writes, but were only doing so on writes; this is
now fixed.

Regarding statuses, we were incorrectly calculating both carry and
overflow. This should now be fixed, although some quick examinations of
disassembly output suggest there is something else amiss. Debugging will
continue shortly.
This commit is contained in:
Peter Evans 2018-01-20 21:01:26 -06:00
parent 103a188faf
commit 2f777ce881
10 changed files with 169 additions and 96 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;
}
// ???

View File

@ -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;
}
// ???

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}