diff --git a/include/apple2.h b/include/apple2.h index ed7f2f2..57159d2 100644 --- a/include/apple2.h +++ b/include/apple2.h @@ -153,13 +153,10 @@ enum bank_switch { BANK_WRITE = 0x2, /* - * When on, the $Dnnn hexapage will use the first RAM bank for - * reads and/or writes. All other RAM access in bank-switched memory - * will still go to bank 1 RAM, with respect to BANK_RAM when it - * comes to reads. (The default behavior is actually to use bank 2 - * RAM for the $D range.) + * When this is on, we will use bank 2 RAM when accessing the $Dnnn + * range; otherwise, we use bank 1 (as you might guess). */ - BANK_RAM1 = 0x4, + BANK_RAM2 = 0x4, /* * This is a weird little bit. When BANK_ALTZP is on, the zero page @@ -264,8 +261,9 @@ extern void apple2_press_key(apple2 *, vm_8bit); extern void apple2_release_key(apple2 *); extern void apple2_reset(apple2 *); extern void apple2_run_loop(apple2 *); -extern void apple2_set_color(apple2 *, int); extern void apple2_set_bank_switch(apple2 *, vm_8bit); +extern void apple2_set_color(apple2 *, int); +extern void apple2_set_memory_mode(apple2 *, vm_8bit); extern void apple2_set_video(apple2 *, int); #endif diff --git a/src/apple2.c b/src/apple2.c index c04f448..f959c36 100644 --- a/src/apple2.c +++ b/src/apple2.c @@ -154,27 +154,42 @@ apple2_create(int width, int height) void apple2_set_bank_switch(apple2 *mach, vm_8bit flags) { - int have_aux = mach->bank_switch & MEMORY_AUX; - int flags_aux = flags & MEMORY_AUX; - - if (flags_aux && !have_aux) { - // Switch to auxiliary memory. - mos6502_set_memory(mach->cpu, mach->aux, mach->aux); - - // We need to copy page 0 and 1 from main over to aux. + // If we already have BANK_ALTZP, and the flags we're setting do + // _not_ have BANK_ALTZP, then we need to copy aux's zero page and + // stack into main. But if we don't have BANK_ALTZP, and flags + // _does_, then we have to do the inverse: copy main's zero page and + // stack into aux. + if (mach->bank_switch & BANK_ALTZP) { + if (~flags & BANK_ALTZP) { + vm_segment_copy(mach->main, mach->aux, 0, 0, 0x200); + } + } else if (flags & BANK_ALTZP) { vm_segment_copy(mach->aux, mach->main, 0, 0, 0x200); - } else if (!flags_aux && have_aux) { - // Switching back to main memory - mos6502_set_memory(mach->cpu, mach->main, mach->main); - - // And, like above, we need to copy page 0 and 1 from aux back - // to main. - vm_segment_copy(mach->main, mach->aux, 0, 0, 0x200); } mach->bank_switch = flags; } +/* + * Set the memory mode of the apple machine. This may cause us to change + * some behavior (i.e. start using or stop using auxiliary memory). + */ +void +apple2_set_memory_mode(apple2 *mach, vm_8bit flags) +{ + vm_segment *rmem = NULL, + *wmem = NULL; + + mach->memory_mode = flags; + + // We may need to change which segments the CPU can read from or + // write to, based upon the below flags. + rmem = (flags & MEMORY_READ_AUX) ? mach->aux : mach->main; + wmem = (flags & MEMORY_WRITE_AUX) ? mach->aux : mach->main; + + mos6502_set_memory(mach->cpu, rmem, wmem); +} + /* * Return true if we are in a state that the apple2 would consider * double resolution. (In practice, this refers to horizontal screen @@ -248,12 +263,9 @@ apple2_reset(apple2 *mach) // Switch video mode back to 40 column text apple2_set_video(mach, VIDEO_40COL_TEXT); - // Default to: - // - read from ROM - // - write to RAM - // - use bank 2 for $Dxxx hexapage + // Switch us back to defaults apple2_set_bank_switch(mach, BANK_DEFAULT); - apple2_set_memory_mode(mach, MEMORY_NOMINAL); + apple2_set_memory_mode(mach, MEMORY_DEFAULT); } /* diff --git a/src/apple2.mem.c b/src/apple2.mem.c index deadc0f..5c651bb 100644 --- a/src/apple2.mem.c +++ b/src/apple2.mem.c @@ -16,7 +16,7 @@ SEGMENT_READER(apple2_mem_read_bank) mach = (apple2 *)_mach; - if (mach->bank_switch & MEMORY_ROM) { + if (~mach->bank_switch & BANK_RAM) { // We need to account for the difference in address location // before we can successfully get any data from ROM. return vm_segment_get(mach->rom, addr - APPLE2_BANK_OFFSET); @@ -26,7 +26,7 @@ SEGMENT_READER(apple2_mem_read_bank) // that you can access through bank-switching in the $D000 - $DFFF // range, which is actually held at the _end_ of memory beyond the // 64k mark. - if (addr < 0xE000 && mach->bank_switch & MEMORY_RAM2) { + if (addr < 0xE000 && mach->bank_switch & BANK_RAM2) { // The same caution holds for getting data from the // second RAM bank. return segment->memory[addr + 0x3000]; @@ -48,7 +48,7 @@ SEGMENT_WRITER(apple2_mem_write_bank) mach = (apple2 *)_mach; // No writes are allowed... sorry! - if (~mach->bank_switch & MEMORY_WRITE) { + if (~mach->bank_switch & BANK_WRITE) { return; } @@ -62,7 +62,7 @@ SEGMENT_WRITER(apple2_mem_write_bank) // In this case, we need to assign the value at the 64-68k range at // the end of memory; this is just a simple offset from the given // address. - if (addr < 0xE000 && mach->bank_switch & MEMORY_RAM2) { + if (addr < 0xE000 && mach->bank_switch & BANK_RAM2) { segment->memory[addr + 0x3000] = value; return; } @@ -195,64 +195,62 @@ SEGMENT_READER(apple2_mem_read_bank_switch) // these soft switches is not actually to read anything useful, // but simply to change the bank switch mode. case 0xC080: - apple2_set_bank_switch(mach, MEMORY_RAM2); + apple2_set_bank_switch(mach, BANK_RAM | BANK_RAM2); return 0; case 0xC081: if (last_addr == addr) { - apple2_set_bank_switch(mach, - MEMORY_ROM | MEMORY_WRITE | MEMORY_RAM2); + apple2_set_bank_switch(mach, BANK_WRITE | BANK_RAM2); } return 0; case 0xC082: - apple2_set_bank_switch(mach, MEMORY_ROM | MEMORY_RAM2); + apple2_set_bank_switch(mach, BANK_RAM2); return 0; case 0xC083: if (last_addr == addr) { - apple2_set_bank_switch(mach, MEMORY_WRITE | MEMORY_RAM2); + apple2_set_bank_switch(mach, BANK_RAM | BANK_WRITE | BANK_RAM2); } return 0; // Conversely, the $C088 - $C08B range control memory access // while using bank 1 RAM. case 0xC088: - // The 0 means there are no special privileges; reads are to - // RAM, writes are disabled, and we are using bank 1 memory. - apple2_set_bank_switch(mach, 0); + apple2_set_bank_switch(mach, BANK_RAM); return 0; case 0xC089: if (last_addr == addr) { - apple2_set_bank_switch(mach, MEMORY_ROM | MEMORY_WRITE); + apple2_set_bank_switch(mach, BANK_WRITE); } return 0; case 0xC08A: - apple2_set_bank_switch(mach, MEMORY_ROM); + apple2_set_bank_switch(mach, BANK_DEFAULT); return 0; case 0xC08B: if (last_addr == addr) { - apple2_set_bank_switch(mach, MEMORY_WRITE); + apple2_set_bank_switch(mach, BANK_RAM | BANK_WRITE); } return 0; // Return high on the 7th bit if we're using bank 2 memory case 0xC011: - return mach->bank_switch & MEMORY_RAM2 + return mach->bank_switch & BANK_RAM2 ? 0x80 : 0x00; // Return high on 7th bit if we're reading RAM case 0xC012: - return ~mach->bank_switch & MEMORY_ROM + return mach->bank_switch & BANK_RAM ? 0x80 : 0x00; - // Return high on the 7th bit if we're using aux memory + // Return high on the 7th bit if we are using the zero page and + // stack from aux memory. case 0xC016: - return mach->bank_switch & MEMORY_AUX + return mach->bank_switch & BANK_ALTZP ? 0x80 : 0x00; } @@ -270,16 +268,16 @@ SEGMENT_WRITER(apple2_mem_write_bank_switch) apple2 *mach = (apple2 *)_mach; switch (addr) { - // Turn on auxiliary memory + // Turn on auxiliary memory for zero page + stack case 0xC008: apple2_set_bank_switch(mach, - mach->bank_switch | MEMORY_AUX); + mach->bank_switch | BANK_ALTZP); return; - // Disable auxiliary memory + // Disable auxiliary memory for zero page + stack case 0xC009: apple2_set_bank_switch(mach, - mach->bank_switch & ~MEMORY_AUX); + mach->bank_switch & ~BANK_ALTZP); return; } diff --git a/tests/apple2.c b/tests/apple2.c index c312d28..da79bd5 100644 --- a/tests/apple2.c +++ b/tests/apple2.c @@ -121,29 +121,26 @@ Test(apple2, set_bank_switch) { apple2_set_bank_switch(mach, 0); cr_assert_eq(mach->bank_switch, 0); - apple2_set_bank_switch(mach, MEMORY_ROM | MEMORY_WRITE | MEMORY_RAM2); - cr_assert_eq(mach->bank_switch, MEMORY_ROM | MEMORY_WRITE | MEMORY_RAM2); + apple2_set_bank_switch(mach, BANK_WRITE | BANK_RAM2); + cr_assert_eq(mach->bank_switch, BANK_WRITE | BANK_RAM2); mos6502_set(mach->cpu, 0x1, 111); mos6502_set(mach->cpu, 0x101, 222); - apple2_set_bank_switch(mach, MEMORY_AUX); - cr_assert_eq(mach->cpu->rmem, mach->aux); + apple2_set_bank_switch(mach, BANK_ALTZP); cr_assert_eq(mos6502_get(mach->cpu, 0x1), 111); cr_assert_eq(mos6502_get(mach->cpu, 0x101), 222); mos6502_set(mach->cpu, 0x1, 222); mos6502_set(mach->cpu, 0x101, 101); - apple2_set_bank_switch(mach, 0); - cr_assert_eq(mach->cpu->rmem, mach->main); + apple2_set_bank_switch(mach, BANK_DEFAULT); cr_assert_eq(mos6502_get(mach->cpu, 0x1), 222); cr_assert_eq(mos6502_get(mach->cpu, 0x101), 101); } Test(apple2, reset) { - apple2_set_bank_switch(mach, MEMORY_ROM); vm_segment_set(mach->rom, 0x2FFC, 0x34); vm_segment_set(mach->rom, 0x2FFD, 0x12); apple2_reset(mach); diff --git a/tests/apple2.mem.c b/tests/apple2.mem.c index 6a7a2ca..2682348 100644 --- a/tests/apple2.mem.c +++ b/tests/apple2.mem.c @@ -64,7 +64,7 @@ Test(apple2_mem, read_bank) // Test that setting a value in the rom segment is returned to us // when addressing from main memory - apple2_set_bank_switch(mach, MEMORY_ROM | MEMORY_WRITE); + apple2_set_bank_switch(mach, BANK_WRITE); val = 123; vm_segment_set(mach->rom, 0x77, val); val = vm_segment_get(mach->rom, 0x77); @@ -74,7 +74,7 @@ Test(apple2_mem, read_bank) // value in memory... but, as a twist, also check that the value is // not set in ROM nor in RAM2. val = 222; - apple2_set_bank_switch(mach, MEMORY_WRITE); + apple2_set_bank_switch(mach, BANK_RAM | BANK_WRITE); vm_segment_set(mach->main, 0xD077, val); cr_assert_eq(vm_segment_get(mach->main, 0xD077), val); cr_assert_neq(vm_segment_get(mach->rom, 0x77), val); @@ -85,7 +85,7 @@ Test(apple2_mem, read_bank) // if it's there when addressing from main memory in the $Dnnn // range. val = 111; - apple2_set_bank_switch(mach, mach->bank_switch | MEMORY_RAM2); + apple2_set_bank_switch(mach, mach->bank_switch | BANK_RAM2); vm_segment_set(mach->main, 0x10077, val); cr_assert_eq(vm_segment_get(mach->main, 0xD077), val); } @@ -100,12 +100,11 @@ Test(apple2_mem, write_bank) { vm_8bit right, wrong; - // In BANK_ROM mode, we expect that updates to ROM will never be + // In BANK_DEFAULT mode, we expect that updates to ROM will never be // successful (after all, it wouldn't be read-only memory if they // were). right = 123; wrong = 222; - apple2_set_bank_switch(mach, MEMORY_ROM); vm_segment_set(mach->rom, 0x77, right); vm_segment_set(mach->main, 0xD077, wrong); cr_assert_eq(vm_segment_get(mach->rom, 0x77), right); @@ -114,7 +113,7 @@ Test(apple2_mem, write_bank) // RAM1 is the main bank; it's all 64k RAM in one chunk. right = 111; wrong = 232; - apple2_set_bank_switch(mach, MEMORY_WRITE); + apple2_set_bank_switch(mach, BANK_RAM | BANK_WRITE); vm_segment_set(mach->main, 0xD078, right); vm_segment_set(mach->main, 0x10078, wrong); cr_assert_eq(vm_segment_get(mach->main, 0xD078), right); @@ -124,7 +123,7 @@ Test(apple2_mem, write_bank) // ($D000..$DFFF) is in ram2. right = 210; wrong = 132; - apple2_set_bank_switch(mach, mach->bank_switch | MEMORY_RAM2); + apple2_set_bank_switch(mach, mach->bank_switch | BANK_RAM2); vm_segment_set(mach->main, 0x10073, wrong); vm_segment_set(mach->main, 0xD073, right); cr_assert_eq(vm_segment_get(mach->main, 0x10073), right); @@ -133,65 +132,65 @@ Test(apple2_mem, write_bank) Test(apple2_mem, read_bank_switch) { vm_segment_get(mach->main, 0xC080); - cr_assert_eq(mach->bank_switch, MEMORY_RAM2); + cr_assert_eq(mach->bank_switch, BANK_RAM | BANK_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); + cr_assert_neq(mach->bank_switch, BANK_WRITE | BANK_RAM2); mach->cpu->last_addr = 0xC081; vm_segment_get(mach->main, 0xC081); - cr_assert_eq(mach->bank_switch, MEMORY_ROM | MEMORY_WRITE | MEMORY_RAM2); + cr_assert_eq(mach->bank_switch, BANK_WRITE | BANK_RAM2); vm_segment_get(mach->main, 0xC082); - cr_assert_eq(mach->bank_switch, MEMORY_ROM | MEMORY_RAM2); + cr_assert_eq(mach->bank_switch, BANK_RAM2); // Another that needs consecutives vm_segment_get(mach->main, 0xC083); - cr_assert_neq(mach->bank_switch, MEMORY_WRITE | MEMORY_RAM2); + cr_assert_neq(mach->bank_switch, BANK_RAM | BANK_WRITE | BANK_RAM2); mach->cpu->last_addr = 0xC083; vm_segment_get(mach->main, 0xC083); - cr_assert_eq(mach->bank_switch, MEMORY_WRITE | MEMORY_RAM2); + cr_assert_eq(mach->bank_switch, BANK_RAM | BANK_WRITE | BANK_RAM2); vm_segment_get(mach->main, 0xC088); - cr_assert_eq(mach->bank_switch, 0); + cr_assert_eq(mach->bank_switch, BANK_RAM); // You get the idea vm_segment_get(mach->main, 0xC089); - cr_assert_neq(mach->bank_switch, MEMORY_ROM | MEMORY_WRITE); + cr_assert_neq(mach->bank_switch, BANK_WRITE); mach->cpu->last_addr = 0xC089; vm_segment_get(mach->main, 0xC089); - cr_assert_eq(mach->bank_switch, MEMORY_ROM | MEMORY_WRITE); + cr_assert_eq(mach->bank_switch, BANK_WRITE); vm_segment_get(mach->main, 0xC08A); - cr_assert_eq(mach->bank_switch, MEMORY_ROM); + cr_assert_eq(mach->bank_switch, BANK_DEFAULT); vm_segment_get(mach->main, 0xC08B); - cr_assert_neq(mach->bank_switch, MEMORY_WRITE); + cr_assert_neq(mach->bank_switch, BANK_RAM | BANK_WRITE); mach->cpu->last_addr = 0xC08B; vm_segment_get(mach->main, 0xC08B); - cr_assert_eq(mach->bank_switch, MEMORY_WRITE); + cr_assert_eq(mach->bank_switch, BANK_RAM | BANK_WRITE); - mach->bank_switch = MEMORY_RAM2; + mach->bank_switch = BANK_RAM | BANK_RAM2; cr_assert_eq(vm_segment_get(mach->main, 0xC011), 0x80); - mach->bank_switch = 0; + mach->bank_switch = BANK_DEFAULT; cr_assert_eq(vm_segment_get(mach->main, 0xC011), 0x00); - mach->bank_switch = 0; + mach->bank_switch = BANK_RAM; cr_assert_eq(vm_segment_get(mach->main, 0xC012), 0x80); - mach->bank_switch = MEMORY_ROM; + mach->bank_switch = BANK_DEFAULT; cr_assert_eq(vm_segment_get(mach->main, 0xC012), 0x00); - mach->bank_switch = MEMORY_AUX; + mach->bank_switch = BANK_ALTZP; cr_assert_eq(vm_segment_get(mach->main, 0xC016), 0x80); - mach->bank_switch = 0; + mach->bank_switch = BANK_DEFAULT; cr_assert_eq(vm_segment_get(mach->main, 0xC016), 0x00); } Test(apple2_mem, write_bank_switch) { vm_segment_set(mach->main, 0xC008, 1); - cr_assert_eq(mach->bank_switch & MEMORY_AUX, MEMORY_AUX); + cr_assert_eq(mach->bank_switch & BANK_ALTZP, BANK_ALTZP); vm_segment_set(mach->main, 0xC009, 1); - cr_assert_eq(mach->bank_switch & MEMORY_AUX, 0); + cr_assert_eq(mach->bank_switch & BANK_ALTZP, 0); } Test(apple2_mem, init_peripheral_rom)