mirror of
https://github.com/TomHarte/CLK.git
synced 2024-07-05 10:28:58 +00:00
Fixed Z80's ownership of its fetch-decode-execute program, its habit of scheduling invalidly when hitting an unrecognised operation and the test machine's habit of dereferencing invalidly.
This commit is contained in:
parent
9e25d014d2
commit
6575091a78
@ -80,6 +80,11 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
|||||||
#pragma mark - Capture class
|
#pragma mark - Capture class
|
||||||
|
|
||||||
@implementation CSTestMachineZ80BusOperationCapture
|
@implementation CSTestMachineZ80BusOperationCapture
|
||||||
|
|
||||||
|
- (NSString *)description {
|
||||||
|
return [NSString stringWithFormat:@"%c %04x %02x [%d]", (self.operation == CSTestMachineZ80BusOperationCaptureOperationRead) ? 'r' : 'w', self.address, self.value, self.timeStamp];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#pragma mark - Test class
|
#pragma mark - Test class
|
||||||
@ -91,6 +96,8 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
|||||||
|
|
||||||
NSMutableArray<CSTestMachineZ80BusOperationCapture *> *_busOperationCaptures;
|
NSMutableArray<CSTestMachineZ80BusOperationCapture *> *_busOperationCaptures;
|
||||||
BOOL _isAtReadOpcode;
|
BOOL _isAtReadOpcode;
|
||||||
|
int _timeSeekingReadOpcode;
|
||||||
|
int _lastOpcodeTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Lifecycle
|
#pragma mark - Lifecycle
|
||||||
@ -150,16 +157,21 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
|||||||
#pragma mark - Z80-specific Runner
|
#pragma mark - Z80-specific Runner
|
||||||
|
|
||||||
- (void)runToNextInstruction {
|
- (void)runToNextInstruction {
|
||||||
_isAtReadOpcode = false;
|
_isAtReadOpcode = NO;
|
||||||
|
_timeSeekingReadOpcode = 0;
|
||||||
while(!_isAtReadOpcode) {
|
while(!_isAtReadOpcode) {
|
||||||
|
_timeSeekingReadOpcode++;
|
||||||
_processor.run_for_cycles(1);
|
_processor.run_for_cycles(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Bus operation accumulation
|
#pragma mark - Bus operation accumulation
|
||||||
|
|
||||||
- (void)testMachineDidPerformBusOperation:(CPU::Z80::BusOperation)operation address:(uint16_t)address value:(uint8_t)value timeStamp:(int)time_stamp {
|
- (void)testMachineDidPerformBusOperation:(CPU::Z80::BusOperation)operation address:(uint16_t)address value:(uint8_t)value timeStamp:(int)timeStamp {
|
||||||
_isAtReadOpcode |= (operation == CPU::Z80::BusOperation::ReadOpcode);
|
int length = timeStamp - _lastOpcodeTime;
|
||||||
|
_lastOpcodeTime = timeStamp;
|
||||||
|
if(operation == CPU::Z80::BusOperation::ReadOpcode && length < _timeSeekingReadOpcode)
|
||||||
|
_isAtReadOpcode = YES;
|
||||||
|
|
||||||
if(self.captureBusActivity) {
|
if(self.captureBusActivity) {
|
||||||
if(!_busOperationCaptures) _busOperationCaptures = [[NSMutableArray alloc] init];
|
if(!_busOperationCaptures) _busOperationCaptures = [[NSMutableArray alloc] init];
|
||||||
@ -169,7 +181,7 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
|||||||
capture.operation = (operation == CPU::Z80::BusOperation::Write) ? CSTestMachineZ80BusOperationCaptureOperationWrite : CSTestMachineZ80BusOperationCaptureOperationRead;
|
capture.operation = (operation == CPU::Z80::BusOperation::Write) ? CSTestMachineZ80BusOperationCaptureOperationWrite : CSTestMachineZ80BusOperationCaptureOperationRead;
|
||||||
capture.address = address;
|
capture.address = address;
|
||||||
capture.value = value;
|
capture.value = value;
|
||||||
capture.timeStamp = time_stamp;
|
capture.timeStamp = timeStamp;
|
||||||
|
|
||||||
[_busOperationCaptures addObject:capture];
|
[_busOperationCaptures addObject:capture];
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ class FUSETests: XCTestCase {
|
|||||||
let outputScanner = Scanner(string: output)
|
let outputScanner = Scanner(string: output)
|
||||||
|
|
||||||
while !inputScanner.isAtEnd {
|
while !inputScanner.isAtEnd {
|
||||||
|
autoreleasepool {
|
||||||
var name: NSString?
|
var name: NSString?
|
||||||
inputScanner.scanUpToCharacters(from: CharacterSet.newlines, into: &name)
|
inputScanner.scanUpToCharacters(from: CharacterSet.newlines, into: &name)
|
||||||
if let name = name {
|
if let name = name {
|
||||||
@ -102,4 +103,5 @@ class FUSETests: XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,6 +161,7 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
|
|||||||
RegisterPair temp16_;
|
RegisterPair temp16_;
|
||||||
uint8_t temp8_;
|
uint8_t temp8_;
|
||||||
|
|
||||||
|
MicroOp *fetch_decode_execute_;
|
||||||
MicroOp **current_instruction_page_;
|
MicroOp **current_instruction_page_;
|
||||||
struct InstructionPage {
|
struct InstructionPage {
|
||||||
MicroOp *instructions[256];
|
MicroOp *instructions[256];
|
||||||
@ -484,22 +485,39 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
|
|||||||
assemble_page(target, base_program_table);
|
assemble_page(target, base_program_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void assemble_fetch_decode_execute() {
|
||||||
|
// TODO: this can't legitimately be static and contain references to this via pc_ and operation_;
|
||||||
|
// make it something else that is built at instance construction.
|
||||||
|
const MicroOp fetch_decode_execute[] = {
|
||||||
|
{ MicroOp::BusOperation, nullptr, nullptr, {ReadOpcode, 4, &pc_.full, &operation_}},
|
||||||
|
{ MicroOp::DecodeOperation },
|
||||||
|
{ MicroOp::MoveToNextProgram }
|
||||||
|
};
|
||||||
|
fetch_decode_execute_ = new MicroOp[3];
|
||||||
|
fetch_decode_execute_[0] = fetch_decode_execute[0];
|
||||||
|
fetch_decode_execute_[1] = fetch_decode_execute[1];
|
||||||
|
fetch_decode_execute_[2] = fetch_decode_execute[2];
|
||||||
|
}
|
||||||
|
|
||||||
void decode_operation(uint8_t operation) {
|
void decode_operation(uint8_t operation) {
|
||||||
if(current_instruction_page_[operation]->type == MicroOp::None) {
|
if(current_instruction_page_[operation]->type == MicroOp::None) {
|
||||||
uint8_t page = 0x00;
|
uint8_t page = 0x00;
|
||||||
if(current_instruction_page_ == ed_page_.instructions) page = 0xed;
|
if(current_instruction_page_ == ed_page_.instructions) page = 0xed;
|
||||||
if(current_instruction_page_ == fd_page_.instructions) page = 0xfd;
|
if(current_instruction_page_ == fd_page_.instructions) page = 0xfd;
|
||||||
printf("Unknown Z80 operation %02x %02x!!!\n", page, operation);
|
printf("Unknown Z80 operation %02x %02x!!!\n", page, operation);
|
||||||
}
|
} else schedule_program(current_instruction_page_[operation]);
|
||||||
schedule_program(current_instruction_page_[operation]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Processor() {
|
Processor() : MicroOpScheduler() {
|
||||||
assemble_base_page(base_page_, hl_, false);
|
assemble_base_page(base_page_, hl_, false);
|
||||||
assemble_base_page(dd_page_, ix_, false);
|
assemble_base_page(dd_page_, ix_, false);
|
||||||
assemble_base_page(fd_page_, iy_, false);
|
assemble_base_page(fd_page_, iy_, false);
|
||||||
assemble_ed_page(ed_page_);
|
assemble_ed_page(ed_page_);
|
||||||
|
assemble_fetch_decode_execute();
|
||||||
|
}
|
||||||
|
~Processor() {
|
||||||
|
delete[] fetch_decode_execute_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -512,18 +530,11 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
|
|||||||
@param number_of_cycles The number of cycles to run the Z80 for.
|
@param number_of_cycles The number of cycles to run the Z80 for.
|
||||||
*/
|
*/
|
||||||
void run_for_cycles(int number_of_cycles) {
|
void run_for_cycles(int number_of_cycles) {
|
||||||
// TODO: this can't legitimately be static and contain references to this via pc_ and operation_;
|
|
||||||
// make it something else that is built at instance construction.
|
|
||||||
static const MicroOp fetch_decode_execute[] = {
|
|
||||||
{ MicroOp::BusOperation, nullptr, nullptr, {ReadOpcode, 4, &pc_.full, &operation_}},
|
|
||||||
{ MicroOp::DecodeOperation },
|
|
||||||
{ MicroOp::MoveToNextProgram }
|
|
||||||
};
|
|
||||||
|
|
||||||
#define checkSchedule() \
|
#define checkSchedule() \
|
||||||
if(!scheduled_programs_[schedule_programs_read_pointer_]) {\
|
if(!scheduled_programs_[schedule_programs_read_pointer_]) {\
|
||||||
current_instruction_page_ = base_page_.instructions;\
|
current_instruction_page_ = base_page_.instructions;\
|
||||||
schedule_program(fetch_decode_execute);\
|
schedule_program(fetch_decode_execute_);\
|
||||||
}
|
}
|
||||||
|
|
||||||
number_of_cycles_ += number_of_cycles;
|
number_of_cycles_ += number_of_cycles;
|
||||||
@ -535,11 +546,10 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
|
|||||||
|
|
||||||
switch(operation->type) {
|
switch(operation->type) {
|
||||||
case MicroOp::BusOperation:
|
case MicroOp::BusOperation:
|
||||||
if(number_of_cycles_ < operation->machine_cycle.length) {
|
if(number_of_cycles_ < operation->machine_cycle.length) { schedule_program_program_counter_--; return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
number_of_cycles_ -= operation->machine_cycle.length;
|
number_of_cycles_ -= operation->machine_cycle.length;
|
||||||
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);
|
||||||
|
if(number_of_cycles_ <= 0) return;
|
||||||
break;
|
break;
|
||||||
case MicroOp::MoveToNextProgram:
|
case MicroOp::MoveToNextProgram:
|
||||||
move_to_next_program();
|
move_to_next_program();
|
||||||
@ -872,7 +882,7 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
|
|||||||
#pragma mark - Internal bookkeeping
|
#pragma mark - Internal bookkeeping
|
||||||
|
|
||||||
case MicroOp::SetInstructionPage:
|
case MicroOp::SetInstructionPage:
|
||||||
schedule_program(fetch_decode_execute);
|
schedule_program(fetch_decode_execute_);
|
||||||
current_instruction_page_ = ((InstructionPage *)operation->source)->instructions;
|
current_instruction_page_ = ((InstructionPage *)operation->source)->instructions;
|
||||||
// printf("+ ");
|
// printf("+ ");
|
||||||
break;
|
break;
|
||||||
|
@ -37,7 +37,7 @@ int AllRAMProcessor::perform_machine_cycle(const MachineCycle *cycle) {
|
|||||||
timestamp_ += cycle->length;
|
timestamp_ += cycle->length;
|
||||||
|
|
||||||
if(delegate_ != nullptr) {
|
if(delegate_ != nullptr) {
|
||||||
delegate_->z80_all_ram_processor_did_perform_bus_operation(*this, cycle->operation, *cycle->address, *cycle->value, timestamp_);
|
delegate_->z80_all_ram_processor_did_perform_bus_operation(*this, cycle->operation, cycle->address ? *cycle->address : 0x0000, cycle->value ? *cycle->value : 0x00, timestamp_);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user