mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-11 08:30:55 +00:00
Merge pull request #1280 from TomHarte/More68000Op
Move fact of fixed operations into type.
This commit is contained in:
commit
8b4cabd228
@ -80,13 +80,10 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - MC68000::BusHandler.
|
||||
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;
|
||||
|
||||
template <typename Microcycle> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
|
||||
// Do a quick advance check for Chip RAM access; add a suitable delay if required.
|
||||
HalfCycles total_length;
|
||||
if(operation & Microcycle::NewAddress && *cycle.address < 0x20'0000) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::NewAddress && *cycle.address < 0x20'0000) {
|
||||
total_length = chipset_.run_until_after_cpu_slot().duration;
|
||||
assert(total_length >= cycle.length);
|
||||
} else {
|
||||
@ -96,19 +93,19 @@ class ConcreteMachine:
|
||||
mc68000_.set_interrupt_level(chipset_.get_interrupt_level());
|
||||
|
||||
// Check for assertion of reset.
|
||||
if(operation & Microcycle::Reset) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Reset) {
|
||||
memory_.reset();
|
||||
LOG("Reset; PC is around " << PADHEX(8) << mc68000_.get_state().registers.program_counter);
|
||||
}
|
||||
|
||||
// Autovector interrupts.
|
||||
if(operation & Microcycle::InterruptAcknowledge) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::InterruptAcknowledge) {
|
||||
mc68000_.set_is_peripheral_address(true);
|
||||
return total_length - cycle.length;
|
||||
}
|
||||
|
||||
// Do nothing if no address is exposed.
|
||||
if(!(operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return total_length - cycle.length;
|
||||
if(!(cycle.operation & (CPU::MC68000::Operation::NewAddress | CPU::MC68000::Operation::SameAddress))) return total_length - cycle.length;
|
||||
|
||||
// Grab the target address to pick a memory source.
|
||||
const uint32_t address = cycle.host_endian_byte_address();
|
||||
@ -117,7 +114,7 @@ class ConcreteMachine:
|
||||
mc68000_.set_is_peripheral_address((address & 0xe0'0000) == 0xa0'0000);
|
||||
|
||||
if(!memory_.regions[address >> 18].read_write_mask) {
|
||||
if((operation & (Microcycle::SelectByte | Microcycle::SelectWord))) {
|
||||
if((cycle.operation & (CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::SelectWord))) {
|
||||
// Check for various potential chip accesses.
|
||||
|
||||
// Per the manual:
|
||||
@ -135,7 +132,7 @@ class ConcreteMachine:
|
||||
const bool select_a = !(address & 0x1000);
|
||||
const bool select_b = !(address & 0x2000);
|
||||
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
uint16_t result = 0xffff;
|
||||
if(select_a) result &= 0xff00 | (chipset_.cia_a.read(reg) << 0);
|
||||
if(select_b) result &= 0x00ff | (chipset_.cia_b.read(reg) << 8);
|
||||
@ -157,13 +154,13 @@ class ConcreteMachine:
|
||||
memory_.perform(cycle);
|
||||
} else {
|
||||
// This'll do for open bus, for now.
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
cycle.set_value16(0xffff);
|
||||
}
|
||||
|
||||
// Don't log for the region that is definitely just ROM this machine doesn't have.
|
||||
if(address < 0xf0'0000) {
|
||||
LOG("Unmapped " << (operation & Microcycle::Read ? "read from " : "write to ") << PADHEX(6) << ((*cycle.address)&0xffffff) << " of " << cycle.value16());
|
||||
LOG("Unmapped " << (cycle.operation & CPU::MC68000::Operation::Read ? "read from " : "write to ") << PADHEX(6) << ((*cycle.address)&0xffffff) << " of " << cycle.value16());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -841,17 +841,6 @@ void Chipset::update_interrupts() {
|
||||
}
|
||||
}
|
||||
|
||||
void Chipset::perform(const CPU::MC68000::Microcycle &cycle) {
|
||||
using Microcycle = CPU::MC68000::Microcycle;
|
||||
|
||||
const uint32_t register_address = *cycle.address & ChipsetAddressMask;
|
||||
if(cycle.operation & Microcycle::Read) {
|
||||
cycle.set_value16(read(register_address));
|
||||
} else {
|
||||
write(register_address, cycle.value16());
|
||||
}
|
||||
}
|
||||
|
||||
void Chipset::write(uint32_t address, uint16_t value, bool allow_conversion) {
|
||||
#define ApplySetClear(target, mask) { \
|
||||
if(value & 0x8000) { \
|
||||
|
@ -58,7 +58,15 @@ class Chipset: private ClockingHint::Observer {
|
||||
Changes run_until_after_cpu_slot();
|
||||
|
||||
/// Performs the provided microcycle, which the caller guarantees to be a memory access.
|
||||
void perform(const CPU::MC68000::Microcycle &);
|
||||
template <typename Microcycle>
|
||||
void perform(const Microcycle &cycle) {
|
||||
const uint32_t register_address = *cycle.address & ChipsetAddressMask;
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
cycle.set_value16(read(register_address));
|
||||
} else {
|
||||
write(register_address, cycle.value16());
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the current state of the CIA interrupt lines.
|
||||
void set_cia_interrupts(bool cia_a, bool cia_b);
|
||||
|
@ -175,8 +175,8 @@ void Chipset::DiskController::set_mtr_sel_side_dir_step(uint8_t value) {
|
||||
const bool is_selected = !(value & select_mask);
|
||||
|
||||
// Both the motor state and the ID shifter are affected upon
|
||||
// changes in drive selection only.
|
||||
if(difference & select_mask) {
|
||||
// the trailing edge of changes in drive selection only.
|
||||
if(difference & select_mask & ~value) {
|
||||
// If transitioning to inactive, shift the drive ID value;
|
||||
// if transitioning to active, possibly reset the drive
|
||||
// ID and definitely latch the new motor state.
|
||||
|
@ -19,8 +19,8 @@ namespace Amiga {
|
||||
|
||||
class MemoryMap {
|
||||
private:
|
||||
static constexpr auto PermitRead = CPU::MC68000::Microcycle::PermitRead;
|
||||
static constexpr auto PermitWrite = CPU::MC68000::Microcycle::PermitWrite;
|
||||
static constexpr auto PermitRead = CPU::MC68000::Operation::PermitRead;
|
||||
static constexpr auto PermitWrite = CPU::MC68000::Operation::PermitWrite;
|
||||
static constexpr auto PermitReadWrite = PermitRead | PermitWrite;
|
||||
|
||||
public:
|
||||
@ -109,13 +109,13 @@ class MemoryMap {
|
||||
|
||||
/// Performs the provided microcycle, which the caller guarantees to be a memory access,
|
||||
/// and in the Zorro register range.
|
||||
bool perform(const CPU::MC68000::Microcycle &cycle) {
|
||||
template <typename Microcycle>
|
||||
bool perform(const Microcycle &cycle) {
|
||||
if(!fast_autoconf_visible_) return false;
|
||||
|
||||
const uint32_t register_address = *cycle.address & 0xfe;
|
||||
|
||||
using Microcycle = CPU::MC68000::Microcycle;
|
||||
if(cycle.operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
// Re: Autoconf:
|
||||
//
|
||||
// "All read registers physically return only the top 4 bits of data, on D31-D28";
|
||||
|
@ -191,15 +191,12 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
mc68000_.run_for(cycles);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
template <typename Microcycle> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
|
||||
// Advance time.
|
||||
advance_time(cycle.length);
|
||||
|
||||
// A null cycle leaves nothing else to do.
|
||||
if(!(operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return HalfCycles(0);
|
||||
if(!(cycle.operation & (CPU::MC68000::Operation::NewAddress | CPU::MC68000::Operation::SameAddress))) return HalfCycles(0);
|
||||
|
||||
// Grab the address.
|
||||
auto address = cycle.host_endian_byte_address();
|
||||
@ -219,7 +216,10 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
// having set VPA above deals with those given that the generated address
|
||||
// for interrupt acknowledge cycles always has all bits set except the
|
||||
// lowest explicit address lines.
|
||||
if(!cycle.data_select_active() || (operation & Microcycle::InterruptAcknowledge)) return HalfCycles(0);
|
||||
if(
|
||||
!cycle.data_select_active() ||
|
||||
(cycle.operation & CPU::MC68000::Operation::InterruptAcknowledge)
|
||||
) return HalfCycles(0);
|
||||
|
||||
// Grab the word-precision address being accessed.
|
||||
uint8_t *memory_base = nullptr;
|
||||
@ -228,18 +228,18 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
default: assert(false);
|
||||
|
||||
case BusDevice::Unassigned:
|
||||
fill_unmapped<op>(cycle);
|
||||
fill_unmapped(cycle);
|
||||
return delay;
|
||||
|
||||
case BusDevice::VIA: {
|
||||
if(*cycle.address & 1) {
|
||||
fill_unmapped<op>(cycle);
|
||||
fill_unmapped(cycle);
|
||||
} else {
|
||||
const int register_address = address >> 9;
|
||||
|
||||
// VIA accesses are via address 0xefe1fe + register*512,
|
||||
// which at word precision is 0x77f0ff + register*256.
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
cycle.set_value8_high(via_.read(register_address));
|
||||
} else {
|
||||
via_.write(register_address, cycle.value8_high());
|
||||
@ -248,7 +248,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
} return delay;
|
||||
|
||||
case BusDevice::PhaseRead: {
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
cycle.set_value8_low(phase_ & 7);
|
||||
}
|
||||
} return delay;
|
||||
@ -258,13 +258,13 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
const int register_address = address >> 9;
|
||||
|
||||
// The IWM; this is a purely polled device, so can be run on demand.
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
cycle.set_value8_low(iwm_->read(register_address));
|
||||
} else {
|
||||
iwm_->write(register_address, cycle.value8_low());
|
||||
}
|
||||
} else {
|
||||
fill_unmapped<op>(cycle);
|
||||
fill_unmapped(cycle);
|
||||
}
|
||||
} return delay;
|
||||
|
||||
@ -275,14 +275,14 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
// Even accesses = read; odd = write.
|
||||
if(*cycle.address & 1) {
|
||||
// Odd access => this is a write. Data will be in the upper byte.
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
scsi_.write(register_address, 0xff, dma_acknowledge);
|
||||
} else {
|
||||
scsi_.write(register_address, cycle.value8_high());
|
||||
}
|
||||
} else {
|
||||
// Even access => this is a read.
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
cycle.set_value8_high(scsi_.read(register_address, dma_acknowledge));
|
||||
}
|
||||
}
|
||||
@ -290,19 +290,19 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
|
||||
case BusDevice::SCCReadResetPhase: {
|
||||
// Any word access here adjusts phase.
|
||||
if(operation & Microcycle::SelectWord) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::SelectWord) {
|
||||
adjust_phase();
|
||||
} else {
|
||||
// A0 = 1 => reset; A0 = 0 => read.
|
||||
if(*cycle.address & 1) {
|
||||
scc_.reset();
|
||||
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
cycle.set_value16(0xffff);
|
||||
}
|
||||
} else {
|
||||
const auto read = scc_.read(int(address >> 1));
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
cycle.set_value8_high(read);
|
||||
}
|
||||
}
|
||||
@ -311,20 +311,20 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
|
||||
case BusDevice::SCCWrite: {
|
||||
// Any word access here adjusts phase.
|
||||
if(operation & Microcycle::SelectWord) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::SelectWord) {
|
||||
adjust_phase();
|
||||
} else {
|
||||
// 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.
|
||||
if(*cycle.address & 1) {
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
scc_.write(int(address >> 1), 0xff);
|
||||
cycle.value->b = 0xff;
|
||||
} else {
|
||||
scc_.write(int(address >> 1), cycle.value->b);
|
||||
}
|
||||
} else {
|
||||
fill_unmapped<op>(cycle);
|
||||
fill_unmapped(cycle);
|
||||
}
|
||||
}
|
||||
} return delay;
|
||||
@ -352,7 +352,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
} break;
|
||||
|
||||
case BusDevice::ROM: {
|
||||
if(!(operation & Microcycle::Read)) return delay;
|
||||
if(!(cycle.operation & CPU::MC68000::Operation::Read)) return delay;
|
||||
memory_base = rom_;
|
||||
address &= rom_mask_;
|
||||
} break;
|
||||
@ -544,10 +544,9 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
++phase_;
|
||||
}
|
||||
|
||||
template <Microcycle::OperationT op>
|
||||
template <typename Microcycle>
|
||||
forceinline void fill_unmapped(const Microcycle &cycle) {
|
||||
const auto operation = (op != Microcycle::DecodeDynamically) ? op : cycle.operation;
|
||||
if(!(operation & Microcycle::Read)) return;
|
||||
if(!(cycle.operation & CPU::MC68000::Operation::Read)) return;
|
||||
cycle.set_value16(0xffff);
|
||||
}
|
||||
|
||||
|
@ -176,10 +176,7 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: MC68000::BusHandler
|
||||
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;
|
||||
|
||||
template <typename Microcycle> HalfCycles perform_bus_operation(const Microcycle &cycle, int is_supervisor) {
|
||||
// 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_bus_error(false);
|
||||
@ -188,15 +185,15 @@ class ConcreteMachine:
|
||||
advance_time(cycle.length);
|
||||
|
||||
// Check for assertion of reset.
|
||||
if(operation & Microcycle::Reset) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Reset) {
|
||||
LOG("Unhandled Reset");
|
||||
}
|
||||
|
||||
// A null cycle leaves nothing else to do.
|
||||
if(!(operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return HalfCycles(0);
|
||||
if(!(cycle.operation & (CPU::MC68000::Operation::NewAddress | CPU::MC68000::Operation::SameAddress))) return HalfCycles(0);
|
||||
|
||||
// An interrupt acknowledge, perhaps?
|
||||
if(operation & Microcycle::InterruptAcknowledge) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::InterruptAcknowledge) {
|
||||
// Current implementation: everything other than 6 (i.e. the MFP) is autovectored.
|
||||
const int interrupt_level = cycle.word_address()&7;
|
||||
if(interrupt_level != 6) {
|
||||
@ -205,7 +202,7 @@ class ConcreteMachine:
|
||||
mc68000_.set_is_peripheral_address(true);
|
||||
return HalfCycles(0);
|
||||
} else {
|
||||
if(operation & Microcycle::SelectByte) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::SelectByte) {
|
||||
const int interrupt = mfp_->acknowledge_interrupt();
|
||||
if(interrupt != Motorola::MFP68901::MFP68901::NoAcknowledgement) {
|
||||
cycle.value->b = uint8_t(interrupt);
|
||||
@ -222,7 +219,7 @@ class ConcreteMachine:
|
||||
|
||||
// If this is a new strobing of the address signal, test for bus error and pre-DTack delay.
|
||||
HalfCycles delay(0);
|
||||
if(operation & Microcycle::NewAddress) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::NewAddress) {
|
||||
// Bus error test.
|
||||
if(
|
||||
// Anything unassigned should generate a bus error.
|
||||
@ -257,7 +254,7 @@ class ConcreteMachine:
|
||||
|
||||
case BusDevice::ROM:
|
||||
memory = rom_.data();
|
||||
if(!(operation & Microcycle::Read)) {
|
||||
if(!(cycle.operation & CPU::MC68000::Operation::Read)) {
|
||||
return delay;
|
||||
}
|
||||
address -= rom_start_;
|
||||
@ -271,12 +268,12 @@ class ConcreteMachine:
|
||||
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.
|
||||
*/
|
||||
switch(operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
||||
switch(cycle.operation & (CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read)) {
|
||||
default: break;
|
||||
case Microcycle::SelectWord | Microcycle::Read:
|
||||
case CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::Read:
|
||||
cycle.value->w = 0xffff;
|
||||
break;
|
||||
case Microcycle::SelectByte | Microcycle::Read:
|
||||
case CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read:
|
||||
cycle.value->b = 0xff;
|
||||
break;
|
||||
}
|
||||
@ -315,7 +312,7 @@ class ConcreteMachine:
|
||||
case 0x8260: case 0x8262:
|
||||
if(!cycle.data_select_active()) return delay;
|
||||
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
cycle.set_value16(video_->read(int(address >> 1)));
|
||||
} else {
|
||||
video_->write(int(address >> 1), cycle.value16());
|
||||
@ -326,7 +323,7 @@ class ConcreteMachine:
|
||||
case 0x8604: case 0x8606: case 0x8608: case 0x860a: case 0x860c:
|
||||
if(!cycle.data_select_active()) return delay;
|
||||
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
cycle.set_value16(dma_->read(int(address >> 1)));
|
||||
} else {
|
||||
dma_->write(int(address >> 1), cycle.value16());
|
||||
@ -356,13 +353,12 @@ class ConcreteMachine:
|
||||
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 0x88f0: case 0x88f2: case 0x88f4: case 0x88f6: case 0x88f8: case 0x88fa: case 0x88fc: case 0x88fe:
|
||||
|
||||
if(!cycle.data_select_active()) return delay;
|
||||
|
||||
advance_time(HalfCycles(2));
|
||||
update_audio();
|
||||
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
cycle.set_value8_high(GI::AY38910::Utility::read(ay_));
|
||||
} else {
|
||||
// Net effect here: addresses with bit 1 set write to a register,
|
||||
@ -382,7 +378,7 @@ class ConcreteMachine:
|
||||
case 0xfa38: case 0xfa3a: case 0xfa3c: case 0xfa3e:
|
||||
if(!cycle.data_select_active()) return delay;
|
||||
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
cycle.set_value8_low(mfp_->read(int(address >> 1)));
|
||||
} else {
|
||||
mfp_->write(int(address >> 1), cycle.value8_low());
|
||||
@ -396,7 +392,7 @@ class ConcreteMachine:
|
||||
if(!cycle.data_select_active()) return delay;
|
||||
|
||||
const auto acia_ = (address & 4) ? &midi_acia_ : &keyboard_acia_;
|
||||
if(operation & Microcycle::Read) {
|
||||
if(cycle.operation & CPU::MC68000::Operation::Read) {
|
||||
cycle.set_value8_high((*acia_)->read(int(address >> 1)));
|
||||
} else {
|
||||
(*acia_)->write(int(address >> 1), cycle.value8_high());
|
||||
@ -410,23 +406,23 @@ class ConcreteMachine:
|
||||
//
|
||||
// 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.
|
||||
switch(operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
||||
switch(cycle.operation & (CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read)) {
|
||||
default:
|
||||
break;
|
||||
|
||||
case Microcycle::SelectWord | Microcycle::Read:
|
||||
case CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::Read:
|
||||
cycle.value->w = *reinterpret_cast<uint16_t *>(&memory[address]);
|
||||
break;
|
||||
case Microcycle::SelectByte | Microcycle::Read:
|
||||
case CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read:
|
||||
cycle.value->b = memory[address];
|
||||
break;
|
||||
case Microcycle::SelectWord:
|
||||
case CPU::MC68000::Operation::SelectWord:
|
||||
if(address >= video_range_.low_address && address < video_range_.high_address)
|
||||
video_.flush();
|
||||
*reinterpret_cast<uint16_t *>(&memory[address]) = cycle.value->w;
|
||||
reinstall_rom_vector();
|
||||
break;
|
||||
case Microcycle::SelectByte:
|
||||
case CPU::MC68000::Operation::SelectByte:
|
||||
if(address >= video_range_.low_address && address < video_range_.high_address)
|
||||
video_.flush();
|
||||
memory[address] = cycle.value->b;
|
||||
|
@ -96,8 +96,8 @@ struct TestProcessor: public CPU::MC68000::BusHandler {
|
||||
if(!instructions_remaining_) comparitor();
|
||||
}
|
||||
|
||||
using Microcycle = CPU::MC68000::Microcycle;
|
||||
template <Microcycle::OperationT op> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
|
||||
|
||||
template <typename Microcycle> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
|
||||
if(cycle.data_select_active()) {
|
||||
cycle.apply(&ram[cycle.host_endian_byte_address()]);
|
||||
}
|
||||
|
@ -36,8 +36,7 @@ class EmuTOS: public ComparativeBusHandler {
|
||||
return m68000_.get_state();
|
||||
}
|
||||
|
||||
using Microcycle = CPU::MC68000::Microcycle;
|
||||
template <Microcycle::OperationT op> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
|
||||
template <typename Microcycle> perform_bus_operation(const Microcycle &cycle, int) {
|
||||
const uint32_t address = cycle.word_address();
|
||||
uint32_t word_address = address;
|
||||
|
||||
|
@ -39,8 +39,7 @@ class QL: public ComparativeBusHandler {
|
||||
return m68000_.get_state();
|
||||
}
|
||||
|
||||
using Microcycle = CPU::MC68000::Microcycle;
|
||||
template <Microcycle::OperationT op> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
|
||||
template <typename Microcycle> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
|
||||
const uint32_t address = cycle.word_address();
|
||||
uint32_t word_address = address;
|
||||
|
||||
@ -60,8 +59,7 @@ class QL: public ComparativeBusHandler {
|
||||
if(cycle.data_select_active()) {
|
||||
uint16_t peripheral_result = 0xffff;
|
||||
|
||||
const auto operation = (op != Microcycle::DecodeDynamically) ? op : cycle.operation;
|
||||
switch(operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
||||
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
||||
default: break;
|
||||
|
||||
case Microcycle::SelectWord | Microcycle::Read:
|
||||
|
@ -78,8 +78,7 @@ class RAM68000: public CPU::MC68000::BusHandler {
|
||||
return &ram_[(address >> 1) % ram_.size()];
|
||||
}
|
||||
|
||||
using Microcycle = CPU::MC68000::Microcycle;
|
||||
template <Microcycle::OperationT op> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
|
||||
template <typename Microcycle> HalfCycles perform_bus_operation(const Microcycle &cycle, int) {
|
||||
const uint32_t word_address = cycle.word_address();
|
||||
duration_ += cycle.length;
|
||||
|
||||
|
@ -13,8 +13,79 @@
|
||||
#include "../../Numeric/RegisterSizes.hpp"
|
||||
#include "../../InstructionSets/M68k/RegisterSet.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
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; these are not
|
||||
// real 68000 signals, they're to do with internal manipulation only.
|
||||
|
||||
/// 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 an implementation detail elsewhere. Shouldn't be exposed.
|
||||
static constexpr OperationT DecodeDynamically = NewAddress | SameAddress;
|
||||
|
||||
// PermitRead and PermitWrite are used as part of the read/write mask
|
||||
// supplied to @c Microcycle::apply; they are picked to be small enough values that
|
||||
// a byte can be used for storage.
|
||||
static constexpr OperationT PermitRead = 1 << 3;
|
||||
static constexpr OperationT PermitWrite = 1 << 4;
|
||||
|
||||
};
|
||||
|
||||
template <OperationT op>
|
||||
struct MicrocycleOperationStorage {
|
||||
static constexpr auto operation = op;
|
||||
};
|
||||
template <>
|
||||
struct MicrocycleOperationStorage<Operation::DecodeDynamically> {
|
||||
OperationT operation = 0;
|
||||
};
|
||||
|
||||
/*!
|
||||
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.
|
||||
@ -38,61 +109,12 @@ namespace CPU::MC68000 {
|
||||
of an address-strobing microcycle, it can just supply those periods for accounting and
|
||||
avoid the runtime cost of actual DTack emulation. But such as the bus allows.)
|
||||
*/
|
||||
struct Microcycle {
|
||||
using OperationT = unsigned int;
|
||||
|
||||
/// 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
|
||||
/// performed by this Microcycle.
|
||||
OperationT operation = 0;
|
||||
template <OperationT op = Operation::DecodeDynamically>
|
||||
struct Microcycle: public MicrocycleOperationStorage<op> {
|
||||
// One of the following is also exposed here via inheritance:
|
||||
//
|
||||
// static constexpr OperationT operation; or
|
||||
// OperationT operation;
|
||||
|
||||
/// Describes the duration of this Microcycle.
|
||||
HalfCycles length = HalfCycles(4);
|
||||
@ -124,24 +146,48 @@ struct Microcycle {
|
||||
*/
|
||||
SlicedInt16 *value = nullptr;
|
||||
|
||||
Microcycle(OperationT operation) : operation(operation) {}
|
||||
Microcycle(OperationT operation, HalfCycles length) : operation(operation), length(length) {}
|
||||
Microcycle() {}
|
||||
constexpr Microcycle() noexcept {}
|
||||
constexpr Microcycle(OperationT dynamic_operation) noexcept {
|
||||
if constexpr (op == Operation::DecodeDynamically) {
|
||||
MicrocycleOperationStorage<op>::operation = dynamic_operation;
|
||||
} else {
|
||||
assert(MicrocycleOperationStorage<op>::operation == dynamic_operation);
|
||||
}
|
||||
}
|
||||
constexpr Microcycle(OperationT dynamic_operation, HalfCycles length) noexcept : Microcycle(dynamic_operation) {
|
||||
this->length = length;
|
||||
}
|
||||
constexpr Microcycle(HalfCycles length) noexcept {
|
||||
static_assert(op != Operation::DecodeDynamically);
|
||||
this->length = length;
|
||||
}
|
||||
|
||||
template <typename MicrocycleRHS>
|
||||
Microcycle &operator =(const MicrocycleRHS &rhs) {
|
||||
static_assert(op == Operation::DecodeDynamically);
|
||||
this->operation = rhs.operation;
|
||||
this->value = rhs.value;
|
||||
this->address = rhs.address;
|
||||
this->length = rhs.length;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @returns @c true if two Microcycles are equal; @c false otherwise.
|
||||
bool operator ==(const Microcycle &rhs) const {
|
||||
template <typename MicrocycleRHS>
|
||||
bool operator ==(const MicrocycleRHS &rhs) const {
|
||||
if(value != rhs.value) return false;
|
||||
if(address != rhs.address) return false;
|
||||
if(length != rhs.length) return false;
|
||||
if(operation != rhs.operation) return false;
|
||||
if(this->operation != rhs.operation) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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));
|
||||
bool data_select_active() const {
|
||||
return bool(this->operation & (Operation::SelectWord | Operation::SelectByte | Operation::InterruptAcknowledge));
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -184,14 +230,14 @@ struct Microcycle {
|
||||
@returns non-zero if the 68000 LDS is asserted; zero otherwise.
|
||||
*/
|
||||
forceinline int lower_data_select() const {
|
||||
return (operation & SelectByte & *address) | (operation & SelectWord);
|
||||
return (this->operation & Operation::SelectByte & *address) | (this->operation & Operation::SelectWord);
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns non-zero if the 68000 UDS is asserted; zero otherwise.
|
||||
*/
|
||||
forceinline int upper_data_select() const {
|
||||
return (operation & SelectByte & ~*address) | (operation & SelectWord);
|
||||
return (this->operation & Operation::SelectByte & ~*address) | (this->operation & Operation::SelectWord);
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -226,9 +272,9 @@ struct Microcycle {
|
||||
*/
|
||||
forceinline uint32_t host_endian_byte_address() const {
|
||||
#if TARGET_RT_BIG_ENDIAN
|
||||
return *address & 0xffffff;
|
||||
return *address & 0xff'ffff;
|
||||
#else
|
||||
return (*address ^ (operation & SelectByte)) & 0xffffff;
|
||||
return (*address ^ (this->operation & Operation::SelectByte)) & 0xff'ffff;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -239,7 +285,7 @@ struct Microcycle {
|
||||
*/
|
||||
forceinline uint16_t value16() const {
|
||||
const uint16_t values[] = { value->w, uint16_t((value->b << 8) | value->b) };
|
||||
return values[operation & SelectByte];
|
||||
return values[this->operation & Operation::SelectByte];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -247,7 +293,7 @@ struct Microcycle {
|
||||
*/
|
||||
forceinline uint8_t value8_high() const {
|
||||
const uint8_t values[] = { uint8_t(value->w >> 8), value->b};
|
||||
return values[operation & SelectByte];
|
||||
return values[this->operation & Operation::SelectByte];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -262,8 +308,8 @@ struct Microcycle {
|
||||
currently being read. Assumes this is a read cycle.
|
||||
*/
|
||||
forceinline void set_value16(uint16_t v) const {
|
||||
assert(operation & Microcycle::Read);
|
||||
if(operation & Microcycle::SelectWord) {
|
||||
assert(this->operation & Operation::Read);
|
||||
if(this->operation & Operation::SelectWord) {
|
||||
value->w = v;
|
||||
} else {
|
||||
value->b = uint8_t(v >> byte_shift());
|
||||
@ -274,8 +320,8 @@ struct Microcycle {
|
||||
Equivalent to set_value16((v << 8) | 0x00ff).
|
||||
*/
|
||||
forceinline void set_value8_high(uint8_t v) const {
|
||||
assert(operation & Microcycle::Read);
|
||||
if(operation & Microcycle::SelectWord) {
|
||||
assert(this->operation & Operation::Read);
|
||||
if(this->operation & Operation::SelectWord) {
|
||||
value->w = uint16_t(0x00ff | (v << 8));
|
||||
} else {
|
||||
value->b = uint8_t(v | byte_mask());
|
||||
@ -286,49 +332,46 @@ struct Microcycle {
|
||||
Equivalent to set_value16(v | 0xff00).
|
||||
*/
|
||||
forceinline void set_value8_low(uint8_t v) const {
|
||||
assert(operation & Microcycle::Read);
|
||||
if(operation & Microcycle::SelectWord) {
|
||||
assert(this->operation & Operation::Read);
|
||||
if(this->operation & Operation::SelectWord) {
|
||||
value->w = 0xff00 | v;
|
||||
} else {
|
||||
value->b = uint8_t(v | untouched_byte_mask());
|
||||
}
|
||||
}
|
||||
|
||||
// PermitRead and PermitWrite are used as part of the read/write mask
|
||||
// supplied to @c apply; they are picked to be small enough values that
|
||||
// a byte can be used for storage.
|
||||
static constexpr OperationT PermitRead = 1 << 3;
|
||||
static constexpr OperationT PermitWrite = 1 << 4;
|
||||
|
||||
/*!
|
||||
Assuming this to be a cycle with a data select active, applies it to @c target
|
||||
subject to the read_write_mask, where 'applies' means:
|
||||
subject to the @c read_write_mask, where 'applies' means:
|
||||
|
||||
* if this is a byte read, reads a single byte from @c target;
|
||||
* if this is a word read, reads a word (in the host platform's endianness) from @c target; and
|
||||
* if this is a write, does the converse of a read.
|
||||
*/
|
||||
forceinline void apply(uint8_t *target, OperationT read_write_mask = PermitRead | PermitWrite) const {
|
||||
assert( (operation & (SelectWord | SelectByte)) != (SelectWord | SelectByte));
|
||||
forceinline void apply(uint8_t *target, OperationT read_write_mask = Operation::PermitRead | Operation::PermitWrite) const {
|
||||
assert( (this->operation & (Operation::SelectWord | Operation::SelectByte)) != (Operation::SelectWord | Operation::SelectByte));
|
||||
|
||||
switch((operation | read_write_mask) & (SelectWord | SelectByte | Read | PermitRead | PermitWrite)) {
|
||||
switch(
|
||||
(this->operation | read_write_mask) &
|
||||
(Operation::SelectWord | Operation::SelectByte | Operation::Read | Operation::PermitRead | Operation::PermitWrite)
|
||||
) {
|
||||
default:
|
||||
break;
|
||||
|
||||
case SelectWord | Read | PermitRead:
|
||||
case SelectWord | Read | PermitRead | PermitWrite:
|
||||
case Operation::SelectWord | Operation::Read | Operation::PermitRead:
|
||||
case Operation::SelectWord | Operation::Read | Operation::PermitRead | Operation::PermitWrite:
|
||||
value->w = *reinterpret_cast<uint16_t *>(target);
|
||||
break;
|
||||
case SelectByte | Read | PermitRead:
|
||||
case SelectByte | Read | PermitRead | PermitWrite:
|
||||
case Operation::SelectByte | Operation::Read | Operation::PermitRead:
|
||||
case Operation::SelectByte | Operation::Read | Operation::PermitRead | Operation::PermitWrite:
|
||||
value->b = *target;
|
||||
break;
|
||||
case SelectWord | PermitWrite:
|
||||
case SelectWord | PermitWrite | PermitRead:
|
||||
case Operation::SelectWord | Operation::PermitWrite:
|
||||
case Operation::SelectWord | Operation::PermitWrite | Operation::PermitRead:
|
||||
*reinterpret_cast<uint16_t *>(target) = value->w;
|
||||
break;
|
||||
case SelectByte | PermitWrite:
|
||||
case SelectByte | PermitWrite | PermitRead:
|
||||
case Operation::SelectByte | Operation::PermitWrite:
|
||||
case Operation::SelectByte | Operation::PermitWrite | Operation::PermitRead:
|
||||
*target = value->b;
|
||||
break;
|
||||
}
|
||||
@ -345,14 +388,13 @@ class BusHandler {
|
||||
Provides the bus handler with a single Microcycle to 'perform'.
|
||||
|
||||
FC0 and FC1 are provided inside the microcycle as the IsData and IsProgram
|
||||
flags; FC2 is provided here as is_supervisor — it'll be either 0 or 1.
|
||||
flags; FC2 is provided here as @c is_supervisor — it'll be either 0 or 1.
|
||||
|
||||
If @c operation is any value other than Microcycle::DecodeDynamically then it
|
||||
can be used to select an appropriate execution path at compile time. Otherwise
|
||||
cycle.operation must be inspected at runtime.
|
||||
The @c Microcycle might be any instantiation of @c Microcycle above;
|
||||
whether with a static constexpr operation or with a runtime-selected one.
|
||||
*/
|
||||
template <Microcycle::OperationT operation>
|
||||
HalfCycles perform_bus_operation([[maybe_unused]] const Microcycle &cycle, [[maybe_unused]] int is_supervisor) {
|
||||
template <typename Microcycle>
|
||||
HalfCycles perform_bus_operation(const Microcycle &, [[maybe_unused]] int is_supervisor) {
|
||||
return HalfCycles(0);
|
||||
}
|
||||
|
||||
|
@ -195,8 +195,8 @@ enum ExecutionState: int {
|
||||
#undef AddressingDispatch
|
||||
|
||||
/// @returns The proper select lines for @c instruction's operand size, assuming it is either byte or word.
|
||||
template <typename InstructionT> Microcycle::OperationT data_select(const InstructionT &instruction) {
|
||||
return Microcycle::OperationT(1 << int(instruction.operand_size()));
|
||||
template <typename InstructionT> OperationT data_select(const InstructionT &instruction) {
|
||||
return OperationT(1 << int(instruction.operand_size()));
|
||||
}
|
||||
|
||||
// MARK: - The state machine.
|
||||
@ -282,8 +282,8 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
|
||||
// Performs the bus operation and then applies a `Spend` of its length
|
||||
// plus any additional length returned by the bus handler.
|
||||
#define PerformBusOperation(x, op) \
|
||||
delay = bus_handler_.template perform_bus_operation<op>(x, is_supervisor_); \
|
||||
#define PerformBusOperation(x) \
|
||||
delay = bus_handler_.perform_bus_operation(x, is_supervisor_); \
|
||||
Spend(x.length + delay)
|
||||
|
||||
// TODO: the templated operation type to perform_bus_operation is intended to allow a much
|
||||
@ -292,7 +292,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
// Performs no bus activity for the specified number of microcycles.
|
||||
#define IdleBus(n) \
|
||||
idle.length = HalfCycles((n) << 2); \
|
||||
PerformBusOperation(idle, 0)
|
||||
PerformBusOperation(idle)
|
||||
|
||||
// Spin until DTACK, VPA or BERR is asserted (unless DTACK is implicit),
|
||||
// holding the bus cycle provided.
|
||||
@ -315,7 +315,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
//
|
||||
// (1) wait until end of current 10-cycle window;
|
||||
// (2) run for the next 10-cycle window.
|
||||
#define CompleteAccess(x, op) \
|
||||
#define CompleteAccess(x) \
|
||||
if(berr_) { \
|
||||
RaiseBusOrAddressError(AccessFault, x); \
|
||||
} \
|
||||
@ -324,11 +324,11 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
} else { \
|
||||
x.length = HalfCycles(4); \
|
||||
} \
|
||||
PerformBusOperation(x, op)
|
||||
PerformBusOperation(x)
|
||||
|
||||
// Performs the memory access implied by the announce, perform pair,
|
||||
// honouring DTACK, BERR and VPA as necessary.
|
||||
#define AccessPair(val, announce, announce_op, perform, perform_op) \
|
||||
#define AccessPair(val, announce, perform) \
|
||||
perform.value = &val; \
|
||||
if constexpr (!dtack_is_implicit) { \
|
||||
announce.length = HalfCycles(4); \
|
||||
@ -336,14 +336,14 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
if(*perform.address & (perform.operation >> 1) & 1) { \
|
||||
RaiseBusOrAddressError(AddressError, perform); \
|
||||
} \
|
||||
PerformBusOperation(announce, announce_op); \
|
||||
PerformBusOperation(announce); \
|
||||
WaitForDTACK(announce); \
|
||||
CompleteAccess(perform, perform_op);
|
||||
CompleteAccess(perform);
|
||||
|
||||
// Sets up the next data access size and read flags.
|
||||
#define SetupDataAccess(read_flag, select_flag) \
|
||||
access_announce.operation = Microcycle::NewAddress | Microcycle::IsData | (read_flag); \
|
||||
access.operation = Microcycle::SameAddress | Microcycle::IsData | (read_flag) | (select_flag);
|
||||
access_announce.operation = Operation::NewAddress | Operation::IsData | (read_flag); \
|
||||
access.operation = Operation::SameAddress | Operation::IsData | (read_flag) | (select_flag);
|
||||
|
||||
// Sets the address source for the next data access.
|
||||
#define SetDataAddress(addr) \
|
||||
@ -351,15 +351,15 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
|
||||
// Performs the access established by SetupDataAccess into val.
|
||||
#define Access(val) \
|
||||
AccessPair(val, access_announce, Microcycle::DecodeDynamically, access, Microcycle::DecodeDynamically)
|
||||
AccessPair(val, access_announce, access)
|
||||
|
||||
// Performs the access established by SetupDataAccess into val.
|
||||
#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))
|
||||
#define AccessOp(val) \
|
||||
AccessPair(val, access_announce, access)
|
||||
|
||||
// Reads the program (i.e. non-data) word from addr into val.
|
||||
#define ReadProgramWord(val) \
|
||||
AccessPair(val, read_program_announce, ReadProgramAnnounceOperation, read_program, ReadProgramOperation); \
|
||||
AccessPair(val, read_program_announce, read_program); \
|
||||
program_counter_.l += 2;
|
||||
|
||||
// Reads one futher word from the program counter and inserts it into
|
||||
@ -384,7 +384,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
// Spin in place, one cycle at a time, until one of DTACK,
|
||||
// BERR or VPA is asserted.
|
||||
BeginState(WaitForDTACK):
|
||||
PerformBusOperation(awaiting_dtack, 0);
|
||||
PerformBusOperation(awaiting_dtack);
|
||||
|
||||
if(dtack_ || berr_ || vpa_) {
|
||||
MoveToStateDynamic(post_dtack_state_);
|
||||
@ -424,20 +424,20 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
should_trace_ = 0;
|
||||
did_update_status();
|
||||
|
||||
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
|
||||
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
||||
SetDataAddress(temporary_address_.l);
|
||||
|
||||
temporary_address_.l = 0;
|
||||
AccessOp(registers_[15].high, Microcycle::Read, Microcycle::SelectWord); // nF
|
||||
AccessOp(registers_[15].high); // nF
|
||||
|
||||
temporary_address_.l += 2;
|
||||
AccessOp(registers_[15].low, Microcycle::Read, Microcycle::SelectWord); // nf
|
||||
AccessOp(registers_[15].low); // nf
|
||||
|
||||
temporary_address_.l += 2;
|
||||
AccessOp(program_counter_.high, Microcycle::Read, Microcycle::SelectWord); // nV
|
||||
AccessOp(program_counter_.high); // nV
|
||||
|
||||
temporary_address_.l += 2;
|
||||
AccessOp(program_counter_.low, Microcycle::Read, Microcycle::SelectWord); // nv
|
||||
AccessOp(program_counter_.low); // nv
|
||||
|
||||
Prefetch(); // np
|
||||
IdleBus(1); // n
|
||||
@ -451,30 +451,30 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
should_trace_ = 0;
|
||||
did_update_status();
|
||||
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
SetDataAddress(registers_[15].l);
|
||||
|
||||
// Push status and current program counter.
|
||||
// Write order is wacky here, but I think it's correct.
|
||||
registers_[15].l -= 2;
|
||||
AccessOp(instruction_address_.low, 0, Microcycle::SelectWord); // ns
|
||||
AccessOp(instruction_address_.low); // ns
|
||||
|
||||
registers_[15].l -= 4;
|
||||
AccessOp(captured_status_, 0, Microcycle::SelectWord); // ns
|
||||
AccessOp(captured_status_); // ns
|
||||
|
||||
registers_[15].l += 2;
|
||||
AccessOp(instruction_address_.high, 0, Microcycle::SelectWord); // nS
|
||||
AccessOp(instruction_address_.high); // nS
|
||||
registers_[15].l -= 2;
|
||||
|
||||
// Grab new program counter.
|
||||
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
|
||||
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
||||
SetDataAddress(temporary_address_.l);
|
||||
|
||||
temporary_address_.l = uint32_t(exception_vector_ << 2);
|
||||
AccessOp(program_counter_.high, Microcycle::Read, Microcycle::SelectWord); // nV
|
||||
AccessOp(program_counter_.high); // nV
|
||||
|
||||
temporary_address_.l += 2;
|
||||
AccessOp(program_counter_.low, Microcycle::Read, Microcycle::SelectWord); // nv
|
||||
AccessOp(program_counter_.low); // nv
|
||||
|
||||
// Populate the prefetch queue.
|
||||
Prefetch(); // np
|
||||
@ -523,24 +523,24 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
should_trace_ = 0;
|
||||
did_update_status();
|
||||
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
SetDataAddress(registers_[15].l);
|
||||
|
||||
// Guess: the written program counter is adjusted to discount the prefetch queue.
|
||||
// COMPLETE GUESS.
|
||||
temporary_address_.l = program_counter_.l - 4;
|
||||
registers_[15].l -= 2;
|
||||
AccessOp(temporary_address_.low, 0, Microcycle::SelectWord); // ns [pc.l]
|
||||
AccessOp(temporary_address_.low); // ns [pc.l]
|
||||
|
||||
registers_[15].l -= 4;
|
||||
AccessOp(captured_status_, 0, Microcycle::SelectWord); // ns [sr]
|
||||
AccessOp(captured_status_); // ns [sr]
|
||||
|
||||
registers_[15].l += 2;
|
||||
AccessOp(temporary_address_.high, 0, Microcycle::SelectWord); // nS [pc.h]
|
||||
AccessOp(temporary_address_.high); // nS [pc.h]
|
||||
|
||||
registers_[15].l -= 4;
|
||||
temporary_value_.w = opcode_;
|
||||
AccessOp(temporary_value_.low, 0, Microcycle::SelectWord); // ns [instruction register]
|
||||
AccessOp(temporary_value_.low); // ns [instruction register]
|
||||
|
||||
// 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.
|
||||
temporary_value_.w =
|
||||
(temporary_value_.w & ~31) |
|
||||
((bus_error_.operation & Microcycle::Read) ? 0x10 : 0x00) |
|
||||
((bus_error_.operation & Microcycle::IsProgram) ? 0x08 : 0x00) |
|
||||
((bus_error_.operation & Microcycle::IsProgram) ? 0x02 : 0x01) |
|
||||
((bus_error_.operation & Operation::Read) ? 0x10 : 0x00) |
|
||||
((bus_error_.operation & Operation::IsProgram) ? 0x08 : 0x00) |
|
||||
((bus_error_.operation & Operation::IsProgram) ? 0x02 : 0x01) |
|
||||
((captured_status_.w & InstructionSet::M68k::ConditionCode::Supervisor) ? 0x04 : 0x00);
|
||||
temporary_address_.l = *bus_error_.address;
|
||||
|
||||
registers_[15].l -= 2;
|
||||
AccessOp(temporary_address_.low, 0, Microcycle::SelectWord); // ns [error address.l]
|
||||
AccessOp(temporary_address_.low); // ns [error address.l]
|
||||
|
||||
registers_[15].l -= 4;
|
||||
AccessOp(temporary_value_.low, 0, Microcycle::SelectWord); // ns [function code]
|
||||
AccessOp(temporary_value_.low); // ns [function code]
|
||||
|
||||
registers_[15].l += 2;
|
||||
AccessOp(temporary_address_.high, 0, Microcycle::SelectWord); // nS [error address.h]
|
||||
AccessOp(temporary_address_.high); // nS [error address.h]
|
||||
registers_[15].l -= 2;
|
||||
|
||||
// Grab new program counter.
|
||||
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
|
||||
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
||||
SetDataAddress(temporary_address_.l);
|
||||
|
||||
temporary_address_.l = uint32_t(exception_vector_ << 2);
|
||||
AccessOp(program_counter_.high, Microcycle::Read, Microcycle::SelectWord); // nV
|
||||
AccessOp(program_counter_.high); // nV
|
||||
|
||||
temporary_address_.l += 2;
|
||||
AccessOp(program_counter_.low, Microcycle::Read, Microcycle::SelectWord); // nv
|
||||
AccessOp(program_counter_.low); // nv
|
||||
|
||||
// Populate the prefetch queue.
|
||||
Prefetch(); // np
|
||||
@ -597,19 +597,19 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
did_update_status();
|
||||
|
||||
// Prepare for stack activity.
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
SetDataAddress(registers_[15].l);
|
||||
|
||||
// Push low part of program counter.
|
||||
registers_[15].l -= 2;
|
||||
AccessOp(instruction_address_.low, 0, Microcycle::SelectWord); // ns
|
||||
AccessOp(instruction_address_.low); // ns
|
||||
|
||||
// Do the interrupt cycle, to obtain a vector.
|
||||
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].value = interrupt_cycles[1].value = &temporary_value_.low;
|
||||
PerformBusOperation(interrupt_cycles[0], InterruptCycleOperations[0]);
|
||||
CompleteAccess(interrupt_cycles[1], InterruptCycleOperations[1]); // ni
|
||||
interrupt_cycle0.address = interrupt_cycle1.address = &temporary_address_.l;
|
||||
interrupt_cycle0.value = interrupt_cycle1.value = &temporary_value_.low;
|
||||
PerformBusOperation(interrupt_cycle0);
|
||||
CompleteAccess(interrupt_cycle1); // ni
|
||||
|
||||
// If VPA is set, autovector.
|
||||
if(vpa_) {
|
||||
@ -628,21 +628,21 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
SetDataAddress(registers_[15].l);
|
||||
|
||||
registers_[15].l -= 4;
|
||||
AccessOp(captured_status_, 0, Microcycle::SelectWord); // ns
|
||||
AccessOp(captured_status_); // ns
|
||||
|
||||
registers_[15].l += 2;
|
||||
AccessOp(instruction_address_.high, 0, Microcycle::SelectWord); // nS
|
||||
AccessOp(instruction_address_.high); // nS
|
||||
registers_[15].l -= 2;
|
||||
|
||||
// Grab new program counter.
|
||||
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
|
||||
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
||||
SetDataAddress(temporary_address_.l);
|
||||
|
||||
temporary_address_.l = uint32_t(temporary_value_.b << 2);
|
||||
AccessOp(program_counter_.high, Microcycle::Read, Microcycle::SelectWord); // nV
|
||||
AccessOp(program_counter_.high); // nV
|
||||
|
||||
temporary_address_.l += 2;
|
||||
AccessOp(program_counter_.low, Microcycle::Read, Microcycle::SelectWord); // nv
|
||||
AccessOp(program_counter_.low); // nv
|
||||
|
||||
// Populate the prefetch queue.
|
||||
Prefetch(); // np
|
||||
@ -707,13 +707,13 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
y; \
|
||||
\
|
||||
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); \
|
||||
} else { \
|
||||
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 { \
|
||||
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); \
|
||||
SetupDataAccess(Operation::Read, Operation::SelectWord); \
|
||||
} \
|
||||
MoveToStateSpecific(FetchOperand_bw); \
|
||||
}
|
||||
@ -823,10 +823,10 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
CASE(ABCD)
|
||||
if(instruction_.mode(0) == Mode::DataRegisterDirect) {
|
||||
perform_state_ = Perform_np_n;
|
||||
SetupDataAccess(Microcycle::Read, Microcycle::SelectByte);
|
||||
SetupDataAccess(Operation::Read, Operation::SelectByte);
|
||||
MoveToStateSpecific(FetchOperand_bw);
|
||||
} else {
|
||||
select_flag_ = Microcycle::SelectByte;
|
||||
select_flag_ = Operation::SelectByte;
|
||||
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) {
|
||||
perform_state_ = Perform_np;
|
||||
} else {
|
||||
select_flag_ = Microcycle::SelectByte;
|
||||
select_flag_ = Operation::SelectByte;
|
||||
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) {
|
||||
perform_state_ = Perform_np;
|
||||
} else {
|
||||
select_flag_ = Microcycle::SelectWord;
|
||||
select_flag_ = Operation::SelectWord;
|
||||
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)];
|
||||
|
||||
SetDataAddress(effective_address_[1].l);
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
|
||||
Access(operand_[next_operand_].high); // nW
|
||||
effective_address_[1].l += 2;
|
||||
@ -1258,7 +1258,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
|
||||
BeginStateMode(MOVE_l, AddressRegisterIndirectWithPostincrement):
|
||||
SetDataAddress(registers_[8 + instruction_.reg(next_operand_)].l);
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
|
||||
Access(operand_[next_operand_].high); // nW
|
||||
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):
|
||||
SetDataAddress(registers_[8 + instruction_.reg(1)].l);
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
|
||||
Prefetch(); // np
|
||||
|
||||
@ -1374,7 +1374,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
uint32_t(int16_t(prefetch_.w));
|
||||
|
||||
SetDataAddress(effective_address_[1].l);
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
|
||||
Prefetch(); // np
|
||||
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));
|
||||
|
||||
SetDataAddress(effective_address_[1].l);
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
|
||||
Prefetch(); // np
|
||||
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);
|
||||
|
||||
SetDataAddress(effective_address_[1].l);
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
|
||||
IdleBus(1); // n
|
||||
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);
|
||||
|
||||
SetDataAddress(effective_address_[1].l);
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
|
||||
IdleBus(1); // n
|
||||
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));
|
||||
|
||||
SetDataAddress(effective_address_[1].l);
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
|
||||
Prefetch(); // np
|
||||
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;
|
||||
|
||||
SetDataAddress(effective_address_[1].l);
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
|
||||
switch(instruction_.mode(0)) {
|
||||
case Mode::AddressRegisterDirect:
|
||||
@ -1867,15 +1867,15 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
BeginState(StoreOperand):
|
||||
switch(instruction_.operand_size()) {
|
||||
case InstructionSet::M68k::DataSize::LongWord:
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
MoveToStateSpecific(StoreOperand_l);
|
||||
|
||||
case InstructionSet::M68k::DataSize::Word:
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
MoveToStateSpecific(StoreOperand_bw);
|
||||
|
||||
case InstructionSet::M68k::DataSize::Byte:
|
||||
SetupDataAccess(0, Microcycle::SelectByte);
|
||||
SetupDataAccess(0, Operation::SelectByte);
|
||||
MoveToStateSpecific(StoreOperand_bw);
|
||||
}
|
||||
|
||||
@ -1919,7 +1919,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
MoveToNextOperand(StoreOperand_l);
|
||||
}
|
||||
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
SetDataAddress(effective_address_[next_operand_].l);
|
||||
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):
|
||||
IdleBus(1); // n
|
||||
|
||||
SetupDataAccess(Microcycle::Read, select_flag_);
|
||||
SetupDataAccess(Operation::Read, select_flag_);
|
||||
|
||||
SetDataAddress(registers_[8 + instruction_.reg(0)].l);
|
||||
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):
|
||||
IdleBus(1); // n
|
||||
|
||||
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
|
||||
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
||||
|
||||
SetDataAddress(registers_[8 + instruction_.reg(0)].l);
|
||||
registers_[8 + instruction_.reg(0)].l -= 2;
|
||||
@ -2034,7 +2034,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
|
||||
PerformDynamic();
|
||||
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
|
||||
registers_[8 + instruction_.reg(1)].l += 2;
|
||||
Access(operand_[1].low); // nw
|
||||
@ -2160,7 +2160,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
|
||||
|
||||
#define Push(x) \
|
||||
SetupDataAccess(0, Microcycle::SelectWord); \
|
||||
SetupDataAccess(0, Operation::SelectWord); \
|
||||
SetDataAddress(registers_[15].l); \
|
||||
registers_[15].l -= 4; \
|
||||
Access(x.high); \
|
||||
@ -2169,7 +2169,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
registers_[15].l -= 2;
|
||||
|
||||
#define Pop(x) \
|
||||
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord); \
|
||||
SetupDataAccess(Operation::Read, Operation::SelectWord); \
|
||||
SetDataAddress(registers_[15].l); \
|
||||
Access(x.high); \
|
||||
registers_[15].l += 2; \
|
||||
@ -2246,7 +2246,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
BeginState(MOVEPtoM_l):
|
||||
temporary_address_.l = registers_[8 + instruction_.reg(1)].l + uint32_t(int16_t(prefetch_.w));
|
||||
SetDataAddress(temporary_address_.l);
|
||||
SetupDataAccess(0, Microcycle::SelectByte);
|
||||
SetupDataAccess(0, Operation::SelectByte);
|
||||
|
||||
Prefetch(); // np
|
||||
|
||||
@ -2271,7 +2271,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
BeginState(MOVEPtoM_w):
|
||||
temporary_address_.l = registers_[8 + instruction_.reg(1)].l + uint32_t(int16_t(prefetch_.w));
|
||||
SetDataAddress(temporary_address_.l);
|
||||
SetupDataAccess(0, Microcycle::SelectByte);
|
||||
SetupDataAccess(0, Operation::SelectByte);
|
||||
|
||||
Prefetch(); // np
|
||||
|
||||
@ -2288,7 +2288,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
BeginState(MOVEPtoR_l):
|
||||
temporary_address_.l = registers_[8 + instruction_.reg(0)].l + uint32_t(int16_t(prefetch_.w));
|
||||
SetDataAddress(temporary_address_.l);
|
||||
SetupDataAccess(Microcycle::Read, Microcycle::SelectByte);
|
||||
SetupDataAccess(Operation::Read, Operation::SelectByte);
|
||||
|
||||
Prefetch(); // np
|
||||
|
||||
@ -2313,7 +2313,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
BeginState(MOVEPtoR_w):
|
||||
temporary_address_.l = registers_[8 + instruction_.reg(0)].l + uint32_t(int16_t(prefetch_.w));
|
||||
SetDataAddress(temporary_address_.l);
|
||||
SetupDataAccess(Microcycle::Read, Microcycle::SelectByte);
|
||||
SetupDataAccess(Operation::Read, Operation::SelectByte);
|
||||
|
||||
Prefetch(); // np
|
||||
|
||||
@ -2353,7 +2353,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
register_index_ = 0;
|
||||
|
||||
SetDataAddress(effective_address_[1].l);
|
||||
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
|
||||
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
||||
|
||||
switch(instruction_.mode(1)) {
|
||||
case Mode::AddressRegisterIndirectWithIndex8bitDisplacement:
|
||||
@ -2426,7 +2426,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
BeginState(MOVEMtoM):
|
||||
next_operand_ = 1;
|
||||
SetDataAddress(effective_address_[1].l);
|
||||
SetupDataAccess(0, Microcycle::SelectWord);
|
||||
SetupDataAccess(0, Operation::SelectWord);
|
||||
|
||||
register_index_ = 0;
|
||||
post_ea_state_ =
|
||||
@ -2625,20 +2625,20 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
//
|
||||
BeginState(TAS):
|
||||
// Populate all addresses.
|
||||
tas_cycles[0].address = tas_cycles[1].address =
|
||||
tas_cycles[2].address =
|
||||
tas_cycles[3].address = tas_cycles[4].address = &effective_address_[0].l;
|
||||
tas_cycle0.address = tas_cycle1.address =
|
||||
tas_cycle2.address =
|
||||
tas_cycle3.address = tas_cycle4.address = &effective_address_[0].l;
|
||||
|
||||
// Populate values to the relevant subset.
|
||||
tas_cycles[0].value = tas_cycles[1].value =
|
||||
tas_cycles[3].value = tas_cycles[4].value = &operand_[0].low;
|
||||
tas_cycle0.value = tas_cycle1.value =
|
||||
tas_cycle3.value = tas_cycle4.value = &operand_[0].low;
|
||||
|
||||
// First two parts: the read.
|
||||
PerformBusOperation(tas_cycles[0], TASOperations[0]);
|
||||
CompleteAccess(tas_cycles[1], TASOperations[1]);
|
||||
PerformBusOperation(tas_cycle0);
|
||||
CompleteAccess(tas_cycle1);
|
||||
|
||||
// Third part: processing time.
|
||||
PerformBusOperation(tas_cycles[2], TASOperations[2]);
|
||||
PerformBusOperation(tas_cycle2);
|
||||
|
||||
// Do the actual TAS operation.
|
||||
status_.overflow_flag = status_.carry_flag = 0;
|
||||
@ -2647,8 +2647,8 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
|
||||
// Final parts: write back.
|
||||
operand_[0].b |= 0x80;
|
||||
PerformBusOperation(tas_cycles[3], TASOperations[3]);
|
||||
CompleteAccess(tas_cycles[4], TASOperations[4]);
|
||||
PerformBusOperation(tas_cycle3);
|
||||
CompleteAccess(tas_cycle4);
|
||||
|
||||
Prefetch();
|
||||
MoveToStateSpecific(Decode);
|
||||
@ -2670,7 +2670,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
// RTR, RTS, RTE
|
||||
//
|
||||
BeginState(RTS):
|
||||
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
|
||||
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
||||
SetDataAddress(registers_[15].l);
|
||||
|
||||
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.
|
||||
|
||||
BeginState(RTE):
|
||||
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
|
||||
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
||||
SetDataAddress(registers_[15].l);
|
||||
|
||||
registers_[15].l += 2;
|
||||
@ -2709,7 +2709,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
MoveToStateSpecific(Decode);
|
||||
|
||||
BeginState(RTR):
|
||||
SetupDataAccess(Microcycle::Read, Microcycle::SelectWord);
|
||||
SetupDataAccess(Operation::Read, Operation::SelectWord);
|
||||
SetDataAddress(registers_[15].l);
|
||||
|
||||
registers_[15].l += 2;
|
||||
@ -2762,7 +2762,7 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
//
|
||||
BeginState(RESET):
|
||||
IdleBus(2);
|
||||
PerformBusOperation(reset_cycle, ResetOperation);
|
||||
PerformBusOperation(reset_cycle);
|
||||
Prefetch();
|
||||
MoveToStateSpecific(Decode);
|
||||
|
||||
@ -3088,13 +3088,13 @@ void Processor<BusHandler, dtack_is_implicit, permit_overrun, signal_will_perfor
|
||||
captured_interrupt_level_ = bus_interrupt_level_;
|
||||
|
||||
read_program.value = &prefetch_.high;
|
||||
bus_handler_.template perform_bus_operation<ReadProgramAnnounceOperation>(read_program_announce, is_supervisor_);
|
||||
bus_handler_.template perform_bus_operation<ReadProgramOperation>(read_program, is_supervisor_);
|
||||
bus_handler_.perform_bus_operation(read_program_announce, is_supervisor_);
|
||||
bus_handler_.perform_bus_operation(read_program, is_supervisor_);
|
||||
program_counter_.l += 2;
|
||||
|
||||
read_program.value = &prefetch_.low;
|
||||
bus_handler_.template perform_bus_operation<ReadProgramAnnounceOperation>(read_program_announce, is_supervisor_);
|
||||
bus_handler_.template perform_bus_operation<ReadProgramOperation>(read_program, is_supervisor_);
|
||||
bus_handler_.perform_bus_operation(read_program_announce, is_supervisor_);
|
||||
bus_handler_.perform_bus_operation(read_program, is_supervisor_);
|
||||
program_counter_.l += 2;
|
||||
}
|
||||
|
||||
|
@ -138,10 +138,10 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
|
||||
|
||||
/// Used by some dedicated read-modify-write perform patterns to
|
||||
/// determine the size of the bus operation.
|
||||
Microcycle::OperationT select_flag_ = 0;
|
||||
OperationT select_flag_ = 0;
|
||||
|
||||
// Captured bus/address-error state.
|
||||
Microcycle bus_error_;
|
||||
Microcycle<Operation::DecodeDynamically> bus_error_;
|
||||
|
||||
// Flow controller methods implemented.
|
||||
using Preinstruction = InstructionSet::M68k::Preinstruction;
|
||||
@ -181,58 +181,54 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController {
|
||||
// Some microcycles that will be modified as required and used in the main loop;
|
||||
// the semantics of a switch statement make in-place declarations awkward and
|
||||
// some of these may persist across multiple calls to run_for.
|
||||
Microcycle idle{0};
|
||||
Microcycle<OperationT(0)> idle;
|
||||
|
||||
// Read a program word. All accesses via the program counter are word sized.
|
||||
static constexpr Microcycle::OperationT
|
||||
ReadProgramAnnounceOperation = Microcycle::Read | Microcycle::NewAddress | Microcycle::IsProgram;
|
||||
static constexpr Microcycle::OperationT
|
||||
ReadProgramOperation = Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectWord | Microcycle::IsProgram;
|
||||
Microcycle read_program_announce { ReadProgramAnnounceOperation };
|
||||
Microcycle read_program { ReadProgramOperation };
|
||||
static constexpr OperationT
|
||||
ReadProgramAnnounceOperation = Operation::Read | Operation::NewAddress | Operation::IsProgram;
|
||||
static constexpr OperationT
|
||||
ReadProgramOperation = Operation::Read | Operation::SameAddress | Operation::SelectWord | Operation::IsProgram;
|
||||
Microcycle<ReadProgramAnnounceOperation> read_program_announce{};
|
||||
Microcycle<ReadProgramOperation> read_program{};
|
||||
|
||||
// Read a data word or byte.
|
||||
Microcycle access_announce {
|
||||
Microcycle::Read | Microcycle::NewAddress | Microcycle::IsData
|
||||
Microcycle<Operation::DecodeDynamically> access_announce {
|
||||
Operation::Read | Operation::NewAddress | Operation::IsData
|
||||
};
|
||||
Microcycle access {
|
||||
Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectWord | Microcycle::IsData
|
||||
Microcycle<Operation::DecodeDynamically> access {
|
||||
Operation::Read | Operation::SameAddress | Operation::SelectWord | Operation::IsData
|
||||
};
|
||||
|
||||
// TAS.
|
||||
static constexpr Microcycle::OperationT
|
||||
static constexpr 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,
|
||||
Operation::Read | Operation::NewAddress | Operation::IsData,
|
||||
Operation::Read | Operation::SameAddress | Operation::IsData | Operation::SelectByte,
|
||||
Operation::SameAddress,
|
||||
Operation::SameAddress | Operation::IsData,
|
||||
Operation::SameAddress | Operation::IsData | Operation::SelectByte,
|
||||
};
|
||||
Microcycle tas_cycles[5] = {
|
||||
{ TASOperations[0] },
|
||||
{ TASOperations[1] },
|
||||
{ TASOperations[2] },
|
||||
{ TASOperations[3] },
|
||||
{ TASOperations[4] },
|
||||
};
|
||||
Microcycle<TASOperations[0]> tas_cycle0;
|
||||
Microcycle<TASOperations[1]> tas_cycle1;
|
||||
Microcycle<TASOperations[2]> tas_cycle2;
|
||||
Microcycle<TASOperations[3]> tas_cycle3;
|
||||
Microcycle<TASOperations[4]> tas_cycle4;
|
||||
|
||||
// Reset.
|
||||
static constexpr Microcycle::OperationT ResetOperation = Microcycle::Reset;
|
||||
Microcycle reset_cycle { ResetOperation, HalfCycles(248) };
|
||||
static constexpr OperationT ResetOperation = CPU::MC68000::Operation::Reset;
|
||||
Microcycle<ResetOperation> reset_cycle { HalfCycles(248) };
|
||||
|
||||
// Interrupt acknowledge.
|
||||
static constexpr Microcycle::OperationT
|
||||
static constexpr OperationT
|
||||
InterruptCycleOperations[2] = {
|
||||
Microcycle::InterruptAcknowledge | Microcycle::Read | Microcycle::NewAddress,
|
||||
Microcycle::InterruptAcknowledge | Microcycle::Read | Microcycle::SameAddress | Microcycle::SelectByte
|
||||
Operation::InterruptAcknowledge | Operation::Read | Operation::NewAddress,
|
||||
Operation::InterruptAcknowledge | Operation::Read | Operation::SameAddress | Operation::SelectByte
|
||||
};
|
||||
Microcycle interrupt_cycles[2] = {
|
||||
{ InterruptCycleOperations[0] },
|
||||
{ InterruptCycleOperations[1] },
|
||||
};
|
||||
Microcycle<InterruptCycleOperations[0]> interrupt_cycle0;
|
||||
Microcycle<InterruptCycleOperations[1]> interrupt_cycle1;
|
||||
|
||||
// Holding spot when awaiting DTACK/etc.
|
||||
Microcycle awaiting_dtack;
|
||||
Microcycle<Operation::DecodeDynamically> awaiting_dtack;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user