diff --git a/include/mos6502.h b/include/mos6502.h index 18918fa..dea1e71 100644 --- a/include/mos6502.h +++ b/include/mos6502.h @@ -61,7 +61,7 @@ typedef struct { vm_segment *wmem; /* - * This contains the last _effective_ address we've resolved in one + * This contains the _effective_ address we've resolved in one * of our address modes. In absolute mode, this would be the literal * operand we read from memory; in indirect mode, this will be the * address we _find_ after dereferencing the operand we read from @@ -70,6 +70,16 @@ typedef struct { */ vm_16bit eff_addr; + /* + * These are the last opcode and last effective address that was + * used in the instruction previous to the one currently being + * executed. Some things (notably soft switches) may need to + * the last opcode. + */ + vm_8bit last_opcode; + vm_8bit last_operand; + vm_16bit last_addr; + /* * Our program counter register; this is what we'll use to determine * where we're "at" in memory while executing opcodes. We use a @@ -127,6 +137,7 @@ extern vm_16bit mos6502_pop_stack(mos6502 *); 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_push_stack(mos6502 *, vm_16bit); extern void mos6502_set(mos6502 *, size_t, vm_8bit); diff --git a/src/apple2.mem.c b/src/apple2.mem.c index dbd84ad..deadc0f 100644 --- a/src/apple2.mem.c +++ b/src/apple2.mem.c @@ -179,9 +179,14 @@ apple2_mem_init_sys_rom(apple2 *mach) SEGMENT_READER(apple2_mem_read_bank_switch) { apple2 *mach; + vm_16bit last_addr; mach = (apple2 *)_mach; + // We need to know the last opcode and address, because some of our + // soft switches require two consecutive reads + mos6502_last_executed(mach->cpu, NULL, NULL, &last_addr); + switch (addr) { // The $C080 - $C083 range all control memory access while using // bank 2 RAM for the $Dnnn range. Note that here and in the @@ -194,15 +199,19 @@ SEGMENT_READER(apple2_mem_read_bank_switch) return 0; case 0xC081: - apple2_set_bank_switch(mach, - MEMORY_ROM | MEMORY_WRITE | MEMORY_RAM2); + if (last_addr == addr) { + apple2_set_bank_switch(mach, + MEMORY_ROM | MEMORY_WRITE | MEMORY_RAM2); + } return 0; case 0xC082: apple2_set_bank_switch(mach, MEMORY_ROM | MEMORY_RAM2); return 0; case 0xC083: - apple2_set_bank_switch(mach, MEMORY_WRITE | MEMORY_RAM2); + if (last_addr == addr) { + apple2_set_bank_switch(mach, MEMORY_WRITE | MEMORY_RAM2); + } return 0; // Conversely, the $C088 - $C08B range control memory access @@ -214,7 +223,9 @@ SEGMENT_READER(apple2_mem_read_bank_switch) return 0; case 0xC089: - apple2_set_bank_switch(mach, MEMORY_ROM | MEMORY_WRITE); + if (last_addr == addr) { + apple2_set_bank_switch(mach, MEMORY_ROM | MEMORY_WRITE); + } return 0; case 0xC08A: @@ -222,7 +233,9 @@ SEGMENT_READER(apple2_mem_read_bank_switch) return 0; case 0xC08B: - apple2_set_bank_switch(mach, MEMORY_WRITE); + if (last_addr == addr) { + apple2_set_bank_switch(mach, MEMORY_WRITE); + } return 0; // Return high on the 7th bit if we're using bank 2 memory diff --git a/src/mos6502.c b/src/mos6502.c index d03d00c..64c5cf5 100644 --- a/src/mos6502.c +++ b/src/mos6502.c @@ -392,10 +392,38 @@ mos6502_execute(mos6502 *cpu) // something. usleep(cycles * 10000); + // We need to record the opcode and the effective address for + // anything which might need to reference it. + cpu->last_opcode = opcode; + cpu->last_addr = cpu->eff_addr; + cpu->last_operand = operand; + // Ok -- we're done! This wasn't so hard, was it? return; } +/* + * Given pointers for an opcode, operand, and effective address, set the + * dereferenced values of those pointers to what the CPU knows to have + * been the last of each. + */ +void +mos6502_last_executed(mos6502 *cpu, vm_8bit *opcode, + vm_8bit *operand, vm_16bit *addr) +{ + if (opcode) { + *opcode = cpu->last_opcode; + } + + if (operand) { + *operand = cpu->last_operand; + } + + if (addr) { + *addr = cpu->last_addr; + } +} + /* * Return true if the given instruction would require that we jump * to somewhere else in the program. diff --git a/tests/apple2.mem.c b/tests/apple2.mem.c index 95c1bca..7376849 100644 --- a/tests/apple2.mem.c +++ b/tests/apple2.mem.c @@ -134,19 +134,40 @@ Test(apple2_mem, read_bank_switch) { vm_segment_get(mach->main, 0xC080); cr_assert_eq(mach->bank_switch, MEMORY_RAM2); + + // This (and a few others) are trickier to test, as they require + // consecutive reads to trigger. + vm_segment_get(mach->main, 0xC081); + cr_assert_neq(mach->bank_switch, MEMORY_ROM | MEMORY_WRITE | MEMORY_RAM2); + mach->cpu->last_addr = 0xC081; vm_segment_get(mach->main, 0xC081); cr_assert_eq(mach->bank_switch, MEMORY_ROM | MEMORY_WRITE | MEMORY_RAM2); + vm_segment_get(mach->main, 0xC082); cr_assert_eq(mach->bank_switch, MEMORY_ROM | MEMORY_RAM2); + + // Another that needs consecutives + vm_segment_get(mach->main, 0xC083); + cr_assert_neq(mach->bank_switch, MEMORY_WRITE | MEMORY_RAM2); + mach->cpu->last_addr = 0xC083; vm_segment_get(mach->main, 0xC083); cr_assert_eq(mach->bank_switch, MEMORY_WRITE | MEMORY_RAM2); vm_segment_get(mach->main, 0xC088); cr_assert_eq(mach->bank_switch, 0); + + vm_segment_get(mach->main, 0xC089); + cr_assert_neq(mach->bank_switch, MEMORY_ROM | MEMORY_WRITE); + mach->cpu->last_addr = 0xC089; vm_segment_get(mach->main, 0xC089); cr_assert_eq(mach->bank_switch, MEMORY_ROM | MEMORY_WRITE); + vm_segment_get(mach->main, 0xC08A); cr_assert_eq(mach->bank_switch, MEMORY_ROM); + + vm_segment_get(mach->main, 0xC08B); + cr_assert_neq(mach->bank_switch, MEMORY_WRITE); + mach->cpu->last_addr = 0xC08B; vm_segment_get(mach->main, 0xC08B); cr_assert_eq(mach->bank_switch, MEMORY_WRITE);