1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-02 16:04:59 +00:00
CLK/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm

258 lines
8.4 KiB
Plaintext

//
// TestMachineZ80.m
// Clock Signal
//
// Created by Thomas Harte on 16/05/2017.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#import "TestMachineZ80.h"
#include "Z80AllRAM.hpp"
#import "TestMachine+ForSubclassEyesOnly.h"
@interface CSTestMachineZ80 ()
- (void)testMachineDidPerformBusOperation:(CPU::Z80::PartialMachineCycle::Operation)operation
address:(uint16_t)address
value:(uint8_t)value
timeStamp:(HalfCycles)time_stamp;
@end
#pragma mark - C++ delegate handlers
class BusOperationHandler: public CPU::Z80::AllRAMProcessor::MemoryAccessDelegate {
public:
BusOperationHandler(CSTestMachineZ80 *targetMachine) : target_(targetMachine) {}
void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, HalfCycles time_stamp) {
[target_ testMachineDidPerformBusOperation:operation address:address value:value timeStamp:time_stamp];
}
private:
CSTestMachineZ80 *target_;
};
#pragma mark - Register enum map
static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
switch (reg) {
case CSTestMachineZ80RegisterAF: return CPU::Z80::Register::AF;
case CSTestMachineZ80RegisterA: return CPU::Z80::Register::A;
case CSTestMachineZ80RegisterF: return CPU::Z80::Register::Flags;
case CSTestMachineZ80RegisterBC: return CPU::Z80::Register::BC;
case CSTestMachineZ80RegisterB: return CPU::Z80::Register::B;
case CSTestMachineZ80RegisterC: return CPU::Z80::Register::C;
case CSTestMachineZ80RegisterDE: return CPU::Z80::Register::DE;
case CSTestMachineZ80RegisterD: return CPU::Z80::Register::D;
case CSTestMachineZ80RegisterE: return CPU::Z80::Register::E;
case CSTestMachineZ80RegisterHL: return CPU::Z80::Register::HL;
case CSTestMachineZ80RegisterH: return CPU::Z80::Register::H;
case CSTestMachineZ80RegisterL: return CPU::Z80::Register::L;
case CSTestMachineZ80RegisterAFDash: return CPU::Z80::Register::AFDash;
case CSTestMachineZ80RegisterBCDash: return CPU::Z80::Register::BCDash;
case CSTestMachineZ80RegisterDEDash: return CPU::Z80::Register::DEDash;
case CSTestMachineZ80RegisterHLDash: return CPU::Z80::Register::HLDash;
case CSTestMachineZ80RegisterIX: return CPU::Z80::Register::IX;
case CSTestMachineZ80RegisterIY: return CPU::Z80::Register::IY;
case CSTestMachineZ80RegisterI: return CPU::Z80::Register::I;
case CSTestMachineZ80RegisterR: return CPU::Z80::Register::R;
case CSTestMachineZ80RegisterIFF1: return CPU::Z80::Register::IFF1;
case CSTestMachineZ80RegisterIFF2: return CPU::Z80::Register::IFF2;
case CSTestMachineZ80RegisterIM: return CPU::Z80::Register::IM;
case CSTestMachineZ80RegisterProgramCounter: return CPU::Z80::Register::ProgramCounter;
case CSTestMachineZ80RegisterStackPointer: return CPU::Z80::Register::StackPointer;
case CSTestMachineZ80RegisterMemPtr: return CPU::Z80::Register::MemPtr;
}
}
#pragma mark - Port Logic
struct PortAccessDelegateTopByte: public CPU::Z80::AllRAMProcessor::PortAccessDelegate {
uint8_t z80_all_ram_processor_input(uint16_t port) final { return port >> 8; }
};
struct PortAccessDelegate191: public CPU::Z80::AllRAMProcessor::PortAccessDelegate {
uint8_t z80_all_ram_processor_input(uint16_t) final { return 191; }
};
#pragma mark - Capture class
@interface CSTestMachineZ80BusOperationCapture()
@property(nonatomic, assign) CSTestMachineZ80BusOperationCaptureOperation operation;
@property(nonatomic, assign) uint16_t address;
@property(nonatomic, assign) uint8_t value;
@property(nonatomic, assign) int timeStamp;
@end
@implementation CSTestMachineZ80BusOperationCapture
- (NSString *)description {
NSString *opName = @"";
switch(self.operation) {
case CSTestMachineZ80BusOperationCaptureOperationReadOpcode: opName = @"ro"; break;
case CSTestMachineZ80BusOperationCaptureOperationRead: opName = @"r"; break;
case CSTestMachineZ80BusOperationCaptureOperationWrite: opName = @"w"; break;
case CSTestMachineZ80BusOperationCaptureOperationPortRead: opName = @"i"; break;
case CSTestMachineZ80BusOperationCaptureOperationPortWrite: opName = @"o"; break;
case CSTestMachineZ80BusOperationCaptureOperationInternalOperation: opName = @"iop"; break;
}
return [NSString stringWithFormat:@"%@ %04x %02x [%d]", opName, self.address, self.value, self.timeStamp];
}
@end
#pragma mark - Test class
@implementation CSTestMachineZ80 {
CPU::Z80::AllRAMProcessor *_processor;
BusOperationHandler *_busOperationHandler;
NSMutableArray<CSTestMachineZ80BusOperationCapture *> *_busOperationCaptures;
int _timeSeekingReadOpcode;
PortAccessDelegateTopByte _topBytePortDelegate;
PortAccessDelegate191 _value191PortDelegate;
}
#pragma mark - Lifecycle
- (instancetype)init {
if(self = [super init]) {
_processor = CPU::Z80::AllRAMProcessor::Processor();
_processor->reset_power_on();
_busOperationHandler = new BusOperationHandler(self);
_busOperationCaptures = [[NSMutableArray alloc] init];
self.portLogic = CSTestMachinePortLogicReturnUpperByte;
}
return self;
}
- (void)dealloc {
delete _busOperationHandler;
}
#pragma mark - Accessors
- (void)setData:(NSData *)data atAddress:(uint16_t)startAddress {
_processor->set_data_at_address(startAddress, data.length, (const uint8_t *)data.bytes);
}
- (void)runForNumberOfCycles:(int)cycles {
_processor->run_for(Cycles(cycles));
}
- (void)runForInstruction {
_processor->run_for_instruction();
}
- (void)setValue:(uint16_t)value forRegister:(CSTestMachineZ80Register)reg {
_processor->set_value_of_register(registerForRegister(reg), value);
}
- (void)setValue:(uint8_t)value atAddress:(uint16_t)address {
_processor->set_data_at_address(address, 1, &value);
}
- (uint8_t)valueAtAddress:(uint16_t)address {
uint8_t value;
_processor->get_data_at_address(address, 1, &value);
return value;
}
- (uint16_t)valueForRegister:(CSTestMachineZ80Register)reg {
return _processor->get_value_of_register(registerForRegister(reg));
}
- (BOOL)isHalted {
return _processor->get_halt_line() ? YES : NO;
}
- (int)completedHalfCycles {
return int(_processor->get_timestamp().as_integral());
}
- (void)setNmiLine:(BOOL)nmiLine {
_nmiLine = nmiLine;
_processor->set_non_maskable_interrupt_line(nmiLine ? true : false);
}
- (void)setIrqLine:(BOOL)irqLine {
_irqLine = irqLine;
_processor->set_interrupt_line(irqLine ? true : false);
}
- (void)setWaitLine:(BOOL)waitLine {
_waitLine = waitLine;
_processor->set_wait_line(waitLine ? true : false);
}
- (void)setPortLogic:(CSTestMachinePortLogic)portLogic {
_portLogic = portLogic;
if(_portLogic == CSTestMachinePortLogicReturn191) {
_processor->set_port_access_delegate(&_value191PortDelegate);
} else {
_processor->set_port_access_delegate(&_topBytePortDelegate);
}
}
- (CPU::AllRAMProcessor *)processor {
return _processor;
}
#pragma mark - Bus operation accumulation
- (void)setCaptureBusActivity:(BOOL)captureBusActivity {
_captureBusActivity = captureBusActivity;
_processor->set_memory_access_delegate(captureBusActivity ? _busOperationHandler : nullptr);
}
- (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) {
case CPU::Z80::PartialMachineCycle::Write:
capture.operation = CSTestMachineZ80BusOperationCaptureOperationWrite;
break;
case CPU::Z80::PartialMachineCycle::Read:
capture.operation = CSTestMachineZ80BusOperationCaptureOperationRead;
break;
case CPU::Z80::PartialMachineCycle::Refresh:
capture.operation = CSTestMachineZ80BusOperationCaptureOperationReadOpcode;
break;
case CPU::Z80::PartialMachineCycle::Input:
capture.operation = CSTestMachineZ80BusOperationCaptureOperationPortRead;
break;
case CPU::Z80::PartialMachineCycle::Output:
capture.operation = CSTestMachineZ80BusOperationCaptureOperationPortWrite;
break;
case CPU::Z80::PartialMachineCycle::Internal:
capture.operation = CSTestMachineZ80BusOperationCaptureOperationInternalOperation;
break;
default: return;
}
capture.address = address;
capture.value = value;
capture.timeStamp = int(timeStamp.as_integral());
[_busOperationCaptures addObject:capture];
}
}
- (NSArray<CSTestMachineZ80BusOperationCapture *> *)busOperationCaptures {
return [_busOperationCaptures copy];
}
@end