From 31cd45f8b504c6ff2be0ed2aeb9153bcc98eeda4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 27 Oct 2020 19:33:47 -0400 Subject: [PATCH] Takes a run at set_card_paging and simplifies method of shadowing. --- Machines/Apple/AppleIIgs/MemoryMap.hpp | 88 ++++++++++++++++++++------ 1 file changed, 70 insertions(+), 18 deletions(-) diff --git a/Machines/Apple/AppleIIgs/MemoryMap.hpp b/Machines/Apple/AppleIIgs/MemoryMap.hpp index 0cfc11bd3..ab978d0f2 100644 --- a/Machines/Apple/AppleIIgs/MemoryMap.hpp +++ b/Machines/Apple/AppleIIgs/MemoryMap.hpp @@ -26,8 +26,9 @@ class MemoryMap { MemoryMap() : auxiliary_switches_(*this), language_card_(*this) {} void set_storage(std::vector &ram, std::vector &rom) { - // Keep a pointer for later. + // Keep a pointer for later; also note the proper RAM offset. ram_ = ram.data(); + shadow_offset = ram.size() - (128*1024); // Establish bank mapping. uint8_t next_region = 0; @@ -87,7 +88,7 @@ class MemoryMap { set_regions(0x01, { 0x0400, 0x0800, 0x0c00, 0x2000, 0x4000, 0x6000, 0xa000, - 0xc000, /* I don't think ROM-over-$Cx00 works in bank $01? */ + 0xc000, 0xc100, 0xc300, 0xc400, 0xc800, 0xd000, 0xe000, 0xffff }); @@ -123,9 +124,9 @@ class MemoryMap { // [Banks $80–$e0: empty]. // Banks $e0, $e1: all locations potentially affected by the language switches or marked for IO. - // TODO: do I need to break up the Cx pages? + // Alas, separate regions are needed due to the same ROM appearing on both pages. for(uint8_t c = 0; c < 2; c++) { - set_regions(0xe0 + c, {0xc000, 0xd000, 0xe000, 0xffff}); + set_regions(0xe0 + c, {0xc000, 0xc100, 0xc300, 0xc400, 0xc800, 0xd000, 0xe000, 0xffff}); } // [Banks $e2–[ROM start]: empty]. @@ -170,10 +171,13 @@ class MemoryMap { set_storage((first_rom_bank + c) << 16, &rom[c << 16], nullptr); } - // Apply initial language/auxiliary state. [TODO: including shadowing register]. + // TODO: set 1Mhz flags. + + // Apply initial language/auxiliary state. set_card_paging(); set_zero_page_paging(); set_main_paging(); + set_shadowing(); } // MARK: - Live bus access notifications. @@ -266,17 +270,62 @@ class MemoryMap { const bool inhibit_banks0001 = shadow_register_ & 0x40; const auto state = auxiliary_switches_.card_state(); - // TODO: all work. + auto apply = [&state, this](uint32_t bank_base) { + auto &c0_region = regions[region_map[bank_base | 0xc0]]; + auto &c1_region = regions[region_map[bank_base | 0xc1]]; + auto &c3_region = regions[region_map[bank_base | 0xc3]]; + auto &c4_region = regions[region_map[bank_base | 0xc4]]; + auto &c8_region = regions[region_map[bank_base | 0xc8]]; + + const uint8_t *const rom = ®ions[region_map[0xffd0]].read[0xffc100] - ((bank_base << 8) + 0xc100); + + // This is applied dynamically as it may be added or lost in banks $00 and $01. + c0_region.flags |= Region::IsIO; + +#define apply_region(flag, region) \ + if(flag) { \ + region.read = rom; \ + region.flags &= ~Region::IsIO; \ + } else { \ + region.flags |= Region::IsIO; \ + } + + apply_region(state.region_C1_C3, c1_region); + apply_region(state.region_C3, c3_region); + apply_region(state.region_C4_C8, c4_region); + apply_region(state.region_C8_D0, c8_region); + +#undef apply_region + + // Sanity checks. + assert(region_map[bank_base | 0xc1] == region_map[bank_base | 0xc0]+1); + assert(region_map[bank_base | 0xc3] == region_map[bank_base | 0xc1]+1); + assert(region_map[bank_base | 0xc4] == region_map[bank_base | 0xc3]+1); + assert(region_map[bank_base | 0xc8] == region_map[bank_base | 0xc4]+1); + }; if(inhibit_banks0001) { - // Set no IO anywhere, all the Cx regions point to regular RAM - // (or possibly auxiliary). + // Set no IO in the Cx00 range for banks $00 and $01, just + // regular RAM (or possibly auxiliary). + const auto auxiliary_state = auxiliary_switches_.main_state(); + for(uint8_t region = region_map[0x00c0]; region < region_map[0x00d0]; region++) { + regions[region].read = auxiliary_state.base.read ? &ram_[0x10000] : ram_; + regions[region].write = auxiliary_state.base.write ? &ram_[0x10000] : ram_; + regions[region].flags &= ~Region::IsIO; + } + for(uint8_t region = region_map[0x01c0]; region < region_map[0x01d0]; region++) { + regions[region].read = regions[region].write = ram_; + regions[region].flags &= ~Region::IsIO; + } } else { // Obey the card state for banks $00 and $01. + apply(0x0000); + apply(0x0100); } // Obey the card state for banks $e0 and $e1. - (void)state; + apply(0xe000); + apply(0xe100); } void set_zero_page_paging() { @@ -291,6 +340,7 @@ class MemoryMap { } void set_shadowing() { + // TODO. } void set_main_paging() { @@ -309,8 +359,11 @@ class MemoryMap { set(0x20, state.region_20_40); #undef set - // This also affects shadowing flags, if shadowing is enabled at all. + // This also affects shadowing flags, if shadowing is enabled at all, + // and might affect RAM in the IO area of bank $00 because the language + // card can be inhibited on a IIgs. set_shadowing(); + set_card_paging(); } public: @@ -321,6 +374,7 @@ class MemoryMap { // Pointers are eight bytes at the time of writing, so the extra level of indirection // reduces what would otherwise be a 1.25mb table down to not a great deal more than 64kb. std::array region_map; + size_t shadow_offset; struct Region { uint8_t *write = nullptr; @@ -328,11 +382,10 @@ class MemoryMap { uint8_t flags = 0; enum Flag: uint8_t { - IsShadowedE0 = 1 << 0, // i.e. writes should also be written to bank $e0, and costed appropriately. - IsShadowedE1 = 1 << 1, // i.e. writes should also be written to bank $e1, and costed appropriately. - IsShadowed = IsShadowedE0 | IsShadowedE1, - Is1Mhz = 1 << 2, // Both reads and writes should be synchronised with the 1Mhz clock. - IsIO = 1 << 3, // Indicates that this region should be checked for soft switches, registers, etc. + IsShadowed = 1 << 0, // Writes should be shadowed to [end of RAM - 128kb + base offset]. + Is1Mhz = 1 << 1, // Both reads and writes should be synchronised with the 1Mhz clock. + IsIO = 1 << 2, // Indicates that this region should be checked for soft switches, registers, etc; + // usurps the shadowed flags. }; }; std::array regions; // The assert above ensures that this is large enough; there's no @@ -345,9 +398,8 @@ class MemoryMap { #define MemoryMapWrite(map, region, address, value) \ if(region.write) { \ region.write[address] = *value; \ - if(region.flags & (MemoryMap::Region::IsShadowed)) { \ - const uint32_t shadowed_address = (address & 0xffff) + (uint32_t(0xe1 - (region.flags & MemoryMap::Region::IsShadowedE0)) << 16); \ - map.regions[map.region_map[shadowed_address >> 8]].write[shadowed_address] = *value; \ + if(region.flags & MemoryMap::Region::IsShadowed) { \ + region.write[address + map.shadow_offset] = *value; \ } \ }