1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-30 22:29:56 +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:
Thomas Harte 2017-05-22 21:50:34 -04:00
parent 9e25d014d2
commit 6575091a78
4 changed files with 112 additions and 88 deletions

View File

@ -80,6 +80,11 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
#pragma mark - Capture class
@implementation CSTestMachineZ80BusOperationCapture
- (NSString *)description {
return [NSString stringWithFormat:@"%c %04x %02x [%d]", (self.operation == CSTestMachineZ80BusOperationCaptureOperationRead) ? 'r' : 'w', self.address, self.value, self.timeStamp];
}
@end
#pragma mark - Test class
@ -91,6 +96,8 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
NSMutableArray<CSTestMachineZ80BusOperationCapture *> *_busOperationCaptures;
BOOL _isAtReadOpcode;
int _timeSeekingReadOpcode;
int _lastOpcodeTime;
}
#pragma mark - Lifecycle
@ -150,16 +157,21 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
#pragma mark - Z80-specific Runner
- (void)runToNextInstruction {
_isAtReadOpcode = false;
_isAtReadOpcode = NO;
_timeSeekingReadOpcode = 0;
while(!_isAtReadOpcode) {
_timeSeekingReadOpcode++;
_processor.run_for_cycles(1);
}
}
#pragma mark - Bus operation accumulation
- (void)testMachineDidPerformBusOperation:(CPU::Z80::BusOperation)operation address:(uint16_t)address value:(uint8_t)value timeStamp:(int)time_stamp {
_isAtReadOpcode |= (operation == CPU::Z80::BusOperation::ReadOpcode);
- (void)testMachineDidPerformBusOperation:(CPU::Z80::BusOperation)operation address:(uint16_t)address value:(uint8_t)value timeStamp:(int)timeStamp {
int length = timeStamp - _lastOpcodeTime;
_lastOpcodeTime = timeStamp;
if(operation == CPU::Z80::BusOperation::ReadOpcode && length < _timeSeekingReadOpcode)
_isAtReadOpcode = YES;
if(self.captureBusActivity) {
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.address = address;
capture.value = value;
capture.timeStamp = time_stamp;
capture.timeStamp = timeStamp;
[_busOperationCaptures addObject:capture];
}

View File

@ -21,82 +21,84 @@ class FUSETests: XCTestCase {
let outputScanner = Scanner(string: output)
while !inputScanner.isAtEnd {
var name: NSString?
inputScanner.scanUpToCharacters(from: CharacterSet.newlines, into: &name)
if let name = name {
let machine = CSTestMachineZ80()
autoreleasepool {
var name: NSString?
inputScanner.scanUpToCharacters(from: CharacterSet.newlines, into: &name)
if let name = name {
let machine = CSTestMachineZ80()
var af: UInt32 = 0, bc: UInt32 = 0, de: UInt32 = 0, hl: UInt32 = 0
var afDash: UInt32 = 0, bcDash: UInt32 = 0, deDash: UInt32 = 0, hlDash: UInt32 = 0
var ix: UInt32 = 0, iy: UInt32 = 0, sp: UInt32 = 0, pc: UInt32 = 0
var i: UInt32 = 0, r: UInt32 = 0, iff1: UInt32 = 0, iff2: UInt32 = 0, interruptMode: UInt32 = 0
var isHalted: UInt32 = 0, tStates: UInt32 = 0
var af: UInt32 = 0, bc: UInt32 = 0, de: UInt32 = 0, hl: UInt32 = 0
var afDash: UInt32 = 0, bcDash: UInt32 = 0, deDash: UInt32 = 0, hlDash: UInt32 = 0
var ix: UInt32 = 0, iy: UInt32 = 0, sp: UInt32 = 0, pc: UInt32 = 0
var i: UInt32 = 0, r: UInt32 = 0, iff1: UInt32 = 0, iff2: UInt32 = 0, interruptMode: UInt32 = 0
var isHalted: UInt32 = 0, tStates: UInt32 = 0
inputScanner.scanHexInt32(&af)
inputScanner.scanHexInt32(&bc)
inputScanner.scanHexInt32(&de)
inputScanner.scanHexInt32(&hl)
inputScanner.scanHexInt32(&afDash)
inputScanner.scanHexInt32(&bcDash)
inputScanner.scanHexInt32(&deDash)
inputScanner.scanHexInt32(&hlDash)
inputScanner.scanHexInt32(&ix)
inputScanner.scanHexInt32(&iy)
inputScanner.scanHexInt32(&sp)
inputScanner.scanHexInt32(&pc)
inputScanner.scanHexInt32(&i)
inputScanner.scanHexInt32(&r)
inputScanner.scanHexInt32(&iff1)
inputScanner.scanHexInt32(&iff2)
inputScanner.scanHexInt32(&interruptMode)
inputScanner.scanHexInt32(&isHalted)
inputScanner.scanHexInt32(&tStates)
inputScanner.scanHexInt32(&af)
inputScanner.scanHexInt32(&bc)
inputScanner.scanHexInt32(&de)
inputScanner.scanHexInt32(&hl)
inputScanner.scanHexInt32(&afDash)
inputScanner.scanHexInt32(&bcDash)
inputScanner.scanHexInt32(&deDash)
inputScanner.scanHexInt32(&hlDash)
inputScanner.scanHexInt32(&ix)
inputScanner.scanHexInt32(&iy)
inputScanner.scanHexInt32(&sp)
inputScanner.scanHexInt32(&pc)
inputScanner.scanHexInt32(&i)
inputScanner.scanHexInt32(&r)
inputScanner.scanHexInt32(&iff1)
inputScanner.scanHexInt32(&iff2)
inputScanner.scanHexInt32(&interruptMode)
inputScanner.scanHexInt32(&isHalted)
inputScanner.scanHexInt32(&tStates)
print("\(name)")
machine.setValue(UInt16(af), for: .AF)
machine.setValue(UInt16(bc), for: .BC)
machine.setValue(UInt16(de), for: .DE)
machine.setValue(UInt16(hl), for: .HL)
machine.setValue(UInt16(afDash), for: .afDash)
machine.setValue(UInt16(bcDash), for: .bcDash)
machine.setValue(UInt16(deDash), for: .deDash)
machine.setValue(UInt16(hlDash), for: .hlDash)
machine.setValue(UInt16(ix), for: .IX)
machine.setValue(UInt16(iy), for: .IY)
machine.setValue(UInt16(sp), for: .stackPointer)
machine.setValue(UInt16(pc), for: .programCounter)
machine.setValue(UInt16(i), for: .I)
machine.setValue(UInt16(r), for: .R)
machine.setValue(UInt16(iff1), for: .IFF1)
machine.setValue(UInt16(iff2), for: .IFF2)
machine.setValue(UInt16(interruptMode), for: .IM)
// TODO: isHalted
print("\(name)")
machine.setValue(UInt16(af), for: .AF)
machine.setValue(UInt16(bc), for: .BC)
machine.setValue(UInt16(de), for: .DE)
machine.setValue(UInt16(hl), for: .HL)
machine.setValue(UInt16(afDash), for: .afDash)
machine.setValue(UInt16(bcDash), for: .bcDash)
machine.setValue(UInt16(deDash), for: .deDash)
machine.setValue(UInt16(hlDash), for: .hlDash)
machine.setValue(UInt16(ix), for: .IX)
machine.setValue(UInt16(iy), for: .IY)
machine.setValue(UInt16(sp), for: .stackPointer)
machine.setValue(UInt16(pc), for: .programCounter)
machine.setValue(UInt16(i), for: .I)
machine.setValue(UInt16(r), for: .R)
machine.setValue(UInt16(iff1), for: .IFF1)
machine.setValue(UInt16(iff2), for: .IFF2)
machine.setValue(UInt16(interruptMode), for: .IM)
// TODO: isHalted
while true {
var address: UInt32 = 0
var negative: Int = 0
if inputScanner.scanHexInt32(&address) {
while true {
var value: UInt32 = 0
if inputScanner.scanHexInt32(&value) {
machine.setValue(UInt8(value), atAddress: UInt16(address))
address = address + 1
} else {
inputScanner.scanInt(&negative)
break
while true {
var address: UInt32 = 0
var negative: Int = 0
if inputScanner.scanHexInt32(&address) {
while true {
var value: UInt32 = 0
if inputScanner.scanHexInt32(&value) {
machine.setValue(UInt8(value), atAddress: UInt16(address))
address = address + 1
} else {
inputScanner.scanInt(&negative)
break
}
}
} else {
inputScanner.scanInt(&negative)
break
}
} else {
inputScanner.scanInt(&negative)
break
}
machine.captureBusActivity = true
machine.runForNumber(ofCycles: Int32(tStates))
machine.runToNextInstruction()
print("\(machine.busOperationCaptures)")
}
machine.captureBusActivity = true
machine.runForNumber(ofCycles: Int32(tStates))
machine.runToNextInstruction()
print("\(machine.busOperationCaptures)")
}
}
}

View File

@ -161,6 +161,7 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
RegisterPair temp16_;
uint8_t temp8_;
MicroOp *fetch_decode_execute_;
MicroOp **current_instruction_page_;
struct InstructionPage {
MicroOp *instructions[256];
@ -484,22 +485,39 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
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) {
if(current_instruction_page_[operation]->type == MicroOp::None) {
uint8_t page = 0x00;
if(current_instruction_page_ == ed_page_.instructions) page = 0xed;
if(current_instruction_page_ == fd_page_.instructions) page = 0xfd;
printf("Unknown Z80 operation %02x %02x!!!\n", page, operation);
}
schedule_program(current_instruction_page_[operation]);
} else schedule_program(current_instruction_page_[operation]);
}
public:
Processor() {
Processor() : MicroOpScheduler() {
assemble_base_page(base_page_, hl_, false);
assemble_base_page(dd_page_, ix_, false);
assemble_base_page(fd_page_, iy_, false);
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.
*/
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() \
if(!scheduled_programs_[schedule_programs_read_pointer_]) {\
current_instruction_page_ = base_page_.instructions;\
schedule_program(fetch_decode_execute);\
schedule_program(fetch_decode_execute_);\
}
number_of_cycles_ += number_of_cycles;
@ -535,11 +546,10 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
switch(operation->type) {
case MicroOp::BusOperation:
if(number_of_cycles_ < operation->machine_cycle.length) {
return;
}
if(number_of_cycles_ < operation->machine_cycle.length) { schedule_program_program_counter_--; return; }
number_of_cycles_ -= operation->machine_cycle.length;
number_of_cycles_ -= static_cast<T *>(this)->perform_machine_cycle(&operation->machine_cycle);
if(number_of_cycles_ <= 0) return;
break;
case MicroOp::MoveToNextProgram:
move_to_next_program();
@ -872,7 +882,7 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
#pragma mark - Internal bookkeeping
case MicroOp::SetInstructionPage:
schedule_program(fetch_decode_execute);
schedule_program(fetch_decode_execute_);
current_instruction_page_ = ((InstructionPage *)operation->source)->instructions;
// printf("+ ");
break;

View File

@ -37,7 +37,7 @@ int AllRAMProcessor::perform_machine_cycle(const MachineCycle *cycle) {
timestamp_ += cycle->length;
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;