1
0
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:
Thomas Harte 2023-11-27 11:48:34 -05:00
parent ed31cfd80a
commit 5c7f94d2ef
6 changed files with 117 additions and 87 deletions

View File

@ -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());
} }
} }
} }

View File

@ -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);
} }

View File

@ -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;

View File

@ -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;

View File

@ -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;
} }

View File

@ -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.