From 85f814c632caac2d0f8d3476be8ef40cbe71e00d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 21 Dec 2023 23:08:18 -0500 Subject: [PATCH] Attempt to build fixed operations into type. This simplifies callees and should make all helper functions automatically able to optimise themselves for fixed operations. --- Machines/Amiga/Amiga.cpp | 21 ++-- Machines/Amiga/Chipset.cpp | 11 -- Machines/Amiga/Chipset.hpp | 10 +- Machines/Amiga/MemoryMap.hpp | 7 +- Machines/Apple/Macintosh/Macintosh.cpp | 46 ++++--- Machines/Atari/ST/AtariST.cpp | 43 +++---- .../68000ComparativeTests.mm | 4 +- .../Mac/Clock SignalTests/EmuTOSTests.mm | 3 +- OSBindings/Mac/Clock SignalTests/QLTests.mm | 6 +- .../Mac/Clock SignalTests/TestRunner68000.hpp | 3 +- Processors/68000/68000.hpp | 116 +++++++++++------- .../Implementation/68000Implementation.hpp | 100 +++++++-------- .../68000/Implementation/68000Storage.hpp | 32 ++--- 13 files changed, 207 insertions(+), 195 deletions(-) diff --git a/Machines/Amiga/Amiga.cpp b/Machines/Amiga/Amiga.cpp index cf85dc1e1..d6e314f80 100644 --- a/Machines/Amiga/Amiga.cpp +++ b/Machines/Amiga/Amiga.cpp @@ -80,13 +80,10 @@ class ConcreteMachine: } // MARK: - MC68000::BusHandler. - using Microcycle = CPU::MC68000::Microcycle; - template HalfCycles perform_bus_operation(const Microcycle &cycle, int) { - const auto operation = (op != CPU::MC68000::Operation::DecodeDynamically) ? op : cycle.operation; - + template 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 & CPU::MC68000::Operation::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 & CPU::MC68000::Operation::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 & CPU::MC68000::Operation::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 & (CPU::MC68000::Operation::NewAddress | CPU::MC68000::Operation::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 & (CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::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 & CPU::MC68000::Operation::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 & CPU::MC68000::Operation::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 & CPU::MC68000::Operation::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()); } } } diff --git a/Machines/Amiga/Chipset.cpp b/Machines/Amiga/Chipset.cpp index 70821a7ca..80c5dc683 100644 --- a/Machines/Amiga/Chipset.cpp +++ b/Machines/Amiga/Chipset.cpp @@ -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 & CPU::MC68000::Operation::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) { \ diff --git a/Machines/Amiga/Chipset.hpp b/Machines/Amiga/Chipset.hpp index 5b3ac8b10..5f1a2c3dd 100644 --- a/Machines/Amiga/Chipset.hpp +++ b/Machines/Amiga/Chipset.hpp @@ -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 + 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); diff --git a/Machines/Amiga/MemoryMap.hpp b/Machines/Amiga/MemoryMap.hpp index 575aa02af..91f2de5ff 100644 --- a/Machines/Amiga/MemoryMap.hpp +++ b/Machines/Amiga/MemoryMap.hpp @@ -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,7 +109,8 @@ 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 + bool perform(const Microcycle &cycle) { if(!fast_autoconf_visible_) return false; const uint32_t register_address = *cycle.address & 0xfe; diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 102349c92..ccbb589d5 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -191,15 +191,12 @@ template class ConcreteMachin mc68000_.run_for(cycles); } - using Microcycle = CPU::MC68000::Microcycle; - template HalfCycles perform_bus_operation(const Microcycle &cycle, int) { - const auto operation = (op != CPU::MC68000::Operation::DecodeDynamically) ? op : cycle.operation; - + template HalfCycles perform_bus_operation(const Microcycle &cycle, int) { // Advance time. advance_time(cycle.length); // A null cycle leaves nothing else to do. - if(!(operation & (CPU::MC68000::Operation::NewAddress | CPU::MC68000::Operation::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(); @@ -220,8 +217,8 @@ template class ConcreteMachin // for interrupt acknowledge cycles always has all bits set except the // lowest explicit address lines. if( - !CPU::MC68000::Operation::data_select_active(operation) || - (operation & CPU::MC68000::Operation::InterruptAcknowledge) + !cycle.data_select_active() || + (cycle.operation & CPU::MC68000::Operation::InterruptAcknowledge) ) return HalfCycles(0); // Grab the word-precision address being accessed. @@ -231,18 +228,18 @@ template class ConcreteMachin default: assert(false); case BusDevice::Unassigned: - fill_unmapped(cycle); + fill_unmapped(cycle); return delay; case BusDevice::VIA: { if(*cycle.address & 1) { - fill_unmapped(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 & CPU::MC68000::Operation::Read) { + if(cycle.operation & CPU::MC68000::Operation::Read) { cycle.set_value8_high(via_.read(register_address)); } else { via_.write(register_address, cycle.value8_high()); @@ -251,7 +248,7 @@ template class ConcreteMachin } return delay; case BusDevice::PhaseRead: { - if(operation & CPU::MC68000::Operation::Read) { + if(cycle.operation & CPU::MC68000::Operation::Read) { cycle.set_value8_low(phase_ & 7); } } return delay; @@ -261,13 +258,13 @@ template class ConcreteMachin const int register_address = address >> 9; // The IWM; this is a purely polled device, so can be run on demand. - if(operation & CPU::MC68000::Operation::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(cycle); + fill_unmapped(cycle); } } return delay; @@ -278,14 +275,14 @@ template 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 & CPU::MC68000::Operation::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 & CPU::MC68000::Operation::Read) { + if(cycle.operation & CPU::MC68000::Operation::Read) { cycle.set_value8_high(scsi_.read(register_address, dma_acknowledge)); } } @@ -293,19 +290,19 @@ template class ConcreteMachin case BusDevice::SCCReadResetPhase: { // Any word access here adjusts phase. - if(operation & CPU::MC68000::Operation::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 & CPU::MC68000::Operation::Read) { + if(cycle.operation & CPU::MC68000::Operation::Read) { cycle.set_value16(0xffff); } } else { const auto read = scc_.read(int(address >> 1)); - if(operation & CPU::MC68000::Operation::Read) { + if(cycle.operation & CPU::MC68000::Operation::Read) { cycle.set_value8_high(read); } } @@ -314,20 +311,20 @@ template class ConcreteMachin case BusDevice::SCCWrite: { // Any word access here adjusts phase. - if(operation & CPU::MC68000::Operation::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 & CPU::MC68000::Operation::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(cycle); + fill_unmapped(cycle); } } } return delay; @@ -355,7 +352,7 @@ template class ConcreteMachin } break; case BusDevice::ROM: { - if(!(operation & CPU::MC68000::Operation::Read)) return delay; + if(!(cycle.operation & CPU::MC68000::Operation::Read)) return delay; memory_base = rom_; address &= rom_mask_; } break; @@ -547,10 +544,9 @@ template class ConcreteMachin ++phase_; } - template + template forceinline void fill_unmapped(const Microcycle &cycle) { - const auto operation = (op != CPU::MC68000::Operation::DecodeDynamically) ? op : cycle.operation; - if(!(operation & CPU::MC68000::Operation::Read)) return; + if(!(cycle.operation & CPU::MC68000::Operation::Read)) return; cycle.set_value16(0xffff); } diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index 9f093b51a..84c74aa2c 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -176,10 +176,7 @@ class ConcreteMachine: } // MARK: MC68000::BusHandler - using Microcycle = CPU::MC68000::Microcycle; - template HalfCycles perform_bus_operation(const Microcycle &cycle, int is_supervisor) { - const auto operation = (op != CPU::MC68000::Operation::DecodeDynamically) ? op : cycle.operation; - + template 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 & CPU::MC68000::Operation::Reset) { + if(cycle.operation & CPU::MC68000::Operation::Reset) { LOG("Unhandled Reset"); } // A null cycle leaves nothing else to do. - if(!(operation & (CPU::MC68000::Operation::NewAddress | CPU::MC68000::Operation::SameAddress))) return HalfCycles(0); + if(!(cycle.operation & (CPU::MC68000::Operation::NewAddress | CPU::MC68000::Operation::SameAddress))) return HalfCycles(0); // An interrupt acknowledge, perhaps? - if(operation & CPU::MC68000::Operation::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 & CPU::MC68000::Operation::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 & CPU::MC68000::Operation::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 & CPU::MC68000::Operation::Read)) { + if(!(cycle.operation & CPU::MC68000::Operation::Read)) { return delay; } address -= rom_start_; @@ -271,7 +268,7 @@ 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 & (CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read)) { + switch(cycle.operation & (CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read)) { default: break; case CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::Read: cycle.value->w = 0xffff; @@ -313,9 +310,9 @@ class ConcreteMachine: case 0x8250: case 0x8252: case 0x8254: case 0x8256: case 0x8258: case 0x825a: case 0x825c: case 0x825e: case 0x8260: case 0x8262: - if(!CPU::MC68000::Operation::data_select_active(operation)) return delay; + if(!cycle.data_select_active()) return delay; - if(operation & CPU::MC68000::Operation::Read) { + if(cycle.operation & CPU::MC68000::Operation::Read) { cycle.set_value16(video_->read(int(address >> 1))); } else { video_->write(int(address >> 1), cycle.value16()); @@ -324,9 +321,9 @@ class ConcreteMachine: // DMA. case 0x8604: case 0x8606: case 0x8608: case 0x860a: case 0x860c: - if(!CPU::MC68000::Operation::data_select_active(operation)) return delay; + if(!cycle.data_select_active()) return delay; - if(operation & CPU::MC68000::Operation::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,12 +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(!CPU::MC68000::Operation::data_select_active(operation)) return delay; + if(!cycle.data_select_active()) return delay; advance_time(HalfCycles(2)); update_audio(); - if(operation & CPU::MC68000::Operation::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, @@ -379,9 +376,9 @@ class ConcreteMachine: case 0xfa28: case 0xfa2a: case 0xfa2c: case 0xfa2e: case 0xfa30: case 0xfa32: case 0xfa34: case 0xfa36: case 0xfa38: case 0xfa3a: case 0xfa3c: case 0xfa3e: - if(!CPU::MC68000::Operation::data_select_active(operation)) return delay; + if(!cycle.data_select_active()) return delay; - if(operation & CPU::MC68000::Operation::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()); @@ -391,11 +388,11 @@ class ConcreteMachine: // ACIAs. case 0xfc00: case 0xfc02: case 0xfc04: case 0xfc06: { // Set VPA. - mc68000_.set_is_peripheral_address(!CPU::MC68000::Operation::data_select_active(operation)); - if(!CPU::MC68000::Operation::data_select_active(operation)) return delay; + mc68000_.set_is_peripheral_address(!cycle.data_select_active()); + if(!cycle.data_select_active()) return delay; const auto acia_ = (address & 4) ? &midi_acia_ : &keyboard_acia_; - if(operation & CPU::MC68000::Operation::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()); @@ -409,7 +406,7 @@ 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 & (CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read)) { + switch(cycle.operation & (CPU::MC68000::Operation::SelectWord | CPU::MC68000::Operation::SelectByte | CPU::MC68000::Operation::Read)) { default: break; diff --git a/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm index 34ca473c4..c58f58b44 100644 --- a/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm @@ -96,8 +96,8 @@ struct TestProcessor: public CPU::MC68000::BusHandler { if(!instructions_remaining_) comparitor(); } - using Microcycle = CPU::MC68000::Microcycle; - template HalfCycles perform_bus_operation(const Microcycle &cycle, int) { + + template HalfCycles perform_bus_operation(const Microcycle &cycle, int) { if(cycle.data_select_active()) { cycle.apply(&ram[cycle.host_endian_byte_address()]); } diff --git a/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm b/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm index 343b61399..698ef7913 100644 --- a/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm +++ b/OSBindings/Mac/Clock SignalTests/EmuTOSTests.mm @@ -36,8 +36,7 @@ class EmuTOS: public ComparativeBusHandler { return m68000_.get_state(); } - using Microcycle = CPU::MC68000::Microcycle; - template HalfCycles perform_bus_operation(const Microcycle &cycle, int) { + template perform_bus_operation(const Microcycle &cycle, int) { const uint32_t address = cycle.word_address(); uint32_t word_address = address; diff --git a/OSBindings/Mac/Clock SignalTests/QLTests.mm b/OSBindings/Mac/Clock SignalTests/QLTests.mm index 5ac39ff1d..9c656480d 100644 --- a/OSBindings/Mac/Clock SignalTests/QLTests.mm +++ b/OSBindings/Mac/Clock SignalTests/QLTests.mm @@ -39,8 +39,7 @@ class QL: public ComparativeBusHandler { return m68000_.get_state(); } - using Microcycle = CPU::MC68000::Microcycle; - template HalfCycles perform_bus_operation(const Microcycle &cycle, int) { + template 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: diff --git a/OSBindings/Mac/Clock SignalTests/TestRunner68000.hpp b/OSBindings/Mac/Clock SignalTests/TestRunner68000.hpp index 973251c49..d6fa8e94f 100644 --- a/OSBindings/Mac/Clock SignalTests/TestRunner68000.hpp +++ b/OSBindings/Mac/Clock SignalTests/TestRunner68000.hpp @@ -78,8 +78,7 @@ class RAM68000: public CPU::MC68000::BusHandler { return &ram_[(address >> 1) % ram_.size()]; } - using Microcycle = CPU::MC68000::Microcycle; - template HalfCycles perform_bus_operation(const Microcycle &cycle, int) { + template HalfCycles perform_bus_operation(const Microcycle &cycle, int) { const uint32_t word_address = cycle.word_address(); duration_ += cycle.length; diff --git a/Processors/68000/68000.hpp b/Processors/68000/68000.hpp index 05b3c1755..610bbfb38 100644 --- a/Processors/68000/68000.hpp +++ b/Processors/68000/68000.hpp @@ -13,6 +13,8 @@ #include "../../Numeric/RegisterSizes.hpp" #include "../../InstructionSets/M68k/RegisterSet.hpp" +#include + namespace CPU::MC68000 { using OperationT = unsigned int; @@ -68,13 +70,23 @@ static constexpr OperationT BusGrant = 1 << 12; /// .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)); -} +// 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 +struct MicrocycleOperationStorage { + static constexpr auto operation = op; +}; +template <> +struct MicrocycleOperationStorage { + 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. @@ -98,13 +110,10 @@ constexpr bool data_select_active(OperationT operation) { 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 { +template +struct Microcycle: public MicrocycleOperationStorage { using OperationT = OperationT; - /// Contains a valid combination of the various static constexpr int flags, describing the operation - /// performed by this Microcycle. - OperationT operation = 0; - /// Describes the duration of this Microcycle. HalfCycles length = HalfCycles(4); @@ -135,21 +144,46 @@ 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::operation = dynamic_operation; + } else { + assert(MicrocycleOperationStorage::operation == dynamic_operation); + } + } + constexpr Microcycle(OperationT dynamic_operation, HalfCycles length) noexcept : + Microcycle(dynamic_operation), length(length) {} + constexpr Microcycle(HalfCycles length) noexcept { + static_assert(op != Operation::DecodeDynamically); + this->length = length; + } + + template + Microcycle &operator =(const MicrocycleRHS &rhs) { + static_assert(op == Operation::DecodeDynamically); + /* TODO */ + this->operation = rhs.operation; + + return *this; + } /// @returns @c true if two Microcycles are equal; @c false otherwise. bool operator ==(const Microcycle &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. */ + bool data_select_active() const { + return bool(this->operation & (Operation::SelectWord | Operation::SelectByte | Operation::InterruptAcknowledge)); + } + /*! @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 @@ -190,14 +224,14 @@ struct Microcycle { @returns non-zero if the 68000 LDS is asserted; zero otherwise. */ forceinline int lower_data_select() const { - return (operation & Operation::SelectByte & *address) | (operation & 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 & Operation::SelectByte & ~*address) | (operation & Operation::SelectWord); + return (this->operation & Operation::SelectByte & ~*address) | (this->operation & Operation::SelectWord); } /*! @@ -232,9 +266,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 & Operation::SelectByte)) & 0xffffff; + return (*address ^ (this->operation & Operation::SelectByte)) & 0xff'ffff; #endif } @@ -245,7 +279,7 @@ struct Microcycle { */ forceinline uint16_t value16() const { const uint16_t values[] = { value->w, uint16_t((value->b << 8) | value->b) }; - return values[operation & Operation::SelectByte]; + return values[this->operation & Operation::SelectByte]; } /*! @@ -253,7 +287,7 @@ struct Microcycle { */ forceinline uint8_t value8_high() const { const uint8_t values[] = { uint8_t(value->w >> 8), value->b}; - return values[operation & Operation::SelectByte]; + return values[this->operation & Operation::SelectByte]; } /*! @@ -268,8 +302,8 @@ struct Microcycle { currently being read. Assumes this is a read cycle. */ forceinline void set_value16(uint16_t v) const { - assert(operation & Operation::Read); - if(operation & Operation::SelectWord) { + assert(this->operation & Operation::Read); + if(this->operation & Operation::SelectWord) { value->w = v; } else { value->b = uint8_t(v >> byte_shift()); @@ -280,8 +314,8 @@ struct Microcycle { Equivalent to set_value16((v << 8) | 0x00ff). */ forceinline void set_value8_high(uint8_t v) const { - assert(operation & Operation::Read); - if(operation & Operation::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()); @@ -292,20 +326,14 @@ struct Microcycle { Equivalent to set_value16(v | 0xff00). */ forceinline void set_value8_low(uint8_t v) const { - assert(operation & Operation::Read); - if(operation & Operation::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: @@ -314,27 +342,27 @@ struct Microcycle { * 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 & (Operation::SelectWord | Operation::SelectByte)) != (Operation::SelectWord | Operation::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) & (Operation::SelectWord | Operation::SelectByte | Operation::Read | PermitRead | PermitWrite)) { + switch((this->operation | read_write_mask) & (Operation::SelectWord | Operation::SelectByte | Operation::Read | Operation::PermitRead | Operation::PermitWrite)) { default: break; - case Operation::SelectWord | Operation::Read | PermitRead: - case Operation::SelectWord | Operation::Read | PermitRead | PermitWrite: + case Operation::SelectWord | Operation::Read | Operation::PermitRead: + case Operation::SelectWord | Operation::Read | Operation::PermitRead | Operation::PermitWrite: value->w = *reinterpret_cast(target); break; - case Operation::SelectByte | Operation::Read | PermitRead: - case Operation::SelectByte | Operation::Read | PermitRead | PermitWrite: + case Operation::SelectByte | Operation::Read | Operation::PermitRead: + case Operation::SelectByte | Operation::Read | Operation::PermitRead | Operation::PermitWrite: value->b = *target; break; - case Operation::SelectWord | PermitWrite: - case Operation::SelectWord | PermitWrite | PermitRead: + case Operation::SelectWord | Operation::PermitWrite: + case Operation::SelectWord | Operation::PermitWrite | Operation::PermitRead: *reinterpret_cast(target) = value->w; break; - case Operation::SelectByte | PermitWrite: - case Operation::SelectByte | PermitWrite | PermitRead: + case Operation::SelectByte | Operation::PermitWrite: + case Operation::SelectByte | Operation::PermitWrite | Operation::PermitRead: *target = value->b; break; } @@ -357,8 +385,8 @@ class BusHandler { can be used to select an appropriate execution path at compile time. Otherwise cycle.operation must be inspected at runtime. */ - template - HalfCycles perform_bus_operation([[maybe_unused]] const Microcycle &cycle, [[maybe_unused]] int is_supervisor) { + template + HalfCycles perform_bus_operation(const Microcycle &, [[maybe_unused]] int is_supervisor) { return HalfCycles(0); } diff --git a/Processors/68000/Implementation/68000Implementation.hpp b/Processors/68000/Implementation/68000Implementation.hpp index 6860094eb..bfb07738f 100644 --- a/Processors/68000/Implementation/68000Implementation.hpp +++ b/Processors/68000/Implementation/68000Implementation.hpp @@ -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 Microcycle::OperationT data_select(const InstructionT &instruction) { - return Microcycle::OperationT(1 << int(instruction.operand_size())); +template OperationT data_select(const InstructionT &instruction) { + return OperationT(1 << int(instruction.operand_size())); } // MARK: - The state machine. @@ -282,8 +282,8 @@ void Processor(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> 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) \ @@ -351,15 +351,15 @@ void Processor(read_program_announce, is_supervisor_); - bus_handler_.template perform_bus_operation(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(read_program_announce, is_supervisor_); - bus_handler_.template perform_bus_operation(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; } diff --git a/Processors/68000/Implementation/68000Storage.hpp b/Processors/68000/Implementation/68000Storage.hpp index 40d03d718..e9332133d 100644 --- a/Processors/68000/Implementation/68000Storage.hpp +++ b/Processors/68000/Implementation/68000Storage.hpp @@ -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 bus_error_; // Flow controller methods implemented. using Preinstruction = InstructionSet::M68k::Preinstruction; @@ -181,26 +181,26 @@ 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 idle; // Read a program word. All accesses via the program counter are word sized. - static constexpr Microcycle::OperationT + static constexpr OperationT ReadProgramAnnounceOperation = Operation::Read | Operation::NewAddress | Operation::IsProgram; - static constexpr Microcycle::OperationT + static constexpr OperationT ReadProgramOperation = Operation::Read | Operation::SameAddress | Operation::SelectWord | Operation::IsProgram; - Microcycle read_program_announce { ReadProgramAnnounceOperation }; - Microcycle read_program { ReadProgramOperation }; + Microcycle read_program_announce{}; + Microcycle read_program{}; // Read a data word or byte. - Microcycle access_announce { + Microcycle access_announce { Operation::Read | Operation::NewAddress | Operation::IsData }; - Microcycle access { + Microcycle access { Operation::Read | Operation::SameAddress | Operation::SelectWord | Operation::IsData }; // TAS. - static constexpr Microcycle::OperationT + static constexpr OperationT TASOperations[5] = { Operation::Read | Operation::NewAddress | Operation::IsData, Operation::Read | Operation::SameAddress | Operation::IsData | Operation::SelectByte, @@ -208,7 +208,7 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController { Operation::SameAddress | Operation::IsData, Operation::SameAddress | Operation::IsData | Operation::SelectByte, }; - Microcycle tas_cycles[5] = { + Microcycle tas_cycles[5] = { { TASOperations[0] }, { TASOperations[1] }, { TASOperations[2] }, @@ -217,22 +217,22 @@ struct ProcessorBase: public InstructionSet::M68k::NullFlowController { }; // Reset. - static constexpr Microcycle::OperationT ResetOperation = CPU::MC68000::Operation::Reset; - Microcycle reset_cycle { ResetOperation, HalfCycles(248) }; + static constexpr OperationT ResetOperation = CPU::MC68000::Operation::Reset; + Microcycle reset_cycle { HalfCycles(248) }; // Interrupt acknowledge. - static constexpr Microcycle::OperationT + static constexpr OperationT InterruptCycleOperations[2] = { Operation::InterruptAcknowledge | Operation::Read | Operation::NewAddress, Operation::InterruptAcknowledge | Operation::Read | Operation::SameAddress | Operation::SelectByte }; - Microcycle interrupt_cycles[2] = { + Microcycle interrupt_cycles[2] = { { InterruptCycleOperations[0] }, { InterruptCycleOperations[1] }, }; // Holding spot when awaiting DTACK/etc. - Microcycle awaiting_dtack; + Microcycle awaiting_dtack; }; }