1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-27 22:30:49 +00:00

Shift operation stuff into its own namespace, making data_select_active a free function.

This commit is contained in:
Thomas Harte 2023-12-21 16:03:53 -05:00
parent 6c4905d961
commit 213dfe037d
8 changed files with 221 additions and 214 deletions

View File

@ -82,11 +82,11 @@ class ConcreteMachine:
// MARK: - MC68000::BusHandler. // MARK: - MC68000::BusHandler.
using Microcycle = CPU::MC68000::Microcycle; using Microcycle = CPU::MC68000::Microcycle;
template <Microcycle::OperationT op> HalfCycles perform_bus_operation(const Microcycle &cycle, int) { template <Microcycle::OperationT op> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
const auto operation = (op != Microcycle::DecodeDynamically) ? op : cycle.operation; const auto operation = (op != CPU::MC68000::Operation::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(operation & Microcycle::NewAddress && *cycle.address < 0x20'0000) { if(operation & CPU::MC68000::Operation::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 {
@ -96,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(operation & Microcycle::Reset) { if(operation & CPU::MC68000::Operation::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(operation & Microcycle::InterruptAcknowledge) { if(operation & CPU::MC68000::Operation::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(!(operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return total_length - cycle.length; if(!(operation & (CPU::MC68000::Operation::NewAddress | CPU::MC68000::Operation::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();
@ -117,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((operation & (Microcycle::SelectByte | Microcycle::SelectWord))) { if((operation & (CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::SelectWord))) {
// Check for various potential chip accesses. // Check for various potential chip accesses.
// Per the manual: // Per the manual:
@ -135,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(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::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);
@ -157,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(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::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 " << (operation & Microcycle::Read ? "read from " : "write to ") << PADHEX(6) << ((*cycle.address)&0xffffff) << " of " << cycle.value16()); LOG("Unmapped " << (operation & CPU::MC68000::Operation::Read ? "read from " : "write to ") << PADHEX(6) << ((*cycle.address)&0xffffff) << " of " << cycle.value16());
} }
} }
} }

View File

@ -845,7 +845,7 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) {
using Microcycle = CPU::MC68000::Microcycle; using Microcycle = CPU::MC68000::Microcycle;
const uint32_t register_address = *cycle.address & ChipsetAddressMask; const uint32_t register_address = *cycle.address & ChipsetAddressMask;
if(cycle.operation & Microcycle::Read) { if(cycle.operation & CPU::MC68000::Operation::Read) {
cycle.set_value16(read(register_address)); cycle.set_value16(read(register_address));
} else { } else {
write(register_address, cycle.value16()); write(register_address, cycle.value16());

View File

@ -114,8 +114,7 @@ class MemoryMap {
const uint32_t register_address = *cycle.address & 0xfe; const uint32_t register_address = *cycle.address & 0xfe;
using Microcycle = CPU::MC68000::Microcycle; if(cycle.operation & CPU::MC68000::Operation::Read) {
if(cycle.operation & Microcycle::Read) {
// Re: Autoconf: // Re: Autoconf:
// //
// "All read registers physically return only the top 4 bits of data, on D31-D28"; // "All read registers physically return only the top 4 bits of data, on D31-D28";

View File

@ -193,13 +193,13 @@ 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) { template <Microcycle::OperationT op> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
const auto operation = (op != Microcycle::DecodeDynamically) ? op : cycle.operation; const auto operation = (op != CPU::MC68000::Operation::DecodeDynamically) ? op : cycle.operation;
// 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(!(operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return HalfCycles(0); if(!(operation & (CPU::MC68000::Operation::NewAddress | CPU::MC68000::Operation::SameAddress))) return HalfCycles(0);
// Grab the address. // Grab the address.
auto address = cycle.host_endian_byte_address(); auto address = cycle.host_endian_byte_address();
@ -219,7 +219,10 @@ 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() || (operation & Microcycle::InterruptAcknowledge)) return HalfCycles(0); if(
!CPU::MC68000::Operation::data_select_active(operation) ||
(operation & CPU::MC68000::Operation::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;
@ -239,7 +242,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
// 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(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::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());
@ -248,7 +251,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
} return delay; } return delay;
case BusDevice::PhaseRead: { case BusDevice::PhaseRead: {
if(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::Read) {
cycle.set_value8_low(phase_ & 7); cycle.set_value8_low(phase_ & 7);
} }
} return delay; } return delay;
@ -258,7 +261,7 @@ 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(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::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());
@ -275,14 +278,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(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::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(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::Read) {
cycle.set_value8_high(scsi_.read(register_address, dma_acknowledge)); cycle.set_value8_high(scsi_.read(register_address, dma_acknowledge));
} }
} }
@ -290,19 +293,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(operation & Microcycle::SelectWord) { if(operation & CPU::MC68000::Operation::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(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::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(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::Read) {
cycle.set_value8_high(read); cycle.set_value8_high(read);
} }
} }
@ -311,13 +314,13 @@ 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(operation & Microcycle::SelectWord) { if(operation & CPU::MC68000::Operation::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(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::Read) {
scc_.write(int(address >> 1), 0xff); scc_.write(int(address >> 1), 0xff);
cycle.value->b = 0xff; cycle.value->b = 0xff;
} else { } else {
@ -352,7 +355,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
} break; } break;
case BusDevice::ROM: { case BusDevice::ROM: {
if(!(operation & Microcycle::Read)) return delay; if(!(operation & CPU::MC68000::Operation::Read)) return delay;
memory_base = rom_; memory_base = rom_;
address &= rom_mask_; address &= rom_mask_;
} break; } break;
@ -546,8 +549,8 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
template <Microcycle::OperationT op> template <Microcycle::OperationT op>
forceinline void fill_unmapped(const Microcycle &cycle) { forceinline void fill_unmapped(const Microcycle &cycle) {
const auto operation = (op != Microcycle::DecodeDynamically) ? op : cycle.operation; const auto operation = (op != CPU::MC68000::Operation::DecodeDynamically) ? op : cycle.operation;
if(!(operation & Microcycle::Read)) return; if(!(operation & CPU::MC68000::Operation::Read)) return;
cycle.set_value16(0xffff); cycle.set_value16(0xffff);
} }

View File

@ -178,7 +178,7 @@ class ConcreteMachine:
// MARK: MC68000::BusHandler // MARK: MC68000::BusHandler
using Microcycle = CPU::MC68000::Microcycle; using Microcycle = CPU::MC68000::Microcycle;
template <Microcycle::OperationT op> HalfCycles perform_bus_operation(const Microcycle &cycle, int is_supervisor) { template <Microcycle::OperationT op> HalfCycles perform_bus_operation(const Microcycle &cycle, int is_supervisor) {
const auto operation = (op != Microcycle::DecodeDynamically) ? op : cycle.operation; const auto operation = (op != CPU::MC68000::Operation::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);
@ -188,15 +188,15 @@ class ConcreteMachine:
advance_time(cycle.length); advance_time(cycle.length);
// Check for assertion of reset. // Check for assertion of reset.
if(operation & Microcycle::Reset) { if(operation & CPU::MC68000::Operation::Reset) {
LOG("Unhandled Reset"); LOG("Unhandled Reset");
} }
// A null cycle leaves nothing else to do. // A null cycle leaves nothing else to do.
if(!(operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return HalfCycles(0); if(!(operation & (CPU::MC68000::Operation::NewAddress | CPU::MC68000::Operation::SameAddress))) return HalfCycles(0);
// An interrupt acknowledge, perhaps? // An interrupt acknowledge, perhaps?
if(operation & Microcycle::InterruptAcknowledge) { if(operation & CPU::MC68000::Operation::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) {
@ -205,7 +205,7 @@ class ConcreteMachine:
mc68000_.set_is_peripheral_address(true); mc68000_.set_is_peripheral_address(true);
return HalfCycles(0); return HalfCycles(0);
} else { } else {
if(operation & Microcycle::SelectByte) { if(operation & CPU::MC68000::Operation::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);
@ -222,7 +222,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(operation & Microcycle::NewAddress) { if(operation & CPU::MC68000::Operation::NewAddress) {
// Bus error test. // Bus error test.
if( if(
// Anything unassigned should generate a bus error. // Anything unassigned should generate a bus error.
@ -257,7 +257,7 @@ class ConcreteMachine:
case BusDevice::ROM: case BusDevice::ROM:
memory = rom_.data(); memory = rom_.data();
if(!(operation & Microcycle::Read)) { if(!(operation & CPU::MC68000::Operation::Read)) {
return delay; return delay;
} }
address -= rom_start_; address -= rom_start_;
@ -271,12 +271,12 @@ 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(operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) { switch(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 = 0xffff; cycle.value->w = 0xffff;
break; break;
case Microcycle::SelectByte | Microcycle::Read: case CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read:
cycle.value->b = 0xff; cycle.value->b = 0xff;
break; break;
} }
@ -313,9 +313,9 @@ class ConcreteMachine:
case 0x8250: case 0x8252: case 0x8254: case 0x8256: case 0x8250: case 0x8252: case 0x8254: case 0x8256:
case 0x8258: case 0x825a: case 0x825c: case 0x825e: case 0x8258: case 0x825a: case 0x825c: case 0x825e:
case 0x8260: case 0x8262: case 0x8260: case 0x8262:
if(!cycle.data_select_active()) return delay; if(!CPU::MC68000::Operation::data_select_active(operation)) return delay;
if(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::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,9 +324,9 @@ class ConcreteMachine:
// DMA. // DMA.
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(!CPU::MC68000::Operation::data_select_active(operation)) return delay;
if(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::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());
@ -356,13 +356,12 @@ class ConcreteMachine:
case 0x88d0: case 0x88d2: case 0x88d4: case 0x88d6: case 0x88d8: case 0x88da: case 0x88dc: case 0x88de: case 0x88d0: case 0x88d2: case 0x88d4: case 0x88d6: case 0x88d8: case 0x88da: case 0x88dc: case 0x88de:
case 0x88e0: case 0x88e2: case 0x88e4: case 0x88e6: case 0x88e8: case 0x88ea: case 0x88ec: case 0x88ee: case 0x88e0: case 0x88e2: case 0x88e4: case 0x88e6: case 0x88e8: case 0x88ea: case 0x88ec: case 0x88ee:
case 0x88f0: case 0x88f2: case 0x88f4: case 0x88f6: case 0x88f8: case 0x88fa: case 0x88fc: case 0x88fe: case 0x88f0: case 0x88f2: case 0x88f4: case 0x88f6: case 0x88f8: case 0x88fa: case 0x88fc: case 0x88fe:
if(!CPU::MC68000::Operation::data_select_active(operation)) return delay;
if(!cycle.data_select_active()) return delay;
advance_time(HalfCycles(2)); advance_time(HalfCycles(2));
update_audio(); update_audio();
if(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::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,9 +379,9 @@ class ConcreteMachine:
case 0xfa28: case 0xfa2a: case 0xfa2c: case 0xfa2e: case 0xfa28: case 0xfa2a: case 0xfa2c: case 0xfa2e:
case 0xfa30: case 0xfa32: case 0xfa34: case 0xfa36: case 0xfa30: case 0xfa32: case 0xfa34: case 0xfa36:
case 0xfa38: case 0xfa3a: case 0xfa3c: case 0xfa3e: case 0xfa38: case 0xfa3a: case 0xfa3c: case 0xfa3e:
if(!cycle.data_select_active()) return delay; if(!CPU::MC68000::Operation::data_select_active(operation)) return delay;
if(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::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());
@ -392,11 +391,11 @@ class ConcreteMachine:
// ACIAs. // ACIAs.
case 0xfc00: case 0xfc02: case 0xfc04: case 0xfc06: { case 0xfc00: case 0xfc02: case 0xfc04: case 0xfc06: {
// Set VPA. // Set VPA.
mc68000_.set_is_peripheral_address(!cycle.data_select_active()); mc68000_.set_is_peripheral_address(!CPU::MC68000::Operation::data_select_active(operation));
if(!cycle.data_select_active()) return delay; if(!CPU::MC68000::Operation::data_select_active(operation)) return delay;
const auto acia_ = (address & 4) ? &midi_acia_ : &keyboard_acia_; const auto acia_ = (address & 4) ? &midi_acia_ : &keyboard_acia_;
if(operation & Microcycle::Read) { if(operation & CPU::MC68000::Operation::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());
@ -410,23 +409,23 @@ class ConcreteMachine:
// //
// In both write cases, immediately reinstall the first eight bytes of RAM from ROM, so that any write to // In both write cases, immediately reinstall the first eight bytes of RAM from ROM, so that any write to
// that area is in effect a no-op. This is cheaper than the conditionality of actually checking. // that area is in effect a no-op. This is cheaper than the conditionality of actually checking.
switch(operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) { switch(operation & (CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read)) {
default: default:
break; break;
case Microcycle::SelectWord | Microcycle::Read: case CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::Read:
cycle.value->w = *reinterpret_cast<uint16_t *>(&memory[address]); cycle.value->w = *reinterpret_cast<uint16_t *>(&memory[address]);
break; break;
case Microcycle::SelectByte | Microcycle::Read: case CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read:
cycle.value->b = memory[address]; cycle.value->b = memory[address];
break; break;
case Microcycle::SelectWord: case CPU::MC68000::Operation::SelectWord:
if(address >= video_range_.low_address && address < video_range_.high_address) if(address >= video_range_.low_address && address < video_range_.high_address)
video_.flush(); video_.flush();
*reinterpret_cast<uint16_t *>(&memory[address]) = cycle.value->w; *reinterpret_cast<uint16_t *>(&memory[address]) = cycle.value->w;
reinstall_rom_vector(); reinstall_rom_vector();
break; break;
case Microcycle::SelectByte: case CPU::MC68000::Operation::SelectByte:
if(address >= video_range_.low_address && address < video_range_.high_address) if(address >= video_range_.low_address && address < video_range_.high_address)
video_.flush(); video_.flush();
memory[address] = cycle.value->b; memory[address] = cycle.value->b;

View File

@ -15,6 +15,66 @@
namespace CPU::MC68000 { namespace CPU::MC68000 {
using OperationT = unsigned int;
namespace Operation {
/// Indicates that the address strobe and exactly one of the data strobes are active; you can determine
/// which by inspecting the low bit of the provided address. The RW line indicates a read.
static constexpr OperationT SelectByte = 1 << 0;
// Maintenance note: this is bit 0 to reduce the cost of getting a host-endian
// bytewise address. The assumption that it is bit 0 is also used for branchless
// selection in a few places. See implementation of host_endian_byte_address(),
// value8_high(), value8_low() and value16().
/// Indicates that the address and both data select strobes are active.
static constexpr OperationT SelectWord = 1 << 1;
/// If set, indicates a read. Otherwise, a write.
static constexpr OperationT Read = 1 << 2;
// Two-bit gap deliberately left here for PermitRead/Write below.
/// A NewAddress cycle is one in which the address strobe is initially low but becomes high;
/// this correlates to states 0 to 5 of a standard read/write cycle.
static constexpr OperationT NewAddress = 1 << 5;
/// A SameAddress cycle is one in which the address strobe is continuously asserted, but neither
/// of the data strobes are.
static constexpr OperationT SameAddress = 1 << 6;
/// A Reset cycle is one in which the RESET output is asserted.
static constexpr OperationT Reset = 1 << 7;
/// Contains the value of line FC0 if it is not implicit via InterruptAcknowledge.
static constexpr OperationT IsData = 1 << 8;
/// Contains the value of line FC1 if it is not implicit via InterruptAcknowledge.
static constexpr OperationT IsProgram = 1 << 9;
/// The interrupt acknowledge cycle is that during which the 68000 seeks to obtain the vector for
/// an interrupt it plans to observe. Noted on a real 68000 by all FCs being set to 1.
static constexpr OperationT InterruptAcknowledge = 1 << 10;
/// Represents the state of the 68000's valid memory address line — indicating whether this microcycle
/// is synchronised with the E clock to satisfy a valid peripheral address request.
static constexpr OperationT IsPeripheral = 1 << 11;
/// Provides the 68000's bus grant line — indicating whether a bus request has been acknowledged.
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;
/*! @returns true if any data select line is active; @c false otherwise. */
constexpr bool data_select_active(OperationT operation) {
return bool(operation & (SelectWord | SelectByte | InterruptAcknowledge));
}
};
/*! /*!
A microcycle is an atomic unit of 68000 bus activity it is a single item large enough A microcycle is an atomic unit of 68000 bus activity it is a single item large enough
fully to specify a sequence of bus events that occur without any possible interruption. fully to specify a sequence of bus events that occur without any possible interruption.
@ -39,56 +99,7 @@ namespace CPU::MC68000 {
avoid the runtime cost of actual DTack emulation. But such as the bus allows.) avoid the runtime cost of actual DTack emulation. But such as the bus allows.)
*/ */
struct Microcycle { struct Microcycle {
using OperationT = unsigned int; using OperationT = OperationT;
/// Indicates that the address strobe and exactly one of the data strobes are active; you can determine
/// which by inspecting the low bit of the provided address. The RW line indicates a read.
static constexpr OperationT SelectByte = 1 << 0;
// Maintenance note: this is bit 0 to reduce the cost of getting a host-endian
// bytewise address. The assumption that it is bit 0 is also used for branchless
// selection in a few places. See implementation of host_endian_byte_address(),
// value8_high(), value8_low() and value16().
/// Indicates that the address and both data select strobes are active.
static constexpr OperationT SelectWord = 1 << 1;
/// If set, indicates a read. Otherwise, a write.
static constexpr OperationT Read = 1 << 2;
// Two-bit gap deliberately left here for PermitRead/Write below.
/// A NewAddress cycle is one in which the address strobe is initially low but becomes high;
/// this correlates to states 0 to 5 of a standard read/write cycle.
static constexpr OperationT NewAddress = 1 << 5;
/// A SameAddress cycle is one in which the address strobe is continuously asserted, but neither
/// of the data strobes are.
static constexpr OperationT SameAddress = 1 << 6;
/// A Reset cycle is one in which the RESET output is asserted.
static constexpr OperationT Reset = 1 << 7;
/// Contains the value of line FC0 if it is not implicit via InterruptAcknowledge.
static constexpr OperationT IsData = 1 << 8;
/// Contains the value of line FC1 if it is not implicit via InterruptAcknowledge.
static constexpr OperationT IsProgram = 1 << 9;
/// The interrupt acknowledge cycle is that during which the 68000 seeks to obtain the vector for
/// an interrupt it plans to observe. Noted on a real 68000 by all FCs being set to 1.
static constexpr OperationT InterruptAcknowledge = 1 << 10;
/// Represents the state of the 68000's valid memory address line — indicating whether this microcycle
/// is synchronised with the E clock to satisfy a valid peripheral address request.
static constexpr OperationT IsPeripheral = 1 << 11;
/// Provides the 68000's bus grant line — indicating whether a bus request has been acknowledged.
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.
@ -139,11 +150,6 @@ struct Microcycle {
// Various inspectors. // Various inspectors.
/*! @returns true if any data select line is active; @c false otherwise. */
forceinline bool data_select_active() const {
return bool(operation & (SelectWord | SelectByte | InterruptAcknowledge));
}
/*! /*!
@returns 0 if this byte access wants the low part of a 16-bit word; 8 if it wants the high part, @returns 0 if this byte access wants the low part of a 16-bit word; 8 if it wants the high part,
i.e. take a word quantity and shift it right by this amount to get the quantity being i.e. take a word quantity and shift it right by this amount to get the quantity being
@ -184,14 +190,14 @@ struct Microcycle {
@returns non-zero if the 68000 LDS is asserted; zero otherwise. @returns non-zero if the 68000 LDS is asserted; zero otherwise.
*/ */
forceinline int lower_data_select() const { forceinline int lower_data_select() const {
return (operation & SelectByte & *address) | (operation & SelectWord); return (operation & Operation::SelectByte & *address) | (operation & Operation::SelectWord);
} }
/*! /*!
@returns non-zero if the 68000 UDS is asserted; zero otherwise. @returns non-zero if the 68000 UDS is asserted; zero otherwise.
*/ */
forceinline int upper_data_select() const { forceinline int upper_data_select() const {
return (operation & SelectByte & ~*address) | (operation & SelectWord); return (operation & Operation::SelectByte & ~*address) | (operation & Operation::SelectWord);
} }
/*! /*!
@ -228,7 +234,7 @@ struct Microcycle {
#if TARGET_RT_BIG_ENDIAN #if TARGET_RT_BIG_ENDIAN
return *address & 0xffffff; return *address & 0xffffff;
#else #else
return (*address ^ (operation & SelectByte)) & 0xffffff; return (*address ^ (operation & Operation::SelectByte)) & 0xffffff;
#endif #endif
} }
@ -239,7 +245,7 @@ struct Microcycle {
*/ */
forceinline uint16_t value16() const { forceinline uint16_t value16() const {
const uint16_t values[] = { value->w, uint16_t((value->b << 8) | value->b) }; const uint16_t values[] = { value->w, uint16_t((value->b << 8) | value->b) };
return values[operation & SelectByte]; return values[operation & Operation::SelectByte];
} }
/*! /*!
@ -247,7 +253,7 @@ struct Microcycle {
*/ */
forceinline uint8_t value8_high() const { forceinline uint8_t value8_high() const {
const uint8_t values[] = { uint8_t(value->w >> 8), value->b}; const uint8_t values[] = { uint8_t(value->w >> 8), value->b};
return values[operation & SelectByte]; return values[operation & Operation::SelectByte];
} }
/*! /*!
@ -262,8 +268,8 @@ struct Microcycle {
currently being read. Assumes this is a read cycle. currently being read. Assumes this is a read cycle.
*/ */
forceinline void set_value16(uint16_t v) const { forceinline void set_value16(uint16_t v) const {
assert(operation & Microcycle::Read); assert(operation & Operation::Read);
if(operation & Microcycle::SelectWord) { if(operation & Operation::SelectWord) {
value->w = v; value->w = v;
} else { } else {
value->b = uint8_t(v >> byte_shift()); value->b = uint8_t(v >> byte_shift());
@ -274,8 +280,8 @@ struct Microcycle {
Equivalent to set_value16((v << 8) | 0x00ff). Equivalent to set_value16((v << 8) | 0x00ff).
*/ */
forceinline void set_value8_high(uint8_t v) const { forceinline void set_value8_high(uint8_t v) const {
assert(operation & Microcycle::Read); assert(operation & Operation::Read);
if(operation & Microcycle::SelectWord) { if(operation & Operation::SelectWord) {
value->w = uint16_t(0x00ff | (v << 8)); value->w = uint16_t(0x00ff | (v << 8));
} else { } else {
value->b = uint8_t(v | byte_mask()); value->b = uint8_t(v | byte_mask());
@ -286,8 +292,8 @@ struct Microcycle {
Equivalent to set_value16(v | 0xff00). Equivalent to set_value16(v | 0xff00).
*/ */
forceinline void set_value8_low(uint8_t v) const { forceinline void set_value8_low(uint8_t v) const {
assert(operation & Microcycle::Read); assert(operation & Operation::Read);
if(operation & Microcycle::SelectWord) { if(operation & Operation::SelectWord) {
value->w = 0xff00 | v; value->w = 0xff00 | v;
} else { } else {
value->b = uint8_t(v | untouched_byte_mask()); value->b = uint8_t(v | untouched_byte_mask());
@ -309,26 +315,26 @@ struct Microcycle {
* if this is a write, does the converse of a read. * if this is a write, does the converse of a read.
*/ */
forceinline void apply(uint8_t *target, OperationT read_write_mask = PermitRead | PermitWrite) const { forceinline void apply(uint8_t *target, OperationT read_write_mask = PermitRead | PermitWrite) const {
assert( (operation & (SelectWord | SelectByte)) != (SelectWord | SelectByte)); assert( (operation & (Operation::SelectWord | Operation::SelectByte)) != (Operation::SelectWord | Operation::SelectByte));
switch((operation | read_write_mask) & (SelectWord | SelectByte | Read | PermitRead | PermitWrite)) { switch((operation | read_write_mask) & (Operation::SelectWord | Operation::SelectByte | Operation::Read | PermitRead | PermitWrite)) {
default: default:
break; break;
case SelectWord | Read | PermitRead: case Operation::SelectWord | Operation::Read | PermitRead:
case SelectWord | Read | PermitRead | PermitWrite: case Operation::SelectWord | Operation::Read | PermitRead | PermitWrite:
value->w = *reinterpret_cast<uint16_t *>(target); value->w = *reinterpret_cast<uint16_t *>(target);
break; break;
case SelectByte | Read | PermitRead: case Operation::SelectByte | Operation::Read | PermitRead:
case SelectByte | Read | PermitRead | PermitWrite: case Operation::SelectByte | Operation::Read | PermitRead | PermitWrite:
value->b = *target; value->b = *target;
break; break;
case SelectWord | PermitWrite: case Operation::SelectWord | PermitWrite:
case SelectWord | PermitWrite | PermitRead: case Operation::SelectWord | PermitWrite | PermitRead:
*reinterpret_cast<uint16_t *>(target) = value->w; *reinterpret_cast<uint16_t *>(target) = value->w;
break; break;
case SelectByte | PermitWrite: case Operation::SelectByte | PermitWrite:
case SelectByte | PermitWrite | PermitRead: case Operation::SelectByte | PermitWrite | PermitRead:
*target = value->b; *target = value->b;
break; break;
} }

View File

@ -342,8 +342,8 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
// 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) \
access_announce.operation = Microcycle::NewAddress | Microcycle::IsData | (read_flag); \ access_announce.operation = Operation::NewAddress | Operation::IsData | (read_flag); \
access.operation = Microcycle::SameAddress | Microcycle::IsData | (read_flag) | (select_flag); access.operation = Operation::SameAddress | Operation::IsData | (read_flag) | (select_flag);
// Sets the address source for the next data access. // Sets the address source for the next data access.
#define SetDataAddress(addr) \ #define SetDataAddress(addr) \
@ -351,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, Microcycle::DecodeDynamically, access, Microcycle::DecodeDynamically) AccessPair(val, access_announce, Operation::DecodeDynamically, access, Operation::DecodeDynamically)
// Performs the access established by SetupDataAccess into val. // Performs the access established by SetupDataAccess into val.
#define AccessOp(val, read_flag, select_flag) \ #define AccessOp(val, read_flag, select_flag) \
AccessPair(val, access_announce, Microcycle::NewAddress | Microcycle::IsData | (read_flag), access, Microcycle::SameAddress | Microcycle::IsData | (read_flag) | (select_flag)) AccessPair(val, access_announce, Operation::NewAddress | Operation::IsData | (read_flag), access, Operation::SameAddress | Operation::IsData | (read_flag) | (select_flag))
// 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) \
@ -424,20 +424,20 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
should_trace_ = 0; should_trace_ = 0;
did_update_status(); did_update_status();
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); SetupDataAccess(Operation::Read, Operation::SelectWord);
SetDataAddress(temporary_address_.l); SetDataAddress(temporary_address_.l);
temporary_address_.l = 0; temporary_address_.l = 0;
AccessOp(registers_[15].high, Microcycle::Read, Microcycle::SelectWord); // nF AccessOp(registers_[15].high, Operation::Read, Operation::SelectWord); // nF
temporary_address_.l += 2; temporary_address_.l += 2;
AccessOp(registers_[15].low, Microcycle::Read, Microcycle::SelectWord); // nf AccessOp(registers_[15].low, Operation::Read, Operation::SelectWord); // nf
temporary_address_.l += 2; temporary_address_.l += 2;
AccessOp(program_counter_.high, Microcycle::Read, Microcycle::SelectWord); // nV AccessOp(program_counter_.high, Operation::Read, Operation::SelectWord); // nV
temporary_address_.l += 2; temporary_address_.l += 2;
AccessOp(program_counter_.low, Microcycle::Read, Microcycle::SelectWord); // nv AccessOp(program_counter_.low, Operation::Read, Operation::SelectWord); // nv
Prefetch(); // np Prefetch(); // np
IdleBus(1); // n IdleBus(1); // n
@ -451,30 +451,30 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
should_trace_ = 0; should_trace_ = 0;
did_update_status(); did_update_status();
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
SetDataAddress(registers_[15].l); SetDataAddress(registers_[15].l);
// Push status and current program counter. // Push status and current program counter.
// Write order is wacky here, but I think it's correct. // Write order is wacky here, but I think it's correct.
registers_[15].l -= 2; registers_[15].l -= 2;
AccessOp(instruction_address_.low, 0, Microcycle::SelectWord); // ns AccessOp(instruction_address_.low, 0, Operation::SelectWord); // ns
registers_[15].l -= 4; registers_[15].l -= 4;
AccessOp(captured_status_, 0, Microcycle::SelectWord); // ns AccessOp(captured_status_, 0, Operation::SelectWord); // ns
registers_[15].l += 2; registers_[15].l += 2;
AccessOp(instruction_address_.high, 0, Microcycle::SelectWord); // nS AccessOp(instruction_address_.high, 0, Operation::SelectWord); // nS
registers_[15].l -= 2; registers_[15].l -= 2;
// Grab new program counter. // Grab new program counter.
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); SetupDataAccess(Operation::Read, Operation::SelectWord);
SetDataAddress(temporary_address_.l); SetDataAddress(temporary_address_.l);
temporary_address_.l = uint32_t(exception_vector_ << 2); temporary_address_.l = uint32_t(exception_vector_ << 2);
AccessOp(program_counter_.high, Microcycle::Read, Microcycle::SelectWord); // nV AccessOp(program_counter_.high, Operation::Read, Operation::SelectWord); // nV
temporary_address_.l += 2; temporary_address_.l += 2;
AccessOp(program_counter_.low, Microcycle::Read, Microcycle::SelectWord); // nv AccessOp(program_counter_.low, Operation::Read, Operation::SelectWord); // nv
// Populate the prefetch queue. // Populate the prefetch queue.
Prefetch(); // np Prefetch(); // np
@ -523,24 +523,24 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
should_trace_ = 0; should_trace_ = 0;
did_update_status(); did_update_status();
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
SetDataAddress(registers_[15].l); SetDataAddress(registers_[15].l);
// Guess: the written program counter is adjusted to discount the prefetch queue. // Guess: the written program counter is adjusted to discount the prefetch queue.
// COMPLETE GUESS. // COMPLETE GUESS.
temporary_address_.l = program_counter_.l - 4; temporary_address_.l = program_counter_.l - 4;
registers_[15].l -= 2; registers_[15].l -= 2;
AccessOp(temporary_address_.low, 0, Microcycle::SelectWord); // ns [pc.l] AccessOp(temporary_address_.low, 0, Operation::SelectWord); // ns [pc.l]
registers_[15].l -= 4; registers_[15].l -= 4;
AccessOp(captured_status_, 0, Microcycle::SelectWord); // ns [sr] AccessOp(captured_status_, 0, Operation::SelectWord); // ns [sr]
registers_[15].l += 2; registers_[15].l += 2;
AccessOp(temporary_address_.high, 0, Microcycle::SelectWord); // nS [pc.h] AccessOp(temporary_address_.high, 0, Operation::SelectWord); // nS [pc.h]
registers_[15].l -= 4; registers_[15].l -= 4;
temporary_value_.w = opcode_; temporary_value_.w = opcode_;
AccessOp(temporary_value_.low, 0, Microcycle::SelectWord); // ns [instruction register] AccessOp(temporary_value_.low, 0, Operation::SelectWord); // ns [instruction register]
// Construct the function code; which is: // Construct the function code; which is:
// //
@ -554,31 +554,31 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
// captured status register I guess maybe it is just duplicative. // captured status register I guess maybe it is just duplicative.
temporary_value_.w = temporary_value_.w =
(temporary_value_.w & ~31) | (temporary_value_.w & ~31) |
((bus_error_.operation & Microcycle::Read) ? 0x10 : 0x00) | ((bus_error_.operation & Operation::Read) ? 0x10 : 0x00) |
((bus_error_.operation & Microcycle::IsProgram) ? 0x08 : 0x00) | ((bus_error_.operation & Operation::IsProgram) ? 0x08 : 0x00) |
((bus_error_.operation & Microcycle::IsProgram) ? 0x02 : 0x01) | ((bus_error_.operation & Operation::IsProgram) ? 0x02 : 0x01) |
((captured_status_.w & InstructionSet::M68k::ConditionCode::Supervisor) ? 0x04 : 0x00); ((captured_status_.w & InstructionSet::M68k::ConditionCode::Supervisor) ? 0x04 : 0x00);
temporary_address_.l = *bus_error_.address; temporary_address_.l = *bus_error_.address;
registers_[15].l -= 2; registers_[15].l -= 2;
AccessOp(temporary_address_.low, 0, Microcycle::SelectWord); // ns [error address.l] AccessOp(temporary_address_.low, 0, Operation::SelectWord); // ns [error address.l]
registers_[15].l -= 4; registers_[15].l -= 4;
AccessOp(temporary_value_.low, 0, Microcycle::SelectWord); // ns [function code] AccessOp(temporary_value_.low, 0, Operation::SelectWord); // ns [function code]
registers_[15].l += 2; registers_[15].l += 2;
AccessOp(temporary_address_.high, 0, Microcycle::SelectWord); // nS [error address.h] AccessOp(temporary_address_.high, 0, Operation::SelectWord); // nS [error address.h]
registers_[15].l -= 2; registers_[15].l -= 2;
// Grab new program counter. // Grab new program counter.
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); SetupDataAccess(Operation::Read, Operation::SelectWord);
SetDataAddress(temporary_address_.l); SetDataAddress(temporary_address_.l);
temporary_address_.l = uint32_t(exception_vector_ << 2); temporary_address_.l = uint32_t(exception_vector_ << 2);
AccessOp(program_counter_.high, Microcycle::Read, Microcycle::SelectWord); // nV AccessOp(program_counter_.high, Operation::Read, Operation::SelectWord); // nV
temporary_address_.l += 2; temporary_address_.l += 2;
AccessOp(program_counter_.low, Microcycle::Read, Microcycle::SelectWord); // nv AccessOp(program_counter_.low, Operation::Read, Operation::SelectWord); // nv
// Populate the prefetch queue. // Populate the prefetch queue.
Prefetch(); // np Prefetch(); // np
@ -597,12 +597,12 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
did_update_status(); did_update_status();
// Prepare for stack activity. // Prepare for stack activity.
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
SetDataAddress(registers_[15].l); SetDataAddress(registers_[15].l);
// Push low part of program counter. // Push low part of program counter.
registers_[15].l -= 2; registers_[15].l -= 2;
AccessOp(instruction_address_.low, 0, Microcycle::SelectWord); // ns AccessOp(instruction_address_.low, 0, Operation::SelectWord); // ns
// Do the interrupt cycle, to obtain a vector. // Do the interrupt cycle, to obtain a vector.
temporary_address_.l = 0xffff'fff1 | uint32_t(captured_interrupt_level_ << 1); temporary_address_.l = 0xffff'fff1 | uint32_t(captured_interrupt_level_ << 1);
@ -628,21 +628,21 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
SetDataAddress(registers_[15].l); SetDataAddress(registers_[15].l);
registers_[15].l -= 4; registers_[15].l -= 4;
AccessOp(captured_status_, 0, Microcycle::SelectWord); // ns AccessOp(captured_status_, 0, Operation::SelectWord); // ns
registers_[15].l += 2; registers_[15].l += 2;
AccessOp(instruction_address_.high, 0, Microcycle::SelectWord); // nS AccessOp(instruction_address_.high, 0, Operation::SelectWord); // nS
registers_[15].l -= 2; registers_[15].l -= 2;
// Grab new program counter. // Grab new program counter.
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); SetupDataAccess(Operation::Read, Operation::SelectWord);
SetDataAddress(temporary_address_.l); SetDataAddress(temporary_address_.l);
temporary_address_.l = uint32_t(temporary_value_.b << 2); temporary_address_.l = uint32_t(temporary_value_.b << 2);
AccessOp(program_counter_.high, Microcycle::Read, Microcycle::SelectWord); // nV AccessOp(program_counter_.high, Operation::Read, Operation::SelectWord); // nV
temporary_address_.l += 2; temporary_address_.l += 2;
AccessOp(program_counter_.low, Microcycle::Read, Microcycle::SelectWord); // nv AccessOp(program_counter_.low, Operation::Read, Operation::SelectWord); // nv
// Populate the prefetch queue. // Populate the prefetch queue.
Prefetch(); // np Prefetch(); // np
@ -707,13 +707,13 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
y; \ y; \
\ \
if constexpr (InstructionSet::M68k::operand_size<InstructionSet::M68k::Operation::x>() == InstructionSet::M68k::DataSize::LongWord) { \ if constexpr (InstructionSet::M68k::operand_size<InstructionSet::M68k::Operation::x>() == InstructionSet::M68k::DataSize::LongWord) { \
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); \ SetupDataAccess(Operation::Read, Operation::SelectWord); \
MoveToStateSpecific(FetchOperand_l); \ MoveToStateSpecific(FetchOperand_l); \
} else { \ } else { \
if constexpr (InstructionSet::M68k::operand_size<InstructionSet::M68k::Operation::x>() == InstructionSet::M68k::DataSize::Byte) { \ if constexpr (InstructionSet::M68k::operand_size<InstructionSet::M68k::Operation::x>() == InstructionSet::M68k::DataSize::Byte) { \
SetupDataAccess(Microcycle::Read, Microcycle::SelectByte); \ SetupDataAccess(Operation::Read, Operation::SelectByte); \
} else { \ } else { \
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); \ SetupDataAccess(Operation::Read, Operation::SelectWord); \
} \ } \
MoveToStateSpecific(FetchOperand_bw); \ MoveToStateSpecific(FetchOperand_bw); \
} }
@ -823,10 +823,10 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
CASE(ABCD) CASE(ABCD)
if(instruction_.mode(0) == Mode::DataRegisterDirect) { if(instruction_.mode(0) == Mode::DataRegisterDirect) {
perform_state_ = Perform_np_n; perform_state_ = Perform_np_n;
SetupDataAccess(Microcycle::Read, Microcycle::SelectByte); SetupDataAccess(Operation::Read, Operation::SelectByte);
MoveToStateSpecific(FetchOperand_bw); MoveToStateSpecific(FetchOperand_bw);
} else { } else {
select_flag_ = Microcycle::SelectByte; select_flag_ = Operation::SelectByte;
MoveToStateSpecific(TwoOp_Predec_bw); MoveToStateSpecific(TwoOp_Predec_bw);
} }
@ -876,7 +876,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
if(instruction_.mode(0) == Mode::DataRegisterDirect) { if(instruction_.mode(0) == Mode::DataRegisterDirect) {
perform_state_ = Perform_np; perform_state_ = Perform_np;
} else { } else {
select_flag_ = Microcycle::SelectByte; select_flag_ = Operation::SelectByte;
MoveToStateSpecific(TwoOp_Predec_bw); MoveToStateSpecific(TwoOp_Predec_bw);
} }
}) })
@ -884,7 +884,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
if(instruction_.mode(0) == Mode::DataRegisterDirect) { if(instruction_.mode(0) == Mode::DataRegisterDirect) {
perform_state_ = Perform_np; perform_state_ = Perform_np;
} else { } else {
select_flag_ = Microcycle::SelectWord; select_flag_ = Operation::SelectWord;
MoveToStateSpecific(TwoOp_Predec_bw); MoveToStateSpecific(TwoOp_Predec_bw);
} }
}) })
@ -1199,7 +1199,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
effective_address_[1] = registers_[8 + instruction_.reg(1)]; effective_address_[1] = registers_[8 + instruction_.reg(1)];
SetDataAddress(effective_address_[1].l); SetDataAddress(effective_address_[1].l);
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
Access(operand_[next_operand_].high); // nW Access(operand_[next_operand_].high); // nW
effective_address_[1].l += 2; effective_address_[1].l += 2;
@ -1258,7 +1258,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
BeginStateMode(MOVE_l, AddressRegisterIndirectWithPostincrement): BeginStateMode(MOVE_l, AddressRegisterIndirectWithPostincrement):
SetDataAddress(registers_[8 + instruction_.reg(next_operand_)].l); SetDataAddress(registers_[8 + instruction_.reg(next_operand_)].l);
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
Access(operand_[next_operand_].high); // nW Access(operand_[next_operand_].high); // nW
registers_[8 + instruction_.reg(next_operand_)].l += 2; registers_[8 + instruction_.reg(next_operand_)].l += 2;
@ -1311,7 +1311,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
BeginStateMode(MOVE_l, AddressRegisterIndirectWithPredecrement): BeginStateMode(MOVE_l, AddressRegisterIndirectWithPredecrement):
SetDataAddress(registers_[8 + instruction_.reg(1)].l); SetDataAddress(registers_[8 + instruction_.reg(1)].l);
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
Prefetch(); // np Prefetch(); // np
@ -1374,7 +1374,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
uint32_t(int16_t(prefetch_.w)); uint32_t(int16_t(prefetch_.w));
SetDataAddress(effective_address_[1].l); SetDataAddress(effective_address_[1].l);
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
Prefetch(); // np Prefetch(); // np
Access(operand_[next_operand_].high); // nW Access(operand_[next_operand_].high); // nW
@ -1450,7 +1450,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
uint32_t(int16_t(prefetch_.w)); uint32_t(int16_t(prefetch_.w));
SetDataAddress(effective_address_[1].l); SetDataAddress(effective_address_[1].l);
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
Prefetch(); // np Prefetch(); // np
Access(operand_[next_operand_].high); // nW Access(operand_[next_operand_].high); // nW
@ -1529,7 +1529,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
effective_address_[1].l = d8Xn(registers_[8 + instruction_.reg(1)].l); effective_address_[1].l = d8Xn(registers_[8 + instruction_.reg(1)].l);
SetDataAddress(effective_address_[1].l); SetDataAddress(effective_address_[1].l);
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
IdleBus(1); // n IdleBus(1); // n
Prefetch(); // np Prefetch(); // np
@ -1607,7 +1607,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
effective_address_[1].l = d8Xn(program_counter_.l - 2); effective_address_[1].l = d8Xn(program_counter_.l - 2);
SetDataAddress(effective_address_[1].l); SetDataAddress(effective_address_[1].l);
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
IdleBus(1); // n IdleBus(1); // n
Prefetch(); // np Prefetch(); // np
@ -1685,7 +1685,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
effective_address_[1].l = uint32_t(int16_t(prefetch_.w)); effective_address_[1].l = uint32_t(int16_t(prefetch_.w));
SetDataAddress(effective_address_[1].l); SetDataAddress(effective_address_[1].l);
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
Prefetch(); // np Prefetch(); // np
Access(operand_[next_operand_].high); // nW Access(operand_[next_operand_].high); // nW
@ -1767,7 +1767,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
effective_address_[1].l = prefetch_.l; effective_address_[1].l = prefetch_.l;
SetDataAddress(effective_address_[1].l); SetDataAddress(effective_address_[1].l);
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
switch(instruction_.mode(0)) { switch(instruction_.mode(0)) {
case Mode::AddressRegisterDirect: case Mode::AddressRegisterDirect:
@ -1867,15 +1867,15 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
BeginState(StoreOperand): BeginState(StoreOperand):
switch(instruction_.operand_size()) { switch(instruction_.operand_size()) {
case InstructionSet::M68k::DataSize::LongWord: case InstructionSet::M68k::DataSize::LongWord:
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
MoveToStateSpecific(StoreOperand_l); MoveToStateSpecific(StoreOperand_l);
case InstructionSet::M68k::DataSize::Word: case InstructionSet::M68k::DataSize::Word:
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
MoveToStateSpecific(StoreOperand_bw); MoveToStateSpecific(StoreOperand_bw);
case InstructionSet::M68k::DataSize::Byte: case InstructionSet::M68k::DataSize::Byte:
SetupDataAccess(0, Microcycle::SelectByte); SetupDataAccess(0, Operation::SelectByte);
MoveToStateSpecific(StoreOperand_bw); MoveToStateSpecific(StoreOperand_bw);
} }
@ -1919,7 +1919,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
MoveToNextOperand(StoreOperand_l); MoveToNextOperand(StoreOperand_l);
} }
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
SetDataAddress(effective_address_[next_operand_].l); SetDataAddress(effective_address_[next_operand_].l);
Access(operand_[next_operand_].low); // nw Access(operand_[next_operand_].low); // nw
@ -1995,7 +1995,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
BeginState(TwoOp_Predec_bw): BeginState(TwoOp_Predec_bw):
IdleBus(1); // n IdleBus(1); // n
SetupDataAccess(Microcycle::Read, select_flag_); SetupDataAccess(Operation::Read, select_flag_);
SetDataAddress(registers_[8 + instruction_.reg(0)].l); SetDataAddress(registers_[8 + instruction_.reg(0)].l);
registers_[8 + instruction_.reg(0)].l -= address_increments[int(instruction_.operand_size())][instruction_.reg(0)]; registers_[8 + instruction_.reg(0)].l -= address_increments[int(instruction_.operand_size())][instruction_.reg(0)];
@ -2016,7 +2016,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
BeginState(TwoOp_Predec_l): BeginState(TwoOp_Predec_l):
IdleBus(1); // n IdleBus(1); // n
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); SetupDataAccess(Operation::Read, Operation::SelectWord);
SetDataAddress(registers_[8 + instruction_.reg(0)].l); SetDataAddress(registers_[8 + instruction_.reg(0)].l);
registers_[8 + instruction_.reg(0)].l -= 2; registers_[8 + instruction_.reg(0)].l -= 2;
@ -2034,7 +2034,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
PerformDynamic(); PerformDynamic();
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
registers_[8 + instruction_.reg(1)].l += 2; registers_[8 + instruction_.reg(1)].l += 2;
Access(operand_[1].low); // nw Access(operand_[1].low); // nw
@ -2160,7 +2160,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
#define Push(x) \ #define Push(x) \
SetupDataAccess(0, Microcycle::SelectWord); \ SetupDataAccess(0, Operation::SelectWord); \
SetDataAddress(registers_[15].l); \ SetDataAddress(registers_[15].l); \
registers_[15].l -= 4; \ registers_[15].l -= 4; \
Access(x.high); \ Access(x.high); \
@ -2169,7 +2169,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
registers_[15].l -= 2; registers_[15].l -= 2;
#define Pop(x) \ #define Pop(x) \
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); \ SetupDataAccess(Operation::Read, Operation::SelectWord); \
SetDataAddress(registers_[15].l); \ SetDataAddress(registers_[15].l); \
Access(x.high); \ Access(x.high); \
registers_[15].l += 2; \ registers_[15].l += 2; \
@ -2246,7 +2246,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
BeginState(MOVEPtoM_l): BeginState(MOVEPtoM_l):
temporary_address_.l = registers_[8 + instruction_.reg(1)].l + uint32_t(int16_t(prefetch_.w)); temporary_address_.l = registers_[8 + instruction_.reg(1)].l + uint32_t(int16_t(prefetch_.w));
SetDataAddress(temporary_address_.l); SetDataAddress(temporary_address_.l);
SetupDataAccess(0, Microcycle::SelectByte); SetupDataAccess(0, Operation::SelectByte);
Prefetch(); // np Prefetch(); // np
@ -2271,7 +2271,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
BeginState(MOVEPtoM_w): BeginState(MOVEPtoM_w):
temporary_address_.l = registers_[8 + instruction_.reg(1)].l + uint32_t(int16_t(prefetch_.w)); temporary_address_.l = registers_[8 + instruction_.reg(1)].l + uint32_t(int16_t(prefetch_.w));
SetDataAddress(temporary_address_.l); SetDataAddress(temporary_address_.l);
SetupDataAccess(0, Microcycle::SelectByte); SetupDataAccess(0, Operation::SelectByte);
Prefetch(); // np Prefetch(); // np
@ -2288,7 +2288,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
BeginState(MOVEPtoR_l): BeginState(MOVEPtoR_l):
temporary_address_.l = registers_[8 + instruction_.reg(0)].l + uint32_t(int16_t(prefetch_.w)); temporary_address_.l = registers_[8 + instruction_.reg(0)].l + uint32_t(int16_t(prefetch_.w));
SetDataAddress(temporary_address_.l); SetDataAddress(temporary_address_.l);
SetupDataAccess(Microcycle::Read, Microcycle::SelectByte); SetupDataAccess(Operation::Read, Operation::SelectByte);
Prefetch(); // np Prefetch(); // np
@ -2313,7 +2313,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
BeginState(MOVEPtoR_w): BeginState(MOVEPtoR_w):
temporary_address_.l = registers_[8 + instruction_.reg(0)].l + uint32_t(int16_t(prefetch_.w)); temporary_address_.l = registers_[8 + instruction_.reg(0)].l + uint32_t(int16_t(prefetch_.w));
SetDataAddress(temporary_address_.l); SetDataAddress(temporary_address_.l);
SetupDataAccess(Microcycle::Read, Microcycle::SelectByte); SetupDataAccess(Operation::Read, Operation::SelectByte);
Prefetch(); // np Prefetch(); // np
@ -2353,7 +2353,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
register_index_ = 0; register_index_ = 0;
SetDataAddress(effective_address_[1].l); SetDataAddress(effective_address_[1].l);
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); SetupDataAccess(Operation::Read, Operation::SelectWord);
switch(instruction_.mode(1)) { switch(instruction_.mode(1)) {
case Mode::AddressRegisterIndirectWithIndex8bitDisplacement: case Mode::AddressRegisterIndirectWithIndex8bitDisplacement:
@ -2426,7 +2426,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
BeginState(MOVEMtoM): BeginState(MOVEMtoM):
next_operand_ = 1; next_operand_ = 1;
SetDataAddress(effective_address_[1].l); SetDataAddress(effective_address_[1].l);
SetupDataAccess(0, Microcycle::SelectWord); SetupDataAccess(0, Operation::SelectWord);
register_index_ = 0; register_index_ = 0;
post_ea_state_ = post_ea_state_ =
@ -2670,7 +2670,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
// RTR, RTS, RTE // RTR, RTS, RTE
// //
BeginState(RTS): BeginState(RTS):
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); SetupDataAccess(Operation::Read, Operation::SelectWord);
SetDataAddress(registers_[15].l); SetDataAddress(registers_[15].l);
Access(program_counter_.high); Access(program_counter_.high);
@ -2688,7 +2688,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
// being the converse of the write order. // being the converse of the write order.
BeginState(RTE): BeginState(RTE):
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); SetupDataAccess(Operation::Read, Operation::SelectWord);
SetDataAddress(registers_[15].l); SetDataAddress(registers_[15].l);
registers_[15].l += 2; registers_[15].l += 2;
@ -2709,7 +2709,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
MoveToStateSpecific(Decode); MoveToStateSpecific(Decode);
BeginState(RTR): BeginState(RTR):
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); SetupDataAccess(Operation::Read, Operation::SelectWord);
SetDataAddress(registers_[15].l); SetDataAddress(registers_[15].l);
registers_[15].l += 2; registers_[15].l += 2;

View File

@ -185,28 +185,28 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
// 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.
static constexpr Microcycle::OperationT static constexpr Microcycle::OperationT
ReadProgramAnnounceOperation = Microcycle::Read | Microcycle::NewAddress | Microcycle::IsProgram; ReadProgramAnnounceOperation = Operation::Read | Operation::NewAddress | Operation::IsProgram;
static constexpr Microcycle::OperationT static constexpr Microcycle::OperationT
ReadProgramOperation = Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectWord | Microcycle::IsProgram; ReadProgramOperation = Operation::Read | Operation::SameAddress | Operation::SelectWord | Operation::IsProgram;
Microcycle read_program_announce { ReadProgramAnnounceOperation }; Microcycle read_program_announce { ReadProgramAnnounceOperation };
Microcycle read_program { ReadProgramOperation }; Microcycle read_program { ReadProgramOperation };
// Read a data word or byte. // Read a data word or byte.
Microcycle access_announce { Microcycle access_announce {
Microcycle::Read | Microcycle::NewAddress | Microcycle::IsData Operation::Read | Operation::NewAddress | Operation::IsData
}; };
Microcycle access { Microcycle access {
Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectWord | Microcycle::IsData Operation::Read | Operation::SameAddress | Operation::SelectWord | Operation::IsData
}; };
// TAS. // TAS.
static constexpr Microcycle::OperationT static constexpr Microcycle::OperationT
TASOperations[5] = { TASOperations[5] = {
Microcycle::Read | Microcycle::NewAddress | Microcycle::IsData, Operation::Read | Operation::NewAddress | Operation::IsData,
Microcycle::Read | Microcycle::SameAddress | Microcycle::IsData | Microcycle::SelectByte, Operation::Read | Operation::SameAddress | Operation::IsData | Operation::SelectByte,
Microcycle::SameAddress, Operation::SameAddress,
Microcycle::SameAddress | Microcycle::IsData, Operation::SameAddress | Operation::IsData,
Microcycle::SameAddress | Microcycle::IsData | Microcycle::SelectByte, Operation::SameAddress | Operation::IsData | Operation::SelectByte,
}; };
Microcycle tas_cycles[5] = { Microcycle tas_cycles[5] = {
{ TASOperations[0] }, { TASOperations[0] },
@ -217,14 +217,14 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
}; };
// Reset. // Reset.
static constexpr Microcycle::OperationT ResetOperation = Microcycle::Reset; static constexpr Microcycle::OperationT ResetOperation = CPU::MC68000::Operation::Reset;
Microcycle reset_cycle { ResetOperation, HalfCycles(248) }; Microcycle reset_cycle { ResetOperation, HalfCycles(248) };
// Interrupt acknowledge. // Interrupt acknowledge.
static constexpr Microcycle::OperationT static constexpr Microcycle::OperationT
InterruptCycleOperations[2] = { InterruptCycleOperations[2] = {
Microcycle::InterruptAcknowledge | Microcycle::Read | Microcycle::NewAddress, Operation::InterruptAcknowledge | Operation::Read | Operation::NewAddress,
Microcycle::InterruptAcknowledge | Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectByte Operation::InterruptAcknowledge | Operation::Read | Operation::SameAddress | Operation::SelectByte
}; };
Microcycle interrupt_cycles[2] = { Microcycle interrupt_cycles[2] = {
{ InterruptCycleOperations[0] }, { InterruptCycleOperations[0] },