mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-27 15:29:34 +00:00
Merge pull request #22 from TomHarte/6522UnitTests
Starts adding unit tests for the 6522, correcting the implementation as required
This commit is contained in:
commit
960cae15b1
@ -14,11 +14,6 @@
|
||||
|
||||
namespace MOS {
|
||||
|
||||
class MOS6522Delegate {
|
||||
public:
|
||||
virtual void mos6522_did_change_interrupt_status(void *mos6522) = 0;
|
||||
};
|
||||
|
||||
template <class T> class MOS6522 {
|
||||
private:
|
||||
enum InterruptFlag: uint8_t {
|
||||
@ -40,17 +35,17 @@ template <class T> class MOS6522 {
|
||||
{
|
||||
case 0x0:
|
||||
_registers.output[1] = value;
|
||||
static_cast<T *>(this)->set_port_output(1, value); // TODO: handshake
|
||||
break;
|
||||
case 0x1:
|
||||
_registers.output[0] = value;
|
||||
static_cast<T *>(this)->set_port_output(0, value); // TODO: handshake
|
||||
static_cast<T *>(this)->set_port_output(1, value, _registers.data_direction[1]); // TODO: handshake
|
||||
break;
|
||||
case 0xf:
|
||||
// No handshake, so write directly
|
||||
case 0x1:
|
||||
_registers.output[0] = value;
|
||||
static_cast<T *>(this)->set_port_output(0, value);
|
||||
static_cast<T *>(this)->set_port_output(0, value, _registers.data_direction[0]); // TODO: handshake
|
||||
break;
|
||||
// // No handshake, so write directly
|
||||
// _registers.output[0] = value;
|
||||
// static_cast<T *>(this)->set_port_output(0, value);
|
||||
// break;
|
||||
|
||||
case 0x2:
|
||||
_registers.data_direction[1] = value;
|
||||
@ -109,10 +104,9 @@ template <class T> class MOS6522 {
|
||||
// printf("6522 %p: %d\n", this, address);
|
||||
switch(address)
|
||||
{
|
||||
// case 0x0: return (_registers.auxiliary_control & 0x40) ? _registers.input[1] : static_cast<T *>(this)->get_port_input(1);
|
||||
case 0x0: return _registers.output[1];//static_cast<T *>(this)->get_port_input(1);
|
||||
case 0x0: return get_port_input(1, _registers.data_direction[1], _registers.output[1]);
|
||||
case 0xf: // TODO: handshake, latching
|
||||
case 0x1: return static_cast<T *>(this)->get_port_input(0);
|
||||
case 0x1: return get_port_input(0, _registers.data_direction[0], _registers.output[0]);
|
||||
|
||||
case 0x2: return _registers.data_direction[1];
|
||||
case 0x3: return _registers.data_direction[0];
|
||||
@ -149,30 +143,49 @@ template <class T> class MOS6522 {
|
||||
{
|
||||
}
|
||||
|
||||
void run_for_cycles(unsigned int number_of_cycles)
|
||||
void run_for_half_cycles(unsigned int number_of_cycles)
|
||||
{
|
||||
_registers.timer[0] -= number_of_cycles;
|
||||
_registers.timer[1] -= number_of_cycles;
|
||||
|
||||
if(!_registers.timer[1] && _timer_is_running[1])
|
||||
while(number_of_cycles--)
|
||||
{
|
||||
_timer_is_running[1] = false;
|
||||
_registers.interrupt_flags |= InterruptFlag::Timer2;
|
||||
reevaluate_interrupts();
|
||||
}
|
||||
if(_is_phase2)
|
||||
{
|
||||
_registers.last_timer[0] = _registers.timer[0];
|
||||
_registers.last_timer[1] = _registers.timer[1];
|
||||
|
||||
if(!_registers.timer[0] && _timer_is_running[0])
|
||||
{
|
||||
_registers.interrupt_flags |= InterruptFlag::Timer1;
|
||||
reevaluate_interrupts();
|
||||
if(_registers.timer_needs_reload)
|
||||
{
|
||||
_registers.timer_needs_reload = false;
|
||||
_registers.timer[0] = _registers.timer_latch[0];
|
||||
}
|
||||
else
|
||||
_registers.timer[0] --;
|
||||
|
||||
// TODO: reload shouldn't occur for a further 1.5 cycles
|
||||
if(_registers.auxiliary_control&0x40)
|
||||
_registers.timer[0] = _registers.timer_latch[0];
|
||||
_registers.timer[1] --;
|
||||
}
|
||||
else
|
||||
_timer_is_running[0] = false;
|
||||
{
|
||||
// IRQ is raised on the half cycle after overflow
|
||||
if((_registers.timer[1] == 0xffff) && !_registers.last_timer[1] && _timer_is_running[1])
|
||||
{
|
||||
_timer_is_running[1] = false;
|
||||
_registers.interrupt_flags |= InterruptFlag::Timer2;
|
||||
reevaluate_interrupts();
|
||||
}
|
||||
|
||||
if((_registers.timer[0] == 0xffff) && !_registers.last_timer[0] && _timer_is_running[0])
|
||||
{
|
||||
_registers.interrupt_flags |= InterruptFlag::Timer1;
|
||||
reevaluate_interrupts();
|
||||
|
||||
if(_registers.auxiliary_control&0x40)
|
||||
_registers.timer_needs_reload = true;
|
||||
else
|
||||
_timer_is_running[0] = false;
|
||||
}
|
||||
}
|
||||
|
||||
_is_phase2 ^= true;
|
||||
}
|
||||
// TODO: lots of other status effects
|
||||
}
|
||||
|
||||
bool get_interrupt_line()
|
||||
@ -181,23 +194,29 @@ template <class T> class MOS6522 {
|
||||
return !!interrupt_status;
|
||||
}
|
||||
|
||||
void set_delegate(MOS6522Delegate *delegate)
|
||||
{
|
||||
_delegate = delegate;
|
||||
}
|
||||
|
||||
MOS6522() :
|
||||
_timer_is_running{false, false},
|
||||
_last_posted_interrupt_status(false)
|
||||
_last_posted_interrupt_status(false),
|
||||
_is_phase2(false)
|
||||
{}
|
||||
|
||||
private:
|
||||
// Intended to be overwritten
|
||||
uint8_t get_port_input(int port) { return 0xff; }
|
||||
void set_port_output(int port, uint8_t value) {}
|
||||
// Expected to be overridden
|
||||
uint8_t get_port_input(int port) { return 0xff; }
|
||||
void set_port_output(int port, uint8_t value, uint8_t direction_mask) {}
|
||||
// void set_interrupt_status(bool status) {}
|
||||
|
||||
// Input/output multiplexer
|
||||
uint8_t get_port_input(int port, uint8_t output_mask, uint8_t output)
|
||||
{
|
||||
uint8_t input = static_cast<T *>(this)->get_port_input(port);
|
||||
return (input & ~output_mask) | (output & output_mask);
|
||||
}
|
||||
|
||||
// Phase toggle
|
||||
bool _is_phase2;
|
||||
|
||||
// Delegate and communications
|
||||
MOS6522Delegate *_delegate;
|
||||
bool _last_posted_interrupt_status;
|
||||
inline void reevaluate_interrupts()
|
||||
{
|
||||
@ -205,29 +224,52 @@ template <class T> class MOS6522 {
|
||||
if(new_interrupt_status != _last_posted_interrupt_status)
|
||||
{
|
||||
_last_posted_interrupt_status = new_interrupt_status;
|
||||
if(_delegate) _delegate->mos6522_did_change_interrupt_status(this);
|
||||
static_cast<T *>(this)->set_interrupt_status(new_interrupt_status);
|
||||
}
|
||||
}
|
||||
|
||||
// The registers
|
||||
struct Registers {
|
||||
uint8_t output[2], input[2], data_direction[2];
|
||||
uint16_t timer[2], timer_latch[2];
|
||||
uint16_t timer[2], timer_latch[2], last_timer[2];
|
||||
uint8_t shift;
|
||||
uint8_t auxiliary_control, peripheral_control;
|
||||
uint8_t interrupt_flags, interrupt_enable;
|
||||
bool timer_needs_reload;
|
||||
|
||||
// "A low reset (RES) input clears all R6522 internal registers to logic 0"
|
||||
Registers() :
|
||||
output{0, 0}, input{0, 0}, data_direction{0, 0},
|
||||
auxiliary_control(0), peripheral_control(0),
|
||||
interrupt_flags(0), interrupt_enable(0) {}
|
||||
interrupt_flags(0), interrupt_enable(0),
|
||||
last_timer{0, 0}, timer_needs_reload(false) {}
|
||||
} _registers;
|
||||
|
||||
// Internal state other than the registers
|
||||
bool _timer_is_running[2];
|
||||
};
|
||||
|
||||
class MOS6522IRQDelegate {
|
||||
public:
|
||||
class Delegate {
|
||||
public:
|
||||
virtual void mos6522_did_change_interrupt_status(void *mos6522) = 0;
|
||||
};
|
||||
|
||||
void set_delegate(Delegate *delegate)
|
||||
{
|
||||
_delegate = delegate;
|
||||
}
|
||||
|
||||
void set_interrupt_status(bool new_status)
|
||||
{
|
||||
if(_delegate) _delegate->mos6522_did_change_interrupt_status(this);
|
||||
}
|
||||
|
||||
private:
|
||||
Delegate *_delegate;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* _522_hpp */
|
||||
|
@ -79,8 +79,8 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
||||
}
|
||||
}
|
||||
|
||||
_userPortVIA.run_for_cycles(1);
|
||||
_keyboardVIA.run_for_cycles(1);
|
||||
_userPortVIA.run_for_half_cycles(2);
|
||||
_keyboardVIA.run_for_half_cycles(2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -44,10 +44,10 @@ enum Key: uint16_t {
|
||||
Key9 = key(0, 0x10), KeyPlus = key(0, 0x20), KeyGBP = key(0, 0x40), KeyDelete = key(0, 0x80),
|
||||
};
|
||||
|
||||
class UserPortVIA: public MOS::MOS6522<UserPortVIA> {
|
||||
class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDelegate {
|
||||
};
|
||||
|
||||
class KeyboardVIA: public MOS::MOS6522<KeyboardVIA> {
|
||||
class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDelegate {
|
||||
public:
|
||||
void set_key_state(Key key, bool isPressed) {
|
||||
if(isPressed)
|
||||
@ -75,9 +75,9 @@ class KeyboardVIA: public MOS::MOS6522<KeyboardVIA> {
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
void set_port_output(int port, uint8_t value) {
|
||||
void set_port_output(int port, uint8_t value, uint8_t mask) {
|
||||
if(port)
|
||||
_activation_mask = value;
|
||||
_activation_mask = (value & mask) | (~mask);
|
||||
}
|
||||
|
||||
KeyboardVIA() {
|
||||
@ -88,7 +88,7 @@ class KeyboardVIA: public MOS::MOS6522<KeyboardVIA> {
|
||||
uint8_t _activation_mask;
|
||||
};
|
||||
|
||||
class Machine: public CPU6502::Processor<Machine>, public CRTMachine::Machine, public MOS::MOS6522Delegate {
|
||||
class Machine: public CPU6502::Processor<Machine>, public CRTMachine::Machine, public MOS::MOS6522IRQDelegate::Delegate {
|
||||
public:
|
||||
Machine();
|
||||
~Machine();
|
||||
|
@ -313,6 +313,8 @@
|
||||
4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */; };
|
||||
4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */; };
|
||||
4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */; };
|
||||
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; };
|
||||
4BC751B61D157EB3006C31D9 /* MOS6522Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B51D157EB3006C31D9 /* MOS6522Bridge.mm */; };
|
||||
4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */; };
|
||||
4BC76E6B1C98F43700E6EF73 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */; };
|
||||
4BC9DF451D044FCA00F44158 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; };
|
||||
@ -685,6 +687,9 @@
|
||||
4BC3B74E1CD194CC00F86E85 /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = "<group>"; };
|
||||
4BC3B7501CD1956900F86E85 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = "<group>"; };
|
||||
4BC3B7511CD1956900F86E85 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = "<group>"; };
|
||||
4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = "<group>"; };
|
||||
4BC751B41D157EB3006C31D9 /* MOS6522Bridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MOS6522Bridge.h; sourceTree = "<group>"; };
|
||||
4BC751B51D157EB3006C31D9 /* MOS6522Bridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MOS6522Bridge.mm; sourceTree = "<group>"; };
|
||||
4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FIRFilter.cpp; sourceTree = "<group>"; };
|
||||
4BC76E681C98E31700E6EF73 /* FIRFilter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FIRFilter.hpp; sourceTree = "<group>"; };
|
||||
4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
|
||||
@ -1214,15 +1219,18 @@
|
||||
4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BB73EB81B587A5100552FC2 /* Info.plist */,
|
||||
4BB297DF1B587D8200A49093 /* Clock SignalTests-Bridging-Header.h */,
|
||||
4B1414631B588A1100E04248 /* Test Binaries */,
|
||||
4BC751B41D157EB3006C31D9 /* MOS6522Bridge.h */,
|
||||
4BB297E21B587D8300A49093 /* TestMachine.h */,
|
||||
4BC751B51D157EB3006C31D9 /* MOS6522Bridge.mm */,
|
||||
4BB297E31B587D8300A49093 /* TestMachine.mm */,
|
||||
4BB73EB81B587A5100552FC2 /* Info.plist */,
|
||||
4BC751B11D157E61006C31D9 /* 6522Tests.swift */,
|
||||
4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */,
|
||||
4B1414611B58888700E04248 /* KlausDormannTests.swift */,
|
||||
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */,
|
||||
4B92EAC91B7C112B00246143 /* TimingTests.swift */,
|
||||
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */,
|
||||
4B1414631B588A1100E04248 /* Test Binaries */,
|
||||
);
|
||||
path = "Clock SignalTests";
|
||||
sourceTree = "<group>";
|
||||
@ -1774,10 +1782,12 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4BC751B61D157EB3006C31D9 /* MOS6522Bridge.mm in Sources */,
|
||||
4B14145E1B5887AA00E04248 /* CPU6502AllRAM.cpp in Sources */,
|
||||
4B14145D1B5887A600E04248 /* CPU6502.cpp in Sources */,
|
||||
4B92EACA1B7C112B00246143 /* TimingTests.swift in Sources */,
|
||||
4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */,
|
||||
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */,
|
||||
4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */,
|
||||
4BB298F01B587D8400A49093 /* TestMachine.mm in Sources */,
|
||||
4B1414601B58885000E04248 /* WolfgangLorenzTests.swift in Sources */,
|
||||
|
113
OSBindings/Mac/Clock SignalTests/6522Tests.swift
Normal file
113
OSBindings/Mac/Clock SignalTests/6522Tests.swift
Normal file
@ -0,0 +1,113 @@
|
||||
//
|
||||
// 6522Tests.swift
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 18/06/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Foundation
|
||||
|
||||
class MOS6522Tests: XCTestCase {
|
||||
|
||||
private func with6522(action: (MOS6522Bridge) -> ()) {
|
||||
let bridge = MOS6522Bridge()
|
||||
action(bridge)
|
||||
}
|
||||
|
||||
// MARK: Timer tests
|
||||
|
||||
func testTimerCount() {
|
||||
with6522 {
|
||||
// set timer 1 to a value of $000a
|
||||
$0.setValue(10, forRegister: 4)
|
||||
$0.setValue(0, forRegister: 5)
|
||||
|
||||
// run for 5 cycles
|
||||
$0.runForHalfCycles(10)
|
||||
|
||||
// check that the timer has gone down by 5
|
||||
XCTAssert($0.valueForRegister(4) == 5, "Low order byte should be 5; was \($0.valueForRegister(4))")
|
||||
XCTAssert($0.valueForRegister(5) == 0, "High order byte should be 0; was \($0.valueForRegister(5))")
|
||||
}
|
||||
}
|
||||
|
||||
func testTimerLatches() {
|
||||
with6522 {
|
||||
// set timer 2 to $1020
|
||||
$0.setValue(0x10, forRegister: 8)
|
||||
$0.setValue(0x20, forRegister: 9)
|
||||
|
||||
// change the low-byte latch
|
||||
$0.setValue(0x40, forRegister: 8)
|
||||
|
||||
// chek that the new latched value hasn't been copied
|
||||
XCTAssert($0.valueForRegister(8) == 0x10, "Low order byte should be 0x10; was \($0.valueForRegister(8))")
|
||||
XCTAssert($0.valueForRegister(9) == 0x20, "High order byte should be 0x20; was \($0.valueForRegister(9))")
|
||||
|
||||
// write the low-byte latch
|
||||
$0.setValue(0x50, forRegister: 9)
|
||||
|
||||
// chek that the latched value has been copied
|
||||
XCTAssert($0.valueForRegister(8) == 0x40, "Low order byte should be 0x50; was \($0.valueForRegister(8))")
|
||||
XCTAssert($0.valueForRegister(9) == 0x50, "High order byte should be 0x40; was \($0.valueForRegister(9))")
|
||||
}
|
||||
}
|
||||
|
||||
func testTimerReload() {
|
||||
with6522 {
|
||||
// set timer 1 to a value of $0010, enable repeating mode
|
||||
$0.setValue(16, forRegister: 4)
|
||||
$0.setValue(0, forRegister: 5)
|
||||
$0.setValue(0x40, forRegister: 11)
|
||||
$0.setValue(0x40 | 0x80, forRegister: 14)
|
||||
|
||||
// run for 16 cycles
|
||||
$0.runForHalfCycles(32)
|
||||
|
||||
// check that the timer has gone down to 0 but not yet triggered an interrupt
|
||||
XCTAssert($0.valueForRegister(4) == 0, "Low order byte should be 0; was \($0.valueForRegister(4))")
|
||||
XCTAssert($0.valueForRegister(5) == 0, "High order byte should be 0; was \($0.valueForRegister(5))")
|
||||
XCTAssert(!$0.irqLine, "IRQ should not yet be active")
|
||||
|
||||
// check that two half-cycles later the timer is $ffff but IRQ still hasn't triggered
|
||||
$0.runForHalfCycles(2)
|
||||
XCTAssert($0.valueForRegister(4) == 0xff, "Low order byte should be 0xff; was \($0.valueForRegister(4))")
|
||||
XCTAssert($0.valueForRegister(5) == 0xff, "High order byte should be 0xff; was \($0.valueForRegister(5))")
|
||||
XCTAssert(!$0.irqLine, "IRQ should not yet be active")
|
||||
|
||||
// check that one half-cycle later the timer is still $ffff and IRQ has triggered...
|
||||
$0.runForHalfCycles(1)
|
||||
XCTAssert($0.irqLine, "IRQ should be active")
|
||||
XCTAssert($0.valueForRegister(4) == 0xff, "Low order byte should be 0xff; was \($0.valueForRegister(4))")
|
||||
XCTAssert($0.valueForRegister(5) == 0xff, "High order byte should be 0xff; was \($0.valueForRegister(5))")
|
||||
|
||||
// ... but that reading the timer cleared the interrupt
|
||||
XCTAssert(!$0.irqLine, "IRQ should be active")
|
||||
|
||||
// check that one half-cycles later the timer has reloaded
|
||||
$0.runForHalfCycles(1)
|
||||
XCTAssert($0.valueForRegister(4) == 0x10, "Low order byte should be 0x10; was \($0.valueForRegister(4))")
|
||||
XCTAssert($0.valueForRegister(5) == 0x00, "High order byte should be 0x00; was \($0.valueForRegister(5))")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Data direction tests
|
||||
func testDataDirection() {
|
||||
with6522 {
|
||||
// set low four bits of register B as output, the top four as input
|
||||
$0.setValue(0xf0, forRegister: 2)
|
||||
|
||||
// ask to output 0x8c
|
||||
$0.setValue(0x8c, forRegister: 0)
|
||||
|
||||
// set current input as 0xda
|
||||
$0.portBInput = 0xda
|
||||
|
||||
// test that the result of reading register B is therefore 0x8a
|
||||
XCTAssert($0.valueForRegister(0) == 0x8a, "Data direction register should mix input and output; got \($0.valueForRegister(0))")
|
||||
}
|
||||
}
|
||||
}
|
@ -3,3 +3,4 @@
|
||||
//
|
||||
|
||||
#import "TestMachine.h"
|
||||
#import "MOS6522Bridge.h"
|
||||
|
22
OSBindings/Mac/Clock SignalTests/MOS6522Bridge.h
Normal file
22
OSBindings/Mac/Clock SignalTests/MOS6522Bridge.h
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// MOS6522Bridge.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 18/06/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface MOS6522Bridge : NSObject
|
||||
|
||||
@property (nonatomic, readonly) BOOL irqLine;
|
||||
@property (nonatomic) uint8_t portBInput;
|
||||
@property (nonatomic) uint8_t portAInput;
|
||||
|
||||
- (void)setValue:(uint8_t)value forRegister:(NSUInteger)registerNumber;
|
||||
- (uint8_t)valueForRegister:(NSUInteger)registerNumber;
|
||||
|
||||
- (void)runForHalfCycles:(NSUInteger)numberOfHalfCycles;
|
||||
|
||||
@end
|
87
OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm
Normal file
87
OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm
Normal file
@ -0,0 +1,87 @@
|
||||
//
|
||||
// MOS6522Bridge.m
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 18/06/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MOS6522Bridge.h"
|
||||
#include "6522.hpp"
|
||||
|
||||
@class MOS6522Bridge;
|
||||
|
||||
class VanillaVIA: public MOS::MOS6522<VanillaVIA> {
|
||||
public:
|
||||
MOS6522Bridge *bridge;
|
||||
bool irq_line;
|
||||
uint8_t port_a_value;
|
||||
uint8_t port_b_value;
|
||||
|
||||
void set_interrupt_status(bool new_status)
|
||||
{
|
||||
irq_line = new_status;
|
||||
}
|
||||
|
||||
uint8_t get_port_input(int port)
|
||||
{
|
||||
return port ? port_b_value : port_a_value;
|
||||
}
|
||||
};
|
||||
|
||||
@implementation MOS6522Bridge
|
||||
{
|
||||
VanillaVIA _via;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if(self)
|
||||
{
|
||||
_via.bridge = self;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setValue:(uint8_t)value forRegister:(NSUInteger)registerNumber
|
||||
{
|
||||
_via.set_register((int)registerNumber, value);
|
||||
}
|
||||
|
||||
- (uint8_t)valueForRegister:(NSUInteger)registerNumber
|
||||
{
|
||||
return _via.get_register((int)registerNumber);
|
||||
}
|
||||
|
||||
- (void)runForHalfCycles:(NSUInteger)numberOfHalfCycles
|
||||
{
|
||||
_via.run_for_half_cycles((int)numberOfHalfCycles);
|
||||
}
|
||||
|
||||
- (BOOL)irqLine
|
||||
{
|
||||
return _via.irq_line;
|
||||
}
|
||||
|
||||
- (void)setPortAInput:(uint8_t)portAInput
|
||||
{
|
||||
_via.port_a_value = portAInput;
|
||||
}
|
||||
|
||||
- (uint8_t)portAInput
|
||||
{
|
||||
return _via.port_a_value;
|
||||
}
|
||||
|
||||
- (void)setPortBInput:(uint8_t)portBInput
|
||||
{
|
||||
_via.port_b_value = portBInput;
|
||||
}
|
||||
|
||||
- (uint8_t)portBInput
|
||||
{
|
||||
return _via.port_b_value;
|
||||
}
|
||||
|
||||
@end
|
@ -1057,6 +1057,13 @@ template <class T> class Processor {
|
||||
static_cast<T *>(this)->synchronise();
|
||||
}
|
||||
|
||||
/*!
|
||||
Called to announce the end of a run_for_cycles period, allowing deferred work to take place.
|
||||
|
||||
Users of the 6502 template may override this.
|
||||
*/
|
||||
void synchronise() {}
|
||||
|
||||
/*!
|
||||
Gets the value of a register.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user