mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-19 07:31:15 +00:00
Introduce the possibility of operation type as a template parameter.
It's already proven possible to provide this for instruction fetch, so I think it'll immediately be a win. But more importantly it opens a path forwards for further improvement.
This commit is contained in:
parent
ed31cfd80a
commit
5c7f94d2ef
@ -80,11 +80,13 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MC68000::BusHandler.
|
// MARK: - MC68000::BusHandler.
|
||||||
template <typename Microcycle> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
|
using Microcycle = CPU::MC68000::Microcycle;
|
||||||
|
template <Microcycle::OperationT op> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
|
||||||
|
const auto operation = (op != Microcycle::DecodeDynamically) ? op : cycle.operation;
|
||||||
|
|
||||||
// Do a quick advance check for Chip RAM access; add a suitable delay if required.
|
// Do a quick advance check for Chip RAM access; add a suitable delay if required.
|
||||||
HalfCycles total_length;
|
HalfCycles total_length;
|
||||||
if(cycle.operation & Microcycle::NewAddress && *cycle.address < 0x20'0000) {
|
if(operation & Microcycle::NewAddress && *cycle.address < 0x20'0000) {
|
||||||
total_length = chipset_.run_until_after_cpu_slot().duration;
|
total_length = chipset_.run_until_after_cpu_slot().duration;
|
||||||
assert(total_length >= cycle.length);
|
assert(total_length >= cycle.length);
|
||||||
} else {
|
} else {
|
||||||
@ -94,19 +96,19 @@ class ConcreteMachine:
|
|||||||
mc68000_.set_interrupt_level(chipset_.get_interrupt_level());
|
mc68000_.set_interrupt_level(chipset_.get_interrupt_level());
|
||||||
|
|
||||||
// Check for assertion of reset.
|
// Check for assertion of reset.
|
||||||
if(cycle.operation & Microcycle::Reset) {
|
if(operation & Microcycle::Reset) {
|
||||||
memory_.reset();
|
memory_.reset();
|
||||||
LOG("Reset; PC is around " << PADHEX(8) << mc68000_.get_state().registers.program_counter);
|
LOG("Reset; PC is around " << PADHEX(8) << mc68000_.get_state().registers.program_counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Autovector interrupts.
|
// Autovector interrupts.
|
||||||
if(cycle.operation & Microcycle::InterruptAcknowledge) {
|
if(operation & Microcycle::InterruptAcknowledge) {
|
||||||
mc68000_.set_is_peripheral_address(true);
|
mc68000_.set_is_peripheral_address(true);
|
||||||
return total_length - cycle.length;
|
return total_length - cycle.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do nothing if no address is exposed.
|
// Do nothing if no address is exposed.
|
||||||
if(!(cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return total_length - cycle.length;
|
if(!(operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return total_length - cycle.length;
|
||||||
|
|
||||||
// Grab the target address to pick a memory source.
|
// Grab the target address to pick a memory source.
|
||||||
const uint32_t address = cycle.host_endian_byte_address();
|
const uint32_t address = cycle.host_endian_byte_address();
|
||||||
@ -115,7 +117,7 @@ class ConcreteMachine:
|
|||||||
mc68000_.set_is_peripheral_address((address & 0xe0'0000) == 0xa0'0000);
|
mc68000_.set_is_peripheral_address((address & 0xe0'0000) == 0xa0'0000);
|
||||||
|
|
||||||
if(!memory_.regions[address >> 18].read_write_mask) {
|
if(!memory_.regions[address >> 18].read_write_mask) {
|
||||||
if((cycle.operation & (Microcycle::SelectByte | Microcycle::SelectWord))) {
|
if((operation & (Microcycle::SelectByte | Microcycle::SelectWord))) {
|
||||||
// Check for various potential chip accesses.
|
// Check for various potential chip accesses.
|
||||||
|
|
||||||
// Per the manual:
|
// Per the manual:
|
||||||
@ -133,7 +135,7 @@ class ConcreteMachine:
|
|||||||
const bool select_a = !(address & 0x1000);
|
const bool select_a = !(address & 0x1000);
|
||||||
const bool select_b = !(address & 0x2000);
|
const bool select_b = !(address & 0x2000);
|
||||||
|
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
uint16_t result = 0xffff;
|
uint16_t result = 0xffff;
|
||||||
if(select_a) result &= 0xff00 | (chipset_.cia_a.read(reg) << 0);
|
if(select_a) result &= 0xff00 | (chipset_.cia_a.read(reg) << 0);
|
||||||
if(select_b) result &= 0x00ff | (chipset_.cia_b.read(reg) << 8);
|
if(select_b) result &= 0x00ff | (chipset_.cia_b.read(reg) << 8);
|
||||||
@ -143,7 +145,7 @@ class ConcreteMachine:
|
|||||||
if(select_b) chipset_.cia_b.write(reg, cycle.value8_high());
|
if(select_b) chipset_.cia_b.write(reg, cycle.value8_high());
|
||||||
}
|
}
|
||||||
|
|
||||||
// LOG("CIA " << (((address >> 12) & 3)^3) << " " << (cycle.operation & Microcycle::Read ? "read " : "write ") << std::dec << (reg & 0xf) << " of " << PADHEX(4) << +cycle.value16());
|
// LOG("CIA " << (((address >> 12) & 3)^3) << " " << (operation & Microcycle::Read ? "read " : "write ") << std::dec << (reg & 0xf) << " of " << PADHEX(4) << +cycle.value16());
|
||||||
} else if(address >= 0xdf'f000 && address <= 0xdf'f1be) {
|
} else if(address >= 0xdf'f000 && address <= 0xdf'f1be) {
|
||||||
chipset_.perform(cycle);
|
chipset_.perform(cycle);
|
||||||
} else if(address >= 0xe8'0000 && address < 0xe9'0000) {
|
} else if(address >= 0xe8'0000 && address < 0xe9'0000) {
|
||||||
@ -155,13 +157,13 @@ class ConcreteMachine:
|
|||||||
memory_.perform(cycle);
|
memory_.perform(cycle);
|
||||||
} else {
|
} else {
|
||||||
// This'll do for open bus, for now.
|
// This'll do for open bus, for now.
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
cycle.set_value16(0xffff);
|
cycle.set_value16(0xffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't log for the region that is definitely just ROM this machine doesn't have.
|
// Don't log for the region that is definitely just ROM this machine doesn't have.
|
||||||
if(address < 0xf0'0000) {
|
if(address < 0xf0'0000) {
|
||||||
LOG("Unmapped " << (cycle.operation & Microcycle::Read ? "read from " : "write to ") << PADHEX(6) << ((*cycle.address)&0xffffff) << " of " << cycle.value16());
|
LOG("Unmapped " << (operation & Microcycle::Read ? "read from " : "write to ") << PADHEX(6) << ((*cycle.address)&0xffffff) << " of " << cycle.value16());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,13 +192,14 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
}
|
}
|
||||||
|
|
||||||
using Microcycle = CPU::MC68000::Microcycle;
|
using Microcycle = CPU::MC68000::Microcycle;
|
||||||
|
template <Microcycle::OperationT op> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
|
||||||
|
const auto operation = (op != Microcycle::DecodeDynamically) ? op : cycle.operation;
|
||||||
|
|
||||||
HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
|
|
||||||
// Advance time.
|
// Advance time.
|
||||||
advance_time(cycle.length);
|
advance_time(cycle.length);
|
||||||
|
|
||||||
// A null cycle leaves nothing else to do.
|
// A null cycle leaves nothing else to do.
|
||||||
if(!(cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return HalfCycles(0);
|
if(!(operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return HalfCycles(0);
|
||||||
|
|
||||||
// Grab the address.
|
// Grab the address.
|
||||||
auto address = cycle.host_endian_byte_address();
|
auto address = cycle.host_endian_byte_address();
|
||||||
@ -218,7 +219,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
// having set VPA above deals with those given that the generated address
|
// having set VPA above deals with those given that the generated address
|
||||||
// for interrupt acknowledge cycles always has all bits set except the
|
// for interrupt acknowledge cycles always has all bits set except the
|
||||||
// lowest explicit address lines.
|
// lowest explicit address lines.
|
||||||
if(!cycle.data_select_active() || (cycle.operation & Microcycle::InterruptAcknowledge)) return HalfCycles(0);
|
if(!cycle.data_select_active() || (operation & Microcycle::InterruptAcknowledge)) return HalfCycles(0);
|
||||||
|
|
||||||
// Grab the word-precision address being accessed.
|
// Grab the word-precision address being accessed.
|
||||||
uint8_t *memory_base = nullptr;
|
uint8_t *memory_base = nullptr;
|
||||||
@ -227,18 +228,18 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
default: assert(false);
|
default: assert(false);
|
||||||
|
|
||||||
case BusDevice::Unassigned:
|
case BusDevice::Unassigned:
|
||||||
fill_unmapped(cycle);
|
fill_unmapped<op>(cycle);
|
||||||
return delay;
|
return delay;
|
||||||
|
|
||||||
case BusDevice::VIA: {
|
case BusDevice::VIA: {
|
||||||
if(*cycle.address & 1) {
|
if(*cycle.address & 1) {
|
||||||
fill_unmapped(cycle);
|
fill_unmapped<op>(cycle);
|
||||||
} else {
|
} else {
|
||||||
const int register_address = address >> 9;
|
const int register_address = address >> 9;
|
||||||
|
|
||||||
// VIA accesses are via address 0xefe1fe + register*512,
|
// VIA accesses are via address 0xefe1fe + register*512,
|
||||||
// which at word precision is 0x77f0ff + register*256.
|
// which at word precision is 0x77f0ff + register*256.
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
cycle.set_value8_high(via_.read(register_address));
|
cycle.set_value8_high(via_.read(register_address));
|
||||||
} else {
|
} else {
|
||||||
via_.write(register_address, cycle.value8_high());
|
via_.write(register_address, cycle.value8_high());
|
||||||
@ -247,7 +248,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
} return delay;
|
} return delay;
|
||||||
|
|
||||||
case BusDevice::PhaseRead: {
|
case BusDevice::PhaseRead: {
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
cycle.set_value8_low(phase_ & 7);
|
cycle.set_value8_low(phase_ & 7);
|
||||||
}
|
}
|
||||||
} return delay;
|
} return delay;
|
||||||
@ -257,13 +258,13 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
const int register_address = address >> 9;
|
const int register_address = address >> 9;
|
||||||
|
|
||||||
// The IWM; this is a purely polled device, so can be run on demand.
|
// The IWM; this is a purely polled device, so can be run on demand.
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
cycle.set_value8_low(iwm_->read(register_address));
|
cycle.set_value8_low(iwm_->read(register_address));
|
||||||
} else {
|
} else {
|
||||||
iwm_->write(register_address, cycle.value8_low());
|
iwm_->write(register_address, cycle.value8_low());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fill_unmapped(cycle);
|
fill_unmapped<op>(cycle);
|
||||||
}
|
}
|
||||||
} return delay;
|
} return delay;
|
||||||
|
|
||||||
@ -274,14 +275,14 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
// Even accesses = read; odd = write.
|
// Even accesses = read; odd = write.
|
||||||
if(*cycle.address & 1) {
|
if(*cycle.address & 1) {
|
||||||
// Odd access => this is a write. Data will be in the upper byte.
|
// Odd access => this is a write. Data will be in the upper byte.
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
scsi_.write(register_address, 0xff, dma_acknowledge);
|
scsi_.write(register_address, 0xff, dma_acknowledge);
|
||||||
} else {
|
} else {
|
||||||
scsi_.write(register_address, cycle.value8_high());
|
scsi_.write(register_address, cycle.value8_high());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Even access => this is a read.
|
// Even access => this is a read.
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
cycle.set_value8_high(scsi_.read(register_address, dma_acknowledge));
|
cycle.set_value8_high(scsi_.read(register_address, dma_acknowledge));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,19 +290,19 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
|
|
||||||
case BusDevice::SCCReadResetPhase: {
|
case BusDevice::SCCReadResetPhase: {
|
||||||
// Any word access here adjusts phase.
|
// Any word access here adjusts phase.
|
||||||
if(cycle.operation & Microcycle::SelectWord) {
|
if(operation & Microcycle::SelectWord) {
|
||||||
adjust_phase();
|
adjust_phase();
|
||||||
} else {
|
} else {
|
||||||
// A0 = 1 => reset; A0 = 0 => read.
|
// A0 = 1 => reset; A0 = 0 => read.
|
||||||
if(*cycle.address & 1) {
|
if(*cycle.address & 1) {
|
||||||
scc_.reset();
|
scc_.reset();
|
||||||
|
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
cycle.set_value16(0xffff);
|
cycle.set_value16(0xffff);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const auto read = scc_.read(int(address >> 1));
|
const auto read = scc_.read(int(address >> 1));
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
cycle.set_value8_high(read);
|
cycle.set_value8_high(read);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,20 +311,20 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
|
|
||||||
case BusDevice::SCCWrite: {
|
case BusDevice::SCCWrite: {
|
||||||
// Any word access here adjusts phase.
|
// Any word access here adjusts phase.
|
||||||
if(cycle.operation & Microcycle::SelectWord) {
|
if(operation & Microcycle::SelectWord) {
|
||||||
adjust_phase();
|
adjust_phase();
|
||||||
} else {
|
} else {
|
||||||
// This is definitely a byte access; either it's to an odd address, in which
|
// This is definitely a byte access; either it's to an odd address, in which
|
||||||
// case it will reach the SCC, or it isn't, in which case it won't.
|
// case it will reach the SCC, or it isn't, in which case it won't.
|
||||||
if(*cycle.address & 1) {
|
if(*cycle.address & 1) {
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
scc_.write(int(address >> 1), 0xff);
|
scc_.write(int(address >> 1), 0xff);
|
||||||
cycle.value->b = 0xff;
|
cycle.value->b = 0xff;
|
||||||
} else {
|
} else {
|
||||||
scc_.write(int(address >> 1), cycle.value->b);
|
scc_.write(int(address >> 1), cycle.value->b);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fill_unmapped(cycle);
|
fill_unmapped<op>(cycle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} return delay;
|
} return delay;
|
||||||
@ -351,7 +352,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case BusDevice::ROM: {
|
case BusDevice::ROM: {
|
||||||
if(!(cycle.operation & Microcycle::Read)) return delay;
|
if(!(operation & Microcycle::Read)) return delay;
|
||||||
memory_base = rom_;
|
memory_base = rom_;
|
||||||
address &= rom_mask_;
|
address &= rom_mask_;
|
||||||
} break;
|
} break;
|
||||||
@ -543,8 +544,10 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
|||||||
++phase_;
|
++phase_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <Microcycle::OperationT op>
|
||||||
forceinline void fill_unmapped(const Microcycle &cycle) {
|
forceinline void fill_unmapped(const Microcycle &cycle) {
|
||||||
if(!(cycle.operation & Microcycle::Read)) return;
|
const auto operation = (op != Microcycle::DecodeDynamically) ? op : cycle.operation;
|
||||||
|
if(!(operation & Microcycle::Read)) return;
|
||||||
cycle.set_value16(0xffff);
|
cycle.set_value16(0xffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +174,10 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: MC68000::BusHandler
|
// MARK: MC68000::BusHandler
|
||||||
template <typename Microcycle> HalfCycles perform_bus_operation(const Microcycle &cycle, int is_supervisor) {
|
using Microcycle = CPU::MC68000::Microcycle;
|
||||||
|
template <Microcycle::OperationT op> HalfCycles perform_bus_operation(const Microcycle &cycle, int is_supervisor) {
|
||||||
|
const auto operation = (op != Microcycle::DecodeDynamically) ? op : cycle.operation;
|
||||||
|
|
||||||
// Just in case the last cycle was an interrupt acknowledge or bus error. TODO: find a better solution?
|
// Just in case the last cycle was an interrupt acknowledge or bus error. TODO: find a better solution?
|
||||||
mc68000_.set_is_peripheral_address(false);
|
mc68000_.set_is_peripheral_address(false);
|
||||||
mc68000_.set_bus_error(false);
|
mc68000_.set_bus_error(false);
|
||||||
@ -183,15 +186,15 @@ class ConcreteMachine:
|
|||||||
advance_time(cycle.length);
|
advance_time(cycle.length);
|
||||||
|
|
||||||
// Check for assertion of reset.
|
// Check for assertion of reset.
|
||||||
if(cycle.operation & Microcycle::Reset) {
|
if(operation & Microcycle::Reset) {
|
||||||
LOG("Unhandled Reset");
|
LOG("Unhandled Reset");
|
||||||
}
|
}
|
||||||
|
|
||||||
// A null cycle leaves nothing else to do.
|
// A null cycle leaves nothing else to do.
|
||||||
if(!(cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return HalfCycles(0);
|
if(!(operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return HalfCycles(0);
|
||||||
|
|
||||||
// An interrupt acknowledge, perhaps?
|
// An interrupt acknowledge, perhaps?
|
||||||
if(cycle.operation & Microcycle::InterruptAcknowledge) {
|
if(operation & Microcycle::InterruptAcknowledge) {
|
||||||
// Current implementation: everything other than 6 (i.e. the MFP) is autovectored.
|
// Current implementation: everything other than 6 (i.e. the MFP) is autovectored.
|
||||||
const int interrupt_level = cycle.word_address()&7;
|
const int interrupt_level = cycle.word_address()&7;
|
||||||
if(interrupt_level != 6) {
|
if(interrupt_level != 6) {
|
||||||
@ -200,7 +203,7 @@ class ConcreteMachine:
|
|||||||
mc68000_.set_is_peripheral_address(true);
|
mc68000_.set_is_peripheral_address(true);
|
||||||
return HalfCycles(0);
|
return HalfCycles(0);
|
||||||
} else {
|
} else {
|
||||||
if(cycle.operation & Microcycle::SelectByte) {
|
if(operation & Microcycle::SelectByte) {
|
||||||
const int interrupt = mfp_->acknowledge_interrupt();
|
const int interrupt = mfp_->acknowledge_interrupt();
|
||||||
if(interrupt != Motorola::MFP68901::MFP68901::NoAcknowledgement) {
|
if(interrupt != Motorola::MFP68901::MFP68901::NoAcknowledgement) {
|
||||||
cycle.value->b = uint8_t(interrupt);
|
cycle.value->b = uint8_t(interrupt);
|
||||||
@ -217,7 +220,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
// If this is a new strobing of the address signal, test for bus error and pre-DTack delay.
|
// If this is a new strobing of the address signal, test for bus error and pre-DTack delay.
|
||||||
HalfCycles delay(0);
|
HalfCycles delay(0);
|
||||||
if(cycle.operation & Microcycle::NewAddress) {
|
if(operation & Microcycle::NewAddress) {
|
||||||
// Bus error test.
|
// Bus error test.
|
||||||
if(
|
if(
|
||||||
// Anything unassigned should generate a bus error.
|
// Anything unassigned should generate a bus error.
|
||||||
@ -269,7 +272,7 @@ class ConcreteMachine:
|
|||||||
TOS 1.0 appears to attempt to read from the catridge before it has setup
|
TOS 1.0 appears to attempt to read from the catridge before it has setup
|
||||||
the bus error vector. Therefore I assume no bus error flows.
|
the bus error vector. Therefore I assume no bus error flows.
|
||||||
*/
|
*/
|
||||||
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
switch(operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
||||||
default: break;
|
default: break;
|
||||||
case Microcycle::SelectWord | Microcycle::Read:
|
case Microcycle::SelectWord | Microcycle::Read:
|
||||||
cycle.value->w = 0xffff;
|
cycle.value->w = 0xffff;
|
||||||
@ -313,7 +316,7 @@ class ConcreteMachine:
|
|||||||
case 0x8260: case 0x8262:
|
case 0x8260: case 0x8262:
|
||||||
if(!cycle.data_select_active()) return delay;
|
if(!cycle.data_select_active()) return delay;
|
||||||
|
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
cycle.set_value16(video_->read(int(address >> 1)));
|
cycle.set_value16(video_->read(int(address >> 1)));
|
||||||
} else {
|
} else {
|
||||||
video_->write(int(address >> 1), cycle.value16());
|
video_->write(int(address >> 1), cycle.value16());
|
||||||
@ -324,7 +327,7 @@ class ConcreteMachine:
|
|||||||
case 0x8604: case 0x8606: case 0x8608: case 0x860a: case 0x860c:
|
case 0x8604: case 0x8606: case 0x8608: case 0x860a: case 0x860c:
|
||||||
if(!cycle.data_select_active()) return delay;
|
if(!cycle.data_select_active()) return delay;
|
||||||
|
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
cycle.set_value16(dma_->read(int(address >> 1)));
|
cycle.set_value16(dma_->read(int(address >> 1)));
|
||||||
} else {
|
} else {
|
||||||
dma_->write(int(address >> 1), cycle.value16());
|
dma_->write(int(address >> 1), cycle.value16());
|
||||||
@ -360,7 +363,7 @@ class ConcreteMachine:
|
|||||||
advance_time(HalfCycles(2));
|
advance_time(HalfCycles(2));
|
||||||
update_audio();
|
update_audio();
|
||||||
|
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
cycle.set_value8_high(GI::AY38910::Utility::read(ay_));
|
cycle.set_value8_high(GI::AY38910::Utility::read(ay_));
|
||||||
} else {
|
} else {
|
||||||
// Net effect here: addresses with bit 1 set write to a register,
|
// Net effect here: addresses with bit 1 set write to a register,
|
||||||
@ -380,7 +383,7 @@ class ConcreteMachine:
|
|||||||
case 0xfa38: case 0xfa3a: case 0xfa3c: case 0xfa3e:
|
case 0xfa38: case 0xfa3a: case 0xfa3c: case 0xfa3e:
|
||||||
if(!cycle.data_select_active()) return delay;
|
if(!cycle.data_select_active()) return delay;
|
||||||
|
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
cycle.set_value8_low(mfp_->read(int(address >> 1)));
|
cycle.set_value8_low(mfp_->read(int(address >> 1)));
|
||||||
} else {
|
} else {
|
||||||
mfp_->write(int(address >> 1), cycle.value8_low());
|
mfp_->write(int(address >> 1), cycle.value8_low());
|
||||||
@ -394,7 +397,7 @@ class ConcreteMachine:
|
|||||||
if(!cycle.data_select_active()) return delay;
|
if(!cycle.data_select_active()) return delay;
|
||||||
|
|
||||||
const auto acia_ = (address & 4) ? &midi_acia_ : &keyboard_acia_;
|
const auto acia_ = (address & 4) ? &midi_acia_ : &keyboard_acia_;
|
||||||
if(cycle.operation & Microcycle::Read) {
|
if(operation & Microcycle::Read) {
|
||||||
cycle.set_value8_high((*acia_)->read(int(address >> 1)));
|
cycle.set_value8_high((*acia_)->read(int(address >> 1)));
|
||||||
} else {
|
} else {
|
||||||
(*acia_)->write(int(address >> 1), cycle.value8_high());
|
(*acia_)->write(int(address >> 1), cycle.value8_high());
|
||||||
@ -405,7 +408,7 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If control has fallen through to here, the access is either a read from ROM, or a read or write to RAM.
|
// If control has fallen through to here, the access is either a read from ROM, or a read or write to RAM.
|
||||||
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
switch(operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -85,6 +85,11 @@ struct Microcycle {
|
|||||||
/// Provides the 68000's bus grant line — indicating whether a bus request has been acknowledged.
|
/// Provides the 68000's bus grant line — indicating whether a bus request has been acknowledged.
|
||||||
static constexpr OperationT BusGrant = 1 << 12;
|
static constexpr OperationT BusGrant = 1 << 12;
|
||||||
|
|
||||||
|
/// An otherwise invalid combination; used as the operaiton template parameter to @c perform_bus_operation if
|
||||||
|
/// the operation wasn't knowable in advance and the receiver should decode dynamically using the microcycle's
|
||||||
|
/// .operation field.
|
||||||
|
static constexpr OperationT DecodeDynamically = NewAddress | SameAddress;
|
||||||
|
|
||||||
/// Contains a valid combination of the various static constexpr int flags, describing the operation
|
/// Contains a valid combination of the various static constexpr int flags, describing the operation
|
||||||
/// performed by this Microcycle.
|
/// performed by this Microcycle.
|
||||||
OperationT operation = 0;
|
OperationT operation = 0;
|
||||||
|
@ -282,14 +282,17 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
|
|
||||||
// Performs the bus operation and then applies a `Spend` of its length
|
// Performs the bus operation and then applies a `Spend` of its length
|
||||||
// plus any additional length returned by the bus handler.
|
// plus any additional length returned by the bus handler.
|
||||||
#define PerformBusOperation(x) \
|
#define PerformBusOperation(x, op) \
|
||||||
delay = bus_handler_.perform_bus_operation(x, is_supervisor_); \
|
delay = bus_handler_.template perform_bus_operation<op>(x, is_supervisor_); \
|
||||||
Spend(x.length + delay)
|
Spend(x.length + delay)
|
||||||
|
|
||||||
|
// TODO: the templated operation type to perform_bus_operation is intended to allow a much
|
||||||
|
// cheaper through cost where the operation is knowable in advance. So use that pathway.
|
||||||
|
|
||||||
// Performs no bus activity for the specified number of microcycles.
|
// Performs no bus activity for the specified number of microcycles.
|
||||||
#define IdleBus(n) \
|
#define IdleBus(n) \
|
||||||
idle.length = HalfCycles((n) << 2); \
|
idle.length = HalfCycles((n) << 2); \
|
||||||
PerformBusOperation(idle)
|
PerformBusOperation(idle, 0)
|
||||||
|
|
||||||
// Spin until DTACK, VPA or BERR is asserted (unless DTACK is implicit),
|
// Spin until DTACK, VPA or BERR is asserted (unless DTACK is implicit),
|
||||||
// holding the bus cycle provided.
|
// holding the bus cycle provided.
|
||||||
@ -312,7 +315,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
//
|
//
|
||||||
// (1) wait until end of current 10-cycle window;
|
// (1) wait until end of current 10-cycle window;
|
||||||
// (2) run for the next 10-cycle window.
|
// (2) run for the next 10-cycle window.
|
||||||
#define CompleteAccess(x) \
|
#define CompleteAccess(x, op) \
|
||||||
if(berr_) { \
|
if(berr_) { \
|
||||||
RaiseBusOrAddressError(AccessFault, x); \
|
RaiseBusOrAddressError(AccessFault, x); \
|
||||||
} \
|
} \
|
||||||
@ -321,11 +324,11 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
} else { \
|
} else { \
|
||||||
x.length = HalfCycles(4); \
|
x.length = HalfCycles(4); \
|
||||||
} \
|
} \
|
||||||
PerformBusOperation(x)
|
PerformBusOperation(x, op)
|
||||||
|
|
||||||
// Performs the memory access implied by the announce, perform pair,
|
// Performs the memory access implied by the announce, perform pair,
|
||||||
// honouring DTACK, BERR and VPA as necessary.
|
// honouring DTACK, BERR and VPA as necessary.
|
||||||
#define AccessPair(val, announce, perform) \
|
#define AccessPair(val, announce, announce_op, perform, perform_op) \
|
||||||
perform.value = &val; \
|
perform.value = &val; \
|
||||||
if constexpr (!dtack_is_implicit) { \
|
if constexpr (!dtack_is_implicit) { \
|
||||||
announce.length = HalfCycles(4); \
|
announce.length = HalfCycles(4); \
|
||||||
@ -333,9 +336,9 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
if(*perform.address & (perform.operation >> 1) & 1) { \
|
if(*perform.address & (perform.operation >> 1) & 1) { \
|
||||||
RaiseBusOrAddressError(AddressError, perform); \
|
RaiseBusOrAddressError(AddressError, perform); \
|
||||||
} \
|
} \
|
||||||
PerformBusOperation(announce); \
|
PerformBusOperation(announce, announce_op); \
|
||||||
WaitForDTACK(announce); \
|
WaitForDTACK(announce); \
|
||||||
CompleteAccess(perform);
|
CompleteAccess(perform, perform_op);
|
||||||
|
|
||||||
// Sets up the next data access size and read flags.
|
// Sets up the next data access size and read flags.
|
||||||
#define SetupDataAccess(read_flag, select_flag) \
|
#define SetupDataAccess(read_flag, select_flag) \
|
||||||
@ -348,11 +351,11 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
|
|
||||||
// Performs the access established by SetupDataAccess into val.
|
// Performs the access established by SetupDataAccess into val.
|
||||||
#define Access(val) \
|
#define Access(val) \
|
||||||
AccessPair(val, access_announce, access)
|
AccessPair(val, access_announce, Microcycle::DecodeDynamically, access, Microcycle::DecodeDynamically)
|
||||||
|
|
||||||
// Reads the program (i.e. non-data) word from addr into val.
|
// Reads the program (i.e. non-data) word from addr into val.
|
||||||
#define ReadProgramWord(val) \
|
#define ReadProgramWord(val) \
|
||||||
AccessPair(val, read_program_announce, read_program); \
|
AccessPair(val, read_program_announce, ReadProgramAnnounceOperation, read_program, ReadProgramOperation); \
|
||||||
program_counter_.l += 2;
|
program_counter_.l += 2;
|
||||||
|
|
||||||
// Reads one futher word from the program counter and inserts it into
|
// Reads one futher word from the program counter and inserts it into
|
||||||
@ -377,7 +380,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
// Spin in place, one cycle at a time, until one of DTACK,
|
// Spin in place, one cycle at a time, until one of DTACK,
|
||||||
// BERR or VPA is asserted.
|
// BERR or VPA is asserted.
|
||||||
BeginState(WaitForDTACK):
|
BeginState(WaitForDTACK):
|
||||||
PerformBusOperation(awaiting_dtack);
|
PerformBusOperation(awaiting_dtack, 0);
|
||||||
|
|
||||||
if(dtack_ || berr_ || vpa_) {
|
if(dtack_ || berr_ || vpa_) {
|
||||||
MoveToStateDynamic(post_dtack_state_);
|
MoveToStateDynamic(post_dtack_state_);
|
||||||
@ -601,8 +604,8 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
temporary_address_.l = 0xffff'fff1 | uint32_t(captured_interrupt_level_ << 1);
|
temporary_address_.l = 0xffff'fff1 | uint32_t(captured_interrupt_level_ << 1);
|
||||||
interrupt_cycles[0].address = interrupt_cycles[1].address = &temporary_address_.l;
|
interrupt_cycles[0].address = interrupt_cycles[1].address = &temporary_address_.l;
|
||||||
interrupt_cycles[0].value = interrupt_cycles[1].value = &temporary_value_.low;
|
interrupt_cycles[0].value = interrupt_cycles[1].value = &temporary_value_.low;
|
||||||
PerformBusOperation(interrupt_cycles[0]);
|
PerformBusOperation(interrupt_cycles[0], InterruptCycleOperations[0]);
|
||||||
CompleteAccess(interrupt_cycles[1]); // ni
|
CompleteAccess(interrupt_cycles[1], InterruptCycleOperations[1]); // ni
|
||||||
|
|
||||||
// If VPA is set, autovector.
|
// If VPA is set, autovector.
|
||||||
if(vpa_) {
|
if(vpa_) {
|
||||||
@ -2627,11 +2630,11 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
tas_cycles[3].value = tas_cycles[4].value = &operand_[0].low;
|
tas_cycles[3].value = tas_cycles[4].value = &operand_[0].low;
|
||||||
|
|
||||||
// First two parts: the read.
|
// First two parts: the read.
|
||||||
PerformBusOperation(tas_cycles[0]);
|
PerformBusOperation(tas_cycles[0], TASOperations[0]);
|
||||||
CompleteAccess(tas_cycles[1]);
|
CompleteAccess(tas_cycles[1], TASOperations[1]);
|
||||||
|
|
||||||
// Third part: processing time.
|
// Third part: processing time.
|
||||||
PerformBusOperation(tas_cycles[2]);
|
PerformBusOperation(tas_cycles[2], TASOperations[2]);
|
||||||
|
|
||||||
// Do the actual TAS operation.
|
// Do the actual TAS operation.
|
||||||
status_.overflow_flag = status_.carry_flag = 0;
|
status_.overflow_flag = status_.carry_flag = 0;
|
||||||
@ -2640,8 +2643,8 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
|
|
||||||
// Final parts: write back.
|
// Final parts: write back.
|
||||||
operand_[0].b |= 0x80;
|
operand_[0].b |= 0x80;
|
||||||
PerformBusOperation(tas_cycles[3]);
|
PerformBusOperation(tas_cycles[3], TASOperations[3]);
|
||||||
CompleteAccess(tas_cycles[4]);
|
CompleteAccess(tas_cycles[4], TASOperations[4]);
|
||||||
|
|
||||||
Prefetch();
|
Prefetch();
|
||||||
MoveToStateSpecific(Decode);
|
MoveToStateSpecific(Decode);
|
||||||
@ -2755,7 +2758,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
//
|
//
|
||||||
BeginState(RESET):
|
BeginState(RESET):
|
||||||
IdleBus(2);
|
IdleBus(2);
|
||||||
PerformBusOperation(reset_cycle);
|
PerformBusOperation(reset_cycle, ResetOperation);
|
||||||
Prefetch();
|
Prefetch();
|
||||||
MoveToStateSpecific(Decode);
|
MoveToStateSpecific(Decode);
|
||||||
|
|
||||||
@ -3081,13 +3084,13 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
|||||||
captured_interrupt_level_ = bus_interrupt_level_;
|
captured_interrupt_level_ = bus_interrupt_level_;
|
||||||
|
|
||||||
read_program.value = &prefetch_.high;
|
read_program.value = &prefetch_.high;
|
||||||
bus_handler_.perform_bus_operation(read_program_announce, is_supervisor_);
|
bus_handler_.perform_bus_operation<ReadProgramAnnounceOperation>(read_program_announce, is_supervisor_);
|
||||||
bus_handler_.perform_bus_operation(read_program, is_supervisor_);
|
bus_handler_.perform_bus_operation<ReadProgramOperation>(read_program, is_supervisor_);
|
||||||
program_counter_.l += 2;
|
program_counter_.l += 2;
|
||||||
|
|
||||||
read_program.value = &prefetch_.low;
|
read_program.value = &prefetch_.low;
|
||||||
bus_handler_.perform_bus_operation(read_program_announce, is_supervisor_);
|
bus_handler_.perform_bus_operation<ReadProgramAnnounceOperation>(read_program_announce, is_supervisor_);
|
||||||
bus_handler_.perform_bus_operation(read_program, is_supervisor_);
|
bus_handler_.perform_bus_operation<ReadProgramOperation>(read_program, is_supervisor_);
|
||||||
program_counter_.l += 2;
|
program_counter_.l += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,12 +184,12 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
|
|||||||
Microcycle idle{0};
|
Microcycle idle{0};
|
||||||
|
|
||||||
// Read a program word. All accesses via the program counter are word sized.
|
// Read a program word. All accesses via the program counter are word sized.
|
||||||
Microcycle read_program_announce {
|
static constexpr Microcycle::OperationT
|
||||||
Microcycle::Read | Microcycle::NewAddress | Microcycle::IsProgram
|
ReadProgramAnnounceOperation = Microcycle::Read | Microcycle::NewAddress | Microcycle::IsProgram;
|
||||||
};
|
static constexpr Microcycle::OperationT
|
||||||
Microcycle read_program {
|
ReadProgramOperation = Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectWord | Microcycle::IsProgram;
|
||||||
Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectWord | Microcycle::IsProgram
|
Microcycle read_program_announce { ReadProgramAnnounceOperation };
|
||||||
};
|
Microcycle read_program { ReadProgramOperation };
|
||||||
|
|
||||||
// Read a data word or byte.
|
// Read a data word or byte.
|
||||||
Microcycle access_announce {
|
Microcycle access_announce {
|
||||||
@ -200,21 +200,35 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TAS.
|
// TAS.
|
||||||
|
static constexpr Microcycle::OperationT
|
||||||
|
TASOperations[5] = {
|
||||||
|
Microcycle::Read | Microcycle::NewAddress | Microcycle::IsData,
|
||||||
|
Microcycle::Read | Microcycle::SameAddress | Microcycle::IsData | Microcycle::SelectByte,
|
||||||
|
Microcycle::SameAddress,
|
||||||
|
Microcycle::SameAddress | Microcycle::IsData,
|
||||||
|
Microcycle::SameAddress | Microcycle::IsData | Microcycle::SelectByte,
|
||||||
|
};
|
||||||
Microcycle tas_cycles[5] = {
|
Microcycle tas_cycles[5] = {
|
||||||
{ Microcycle::Read | Microcycle::NewAddress | Microcycle::IsData },
|
{ TASOperations[0] },
|
||||||
{ Microcycle::Read | Microcycle::SameAddress | Microcycle::IsData | Microcycle::SelectByte },
|
{ TASOperations[1] },
|
||||||
{ Microcycle::SameAddress },
|
{ TASOperations[2] },
|
||||||
{ Microcycle::SameAddress | Microcycle::IsData },
|
{ TASOperations[3] },
|
||||||
{ Microcycle::SameAddress | Microcycle::IsData | Microcycle::SelectByte },
|
{ TASOperations[4] },
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reset.
|
// Reset.
|
||||||
Microcycle reset_cycle { Microcycle::Reset, HalfCycles(248) };
|
static constexpr Microcycle::OperationT ResetOperation = Microcycle::Reset;
|
||||||
|
Microcycle reset_cycle { ResetOperation, HalfCycles(248) };
|
||||||
|
|
||||||
// Interrupt acknowledge.
|
// Interrupt acknowledge.
|
||||||
|
static constexpr Microcycle::OperationT
|
||||||
|
InterruptCycleOperations[2] = {
|
||||||
|
Microcycle::InterruptAcknowledge | Microcycle::Read | Microcycle::NewAddress,
|
||||||
|
Microcycle::InterruptAcknowledge | Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectByte
|
||||||
|
};
|
||||||
Microcycle interrupt_cycles[2] = {
|
Microcycle interrupt_cycles[2] = {
|
||||||
{ Microcycle::InterruptAcknowledge | Microcycle::Read | Microcycle::NewAddress },
|
{ InterruptCycleOperations[0] },
|
||||||
{ Microcycle::InterruptAcknowledge | Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectByte },
|
{ InterruptCycleOperations[1] },
|
||||||
};
|
};
|
||||||
|
|
||||||
// Holding spot when awaiting DTACK/etc.
|
// Holding spot when awaiting DTACK/etc.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user