1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-10-01 13:58:20 +00:00

Switches to table-based address decoding.

This commit is contained in:
Thomas Harte 2019-08-02 21:30:04 -04:00
parent 83393e8e91
commit 5e76d593af

View File

@ -131,6 +131,9 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
// Insert any supplied media.
insert_media(target.media);
// Set the immutables of the memory map.
setup_memory_map();
}
~ConcreteMachine() {
@ -172,13 +175,20 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
// a read or write.
if(!cycle.data_select_active()) return delay;
// Check whether this access maps into the IO area; if so then
// apply more complicated decoding logic.
if(word_address >= 0x400000) {
const int register_address = word_address >> 8;
uint16_t *memory_base = nullptr;
switch(memory_map_[word_address >> 18]) {
default: break;
case BusDevice::Unassigned:
fill_unmapped(cycle);
return delay;
case BusDevice::VIA: {
if(*cycle.address & 1) {
fill_unmapped(cycle);
} else {
const int register_address = word_address >> 8;
switch(word_address & 0x78f000) {
case 0x70f000:
// VIA accesses are via address 0xefe1fe + register*512,
// which at word precision is 0x77f0ff + register*256.
if(cycle.operation & Microcycle::Read) {
@ -186,9 +196,23 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
} else {
via_.set_register(register_address, cycle.value->halves.low);
}
break;
case 0x68f000:
if(cycle.operation & Microcycle::SelectWord) cycle.value->halves.high = 0xff;
}
} return delay;
case BusDevice::PhaseRead: {
if(cycle.operation & Microcycle::Read) {
cycle.value->halves.low = phase_ & 7;
}
if(cycle.operation & Microcycle::SelectWord) cycle.value->halves.high = 0xff;
} return delay;
case BusDevice::IWM: {
if(*cycle.address & 1) {
const int register_address = word_address >> 8;
// The IWM; this is a purely polled device, so can be run on demand.
iwm_.flush();
if(cycle.operation & Microcycle::Read) {
@ -196,101 +220,79 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
} else {
iwm_.iwm.write(register_address, cycle.value->halves.low);
}
break;
case 0x780000:
// Phase read.
if(cycle.operation & Microcycle::Read) {
cycle.value->halves.low = phase_ & 7;
}
break;
if(cycle.operation & Microcycle::SelectWord) cycle.value->halves.high = 0xff;
} else {
fill_unmapped(cycle);
}
} return delay;
case 0x480000: case 0x48f000:
case 0x580000: case 0x58f000:
// Any word access here adjusts phase.
if(cycle.operation & Microcycle::SelectWord) {
++phase_;
case BusDevice::SCCReadResetPhase: {
// Any word access here adjusts phase.
if(cycle.operation & Microcycle::SelectWord) {
adjust_phase();
} else {
// A0 = 1 => reset; A0 = 0 => read.
if(*cycle.address & 1) {
scc_.reset();
if(cycle.operation & Microcycle::Read) {
cycle.value->halves.low = 0xff;
}
} else {
if(word_address < 0x500000) {
// A0 = 1 => reset; A0 = 0 => read.
if(*cycle.address & 1) {
scc_.reset();
} else {
const auto read = scc_.read(int(word_address));
if(cycle.operation & Microcycle::Read) {
cycle.value->halves.low = read;
}
}
} else {
if(*cycle.address & 1) {
if(cycle.operation & Microcycle::Read) {
scc_.write(int(word_address), 0xff);
} else {
scc_.write(int(word_address), cycle.value->halves.low);
}
}
const auto read = scc_.read(int(word_address));
if(cycle.operation & Microcycle::Read) {
cycle.value->halves.low = read;
}
}
break;
default:
if(cycle.operation & Microcycle::Read) {
LOG("Unrecognised read " << PADHEX(6) << (*cycle.address & 0xffffff));
cycle.value->halves.low = 0x00;
} else {
LOG("Unrecognised write %06x" << PADHEX(6) << (*cycle.address & 0xffffff));
}
break;
}
if(cycle.operation & Microcycle::SelectWord) cycle.value->halves.high = 0xff;
return delay;
}
// Having reached here, this is a RAM or ROM access.
// When ROM overlay is enabled, the ROM begins at both $000000 and $400000,
// and RAM is available at $600000.
//
// Otherwise RAM is mapped at $000000 and ROM from $400000.
uint16_t *memory_base;
if(
(!ROM_is_overlay_ && word_address < 0x200000) ||
(ROM_is_overlay_ && word_address >= 0x300000)
) {
memory_base = ram_;
word_address &= ram_mask_;
// This is coupled with the Macintosh implementation of video; the magic
// constant should probably be factored into the Video class.
// It embodies knowledge of the fact that video (and audio) will always
// be fetched from the final $d900 bytes (i.e. $6c80 words) of memory.
// (And that ram_mask_ = ram size - 1).
if(word_address > ram_mask_ - 0x6c80)
update_video();
} else {
memory_base = rom_;
word_address &= rom_mask_;
// Writes to ROM have no effect, and it doesn't mirror above 0x60000.
if(!(cycle.operation & Microcycle::Read)) return delay;
if(word_address >= 0x300000) {
if(cycle.operation & Microcycle::SelectWord) {
cycle.value->full = 0xffff;
} else {
cycle.value->halves.low = 0xff;
}
return delay;
}
} return delay;
case BusDevice::SCCWrite: {
// Any word access here adjusts phase.
if(cycle.operation & Microcycle::SelectWord) {
adjust_phase();
} else {
if(*cycle.address & 1) {
if(cycle.operation & Microcycle::Read) {
scc_.write(int(word_address), 0xff);
cycle.value->halves.low = 0xff;
} else {
scc_.write(int(word_address), cycle.value->halves.low);
}
} else {
fill_unmapped(cycle);
}
}
} return delay;
case BusDevice::RAM: {
// This is coupled with the Macintosh implementation of video; the magic
// constant should probably be factored into the Video class.
// It embodies knowledge of the fact that video (and audio) will always
// be fetched from the final $d900 bytes (i.e. $6c80 words) of memory.
// (And that ram_mask_ = ram size - 1).
if(word_address > ram_mask_ - 0x6c80)
update_video();
memory_base = ram_;
word_address &= ram_mask_;
} break;
case BusDevice::ROM: {
if(!(cycle.operation & Microcycle::Read)) return delay;
memory_base = rom_;
word_address &= rom_mask_;
} break;
}
// If control has fallen through to here, the access is either a read from ROM, or a read or write to RAM.
// TODO: interrupt acknowledge cycles also end up here, which may suggest the 68000 is loading the address bus
// incorrectly during interrupt acknowledgment cycles. Check.
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read | Microcycle::InterruptAcknowledge)) {
default:
break;
// Catches the deliberation set of operation to 0 above.
case 0: break;
case Microcycle::InterruptAcknowledge | Microcycle::SelectByte:
// The Macintosh uses autovectored interrupts.
mc68000_.set_is_peripheral_address(true);
@ -313,17 +315,6 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
break;
}
/*
Normal memory map:
000000: RAM
400000: ROM
9FFFF8+: SCC read operations
BFFFF8+: SCC write operations
DFE1FF+: IWM
EFE1FE+: VIA
*/
return delay;
}
@ -344,6 +335,21 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
void set_rom_is_overlay(bool rom_is_overlay) {
ROM_is_overlay_ = rom_is_overlay;
populate_memory_map([rom_is_overlay] (std::function<void(int target, BusDevice device)> map_to) {
// Addresses up to $80 0000 aren't affected by this bit.
if(rom_is_overlay) {
// Up to $60 0000 mirrors of the ROM alternate with unassigned areas every $10 0000 byes.
for(int c = 0; c <= 0x600000; c += 0x100000) {
map_to(c, ((c >> 20)&1) ? BusDevice::ROM : BusDevice::Unassigned);
}
map_to(0x800000, BusDevice::RAM);
} else {
map_to(0x400000, BusDevice::RAM);
map_to(0x500000, BusDevice::ROM);
map_to(0x800000, BusDevice::Unassigned);
}
});
}
bool video_is_outputting() {
@ -406,6 +412,19 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
}
private:
forceinline void adjust_phase() {
++phase_;
}
forceinline void fill_unmapped(const Microcycle &cycle) {
if(!(cycle.operation & Microcycle::Read)) return;
if(cycle.operation & Microcycle::SelectWord) {
cycle.value->full = 0xffff;
} else {
cycle.value->halves.low = 0xff;
}
}
/// Advances all non-CPU components by @c duration half cycles.
forceinline void run_for(HalfCycles duration) {
time_since_video_update_ += duration;
@ -640,6 +659,66 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
Apple::Macintosh::KeyboardMapper keyboard_mapper_;
enum class BusDevice {
RAM, ROM, VIA, IWM, SCCWrite, SCCReadResetPhase, SCSI, PhaseRead, Unassigned
};
/// Divides the 24-bit address space up into $80000 (i.e. 512kb) segments, recording
/// which device is current mapped in each area. Keeping it in a table is a bit faster
/// than the multi-level address inspection that is otherwise required, as well as
/// simplifying slightly the handling of different models.
///
/// So: index with the top 5 bits of the 24-bit address.
BusDevice memory_map_[32];
void setup_memory_map() {
// Apply the power-up memory map, i.e. assume that ROM_is_overlay_ = true.
using Model = Analyser::Static::Macintosh::Target::Model;
switch(model) {
default: assert(false);
case Model::Mac128k:
case Model::Mac512k:
case Model::Mac512ke:
populate_memory_map([] (std::function<void(int target, BusDevice device)> map_to) {
// Up to $60 0000 mirrors of the ROM alternate with unassigned areas every $10 0000 byes.
for(int c = 0; c <= 0x600000; c += 0x100000) {
map_to(c, ((c >> 20)&1) ? BusDevice::ROM : BusDevice::Unassigned);
}
map_to(0x800000, BusDevice::RAM);
map_to(0x900000, BusDevice::Unassigned);
map_to(0xa00000, BusDevice::SCCReadResetPhase);
map_to(0xb00000, BusDevice::Unassigned);
map_to(0xc00000, BusDevice::SCCWrite);
map_to(0xd00000, BusDevice::Unassigned);
map_to(0xe00000, BusDevice::IWM);
map_to(0xe80000, BusDevice::Unassigned);
map_to(0xf00000, BusDevice::VIA);
map_to(0xf80000, BusDevice::PhaseRead);
map_to(0x1000000, BusDevice::Unassigned);
});
break;
// case Model::MacPlus: {
// int segment = 0;
// } break;
}
}
void populate_memory_map(std::function<void(std::function<void(int, BusDevice)>)> populator) {
// Define semantics for below; map_to will write from the current cursor position
// to the supplied 24-bit address, setting a particular mapped device.
int segment = 0;
auto map_to = [&segment, this](int address, BusDevice device) {
for(; segment < address >> 19; ++segment) {
this->memory_map_[segment] = device;
}
};
populator(map_to);
}
uint32_t ram_mask_ = 0;
uint32_t rom_mask_ = 0;
uint16_t rom_[64*1024];