1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-26 10:29:31 +00:00

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.

This commit is contained in:
Thomas Harte 2017-07-27 20:17:13 -04:00
parent 25fd95044c
commit 37950143fc
10 changed files with 49 additions and 39 deletions

View File

@ -61,7 +61,7 @@ typedef NS_ENUM(NSInteger, CSTestMachineZ80Register) {
@property(nonatomic, readonly, nonnull) NSArray<CSTestMachineZ80BusOperationCapture *> *busOperationCaptures;
@property(nonatomic, readonly) BOOL isHalted;
@property(nonatomic, readonly) int completedCycles;
@property(nonatomic, readonly) int completedHalfCycles;
@property(nonatomic) BOOL nmiLine;
@property(nonatomic) BOOL irqLine;

View File

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

View File

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

View File

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

View File

@ -21,7 +21,7 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<Concrete
}
inline Cycles perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) {
timestamp_++;
timestamp_ += Cycles(1);
if(operation == BusOperation::ReadOpcode) {
check_address_for_trap(address);

View File

@ -25,7 +25,7 @@ void AllRAMProcessor::get_data_at_address(uint16_t startAddress, size_t length,
memcpy(data, &memory_[startAddress], endAddress - startAddress);
}
uint32_t AllRAMProcessor::get_timestamp() {
HalfCycles AllRAMProcessor::get_timestamp() {
return timestamp_;
}

View File

@ -13,12 +13,14 @@
#include <set>
#include <vector>
#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<uint8_t> memory_;
uint32_t timestamp_;
HalfCycles timestamp_;
inline void check_address_for_trap(uint16_t address) {
if(traps_[address]) {

View File

@ -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 T> 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 T> 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 T> 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 T> 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 },

View File

@ -17,14 +17,14 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<Concrete
ConcreteAllRAMProcessor() : AllRAMProcessor() {}
inline Cycles perform_machine_cycle(const PartialMachineCycle &cycle) {
timestamp_ += cycle.length.as_int();
timestamp_ += cycle.length;
if(!cycle.is_terminal()) {
return Cycles(0);
}
uint16_t address = cycle.address ? *cycle.address : 0x0000;
switch(cycle.operation) {
case PartialMachineCycle::ReadOpcodeStart:
case PartialMachineCycle::ReadOpcode:
check_address_for_trap(address);
case PartialMachineCycle::Read:
*cycle.value = memory_[address];
@ -57,7 +57,7 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<Concrete
}
if(delegate_ != nullptr) {
delegate_->z80_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<Concrete
void set_wait_line(bool value) {
CPU::Z80::Processor<ConcreteAllRAMProcessor>::set_wait_line(value);
}
uint32_t get_timestamp() {
return timestamp_ >> 1;
}
};
}

View File

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