1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-08-15 14:27:29 +00:00

Sought to simplify the way partial machine cycles are communicated, for ease of machine implementation. Also implemented the wait line.

This commit is contained in:
Thomas Harte
2017-06-21 20:32:08 -04:00
parent 45f442ea63
commit 36e8a11505
6 changed files with 131 additions and 118 deletions

View File

@@ -59,6 +59,7 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) {
uint16_t refresh = 0; uint16_t refresh = 0;
uint16_t address = cycle.address ? *cycle.address : 0; uint16_t address = cycle.address ? *cycle.address : 0;
bool is_opcode_read = false;
switch(cycle.operation) { switch(cycle.operation) {
case CPU::Z80::MachineCycle::Operation::Output: case CPU::Z80::MachineCycle::Operation::Output:
set_vsync(false); set_vsync(false);
@@ -90,7 +91,8 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) {
horizontal_counter_ = 0; horizontal_counter_ = 0;
break; break;
case CPU::Z80::MachineCycle::Operation::ReadOpcode: case CPU::Z80::MachineCycle::Operation::ReadOpcodeStart:
case CPU::Z80::MachineCycle::Operation::ReadOpcodeWait:
// The ZX80 and 81 signal an interrupt while refresh is active and bit 6 of the refresh // The ZX80 and 81 signal an interrupt while refresh is active and bit 6 of the refresh
// address is low. The Z80 signals a refresh, providing the refresh address during the // address is low. The Z80 signals a refresh, providing the refresh address during the
// final two cycles of an opcode fetch. Therefore communicate a transient signalling // final two cycles of an opcode fetch. Therefore communicate a transient signalling
@@ -110,6 +112,7 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) {
return 0; return 0;
} }
} }
is_opcode_read = true;
case CPU::Z80::MachineCycle::Operation::Read: case CPU::Z80::MachineCycle::Operation::Read:
if(address < ram_base_) { if(address < ram_base_) {
@@ -120,7 +123,7 @@ int Machine::perform_machine_cycle(const CPU::Z80::MachineCycle &cycle) {
// If this is an M1 cycle reading from above the 32kb mark and HALT is not // If this is an M1 cycle reading from above the 32kb mark and HALT is not
// currently active, perform a video output and return a NOP. Otherwise, // currently active, perform a video output and return a NOP. Otherwise,
// just return the value as read. // just return the value as read.
if(cycle.operation == CPU::Z80::MachineCycle::Operation::ReadOpcode && address&0x8000 && !(value & 0x40) && !get_halt_line()) { if(is_opcode_read && address&0x8000 && !(value & 0x40) && !get_halt_line()) {
size_t char_address = (size_t)((refresh & 0xff00) | ((value & 0x3f) << 3) | line_counter_); size_t char_address = (size_t)((refresh & 0xff00) | ((value & 0x3f) << 3) | line_counter_);
if(char_address < ram_base_) { if(char_address < ram_base_) {
uint8_t mask = (value & 0x80) ? 0x00 : 0xff; uint8_t mask = (value & 0x80) ? 0x00 : 0xff;

View File

@@ -52,7 +52,6 @@ typedef NS_ENUM(NSInteger, CSTestMachineZ80Register) {
- (uint8_t)valueAtAddress:(uint16_t)address; - (uint8_t)valueAtAddress:(uint16_t)address;
- (void)runForNumberOfCycles:(int)cycles; - (void)runForNumberOfCycles:(int)cycles;
- (void)runToNextInstruction;
- (void)setValue:(uint16_t)value forRegister:(CSTestMachineZ80Register)reg; - (void)setValue:(uint16_t)value forRegister:(CSTestMachineZ80Register)reg;
- (uint16_t)valueForRegister:(CSTestMachineZ80Register)reg; - (uint16_t)valueForRegister:(CSTestMachineZ80Register)reg;

View File

@@ -12,7 +12,6 @@
@interface CSTestMachineZ80 () @interface CSTestMachineZ80 ()
- (void)testMachineDidPerformBusOperation:(CPU::Z80::MachineCycle::Operation)operation - (void)testMachineDidPerformBusOperation:(CPU::Z80::MachineCycle::Operation)operation
phase:(CPU::Z80::MachineCycle::Phase)phase
address:(uint16_t)address address:(uint16_t)address
value:(uint8_t)value value:(uint8_t)value
timeStamp:(int)time_stamp; timeStamp:(int)time_stamp;
@@ -24,8 +23,8 @@ class BusOperationHandler: public CPU::Z80::AllRAMProcessor::MemoryAccessDelegat
public: public:
BusOperationHandler(CSTestMachineZ80 *targetMachine) : target_(targetMachine) {} BusOperationHandler(CSTestMachineZ80 *targetMachine) : target_(targetMachine) {}
void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::MachineCycle::Operation operation, CPU::Z80::MachineCycle::Phase phase, uint16_t address, uint8_t value, int time_stamp) { void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::MachineCycle::Operation operation, uint16_t address, uint8_t value, int time_stamp) {
[target_ testMachineDidPerformBusOperation:operation phase:phase address:address value:value timeStamp:time_stamp]; [target_ testMachineDidPerformBusOperation:operation address:address value:value timeStamp:time_stamp];
} }
private: private:
@@ -102,7 +101,6 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
BusOperationHandler *_busOperationHandler; BusOperationHandler *_busOperationHandler;
NSMutableArray<CSTestMachineZ80BusOperationCapture *> *_busOperationCaptures; NSMutableArray<CSTestMachineZ80BusOperationCapture *> *_busOperationCaptures;
BOOL _isAtReadOpcode;
int _timeSeekingReadOpcode; int _timeSeekingReadOpcode;
int _lastOpcodeTime; int _lastOpcodeTime;
} }
@@ -173,17 +171,6 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
return _processor; return _processor;
} }
#pragma mark - Z80-specific Runner
- (void)runToNextInstruction {
_isAtReadOpcode = NO;
_timeSeekingReadOpcode = 0;
while(!_isAtReadOpcode) {
_timeSeekingReadOpcode++;
_processor->run_for_cycles(1);
}
}
#pragma mark - Bus operation accumulation #pragma mark - Bus operation accumulation
- (void)setCaptureBusActivity:(BOOL)captureBusActivity { - (void)setCaptureBusActivity:(BOOL)captureBusActivity {
@@ -191,50 +178,44 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
_processor->set_memory_access_delegate(captureBusActivity ? _busOperationHandler : nullptr); _processor->set_memory_access_delegate(captureBusActivity ? _busOperationHandler : nullptr);
} }
- (void)testMachineDidPerformBusOperation:(CPU::Z80::MachineCycle::Operation)operation phase:(CPU::Z80::MachineCycle::Phase)phase address:(uint16_t)address value:(uint8_t)value timeStamp:(int)timeStamp { - (void)testMachineDidPerformBusOperation:(CPU::Z80::MachineCycle::Operation)operation address:(uint16_t)address value:(uint8_t)value timeStamp:(int)timeStamp {
int length = timeStamp - _lastOpcodeTime; int length = timeStamp - _lastOpcodeTime;
_lastOpcodeTime = timeStamp; _lastOpcodeTime = timeStamp;
if(operation == CPU::Z80::MachineCycle::Operation::ReadOpcode && length < _timeSeekingReadOpcode)
_isAtReadOpcode = YES;
if(self.captureBusActivity) { if(self.captureBusActivity) {
CSTestMachineZ80BusOperationCapture *capture = [[CSTestMachineZ80BusOperationCapture alloc] init]; CSTestMachineZ80BusOperationCapture *capture = [[CSTestMachineZ80BusOperationCapture alloc] init];
if(phase == CPU::Z80::MachineCycle::Phase::End) { switch(operation) {
switch(operation) { case CPU::Z80::MachineCycle::Operation::Write:
case CPU::Z80::MachineCycle::Operation::Write: capture.operation = CSTestMachineZ80BusOperationCaptureOperationWrite;
capture.operation = CSTestMachineZ80BusOperationCaptureOperationWrite; break;
break;
case CPU::Z80::MachineCycle::Operation::Read: case CPU::Z80::MachineCycle::Operation::Read:
capture.operation = CSTestMachineZ80BusOperationCaptureOperationRead; capture.operation = CSTestMachineZ80BusOperationCaptureOperationRead;
break; break;
case CPU::Z80::MachineCycle::Operation::ReadOpcode: case CPU::Z80::MachineCycle::Operation::Refresh:
case CPU::Z80::MachineCycle::Operation::Refresh: capture.operation = CSTestMachineZ80BusOperationCaptureOperationReadOpcode;
capture.operation = CSTestMachineZ80BusOperationCaptureOperationReadOpcode; break;
break;
case CPU::Z80::MachineCycle::Operation::Input: case CPU::Z80::MachineCycle::Operation::Input:
capture.operation = CSTestMachineZ80BusOperationCaptureOperationPortRead; capture.operation = CSTestMachineZ80BusOperationCaptureOperationPortRead;
break; break;
case CPU::Z80::MachineCycle::Operation::Output: case CPU::Z80::MachineCycle::Operation::Output:
capture.operation = CSTestMachineZ80BusOperationCaptureOperationPortWrite; capture.operation = CSTestMachineZ80BusOperationCaptureOperationPortWrite;
break; break;
case CPU::Z80::MachineCycle::Operation::Internal: case CPU::Z80::MachineCycle::Operation::Internal:
capture.operation = CSTestMachineZ80BusOperationCaptureOperationInternalOperation; capture.operation = CSTestMachineZ80BusOperationCaptureOperationInternalOperation;
break; break;
default: default: return;
return;
}
capture.address = address;
capture.value = value;
capture.timeStamp = timeStamp;
[_busOperationCaptures addObject:capture];
} }
capture.address = address;
capture.value = value;
capture.timeStamp = timeStamp;
[_busOperationCaptures addObject:capture];
} }
} }

View File

@@ -64,47 +64,62 @@ enum Flag: uint8_t {
*/ */
struct MachineCycle { struct MachineCycle {
enum Operation { enum Operation {
ReadOpcode = 0, ReadOpcodeStart = 0,
Refresh, ReadOpcodeWait,
Read, Write, Read,
Input, Output, Write,
Input,
Output,
Interrupt, Interrupt,
Refresh,
Internal,
BusAcknowledge, BusAcknowledge,
Internal
ReadStart,
ReadWait,
WriteStart,
WriteWait,
InputStart,
InputWait,
OutputStart,
OutputWait
} operation; } operation;
enum Phase {
Start,
Wait,
End
} phase;
int length; int length;
uint16_t *address; uint16_t *address;
uint8_t *value; uint8_t *value;
bool was_requested; bool was_requested;
inline bool expects_action() const {
return operation <= Operation::Interrupt;
}
inline bool is_terminal() const {
return operation <= Operation::BusAcknowledge;
}
}; };
// Elemental bus operations // Elemental bus operations
#define ReadOpcodeStart() {MachineCycle::ReadOpcode, MachineCycle::Phase::Start, 2, &pc_.full, &operation_, false} #define ReadOpcodeStart() {MachineCycle::ReadOpcodeStart, 2, &pc_.full, &operation_, false}
#define ReadOpcodeWait(length, f) {MachineCycle::ReadOpcode, MachineCycle::Phase::Wait, length, &pc_.full, &operation_, f} #define ReadOpcodeWait(length, f) {MachineCycle::ReadOpcodeWait, length, &pc_.full, &operation_, f}
#define Refresh(len) {MachineCycle::Refresh, MachineCycle::Phase::End, len, &ir_.full, nullptr, false} #define Refresh(len) {MachineCycle::Refresh, len, &ir_.full, nullptr, false}
#define ReadStart(addr, val) {MachineCycle::Read, MachineCycle::Phase::Start, 2, &addr.full, &val, false} #define ReadStart(addr, val) {MachineCycle::ReadStart, 2, &addr.full, &val, false}
#define ReadWait(l, addr, val, f) {MachineCycle::Read, MachineCycle::Phase::Wait, l, &addr.full, &val, f} #define ReadWait(l, addr, val, f) {MachineCycle::ReadWait, l, &addr.full, &val, f}
#define ReadEnd(addr, val) {MachineCycle::Read, MachineCycle::Phase::End, 1, &addr.full, &val, false} #define ReadEnd(addr, val) {MachineCycle::Read, 1, &addr.full, &val, false}
#define WriteStart(addr, val) {MachineCycle::Write, MachineCycle::Phase::Start, 2, &addr.full, &val, false} #define WriteStart(addr, val) {MachineCycle::WriteStart, 2, &addr.full, &val, false}
#define WriteWait(l, addr, val, f) {MachineCycle::Write, MachineCycle::Phase::Wait, l, &addr.full, &val, f} #define WriteWait(l, addr, val, f) {MachineCycle::WriteWait, l, &addr.full, &val, f}
#define WriteEnd(addr, val) {MachineCycle::Write, MachineCycle::Phase::End, 1, &addr.full, &val, false} #define WriteEnd(addr, val) {MachineCycle::Write, 1, &addr.full, &val, false}
#define InputStart(addr, val) {MachineCycle::Input, MachineCycle::Phase::Start, 2, &addr.full, &val, false} #define InputStart(addr, val) {MachineCycle::InputStart, 2, &addr.full, &val, false}
#define InputWait(addr, val, f) {MachineCycle::Input, MachineCycle::Phase::Wait, 1, &addr.full, &val, f} #define InputWait(addr, val, f) {MachineCycle::InputWait, 1, &addr.full, &val, f}
#define InputEnd(addr, val) {MachineCycle::Input, MachineCycle::Phase::End, 1, &addr.full, &val, false} #define InputEnd(addr, val) {MachineCycle::Input, 1, &addr.full, &val, false}
#define OutputStart(addr, val) {MachineCycle::Output, MachineCycle::Phase::Start, 2, &addr.full, &val} #define OutputStart(addr, val) {MachineCycle::OutputStart, 2, &addr.full, &val}
#define OutputWait(addr, val, f) {MachineCycle::Output, MachineCycle::Phase::Wait, 1, &addr.full, &val, f} #define OutputWait(addr, val, f) {MachineCycle::OutputWait, 1, &addr.full, &val, f}
#define OutputEnd(addr, val) {MachineCycle::Output, MachineCycle::Phase::End, 1, &addr.full, &val} #define OutputEnd(addr, val) {MachineCycle::Output, 1, &addr.full, &val}
#define IntAck(length, val) {MachineCycle::Operation::Interrupt, MachineCycle::Phase::End, length, nullptr, &val} #define IntAck(length, val) {MachineCycle::Interrupt, length, nullptr, &val}
// A wrapper to express a bus operation as a micro-op // A wrapper to express a bus operation as a micro-op
#define BusOp(op) {MicroOp::BusOperation, nullptr, nullptr, op} #define BusOp(op) {MicroOp::BusOperation, nullptr, nullptr, op}
@@ -119,7 +134,7 @@ struct MachineCycle {
#define Input(addr, val) BusOp(InputStart(addr, val)), BusOp(InputWait(addr, val, false)), BusOp(InputWait(addr, val, true)), BusOp(InputEnd(addr, val)) #define Input(addr, val) BusOp(InputStart(addr, val)), BusOp(InputWait(addr, val, false)), BusOp(InputWait(addr, val, true)), BusOp(InputEnd(addr, val))
#define Output(addr, val) BusOp(OutputStart(addr, val)), BusOp(OutputWait(addr, val, false)), BusOp(OutputWait(addr, val, true)), BusOp(OutputEnd(addr, val)) #define Output(addr, val) BusOp(OutputStart(addr, val)), BusOp(OutputWait(addr, val, false)), BusOp(OutputWait(addr, val, true)), BusOp(OutputEnd(addr, val))
#define InternalOperation(len) {MicroOp::BusOperation, nullptr, nullptr, {MachineCycle::Internal, MachineCycle::Phase::End, len}} #define InternalOperation(len) {MicroOp::BusOperation, nullptr, nullptr, {MachineCycle::Internal, len}}
/// A sequence is a series of micro-ops that ends in a move-to-next-program operation. /// A sequence is a series of micro-ops that ends in a move-to-next-program operation.
#define Sequence(...) { __VA_ARGS__, {MicroOp::MoveToNextProgram} } #define Sequence(...) { __VA_ARGS__, {MicroOp::MoveToNextProgram} }
@@ -173,6 +188,7 @@ template <class T> class Processor {
uint8_t last_request_status_; uint8_t last_request_status_;
bool irq_line_; bool irq_line_;
bool bus_request_line_; bool bus_request_line_;
bool wait_line_;
uint8_t operation_; uint8_t operation_;
RegisterPair temp16_, memptr_; RegisterPair temp16_, memptr_;
@@ -745,6 +761,7 @@ template <class T> class Processor {
halt_mask_(0xff), halt_mask_(0xff),
number_of_cycles_(0), number_of_cycles_(0),
interrupt_mode_(0), interrupt_mode_(0),
wait_line_(false),
request_status_(Interrupt::PowerOn), request_status_(Interrupt::PowerOn),
last_request_status_(Interrupt::PowerOn), last_request_status_(Interrupt::PowerOn),
irq_line_(false), irq_line_(false),
@@ -855,7 +872,7 @@ template <class T> class Processor {
while(1) { while(1) {
while(bus_request_line_) { while(bus_request_line_) {
static MachineCycle bus_acknowledge_cycle = {MachineCycle::Operation::BusAcknowledge, MachineCycle::Phase::End, 1}; static MachineCycle bus_acknowledge_cycle = {MachineCycle::Operation::BusAcknowledge, 1};
number_of_cycles_ -= static_cast<T *>(this)->perform_machine_cycle(bus_acknowledge_cycle) + 1; number_of_cycles_ -= static_cast<T *>(this)->perform_machine_cycle(bus_acknowledge_cycle) + 1;
if(!number_of_cycles_) { if(!number_of_cycles_) {
static_cast<T *>(this)->flush(); static_cast<T *>(this)->flush();
@@ -875,14 +892,18 @@ template <class T> class Processor {
switch(operation->type) { switch(operation->type) {
case MicroOp::BusOperation: case MicroOp::BusOperation:
if(operation->machine_cycle.was_requested) { // TODO: && !wait_line_
continue;
}
if(number_of_cycles_ < operation->machine_cycle.length) { if(number_of_cycles_ < operation->machine_cycle.length) {
scheduled_program_counter_--; scheduled_program_counter_--;
static_cast<T *>(this)->flush(); static_cast<T *>(this)->flush();
return; return;
} }
if(operation->machine_cycle.was_requested) {
if(wait_line_) {
scheduled_program_counter_--;
} else {
continue;
}
}
number_of_cycles_ -= operation->machine_cycle.length; number_of_cycles_ -= operation->machine_cycle.length;
last_request_status_ = request_status_; last_request_status_ = request_status_;
number_of_cycles_ -= static_cast<T *>(this)->perform_machine_cycle(operation->machine_cycle); number_of_cycles_ -= static_cast<T *>(this)->perform_machine_cycle(operation->machine_cycle);
@@ -1892,6 +1913,13 @@ template <class T> class Processor {
last_request_status_ &= ~Interrupt::PowerOn; last_request_status_ &= ~Interrupt::PowerOn;
} }
/*!
Sets the logical value of the wait line.
*/
inline void set_wait_line(bool value) {
wait_line_ = value;
}
/*! /*!
For receivers of perform_machine_cycle only. Temporarily rejects the current machine For receivers of perform_machine_cycle only. Temporarily rejects the current machine
cycle, causing time to be rewinded to its beginning. cycle, causing time to be rewinded to its beginning.

View File

@@ -17,45 +17,47 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<Concrete
ConcreteAllRAMProcessor() : AllRAMProcessor() {} ConcreteAllRAMProcessor() : AllRAMProcessor() {}
inline int perform_machine_cycle(const MachineCycle &cycle) { inline int perform_machine_cycle(const MachineCycle &cycle) {
uint16_t address = cycle.address ? *cycle.address : 0x0000;
// if(cycle.phase == MachineCycle::Phase::End) {
switch(cycle.operation) {
case MachineCycle::Operation::ReadOpcode:
check_address_for_trap(address);
case MachineCycle::Operation::Read:
*cycle.value = memory_[address];
break;
case MachineCycle::Operation::Write:
memory_[address] = *cycle.value;
break;
case MachineCycle::Operation::Output:
break;
case MachineCycle::Operation::Input:
// This logic is selected specifically because it seems to match
// the FUSE unit tests. It might need factoring out.
*cycle.value = address >> 8;
break;
case MachineCycle::Operation::Internal:
case MachineCycle::Operation::Refresh:
break;
case MachineCycle::Operation::Interrupt:
// A pick that means LD HL, (nn) if interpreted as an instruction but is otherwise
// arbitrary.
*cycle.value = 0x21;
break;
default:
printf("???\n");
break;
}
// }
timestamp_ += cycle.length; timestamp_ += cycle.length;
if(!cycle.is_terminal()) {
return 0;
}
uint16_t address = cycle.address ? *cycle.address : 0x0000;
switch(cycle.operation) {
case MachineCycle::Operation::ReadOpcodeStart:
check_address_for_trap(address);
case MachineCycle::Operation::Read:
*cycle.value = memory_[address];
break;
case MachineCycle::Operation::Write:
memory_[address] = *cycle.value;
break;
case MachineCycle::Operation::Output:
break;
case MachineCycle::Operation::Input:
// This logic is selected specifically because it seems to match
// the FUSE unit tests. It might need factoring out.
*cycle.value = address >> 8;
break;
case MachineCycle::Operation::Internal:
case MachineCycle::Operation::Refresh:
break;
case MachineCycle::Operation::Interrupt:
// A pick that means LD HL, (nn) if interpreted as an instruction but is otherwise
// arbitrary.
*cycle.value = 0x21;
break;
default:
printf("???\n");
break;
}
if(delegate_ != nullptr) { if(delegate_ != nullptr) {
delegate_->z80_all_ram_processor_did_perform_bus_operation(*this, cycle.operation, cycle.phase, address, cycle.value ? *cycle.value : 0x00, timestamp_); delegate_->z80_all_ram_processor_did_perform_bus_operation(*this, cycle.operation, address, cycle.value ? *cycle.value : 0x00, timestamp_);
} }
return 0; return 0;

View File

@@ -22,7 +22,7 @@ class AllRAMProcessor:
static AllRAMProcessor *Processor(); static AllRAMProcessor *Processor();
struct MemoryAccessDelegate { struct MemoryAccessDelegate {
virtual void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::MachineCycle::Operation operation, CPU::Z80::MachineCycle::Phase phase, 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::MachineCycle::Operation operation, uint16_t address, uint8_t value, int time_stamp) = 0;
}; };
inline void set_memory_access_delegate(MemoryAccessDelegate *delegate) { inline void set_memory_access_delegate(MemoryAccessDelegate *delegate) {
delegate_ = delegate; delegate_ = delegate;