mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 08:49:37 +00:00
Merge pull request #1301 from TomHarte/MoreIIgsPaging
Normalise IIgs memory map interface.
This commit is contained in:
commit
ad31c50dfd
@ -384,7 +384,7 @@ class ConcreteMachine:
|
|||||||
// MARK: BusHandler.
|
// MARK: BusHandler.
|
||||||
uint64_t total = 0;
|
uint64_t total = 0;
|
||||||
forceinline Cycles perform_bus_operation(const CPU::WDC65816::BusOperation operation, const uint32_t address, uint8_t *const value) {
|
forceinline Cycles perform_bus_operation(const CPU::WDC65816::BusOperation operation, const uint32_t address, uint8_t *const value) {
|
||||||
const auto ®ion = MemoryMapRegion(memory_, address);
|
const auto ®ion = memory_.region(address);
|
||||||
static bool log = false;
|
static bool log = false;
|
||||||
bool is_1Mhz = false;
|
bool is_1Mhz = false;
|
||||||
|
|
||||||
@ -945,7 +945,7 @@ class ConcreteMachine:
|
|||||||
is_1Mhz = region.flags & MemoryMap::Region::Is1Mhz;
|
is_1Mhz = region.flags & MemoryMap::Region::Is1Mhz;
|
||||||
|
|
||||||
if(isReadOperation(operation)) {
|
if(isReadOperation(operation)) {
|
||||||
MemoryMapRead(region, address, value);
|
*value = memory_.read(region, address);
|
||||||
} else {
|
} else {
|
||||||
// Shadowed writes also occur "at 1Mhz".
|
// Shadowed writes also occur "at 1Mhz".
|
||||||
// TODO: this is probably an approximation. I'm assuming that there's the ability asynchronously to post
|
// TODO: this is probably an approximation. I'm assuming that there's the ability asynchronously to post
|
||||||
@ -954,7 +954,7 @@ class ConcreteMachine:
|
|||||||
// get by adding periodic NOPs within their copy-to-shadow step.
|
// get by adding periodic NOPs within their copy-to-shadow step.
|
||||||
//
|
//
|
||||||
// Maybe the interaction with 2.8Mhz refresh isn't as straightforward as I think?
|
// Maybe the interaction with 2.8Mhz refresh isn't as straightforward as I think?
|
||||||
const bool is_shadowed = IsShadowed(memory_, region, address);
|
const bool is_shadowed = memory_.is_shadowed(address);
|
||||||
is_1Mhz |= is_shadowed;
|
is_1Mhz |= is_shadowed;
|
||||||
|
|
||||||
// Use a very broad test for flushing video: any write to $e0 or $e1, or any write that is shadowed.
|
// Use a very broad test for flushing video: any write to $e0 or $e1, or any write that is shadowed.
|
||||||
@ -963,7 +963,7 @@ class ConcreteMachine:
|
|||||||
video_.flush();
|
video_.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryMapWrite(memory_, region, address, value);
|
memory_.write(region, address, *value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,13 +248,124 @@ class MemoryMap {
|
|||||||
uint8_t speed_register_ = 0x00;
|
uint8_t speed_register_ = 0x00;
|
||||||
|
|
||||||
// MARK: - Memory banking.
|
// MARK: - Memory banking.
|
||||||
|
void assert_is_region(uint8_t start, uint8_t end) {
|
||||||
#define assert_is_region(start, end) \
|
assert(region_map[start] == region_map[start-1]+1);
|
||||||
assert(region_map[start] == region_map[start-1]+1); \
|
assert(region_map[end-1] == region_map[start]);
|
||||||
assert(region_map[end-1] == region_map[start]); \
|
assert(region_map[end] == region_map[end-1]+1);
|
||||||
assert(region_map[end] == region_map[end-1]+1);
|
}
|
||||||
|
|
||||||
template <int type> void set_paging() {
|
template <int type> void set_paging() {
|
||||||
|
// Establish whether main or auxiliary RAM
|
||||||
|
// is exposed in bank $00 for a bunch of regions.
|
||||||
|
if constexpr (type & PagingType::Main) {
|
||||||
|
const auto set = [&](std::size_t page, const auto &flags) {
|
||||||
|
auto ®ion = regions[region_map[page]];
|
||||||
|
region.read = flags.read ? &ram_base[0x01'0000] : ram_base;
|
||||||
|
region.write = flags.write ? &ram_base[0x01'0000] : ram_base;
|
||||||
|
};
|
||||||
|
const auto state = auxiliary_switches_.main_state();
|
||||||
|
|
||||||
|
// Base: $0200–$03FF.
|
||||||
|
set(0x02, state.base);
|
||||||
|
assert_is_region(0x02, 0x04);
|
||||||
|
|
||||||
|
// Region $0400–$07ff.
|
||||||
|
set(0x04, state.region_04_08);
|
||||||
|
assert_is_region(0x04, 0x08);
|
||||||
|
|
||||||
|
// Base: $0800–$1FFF.
|
||||||
|
set(0x08, state.base);
|
||||||
|
assert_is_region(0x08, 0x20);
|
||||||
|
|
||||||
|
// Region $2000–$3FFF.
|
||||||
|
set(0x20, state.region_20_40);
|
||||||
|
assert_is_region(0x20, 0x40);
|
||||||
|
|
||||||
|
// Base: $4000–$BFFF.
|
||||||
|
set(0x40, state.base);
|
||||||
|
assert_is_region(0x40, 0xc0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update whether base or auxiliary RAM is visible in: (i) the zero
|
||||||
|
// and stack pages; and (ii) anywhere that the language card is exposing RAM instead of ROM.
|
||||||
|
if constexpr (bool(type & PagingType::ZeroPage)) {
|
||||||
|
// Affects bank $00 only, and should be a single region.
|
||||||
|
auto ®ion = regions[region_map[0]];
|
||||||
|
region.read = region.write = auxiliary_switches_.zero_state() ? &ram_base[0x01'0000] : ram_base;
|
||||||
|
assert(region_map[0x0000] == region_map[0x0001]);
|
||||||
|
assert(region_map[0x0001]+1 == region_map[0x0002]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Establish whether ROM or card switches are exposed in the distinct
|
||||||
|
// regions C100–C2FF, C300–C3FF, C400–C7FF and C800–CFFF.
|
||||||
|
//
|
||||||
|
// On the IIgs it intersects with the current shadow register.
|
||||||
|
if constexpr (bool(type & (PagingType::CardArea | PagingType::Main))) {
|
||||||
|
const bool inhibit_banks0001 = shadow_register_ & 0x40;
|
||||||
|
const auto state = auxiliary_switches_.card_state();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
const auto apply_region = [&](bool flag, auto ®ion) {
|
||||||
|
region.write = nullptr;
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Sanity checks.
|
||||||
|
assert(region_map[bank_base | 0xc1] == region_map[bank_base | 0xc0]+1);
|
||||||
|
assert(region_map[bank_base | 0xc2] == region_map[bank_base | 0xc1]);
|
||||||
|
assert(region_map[bank_base | 0xc3] == region_map[bank_base | 0xc2]+1);
|
||||||
|
assert(region_map[bank_base | 0xc4] == region_map[bank_base | 0xc3]+1);
|
||||||
|
assert(region_map[bank_base | 0xc7] == region_map[bank_base | 0xc4]);
|
||||||
|
assert(region_map[bank_base | 0xc8] == region_map[bank_base | 0xc7]+1);
|
||||||
|
assert(region_map[bank_base | 0xcf] == region_map[bank_base | 0xc8]);
|
||||||
|
assert(region_map[bank_base | 0xd0] == region_map[bank_base | 0xcf]+1);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(inhibit_banks0001) {
|
||||||
|
// 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_base[0x01'0000] : ram_base;
|
||||||
|
regions[region].write = auxiliary_state.base.write ? &ram_base[0x01'0000] : ram_base;
|
||||||
|
regions[region].flags &= ~Region::IsIO;
|
||||||
|
}
|
||||||
|
for(uint8_t region = region_map[0x01c0]; region < region_map[0x01d0]; region++) {
|
||||||
|
regions[region].read = regions[region].write = ram_base;
|
||||||
|
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.
|
||||||
|
apply(0xe000);
|
||||||
|
apply(0xe100);
|
||||||
|
}
|
||||||
|
|
||||||
// Update the region from
|
// Update the region from
|
||||||
// $D000 onwards as per the state of the language card flags — there may
|
// $D000 onwards as per the state of the language card flags — there may
|
||||||
// end up being ROM or RAM (or auxiliary RAM), and the first 4kb of it
|
// end up being ROM or RAM (or auxiliary RAM), and the first 4kb of it
|
||||||
@ -314,121 +425,6 @@ class MemoryMap {
|
|||||||
apply(0xe000, e0_ram);
|
apply(0xe000, e0_ram);
|
||||||
apply(0xe100, e0_ram);
|
apply(0xe100, e0_ram);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Establish whether main or auxiliary RAM
|
|
||||||
// is exposed in bank $00 for a bunch of regions.
|
|
||||||
if constexpr (type & PagingType::Main) {
|
|
||||||
const auto state = auxiliary_switches_.main_state();
|
|
||||||
|
|
||||||
#define set(page, flags) {\
|
|
||||||
auto ®ion = regions[region_map[page]]; \
|
|
||||||
region.read = flags.read ? &ram_base[0x01'0000] : ram_base; \
|
|
||||||
region.write = flags.write ? &ram_base[0x01'0000] : ram_base; \
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base: $0200–$03FF.
|
|
||||||
set(0x02, state.base);
|
|
||||||
assert_is_region(0x02, 0x04);
|
|
||||||
|
|
||||||
// Region $0400–$07ff.
|
|
||||||
set(0x04, state.region_04_08);
|
|
||||||
assert_is_region(0x04, 0x08);
|
|
||||||
|
|
||||||
// Base: $0800–$1FFF.
|
|
||||||
set(0x08, state.base);
|
|
||||||
assert_is_region(0x08, 0x20);
|
|
||||||
|
|
||||||
// Region $2000–$3FFF.
|
|
||||||
set(0x20, state.region_20_40);
|
|
||||||
assert_is_region(0x20, 0x40);
|
|
||||||
|
|
||||||
// Base: $4000–$BFFF.
|
|
||||||
set(0x40, state.base);
|
|
||||||
assert_is_region(0x40, 0xc0);
|
|
||||||
|
|
||||||
#undef set
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update whether base or auxiliary RAM is visible in: (i) the zero
|
|
||||||
// and stack pages; and (ii) anywhere that the language card is exposing RAM instead of ROM.
|
|
||||||
if constexpr (bool(type & PagingType::ZeroPage)) {
|
|
||||||
// Affects bank $00 only, and should be a single region.
|
|
||||||
auto ®ion = regions[region_map[0]];
|
|
||||||
region.read = region.write = auxiliary_switches_.zero_state() ? &ram_base[0x01'0000] : ram_base;
|
|
||||||
assert(region_map[0x0000] == region_map[0x0001]);
|
|
||||||
assert(region_map[0x0001]+1 == region_map[0x0002]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Establish whether ROM or card switches are exposed in the distinct
|
|
||||||
// regions C100–C2FF, C300–C3FF, C400–C7FF and C800–CFFF.
|
|
||||||
//
|
|
||||||
// On the IIgs it intersects with the current shadow register.
|
|
||||||
if constexpr (bool(type & (PagingType::CardArea | PagingType::Main))) {
|
|
||||||
const bool inhibit_banks0001 = shadow_register_ & 0x40;
|
|
||||||
const auto state = auxiliary_switches_.card_state();
|
|
||||||
|
|
||||||
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) \
|
|
||||||
region.write = nullptr; \
|
|
||||||
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 | 0xc2] == region_map[bank_base | 0xc1]);
|
|
||||||
assert(region_map[bank_base | 0xc3] == region_map[bank_base | 0xc2]+1);
|
|
||||||
assert(region_map[bank_base | 0xc4] == region_map[bank_base | 0xc3]+1);
|
|
||||||
assert(region_map[bank_base | 0xc7] == region_map[bank_base | 0xc4]);
|
|
||||||
assert(region_map[bank_base | 0xc8] == region_map[bank_base | 0xc7]+1);
|
|
||||||
assert(region_map[bank_base | 0xcf] == region_map[bank_base | 0xc8]);
|
|
||||||
assert(region_map[bank_base | 0xd0] == region_map[bank_base | 0xcf]+1);
|
|
||||||
};
|
|
||||||
|
|
||||||
if(inhibit_banks0001) {
|
|
||||||
// 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_base[0x01'0000] : ram_base;
|
|
||||||
regions[region].write = auxiliary_state.base.write ? &ram_base[0x01'0000] : ram_base;
|
|
||||||
regions[region].flags &= ~Region::IsIO;
|
|
||||||
}
|
|
||||||
for(uint8_t region = region_map[0x01c0]; region < region_map[0x01d0]; region++) {
|
|
||||||
regions[region].read = regions[region].write = ram_base;
|
|
||||||
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.
|
|
||||||
apply(0xe000);
|
|
||||||
apply(0xe100);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IIgs specific: sets or resets the ::IsShadowed flag across affected banks as
|
// IIgs specific: sets or resets the ::IsShadowed flag across affected banks as
|
||||||
@ -563,8 +559,6 @@ class MemoryMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef assert_is_region
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Various precomputed bitsets describing key regions; std::bitset doesn't support constexpr instantiation
|
// Various precomputed bitsets describing key regions; std::bitset doesn't support constexpr instantiation
|
||||||
// beyond the first 64 bits at the time of writing, alas, so these are generated at runtime.
|
// beyond the first 64 bits at the time of writing, alas, so these are generated at runtime.
|
||||||
@ -637,55 +631,49 @@ class MemoryMap {
|
|||||||
std::array<Region, 40> regions; // An assert above ensures that this is large enough; there's no
|
std::array<Region, 40> regions; // An assert above ensures that this is large enough; there's no
|
||||||
// doctrinal reason for it to be whatever size it is now, just
|
// doctrinal reason for it to be whatever size it is now, just
|
||||||
// adjust as required.
|
// adjust as required.
|
||||||
|
|
||||||
|
// The below encapsulates an assumption that Apple intends to shadow physical addresses (i.e. after mapping).
|
||||||
|
// If the Apple shadows logical addresses (i.e. prior to mapping) then see commented out alternatives.
|
||||||
|
|
||||||
|
const Region ®ion(uint32_t address) { return regions[region_map[address >> 8]]; }
|
||||||
|
uint8_t read(const Region ®ion, uint32_t address) {
|
||||||
|
return region.read ? region.read[address] : 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_shadowed(uint32_t address) const {
|
||||||
|
// Logical mapping alternative:
|
||||||
|
// shadow_pages[((®ion.write[address] - ram_base) >> 10) & 127] & shadow_banks[address >> 17]
|
||||||
|
|
||||||
|
return shadow_pages[(address >> 10) & 127] & shadow_banks[address >> 17];
|
||||||
|
|
||||||
|
// Quick notes on contortions above:
|
||||||
|
//
|
||||||
|
// The objective is to support shadowing:
|
||||||
|
// 1. without storing a whole extra pointer, and such that the shadowing flags
|
||||||
|
// are orthogonal to the current auxiliary memory settings;
|
||||||
|
// 2. in such a way as to support shadowing both in banks $00/$01 and elsewhere; and
|
||||||
|
// 3. to do so without introducing too much in the way of branching.
|
||||||
|
//
|
||||||
|
// Hence the implemented solution: if shadowing is enabled then use the distance from the start of
|
||||||
|
// physical RAM modulo 128k indexed into the bank $e0/$e1 RAM.
|
||||||
|
//
|
||||||
|
// With a further twist: the modulo and pointer are indexed on ::IsShadowed to eliminate a branch
|
||||||
|
// even on that.
|
||||||
|
}
|
||||||
|
void write(const Region ®ion, uint32_t address, uint8_t value) {
|
||||||
|
if(!region.write) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
region.write[address] = value;
|
||||||
|
const bool shadowed = is_shadowed(address);
|
||||||
|
shadow_base[shadowed][(®ion.write[address] - ram_base) & shadow_mask[shadowed]] = value;
|
||||||
|
|
||||||
|
// Logical mapping alternative:
|
||||||
|
// shadow_base[shadowed][address & shadow_mask[shadowed]]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: branching below on region.read/write is predicated on the idea that extra scratch space
|
|
||||||
// would be less efficient. Verify that?
|
|
||||||
|
|
||||||
#define MemoryMapRegion(map, address) map.regions[map.region_map[address >> 8]]
|
|
||||||
#define MemoryMapRead(region, address, value) *value = region.read ? region.read[address] : 0xff
|
|
||||||
|
|
||||||
// The below encapsulates the fact that I've yet to determine whether Apple intends to
|
|
||||||
// indicate that logical addresses (i.e. those prior to being mapped per the current paging)
|
|
||||||
// or physical addresses (i.e. after mapping) are subject to shadowing.
|
|
||||||
#ifdef SHADOW_LOGICAL
|
|
||||||
|
|
||||||
#define IsShadowed(map, region, address) \
|
|
||||||
(map.shadow_pages[((®ion.write[address] - map.ram_base) >> 10) & 127] & map.shadow_banks[address >> 17])
|
|
||||||
|
|
||||||
#define MemoryMapWrite(map, region, address, value) \
|
|
||||||
if(region.write) { \
|
|
||||||
region.write[address] = *value; \
|
|
||||||
const bool _mm_is_shadowed = IsShadowed(map, region, address); \
|
|
||||||
map.shadow_base[_mm_is_shadowed][address & map.shadow_mask[_mm_is_shadowed]] = *value; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define IsShadowed(map, region, address) \
|
|
||||||
(map.shadow_pages[(address >> 10) & 127] & map.shadow_banks[address >> 17])
|
|
||||||
|
|
||||||
#define MemoryMapWrite(map, region, address, value) \
|
|
||||||
if(region.write) { \
|
|
||||||
region.write[address] = *value; \
|
|
||||||
const bool _mm_is_shadowed = IsShadowed(map, region, address); \
|
|
||||||
map.shadow_base[_mm_is_shadowed][(®ion.write[address] - map.ram_base) & map.shadow_mask[_mm_is_shadowed]] = *value; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Quick notes on ::IsShadowed contortions:
|
|
||||||
//
|
|
||||||
// The objective is to support shadowing:
|
|
||||||
// 1. without storing a whole extra pointer, and such that the shadowing flags are orthogonal to the current auxiliary memory settings;
|
|
||||||
// 2. in such a way as to support shadowing both in banks $00/$01 and elsewhere; and
|
|
||||||
// 3. to do so without introducing too much in the way of branching.
|
|
||||||
//
|
|
||||||
// Hence the implemented solution: if shadowing is enabled then use the distance from the start of physical RAM
|
|
||||||
// modulo 128k indexed into the bank $e0/$e1 RAM.
|
|
||||||
//
|
|
||||||
// With a further twist: the modulo and pointer are indexed on ::IsShadowed to eliminate a branch even on that.
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* MemoryMap_h */
|
#endif /* MemoryMap_h */
|
||||||
|
@ -535,7 +535,6 @@
|
|||||||
4B8334861F5DA3780097E338 /* 6502Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8334851F5DA3780097E338 /* 6502Storage.cpp */; };
|
4B8334861F5DA3780097E338 /* 6502Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8334851F5DA3780097E338 /* 6502Storage.cpp */; };
|
||||||
4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8334891F5DB94B0097E338 /* IRQDelegatePortHandler.cpp */; };
|
4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8334891F5DB94B0097E338 /* IRQDelegatePortHandler.cpp */; };
|
||||||
4B8334951F5E25B60097E338 /* C1540.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8334941F5E25B60097E338 /* C1540.cpp */; };
|
4B8334951F5E25B60097E338 /* C1540.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8334941F5E25B60097E338 /* C1540.cpp */; };
|
||||||
4B85322D227793CB00F26553 /* etos192uk.trace.txt.gz in Resources */ = {isa = PBXBuildFile; fileRef = 4B85322C227793CA00F26553 /* etos192uk.trace.txt.gz */; };
|
|
||||||
4B85322F2277ABDE00F26553 /* tos100.trace.txt.gz in Resources */ = {isa = PBXBuildFile; fileRef = 4B85322E2277ABDD00F26553 /* tos100.trace.txt.gz */; };
|
4B85322F2277ABDE00F26553 /* tos100.trace.txt.gz in Resources */ = {isa = PBXBuildFile; fileRef = 4B85322E2277ABDD00F26553 /* tos100.trace.txt.gz */; };
|
||||||
4B86E25B1F8C628F006FAA45 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B86E2591F8C628F006FAA45 /* Keyboard.cpp */; };
|
4B86E25B1F8C628F006FAA45 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B86E2591F8C628F006FAA45 /* Keyboard.cpp */; };
|
||||||
4B8805F01DCFC99C003085B1 /* Acorn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805EE1DCFC99C003085B1 /* Acorn.cpp */; };
|
4B8805F01DCFC99C003085B1 /* Acorn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805EE1DCFC99C003085B1 /* Acorn.cpp */; };
|
||||||
@ -1622,7 +1621,6 @@
|
|||||||
4B8334911F5E24FF0097E338 /* C1540Base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = C1540Base.hpp; path = Implementation/C1540Base.hpp; sourceTree = "<group>"; };
|
4B8334911F5E24FF0097E338 /* C1540Base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = C1540Base.hpp; path = Implementation/C1540Base.hpp; sourceTree = "<group>"; };
|
||||||
4B8334941F5E25B60097E338 /* C1540.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = C1540.cpp; path = Implementation/C1540.cpp; sourceTree = "<group>"; };
|
4B8334941F5E25B60097E338 /* C1540.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = C1540.cpp; path = Implementation/C1540.cpp; sourceTree = "<group>"; };
|
||||||
4B85322922778E4200F26553 /* Comparative68000.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Comparative68000.hpp; sourceTree = "<group>"; };
|
4B85322922778E4200F26553 /* Comparative68000.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Comparative68000.hpp; sourceTree = "<group>"; };
|
||||||
4B85322C227793CA00F26553 /* etos192uk.trace.txt.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = etos192uk.trace.txt.gz; sourceTree = "<group>"; };
|
|
||||||
4B85322E2277ABDD00F26553 /* tos100.trace.txt.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = tos100.trace.txt.gz; sourceTree = "<group>"; };
|
4B85322E2277ABDD00F26553 /* tos100.trace.txt.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = tos100.trace.txt.gz; sourceTree = "<group>"; };
|
||||||
4B86E2591F8C628F006FAA45 /* Keyboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
|
4B86E2591F8C628F006FAA45 /* Keyboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
|
||||||
4B86E25A1F8C628F006FAA45 /* Keyboard.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
|
4B86E25A1F8C628F006FAA45 /* Keyboard.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
|
||||||
@ -3540,7 +3538,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4B85322E2277ABDD00F26553 /* tos100.trace.txt.gz */,
|
4B85322E2277ABDD00F26553 /* tos100.trace.txt.gz */,
|
||||||
4B85322C227793CA00F26553 /* etos192uk.trace.txt.gz */,
|
|
||||||
);
|
);
|
||||||
path = "TOS Startup";
|
path = "TOS Startup";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -5411,7 +5408,6 @@
|
|||||||
4BB298FB1B587D8400A49093 /* ancb in Resources */,
|
4BB298FB1B587D8400A49093 /* ancb in Resources */,
|
||||||
4BB299431B587D8400A49093 /* dcma in Resources */,
|
4BB299431B587D8400A49093 /* dcma in Resources */,
|
||||||
4BB298FD1B587D8400A49093 /* andax in Resources */,
|
4BB298FD1B587D8400A49093 /* andax in Resources */,
|
||||||
4B85322D227793CB00F26553 /* etos192uk.trace.txt.gz in Resources */,
|
|
||||||
4B8DF6262550D91600F3433C /* CPUEOR-trace_compare.log in Resources */,
|
4B8DF6262550D91600F3433C /* CPUEOR-trace_compare.log in Resources */,
|
||||||
4BB299401B587D8400A49093 /* cpya in Resources */,
|
4BB299401B587D8400A49093 /* cpya in Resources */,
|
||||||
4BB299BE1B587D8400A49093 /* rraix in Resources */,
|
4BB299BE1B587D8400A49093 /* rraix in Resources */,
|
||||||
|
@ -36,7 +36,7 @@ class EmuTOS: public ComparativeBusHandler {
|
|||||||
return m68000_.get_state();
|
return m68000_.get_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Microcycle> perform_bus_operation(const Microcycle &cycle, int) {
|
template <typename Microcycle> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
|
||||||
const uint32_t address = cycle.word_address();
|
const uint32_t address = cycle.word_address();
|
||||||
uint32_t word_address = address;
|
uint32_t word_address = address;
|
||||||
|
|
||||||
@ -56,7 +56,6 @@ class EmuTOS: public ComparativeBusHandler {
|
|||||||
word_address %= ram_.size();
|
word_address %= ram_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
using Microcycle = CPU::MC68000::Microcycle;
|
|
||||||
if(cycle.data_select_active()) {
|
if(cycle.data_select_active()) {
|
||||||
uint16_t peripheral_result = 0xffff;
|
uint16_t peripheral_result = 0xffff;
|
||||||
if(is_peripheral) {
|
if(is_peripheral) {
|
||||||
@ -68,20 +67,20 @@ class EmuTOS: public ComparativeBusHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto operation = (op != Microcycle::DecodeDynamically) ? op : cycle.operation;
|
using namespace CPU::MC68000;
|
||||||
switch(operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
switch(cycle.operation & (Operation::SelectWord | Operation::SelectByte | Operation::Read)) {
|
||||||
default: break;
|
default: break;
|
||||||
|
|
||||||
case Microcycle::SelectWord | Microcycle::Read:
|
case Operation::SelectWord | Operation::Read:
|
||||||
cycle.value->w = is_peripheral ? peripheral_result : base[word_address];
|
cycle.value->w = is_peripheral ? peripheral_result : base[word_address];
|
||||||
break;
|
break;
|
||||||
case Microcycle::SelectByte | Microcycle::Read:
|
case Operation::SelectByte | Operation::Read:
|
||||||
cycle.value->b = (is_peripheral ? peripheral_result : base[word_address]) >> cycle.byte_shift();
|
cycle.value->b = (is_peripheral ? peripheral_result : base[word_address]) >> cycle.byte_shift();
|
||||||
break;
|
break;
|
||||||
case Microcycle::SelectWord:
|
case Operation::SelectWord:
|
||||||
base[word_address] = cycle.value->w;
|
base[word_address] = cycle.value->w;
|
||||||
break;
|
break;
|
||||||
case Microcycle::SelectByte:
|
case Operation::SelectByte:
|
||||||
base[word_address] = (cycle.value->b << cycle.byte_shift()) | (base[word_address] & (0xffff ^ cycle.byte_mask()));
|
base[word_address] = (cycle.value->b << cycle.byte_shift()) | (base[word_address] & (0xffff ^ cycle.byte_mask()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -38,16 +38,14 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)write:(uint8_t)value address:(uint32_t)address {
|
- (void)write:(uint8_t)value address:(uint32_t)address {
|
||||||
const auto ®ion = MemoryMapRegion(_memoryMap, address);
|
const auto ®ion = _memoryMap.region(address);
|
||||||
XCTAssertFalse(region.flags & MemoryMap::Region::IsIO);
|
XCTAssertFalse(region.flags & MemoryMap::Region::IsIO);
|
||||||
MemoryMapWrite(_memoryMap, region, address, &value);
|
_memoryMap.write(region, address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (uint8_t)readAddress:(uint32_t)address {
|
- (uint8_t)readAddress:(uint32_t)address {
|
||||||
const auto ®ion = MemoryMapRegion(_memoryMap, address);
|
const auto ®ion = _memoryMap.region(address);
|
||||||
uint8_t value;
|
return _memoryMap.read(region, address);
|
||||||
MemoryMapRead(region, address, &value);
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testAllRAM {
|
- (void)testAllRAM {
|
||||||
@ -371,8 +369,7 @@ namespace {
|
|||||||
while(logical < [next intValue]) {
|
while(logical < [next intValue]) {
|
||||||
[[maybe_unused]] const auto ®ion =
|
[[maybe_unused]] const auto ®ion =
|
||||||
self->_memoryMap.regions[self->_memoryMap.region_map[logical]];
|
self->_memoryMap.regions[self->_memoryMap.region_map[logical]];
|
||||||
const bool isShadowed =
|
const bool isShadowed = _memoryMap.is_shadowed(logical << 8);
|
||||||
IsShadowed(_memoryMap, region, (logical << 8));
|
|
||||||
|
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
isShadowed,
|
isShadowed,
|
||||||
|
@ -59,20 +59,21 @@ class QL: public ComparativeBusHandler {
|
|||||||
if(cycle.data_select_active()) {
|
if(cycle.data_select_active()) {
|
||||||
uint16_t peripheral_result = 0xffff;
|
uint16_t peripheral_result = 0xffff;
|
||||||
|
|
||||||
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
using namespace CPU::MC68000;
|
||||||
|
switch(cycle.operation & (Operation::SelectWord | Operation::SelectByte | Operation::Read)) {
|
||||||
default: break;
|
default: break;
|
||||||
|
|
||||||
case Microcycle::SelectWord | Microcycle::Read:
|
case Operation::SelectWord | Operation::Read:
|
||||||
cycle.value->w = is_peripheral ? peripheral_result : base[word_address];
|
cycle.value->w = is_peripheral ? peripheral_result : base[word_address];
|
||||||
break;
|
break;
|
||||||
case Microcycle::SelectByte | Microcycle::Read:
|
case Operation::SelectByte | Operation::Read:
|
||||||
cycle.value->b = (is_peripheral ? peripheral_result : base[word_address]) >> cycle.byte_shift();
|
cycle.value->b = (is_peripheral ? peripheral_result : base[word_address]) >> cycle.byte_shift();
|
||||||
break;
|
break;
|
||||||
case Microcycle::SelectWord:
|
case Operation::SelectWord:
|
||||||
assert(!(is_rom && !is_peripheral));
|
assert(!(is_rom && !is_peripheral));
|
||||||
if(!is_peripheral) base[word_address] = cycle.value->w;
|
if(!is_peripheral) base[word_address] = cycle.value->w;
|
||||||
break;
|
break;
|
||||||
case Microcycle::SelectByte:
|
case Operation::SelectByte:
|
||||||
assert(!(is_rom && !is_peripheral));
|
assert(!(is_rom && !is_peripheral));
|
||||||
if(!is_peripheral) base[word_address] = (cycle.value->b << cycle.byte_shift()) | (base[word_address] & (0xffff ^ cycle.byte_mask()));
|
if(!is_peripheral) base[word_address] = (cycle.value->b << cycle.byte_shift()) | (base[word_address] & (0xffff ^ cycle.byte_mask()));
|
||||||
break;
|
break;
|
||||||
|
@ -82,24 +82,23 @@ class RAM68000: public CPU::MC68000::BusHandler {
|
|||||||
const uint32_t word_address = cycle.word_address();
|
const uint32_t word_address = cycle.word_address();
|
||||||
duration_ += cycle.length;
|
duration_ += cycle.length;
|
||||||
|
|
||||||
const auto operation = (op != Microcycle::DecodeDynamically) ? op : cycle.operation;
|
|
||||||
if(cycle.data_select_active()) {
|
if(cycle.data_select_active()) {
|
||||||
if(operation & Microcycle::InterruptAcknowledge) {
|
if(cycle.operation & CPU::MC68000::Operation::InterruptAcknowledge) {
|
||||||
cycle.value->b = 10;
|
cycle.value->b = 10;
|
||||||
} else {
|
} else {
|
||||||
switch(operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
switch(cycle.operation & (CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read)) {
|
||||||
default: break;
|
default: break;
|
||||||
|
|
||||||
case Microcycle::SelectWord | Microcycle::Read:
|
case CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::Read:
|
||||||
cycle.value->w = ram_[word_address % ram_.size()];
|
cycle.value->w = ram_[word_address % ram_.size()];
|
||||||
break;
|
break;
|
||||||
case Microcycle::SelectByte | Microcycle::Read:
|
case CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read:
|
||||||
cycle.value->b = ram_[word_address % ram_.size()] >> cycle.byte_shift();
|
cycle.value->b = ram_[word_address % ram_.size()] >> cycle.byte_shift();
|
||||||
break;
|
break;
|
||||||
case Microcycle::SelectWord:
|
case CPU::MC68000::Operation::SelectWord:
|
||||||
ram_[word_address % ram_.size()] = cycle.value->w;
|
ram_[word_address % ram_.size()] = cycle.value->w;
|
||||||
break;
|
break;
|
||||||
case Microcycle::SelectByte:
|
case CPU::MC68000::Operation::SelectByte:
|
||||||
ram_[word_address % ram_.size()] = uint16_t(
|
ram_[word_address % ram_.size()] = uint16_t(
|
||||||
(cycle.value->b << cycle.byte_shift()) |
|
(cycle.value->b << cycle.byte_shift()) |
|
||||||
(ram_[word_address % ram_.size()] & cycle.untouched_byte_mask())
|
(ram_[word_address % ram_.size()] & cycle.untouched_byte_mask())
|
||||||
|
Loading…
Reference in New Issue
Block a user