From 3db536a83d813ac464413e83834f190e310d41d2 Mon Sep 17 00:00:00 2001 From: Peter Evans Date: Wed, 10 Jan 2018 19:59:33 -0600 Subject: [PATCH] Change memory_mode -> bank_switch This also changes the concept of the field; bank_switch is a collection of bit flags now. --- include/apple2.h | 31 ++++++++++++++----- src/apple2.c | 20 +++++++----- src/apple2.mem.c | 77 ++++++++++++++++++++++------------------------ tests/apple2.c | 11 +++---- tests/apple2.mem.c | 12 ++++---- 5 files changed, 82 insertions(+), 69 deletions(-) diff --git a/include/apple2.h b/include/apple2.h index 288d070..e8a4697 100644 --- a/include/apple2.h +++ b/include/apple2.h @@ -70,15 +70,30 @@ enum lores_colors { LORES_WHITE, }; +// Write-protect on/off. +// Read target = ROM or RAM. +// Write target = RAM. +// Set mode of $Dxxx hexapage bank1 or bank2 ram. + +// 0 - 0=off 1=on +// 1 - 0=ROM 1=RAM +// 2 - 0=BANK1 1=BANK2 + /* - * These are the potential memory modes we understand. You can only have - * one memory mode at a time. + * An Apple II has bank-switched memory beginning with $D000 extending + * through $FFFF. The enums below define bit flag names to determine + * what is accessible through those addresses. + * + * Note that it _is_ possible to write while reading ROM, but your + * writes will not go to ROM; they'll go to _RAM_. Any write to $E000 - + * $FFFF may only be sent to bank 1 RAM. Writes to $D000-$DFFF may + * either be sent to bank 1 RAM or bank 2 RAM based upon the RAM2 bit + * flag below. */ enum memory_mode { - MEMORY_BANK_ROM, // the last 12k is system ROM - MEMORY_BANK_RAM1, // the last 12k is system RAM - MEMORY_BANK_RAM2, // the first 4k of the last 12k is a separate RAM - // block from that in RAM1 + MEMORY_ROM = 1, // on = read ROM; off = read RAM + MEMORY_WRITE = 2, // on = allow writes to RAM; off = disallow writes + MEMORY_RAM2 = 4, // on = use bank 2 for $D000-$DFFF; off = use bank 1 }; typedef struct { @@ -140,7 +155,7 @@ typedef struct { * our read/write mappers to know where writes into the * bank-switched area of memory should target. */ - int memory_mode; + vm_8bit bank_switch; /* * Our two disk drives. @@ -159,7 +174,7 @@ 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_memory(apple2 *, int); +extern void apple2_set_bank_switch(apple2 *, vm_8bit); extern void apple2_set_video(apple2 *, int); #endif diff --git a/src/apple2.c b/src/apple2.c index 75bc311..f07876f 100644 --- a/src/apple2.c +++ b/src/apple2.c @@ -119,9 +119,10 @@ apple2_create(int width, int height) // We default to lo-res mode. apple2_set_video(mach, VIDEO_LORES); - // By default we should have ROM be the addressable last 12k of - // memory. - apple2_set_memory(mach, MEMORY_BANK_ROM); + // At cold boot, we don't care what the bank switch is; the reset + // function will do the right thing, so let's default the flags to + // zero. + mach->bank_switch = 0; // Let's install our bitmap font. mach->sysfont = vm_bitfont_create(mach->screen, @@ -139,12 +140,12 @@ apple2_create(int width, int height) } /* - * Change the memory mode of the apple2 to a given mode. + * Change the bank switch flags for the apple 2. */ void -apple2_set_memory(apple2 *mach, int mode) +apple2_set_bank_switch(apple2 *mach, vm_8bit flags) { - mach->memory_mode = mode; + mach->bank_switch = flags; } /* @@ -224,8 +225,11 @@ apple2_reset(apple2 *mach) // Switch video mode back to 40 column text apple2_set_video(mach, VIDEO_40COL_TEXT); - // Default to read from ROM - apple2_set_memory(mach, MEMORY_BANK_ROM); + // Default to: + // - read from ROM + // - write to RAM + // - use bank 2 for $Dxxx hexapage + apple2_set_bank_switch(mach, MEMORY_ROM | MEMORY_WRITE | MEMORY_RAM2); } /* diff --git a/src/apple2.mem.c b/src/apple2.mem.c index 4adccd7..270c3e6 100644 --- a/src/apple2.mem.c +++ b/src/apple2.mem.c @@ -17,33 +17,23 @@ apple2_mem_read_bank(vm_segment *segment, size_t address, void *_mach) mach = (apple2 *)_mach; - switch (mach->memory_mode) { - // Return memory from the rom bank - case MEMORY_BANK_ROM: - // 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, address - APPLE2_BANK_OFFSET); - - // If the address is $D000..$DFFF, then we need to get the byte - // from the ram2 bank. Otherwise, we break to use default - // behavior. - case MEMORY_BANK_RAM2: - if (address < 0xE000) { - // The same caution holds for getting data from the - // second RAM bank. - return vm_segment_get(mach->ram2, - address - APPLE2_BANK_OFFSET); - } - - break; - - case MEMORY_BANK_RAM1: - default: - break; + if (mach->bank_switch & MEMORY_ROM) { + // 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, address - APPLE2_BANK_OFFSET); } - // The "default" behavior as referred-to above is simply to return - // the value as held in our primary memory bank. + // If the address is $D000..$DFFF, then we may need to get the byte + // from the ram2 bank. + if (address < 0xE000 && mach->bank_switch & MEMORY_RAM2) { + // The same caution holds for getting data from the + // second RAM bank. + return vm_segment_get(mach->ram2, + address - APPLE2_BANK_OFFSET); + } + + // Otherwise, the byte is returned from bank 1 RAM, which is the + // literal memory available in the segment. return segment->memory[address]; } @@ -59,24 +49,29 @@ apple2_mem_write_bank(vm_segment *segment, mach = (apple2 *)_mach; - switch (mach->memory_mode) { - // Whoops -- we can't write any data into ROM. - case MEMORY_BANK_ROM: - return; - - case MEMORY_BANK_RAM2: - if (address < 0xE000) { - vm_segment_set(mach->ram2, - address - APPLE2_BANK_OFFSET, value); - return; - } - - case MEMORY_BANK_RAM1: - default: - break; + // No writes are allowed... sorry! + if (~mach->bank_switch & MEMORY_WRITE) { + return; } - // Just set the value in main memory + // You will note, if we've gotten here, that it's possible to write + // to the bank-switch addresses even if the ROM flag is 1. It's + // true! Except that writes never go to ROM. That is to say, it's + // possible to read from ROM and write to RAM at the same + // time--well, nearly the same time, considering the 6502 does not + // allow parallel actions! + + // If bank 2 RAM is turned on, and the address is in the $D000 + // hexapage, then we write to our ram2 segment. + if (address < 0xE000 && mach->bank_switch & MEMORY_RAM2) { + vm_segment_set(mach->ram2, + address - APPLE2_BANK_OFFSET, value); + return; + } + + // But if bank 2 RAM is not turned on, or the address is between + // $E000 - $FFFF, then writes go to bank 1 RAM, which is our main + // memory. segment->memory[address] = value; } diff --git a/tests/apple2.c b/tests/apple2.c index 8e61197..71a38ca 100644 --- a/tests/apple2.c +++ b/tests/apple2.c @@ -117,17 +117,16 @@ Test(apple2, set_video) cr_assert_eq(mach->video_mode, VIDEO_LORES); } -Test(apple2, set_memory) +Test(apple2, set_bank_switch) { - apple2_set_memory(mach, MEMORY_BANK_RAM1); - cr_assert_eq(mach->memory_mode, MEMORY_BANK_RAM1); - apple2_set_memory(mach, MEMORY_BANK_RAM2); - cr_assert_eq(mach->memory_mode, MEMORY_BANK_RAM2); + 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); } Test(apple2, reset) { - apple2_set_memory(mach, MEMORY_BANK_ROM); + 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 bf03180..f99c445 100644 --- a/tests/apple2.mem.c +++ b/tests/apple2.mem.c @@ -40,7 +40,7 @@ Test(apple2_mem, read_bank) // Test that setting a value in the rom segment is returned to us // when addressing from main memory - mach->memory_mode = MEMORY_BANK_ROM; + apple2_set_bank_switch(mach, MEMORY_ROM | MEMORY_WRITE); val = 123; vm_segment_set(mach->rom, 0x77, val); val = vm_segment_get(mach->rom, 0x77); @@ -50,7 +50,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; - mach->memory_mode = MEMORY_BANK_RAM1; + apple2_set_bank_switch(mach, MEMORY_WRITE); vm_segment_set(mach->memory, 0xD077, val); cr_assert_eq(vm_segment_get(mach->memory, 0xD077), val); cr_assert_neq(vm_segment_get(mach->rom, 0x77), val); @@ -60,7 +60,7 @@ Test(apple2_mem, read_bank) // the value directly in ram2 and see if it's there when addressing // from main memory. val = 111; - mach->memory_mode = MEMORY_BANK_RAM2; + apple2_set_bank_switch(mach, mach->bank_switch | MEMORY_RAM2); vm_segment_set(mach->ram2, 0x77, val); cr_assert_eq(vm_segment_get(mach->memory, 0xD077), val); } @@ -80,7 +80,7 @@ Test(apple2_mem, write_bank) // were). right = 123; wrong = 222; - mach->memory_mode = MEMORY_BANK_ROM; + apple2_set_bank_switch(mach, MEMORY_ROM); vm_segment_set(mach->rom, 0x77, right); vm_segment_set(mach->memory, 0xD077, wrong); cr_assert_eq(vm_segment_get(mach->rom, 0x77), right); @@ -89,7 +89,7 @@ Test(apple2_mem, write_bank) // RAM1 is the main bank; it's all 64k RAM in one chunk. right = 111; wrong = 232; - mach->memory_mode = MEMORY_BANK_RAM1; + apple2_set_bank_switch(mach, MEMORY_WRITE); vm_segment_set(mach->memory, 0xD078, right); vm_segment_set(mach->ram2, 0x78, wrong); cr_assert_eq(vm_segment_get(mach->memory, 0xD078), right); @@ -99,7 +99,7 @@ Test(apple2_mem, write_bank) // ($D000..$DFFF) is in ram2. right = 210; wrong = 132; - mach->memory_mode = MEMORY_BANK_RAM2; + apple2_set_bank_switch(mach, mach->bank_switch | MEMORY_RAM2); vm_segment_set(mach->ram2, 0x73, wrong); vm_segment_set(mach->memory, 0xD073, right); cr_assert_eq(vm_segment_get(mach->ram2, 0x73), right);