From 37950143fc551a4d59561a0f49af0a7c6074f595 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 27 Jul 2017 20:17:13 -0400 Subject: [PATCH] Attempted to nudge wait timing onto half-cycle boundaries, which expands the number of partial machine cycles the Z80 can post but pleasingly also regularises them. Switched the AllRAMProcessor to reporting half cycles by default and corrected all Z80 tests. --- .../Bridges/TestMachineZ80.h | 2 +- .../Bridges/TestMachineZ80.mm | 12 +++--- .../Mac/Clock SignalTests/FUSETests.swift | 4 +- .../Z80MachineCycleTests.swift | 4 +- Processors/6502/6502AllRAM.cpp | 2 +- Processors/AllRAMProcessor.cpp | 2 +- Processors/AllRAMProcessor.hpp | 6 ++- Processors/Z80/Z80.hpp | 42 ++++++++++++------- Processors/Z80/Z80AllRAM.cpp | 10 ++--- Processors/Z80/Z80AllRAM.hpp | 4 +- 10 files changed, 49 insertions(+), 39 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.h b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.h index 1abdf63d8..dd6ab350b 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.h +++ b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.h @@ -61,7 +61,7 @@ typedef NS_ENUM(NSInteger, CSTestMachineZ80Register) { @property(nonatomic, readonly, nonnull) NSArray *busOperationCaptures; @property(nonatomic, readonly) BOOL isHalted; -@property(nonatomic, readonly) int completedCycles; +@property(nonatomic, readonly) int completedHalfCycles; @property(nonatomic) BOOL nmiLine; @property(nonatomic) BOOL irqLine; diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm index ffb03ae0b..a2d4889ff 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm +++ b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm @@ -14,7 +14,7 @@ - (void)testMachineDidPerformBusOperation:(CPU::Z80::PartialMachineCycle::Operation)operation address:(uint16_t)address value:(uint8_t)value - timeStamp:(int)time_stamp; + timeStamp:(HalfCycles)time_stamp; @end #pragma mark - C++ delegate handlers @@ -23,7 +23,7 @@ class BusOperationHandler: public CPU::Z80::AllRAMProcessor::MemoryAccessDelegat public: BusOperationHandler(CSTestMachineZ80 *targetMachine) : target_(targetMachine) {} - void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, int time_stamp) { + void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, HalfCycles time_stamp) { [target_ testMachineDidPerformBusOperation:operation address:address value:value timeStamp:time_stamp]; } @@ -154,8 +154,8 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) { return _processor->get_halt_line() ? YES : NO; } -- (int)completedCycles { - return _processor->get_timestamp(); +- (int)completedHalfCycles { + return _processor->get_timestamp().as_int(); } - (void)setNmiLine:(BOOL)nmiLine { @@ -184,7 +184,7 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) { _processor->set_memory_access_delegate(captureBusActivity ? _busOperationHandler : nullptr); } -- (void)testMachineDidPerformBusOperation:(CPU::Z80::PartialMachineCycle::Operation)operation address:(uint16_t)address value:(uint8_t)value timeStamp:(int)timeStamp { +- (void)testMachineDidPerformBusOperation:(CPU::Z80::PartialMachineCycle::Operation)operation address:(uint16_t)address value:(uint8_t)value timeStamp:(HalfCycles)timeStamp { if(self.captureBusActivity) { CSTestMachineZ80BusOperationCapture *capture = [[CSTestMachineZ80BusOperationCapture alloc] init]; switch(operation) { @@ -216,7 +216,7 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) { } capture.address = address; capture.value = value; - capture.timeStamp = timeStamp; + capture.timeStamp = timeStamp.as_int(); [_busOperationCaptures addObject:capture]; } diff --git a/OSBindings/Mac/Clock SignalTests/FUSETests.swift b/OSBindings/Mac/Clock SignalTests/FUSETests.swift index c8c193b25..c3bd83c3a 100644 --- a/OSBindings/Mac/Clock SignalTests/FUSETests.swift +++ b/OSBindings/Mac/Clock SignalTests/FUSETests.swift @@ -195,8 +195,8 @@ class FUSETests: XCTestCase { machine.runForNumber(ofCycles: Int32(targetState.tStates)) // Verify that exactly the right number of cycles was hit; this is a primitive cycle length tester. - let cyclesRun = machine.completedCycles - XCTAssert(cyclesRun == Int32(targetState.tStates), "Instruction length off; was \(machine.completedCycles) but should be \(targetState.tStates): \(name)") + let halfCyclesRun = machine.completedHalfCycles + XCTAssert(halfCyclesRun == Int32(targetState.tStates) * 2, "Instruction length off; was \(machine.completedHalfCycles) but should be \(targetState.tStates * 2): \(name)") let finalState = RegisterState(machine: machine) diff --git a/OSBindings/Mac/Clock SignalTests/Z80MachineCycleTests.swift b/OSBindings/Mac/Clock SignalTests/Z80MachineCycleTests.swift index a8a115827..086eb1bcf 100644 --- a/OSBindings/Mac/Clock SignalTests/Z80MachineCycleTests.swift +++ b/OSBindings/Mac/Clock SignalTests/Z80MachineCycleTests.swift @@ -61,7 +61,9 @@ class Z80MachineCycleTests: XCTestCase { // array access break } else { - if length != busCycles[index].length || cycle.operation != busCycles[index].operation { + XCTAssert(length & Int32(1) == 0, "While performing \(machine.busOperationCaptures) Z80 ended on a half cycle") + + if length != busCycles[index].length*2 || cycle.operation != busCycles[index].operation { matches = false break; } diff --git a/Processors/6502/6502AllRAM.cpp b/Processors/6502/6502AllRAM.cpp index 11ec5cfda..8bc1b315d 100644 --- a/Processors/6502/6502AllRAM.cpp +++ b/Processors/6502/6502AllRAM.cpp @@ -21,7 +21,7 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor #include +#include "../ClockReceiver/ClockReceiver.hpp" + namespace CPU { class AllRAMProcessor { public: AllRAMProcessor(size_t memory_size); - virtual uint32_t get_timestamp(); + HalfCycles get_timestamp(); void set_data_at_address(uint16_t startAddress, size_t length, const uint8_t *data); void get_data_at_address(uint16_t startAddress, size_t length, uint8_t *data); @@ -31,7 +33,7 @@ class AllRAMProcessor { protected: std::vector memory_; - uint32_t timestamp_; + HalfCycles timestamp_; inline void check_address_for_trap(uint16_t address) { if(traps_[address]) { diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp index acff26426..ce4165264 100644 --- a/Processors/Z80/Z80.hpp +++ b/Processors/Z80/Z80.hpp @@ -67,8 +67,7 @@ enum Flag: uint8_t { */ struct PartialMachineCycle { enum Operation { - ReadOpcodeStart = 0, - ReadOpcodeWait, + ReadOpcode = 0, Read, Write, Input, @@ -79,16 +78,19 @@ struct PartialMachineCycle { Internal, BusAcknowledge, + ReadOpcodeWait, ReadWait, WriteWait, InputWait, OutputWait, InterruptWait, + ReadOpcodeStart, ReadStart, WriteStart, InputStart, OutputStart, + InterruptStart, } operation; HalfCycles length; uint16_t *address; @@ -107,28 +109,32 @@ struct PartialMachineCycle { }; // Elemental bus operations -#define ReadOpcodeStart() {PartialMachineCycle::ReadOpcodeStart, HalfCycles(4), &pc_.full, &operation_, false} +#define ReadOpcodeStart() {PartialMachineCycle::ReadOpcodeStart, HalfCycles(3), &pc_.full, &operation_, false} #define ReadOpcodeWait(f) {PartialMachineCycle::ReadOpcodeWait, HalfCycles(2), &pc_.full, &operation_, f} +#define ReadOpcodeEnd() {PartialMachineCycle::ReadOpcode, HalfCycles(1), &pc_.full, &operation_, false} + #define Refresh(len) {PartialMachineCycle::Refresh, HalfCycles(len), &refresh_addr_.full, nullptr, false} -#define ReadStart(addr, val) {PartialMachineCycle::ReadStart, HalfCycles(4), &addr.full, &val, false} +#define ReadStart(addr, val) {PartialMachineCycle::ReadStart, HalfCycles(3), &addr.full, &val, false} #define ReadWait(l, addr, val, f) {PartialMachineCycle::ReadWait, HalfCycles(l), &addr.full, &val, f} -#define ReadEnd(addr, val) {PartialMachineCycle::Read, HalfCycles(2), &addr.full, &val, false} +#define ReadEnd(addr, val) {PartialMachineCycle::Read, HalfCycles(3), &addr.full, &val, false} -#define WriteStart(addr, val) {PartialMachineCycle::WriteStart,HalfCycles(4), &addr.full, &val, false} +#define WriteStart(addr, val) {PartialMachineCycle::WriteStart,HalfCycles(3), &addr.full, &val, false} #define WriteWait(l, addr, val, f) {PartialMachineCycle::WriteWait, HalfCycles(l), &addr.full, &val, f} -#define WriteEnd(addr, val) {PartialMachineCycle::Write, HalfCycles(2), &addr.full, &val, false} +#define WriteEnd(addr, val) {PartialMachineCycle::Write, HalfCycles(3), &addr.full, &val, false} -#define InputStart(addr, val) {PartialMachineCycle::InputStart, HalfCycles(4), &addr.full, &val, false} +#define InputStart(addr, val) {PartialMachineCycle::InputStart, HalfCycles(3), &addr.full, &val, false} #define InputWait(addr, val, f) {PartialMachineCycle::InputWait, HalfCycles(2), &addr.full, &val, f} -#define InputEnd(addr, val) {PartialMachineCycle::Input, HalfCycles(2), &addr.full, &val, false} +#define InputEnd(addr, val) {PartialMachineCycle::Input, HalfCycles(3), &addr.full, &val, false} -#define OutputStart(addr, val) {PartialMachineCycle::OutputStart, HalfCycles(4), &addr.full, &val, false} +#define OutputStart(addr, val) {PartialMachineCycle::OutputStart, HalfCycles(3), &addr.full, &val, false} #define OutputWait(addr, val, f) {PartialMachineCycle::OutputWait, HalfCycles(2), &addr.full, &val, f} -#define OutputEnd(addr, val) {PartialMachineCycle::Output, HalfCycles(2), &addr.full, &val, false} +#define OutputEnd(addr, val) {PartialMachineCycle::Output, HalfCycles(3), &addr.full, &val, false} -#define IntAck(length, val) {PartialMachineCycle::Interrupt, HalfCycles(length), nullptr, &val, false} +#define IntAckStart(length, val) {PartialMachineCycle::InterruptStart, HalfCycles(length), nullptr, &val, false} #define IntWait(val) {PartialMachineCycle::InterruptWait, HalfCycles(2), nullptr, &val, true} +#define IntAckEnd(val) {PartialMachineCycle::Interrupt, HalfCycles(3), nullptr, &val, false} + // A wrapper to express a bus operation as a micro-op #define BusOp(op) {MicroOp::BusOperation, nullptr, nullptr, op} @@ -741,12 +747,14 @@ template class Processor { const MicroOp normal_fetch_decode_execute[] = { BusOp(ReadOpcodeStart()), BusOp(ReadOpcodeWait(true)), + BusOp(ReadOpcodeEnd()), { MicroOp::DecodeOperation } }; const MicroOp short_fetch_decode_execute[] = { BusOp(ReadOpcodeStart()), BusOp(ReadOpcodeWait(false)), BusOp(ReadOpcodeWait(true)), + BusOp(ReadOpcodeEnd()), { MicroOp::DecodeOperation } }; copy_program((length == 4) ? normal_fetch_decode_execute : short_fetch_decode_execute, target.fetch_decode_execute); @@ -813,6 +821,7 @@ template class Processor { { MicroOp::BeginNMI }, BusOp(ReadOpcodeStart()), BusOp(ReadOpcodeWait(true)), + BusOp(ReadOpcodeEnd()), BusOp(Refresh(6)), Push(pc_), { MicroOp::JumpTo66, nullptr, nullptr}, @@ -820,14 +829,16 @@ template class Processor { }; MicroOp irq_mode0_program[] = { { MicroOp::BeginIRQMode0 }, - BusOp(IntAck(8, operation_)), + BusOp(IntAckStart(5, operation_)), BusOp(IntWait(operation_)), + BusOp(IntAckEnd(operation_)), { MicroOp::DecodeOperationNoRChange } }; MicroOp irq_mode1_program[] = { { MicroOp::BeginIRQ }, - BusOp(IntAck(10, operation_)), + BusOp(IntAckStart(7, operation_)), BusOp(IntWait(operation_)), + BusOp(IntAckEnd(operation_)), BusOp(Refresh(4)), Push(pc_), { MicroOp::Move16, &temp16_.full, &pc_.full }, @@ -835,8 +846,9 @@ template class Processor { }; MicroOp irq_mode2_program[] = { { MicroOp::BeginIRQ }, - BusOp(IntAck(10, temp16_.bytes.low)), + BusOp(IntAckStart(7, temp16_.bytes.low)), BusOp(IntWait(temp16_.bytes.low)), + BusOp(IntAckEnd(temp16_.bytes.low)), BusOp(Refresh(4)), Push(pc_), { MicroOp::Move8, &ir_.bytes.high, &temp16_.bytes.high }, diff --git a/Processors/Z80/Z80AllRAM.cpp b/Processors/Z80/Z80AllRAM.cpp index 63a6744df..57c14a48f 100644 --- a/Processors/Z80/Z80AllRAM.cpp +++ b/Processors/Z80/Z80AllRAM.cpp @@ -17,14 +17,14 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processorz80_all_ram_processor_did_perform_bus_operation(*this, cycle.operation, address, cycle.value ? *cycle.value : 0x00, timestamp_ >> 1); + delegate_->z80_all_ram_processor_did_perform_bus_operation(*this, cycle.operation, address, cycle.value ? *cycle.value : 0x00, timestamp_); } return Cycles(0); @@ -94,10 +94,6 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor::set_wait_line(value); } - - uint32_t get_timestamp() { - return timestamp_ >> 1; - } }; } diff --git a/Processors/Z80/Z80AllRAM.hpp b/Processors/Z80/Z80AllRAM.hpp index 972401fbd..2fe5a0e3b 100644 --- a/Processors/Z80/Z80AllRAM.hpp +++ b/Processors/Z80/Z80AllRAM.hpp @@ -22,7 +22,7 @@ class AllRAMProcessor: static AllRAMProcessor *Processor(); struct MemoryAccessDelegate { - virtual void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, int time_stamp) = 0; + virtual void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, HalfCycles time_stamp) = 0; }; inline void set_memory_access_delegate(MemoryAccessDelegate *delegate) { delegate_ = delegate; @@ -38,8 +38,6 @@ class AllRAMProcessor: virtual void set_non_maskable_interrupt_line(bool value) = 0; virtual void set_wait_line(bool value) = 0; - virtual uint32_t get_timestamp() = 0; - protected: MemoryAccessDelegate *delegate_; AllRAMProcessor() : ::CPU::AllRAMProcessor(65536), delegate_(nullptr) {}