From a49f2b41cce2390595fbbe1d9964a9ca6ec05055 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Mon, 4 Jul 2016 21:48:25 -0400
Subject: [PATCH 1/3] Added C++ side of things. I think.

---
 Machines/Vic-20/Vic20.hpp | 39 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 36 insertions(+), 3 deletions(-)

diff --git a/Machines/Vic-20/Vic20.hpp b/Machines/Vic-20/Vic20.hpp
index a7dc1d692..303c5d4be 100644
--- a/Machines/Vic-20/Vic20.hpp
+++ b/Machines/Vic-20/Vic20.hpp
@@ -49,11 +49,20 @@ enum Key: uint16_t {
 	TerminateSequence = 0,	NotMapped = 0xffff
 };
 
+enum JoystickInput {
+	Up = 0x04,
+	Down = 0x08,
+	Left = 0x10,
+	Right = 0x80,
+	Fire = 0x20
+};
+
+
 class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDelegate {
 	public:
 		uint8_t get_port_input(Port port) {
 			if(!port) {
-				return 0x00;	// TODO: bit 6 should be high if there is no tape, low otherwise
+				return _portA;	// TODO: bit 6 should be high if there is no tape, low otherwise
 			}
 			return 0xff;
 		}
@@ -64,12 +73,24 @@ class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDeleg
 			}
 		}
 
+		void set_joystick_state(JoystickInput input, bool value) {
+			if(input != JoystickInput::Right)
+			{
+				_portA = (_portA & ~input) | (value ? 0 : input);
+			}
+		}
+
 		using MOS6522IRQDelegate::set_interrupt_status;
+
+		UserPortVIA() : _portA(0xbf) {}
+
+	private:
+		uint8_t _portA;
 };
 
 class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDelegate {
 	public:
-		KeyboardVIA() {
+		KeyboardVIA() : _portB(0xff) {
 			clear_all_keys();
 		}
 
@@ -96,7 +117,7 @@ class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDeleg
 				return result;
 			}
 
-			return 0xff;
+			return _portB;
 		}
 
 		void set_port_output(Port port, uint8_t value, uint8_t mask) {
@@ -110,9 +131,17 @@ class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDeleg
 			}
 		}
 
+		void set_joystick_state(JoystickInput input, bool value) {
+			if(input == JoystickInput::Right)
+			{
+				_portB = (_portB & ~input) | (value ? 0 : input);
+			}
+		}
+
 		using MOS6522IRQDelegate::set_interrupt_status;
 
 	private:
+		uint8_t _portB;
 		uint8_t _columns[8];
 		uint8_t _activation_mask;
 };
@@ -158,6 +187,10 @@ class Machine:
 
 		void set_key_state(Key key, bool isPressed) { _keyboardVIA.set_key_state(key, isPressed); }
 		void clear_all_keys() { _keyboardVIA.clear_all_keys(); }
+		void set_joystick_state(JoystickInput input, bool isPressed) {
+			_userPortVIA.set_joystick_state(input, isPressed);
+			_keyboardVIA.set_joystick_state(input, isPressed);
+		}
 
 		inline void set_use_fast_tape_hack(bool activate) { _use_fast_tape_hack = activate; }
 

From df4724eb3e8f9a57caa20d70da572124fa4c03a5 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Mon, 4 Jul 2016 22:01:07 -0400
Subject: [PATCH 2/3] Added a modal toggle allowing the keyboard to be switched
 between being a keyboard and being a joystick for the Vic.

---
 .../Clock Signal/Machine/Wrappers/CSVic20.mm  | 54 +++++++++++++------
 1 file changed, 37 insertions(+), 17 deletions(-)

diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm
index 3dbf8f626..434c7f197 100644
--- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm	
+++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm	
@@ -13,6 +13,7 @@
 
 @implementation CSVic20 {
 	Vic20::Machine _vic20;
+	BOOL _joystickMode;
 }
 
 - (CRTMachine::Machine * const)machine {
@@ -109,24 +110,43 @@
 	//	KeyPlus
 	//	KeyGBP
 
-	@synchronized(self) {
-		switch(key)
-		{
-			default: {
-				NSNumber *targetKey = vicKeysByKeys[@(key)];
-				if(targetKey)
-				{
-					_vic20.set_key_state((Vic20::Key)targetKey.integerValue, isPressed);
-				}
-				else
-					NSLog(@"Unmapped: %02x", key);
-			} break;
+	if(key == VK_Tab && isPressed)
+	{
+		_joystickMode ^= YES;
+	}
 
-			case VK_Shift:
-				// Yuck
-				_vic20.set_key_state(Vic20::Key::KeyLShift, isPressed);
-				_vic20.set_key_state(Vic20::Key::KeyRShift, isPressed);
-			break;
+	@synchronized(self) {
+		if(_joystickMode)
+		{
+			switch(key)
+			{
+				case VK_UpArrow:	_vic20.set_joystick_state(Vic20::JoystickInput::Up, isPressed);		break;
+				case VK_DownArrow:	_vic20.set_joystick_state(Vic20::JoystickInput::Down, isPressed);	break;
+				case VK_LeftArrow:	_vic20.set_joystick_state(Vic20::JoystickInput::Left, isPressed);	break;
+				case VK_RightArrow:	_vic20.set_joystick_state(Vic20::JoystickInput::Right, isPressed);	break;
+				case VK_ANSI_A:		_vic20.set_joystick_state(Vic20::JoystickInput::Fire, isPressed);	break;
+			}
+		}
+		else
+		{
+			switch(key)
+			{
+				default: {
+					NSNumber *targetKey = vicKeysByKeys[@(key)];
+					if(targetKey)
+					{
+						_vic20.set_key_state((Vic20::Key)targetKey.integerValue, isPressed);
+					}
+					else
+						NSLog(@"Unmapped: %02x", key);
+				} break;
+
+				case VK_Shift:
+					// Yuck
+					_vic20.set_key_state(Vic20::Key::KeyLShift, isPressed);
+					_vic20.set_key_state(Vic20::Key::KeyRShift, isPressed);
+				break;
+			}
 		}
 	}
 }

From 97751a9d86d89b37dbe1a583473909939b7d605a Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Tue, 5 Jul 2016 10:55:47 -0400
Subject: [PATCH 3/3] Took the preliminary steps necessary to wire up a serial
 port.

---
 Components/6522/6522.hpp  |  6 ++--
 Machines/Vic-20/Vic20.cpp | 68 ++++++++++++++++++++++++++++++--------
 Machines/Vic-20/Vic20.hpp | 69 ++++++++++++++++++++++++++++++++-------
 3 files changed, 115 insertions(+), 28 deletions(-)

diff --git a/Components/6522/6522.hpp b/Components/6522/6522.hpp
index 91e785aef..34bb53f74 100644
--- a/Components/6522/6522.hpp
+++ b/Components/6522/6522.hpp
@@ -112,17 +112,17 @@ template <class T> class MOS6522 {
 					_registers.auxiliary_control = value;
 				break;
 				case 0xc:
-					printf("Peripheral control %02x\n", value);
+//					printf("Peripheral control %02x\n", value);
 					_registers.peripheral_control = value;
 					switch(value & 0x0e)
 					{
-						default: break;
+						default: printf("Unimplemented control line mode %d\n", (value >> 1)&7); break;
 						case 0x0c:	static_cast<T *>(this)->set_control_line_output(Port::A, Line::Two, false);		break;
 						case 0x0e:	static_cast<T *>(this)->set_control_line_output(Port::A, Line::Two, true);		break;
 					}
 					switch(value & 0xe0)
 					{
-						default: break;
+						default: printf("Unimplemented control line mode %d\n", (value >> 5)&7); break;
 						case 0xc0:	static_cast<T *>(this)->set_control_line_output(Port::B, Line::Two, false);		break;
 						case 0xe0:	static_cast<T *>(this)->set_control_line_output(Port::B, Line::Two, true);		break;
 					}
diff --git a/Machines/Vic-20/Vic20.cpp b/Machines/Vic-20/Vic20.cpp
index ca19e6c70..c912908b4 100644
--- a/Machines/Vic-20/Vic20.cpp
+++ b/Machines/Vic-20/Vic20.cpp
@@ -15,8 +15,16 @@ using namespace Vic20;
 Machine::Machine() :
 	_rom(nullptr)
 {
-	_userPortVIA.set_delegate(this);
-	_keyboardVIA.set_delegate(this);
+	_userPortVIA.reset(new UserPortVIA);
+	_keyboardVIA.reset(new KeyboardVIA);
+	_serialPort.reset(new SerialPort);
+
+	_userPortVIA->set_serial_port(_serialPort);
+	_keyboardVIA->set_serial_port(_serialPort);
+	_serialPort->set_vias(_userPortVIA, _keyboardVIA);
+
+	_userPortVIA->set_delegate(this);
+	_keyboardVIA->set_delegate(this);
 	_tape.set_delegate(this);
 
 	memset(_videoMemoryMap, 0, sizeof(_videoMemoryMap));
@@ -71,8 +79,8 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
 		if((address&0xfc00) == 0x9000)
 		{
 			if((address&0xff00) == 0x9000)	result &= _mos6560->get_register(address);
-			if((address&0xfc10) == 0x9010)	result &= _userPortVIA.get_register(address);
-			if((address&0xfc20) == 0x9020)	result &= _keyboardVIA.get_register(address);
+			if((address&0xfc10) == 0x9010)	result &= _userPortVIA->get_register(address);
+			if((address&0xfc20) == 0x9020)	result &= _keyboardVIA->get_register(address);
 		}
 		*value = result;
 
@@ -80,10 +88,10 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
 		if(_use_fast_tape_hack && address == 0xf92f && operation == CPU6502::BusOperation::ReadOpcode)
 		{
 			// advance time on the tape and the VIAs until an interrupt is signalled
-			while(!_userPortVIA.get_interrupt_line() && !_keyboardVIA.get_interrupt_line())
+			while(!_userPortVIA->get_interrupt_line() && !_keyboardVIA->get_interrupt_line())
 			{
-				_userPortVIA.run_for_half_cycles(2);
-				_keyboardVIA.run_for_half_cycles(2);
+				_userPortVIA->run_for_half_cycles(2);
+				_keyboardVIA->run_for_half_cycles(2);
 				_tape.run_for_cycles(1);
 			}
 		}
@@ -95,13 +103,13 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
 		if((address&0xfc00) == 0x9000)
 		{
 			if((address&0xff00) == 0x9000)	_mos6560->set_register(address, *value);
-			if((address&0xfc10) == 0x9010)	_userPortVIA.set_register(address, *value);
-			if((address&0xfc20) == 0x9020)	_keyboardVIA.set_register(address, *value);
+			if((address&0xfc10) == 0x9010)	_userPortVIA->set_register(address, *value);
+			if((address&0xfc20) == 0x9020)	_keyboardVIA->set_register(address, *value);
 		}
 	}
 
-	_userPortVIA.run_for_half_cycles(2);
-	_keyboardVIA.run_for_half_cycles(2);
+	_userPortVIA->run_for_half_cycles(2);
+	_keyboardVIA->run_for_half_cycles(2);
 	if(_typer) _typer->update(1);
 	_tape.run_for_cycles(1);
 	return 1;
@@ -111,8 +119,8 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
 
 void Machine::mos6522_did_change_interrupt_status(void *mos6522)
 {
-	set_nmi_line(_userPortVIA.get_interrupt_line());
-	set_irq_line(_keyboardVIA.get_interrupt_line());
+	set_nmi_line(_userPortVIA->get_interrupt_line());
+	set_irq_line(_keyboardVIA->get_interrupt_line());
 }
 
 #pragma mark - Setup
@@ -172,7 +180,7 @@ void Machine::set_tape(std::shared_ptr<Storage::Tape> tape)
 
 void Machine::tape_did_change_input(Tape *tape)
 {
-	_keyboardVIA.set_control_line_input(KeyboardVIA::Port::A, KeyboardVIA::Line::One, tape->get_input());
+	_keyboardVIA->set_control_line_input(KeyboardVIA::Port::A, KeyboardVIA::Line::One, tape->get_input());
 }
 
 #pragma mark - Typer
@@ -306,3 +314,35 @@ void Tape::process_input_pulse(Storage::Tape::Pulse pulse)
 		if(_delegate) _delegate->tape_did_change_input(this);
 	}
 }
+
+#pragma mark - Serial Port
+
+void SerialPort::set_clock_output(bool value)
+{
+	printf("Serial port clock output %s\n", value ? "on" : "off");
+}
+
+void SerialPort::set_data_output(bool value)
+{
+	printf("Serial port data output %s\n", value ? "on" : "off");
+}
+
+void SerialPort::set_attention_output(bool value)
+{
+	printf("Serial port attention output %s\n", value ? "on" : "off");
+}
+
+void SerialPort::set_clock_input(bool value)
+{
+	printf("Serial port clock input %s\n", value ? "on" : "off");
+}
+
+void SerialPort::set_data_input(bool value)
+{
+	printf("Serial port data input %s\n", value ? "on" : "off");
+}
+
+void SerialPort::set_attention_input(bool value)
+{
+	printf("Serial port attention input %s\n", value ? "on" : "off");
+}
diff --git a/Machines/Vic-20/Vic20.hpp b/Machines/Vic-20/Vic20.hpp
index 303c5d4be..4c298aa13 100644
--- a/Machines/Vic-20/Vic20.hpp
+++ b/Machines/Vic-20/Vic20.hpp
@@ -57,6 +57,28 @@ enum JoystickInput {
 	Fire = 0x20
 };
 
+class UserPortVIA;
+class KeyboardVIA;
+
+class SerialPort {
+	public:
+		void set_clock_output(bool value);
+		void set_data_output(bool value);
+		void set_attention_output(bool value);
+
+		void set_clock_input(bool value);
+		void set_data_input(bool value);
+		void set_attention_input(bool value);
+
+		void set_vias(std::shared_ptr<UserPortVIA> userPortVIA, std::shared_ptr<KeyboardVIA> keyboardVIA) {
+			_userPortVIA = userPortVIA;
+			_keyboardVIA = keyboardVIA;
+		}
+
+	private:
+		std::weak_ptr<UserPortVIA> _userPortVIA;
+		std::weak_ptr<KeyboardVIA> _keyboardVIA;
+};
 
 class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDelegate {
 	public:
@@ -68,9 +90,9 @@ class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDeleg
 		}
 
 		void set_control_line_output(Port port, Line line, bool value) {
-			if(port == Port::A && line == Line::Two) {
-				printf("Tape motor %s\n", value ? "on" : "off");
-			}
+//			if(port == Port::A && line == Line::Two) {
+//				printf("Tape motor %s\n", value ? "on" : "off");
+//			}
 		}
 
 		void set_joystick_state(JoystickInput input, bool value) {
@@ -80,12 +102,24 @@ class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDeleg
 			}
 		}
 
+		void set_port_output(Port port, uint8_t value, uint8_t mask) {
+			if(!port) {
+				std::shared_ptr<SerialPort> serialPort = _serialPort.lock();
+				if(serialPort) serialPort->set_attention_output(!(value&0x80));
+			}
+		}
+
 		using MOS6522IRQDelegate::set_interrupt_status;
 
 		UserPortVIA() : _portA(0xbf) {}
 
+		void set_serial_port(std::shared_ptr<SerialPort> serialPort) {
+			_serialPort = serialPort;
+		}
+
 	private:
 		uint8_t _portA;
+		std::weak_ptr<SerialPort> _serialPort;
 };
 
 class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDelegate {
@@ -126,8 +160,15 @@ class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDeleg
 		}
 
 		void set_control_line_output(Port port, Line line, bool value) {
-			if(port == Port::A && line == Line::Two) {
-				printf("Blah Tape motor %s\n", value ? "on" : "off");
+			if(line == Line::Two) {
+				std::shared_ptr<SerialPort> serialPort = _serialPort.lock();
+				if(serialPort) {
+					if(port == Port::A) {
+						serialPort->set_clock_output(value);
+					} else {
+						serialPort->set_data_output(value);
+					}
+				}
 			}
 		}
 
@@ -140,10 +181,15 @@ class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDeleg
 
 		using MOS6522IRQDelegate::set_interrupt_status;
 
+		void set_serial_port(std::shared_ptr<SerialPort> serialPort) {
+			_serialPort = serialPort;
+		}
+
 	private:
 		uint8_t _portB;
 		uint8_t _columns[8];
 		uint8_t _activation_mask;
+		std::weak_ptr<SerialPort> _serialPort;
 };
 
 class Tape: public Storage::TapePlayer {
@@ -185,11 +231,11 @@ class Machine:
 		void add_prg(size_t length, const uint8_t *data);
 		void set_tape(std::shared_ptr<Storage::Tape> tape);
 
-		void set_key_state(Key key, bool isPressed) { _keyboardVIA.set_key_state(key, isPressed); }
-		void clear_all_keys() { _keyboardVIA.clear_all_keys(); }
+		void set_key_state(Key key, bool isPressed) { _keyboardVIA->set_key_state(key, isPressed); }
+		void clear_all_keys() { _keyboardVIA->clear_all_keys(); }
 		void set_joystick_state(JoystickInput input, bool isPressed) {
-			_userPortVIA.set_joystick_state(input, isPressed);
-			_keyboardVIA.set_joystick_state(input, isPressed);
+			_userPortVIA->set_joystick_state(input, isPressed);
+			_keyboardVIA->set_joystick_state(input, isPressed);
 		}
 
 		inline void set_use_fast_tape_hack(bool activate) { _use_fast_tape_hack = activate; }
@@ -237,8 +283,9 @@ class Machine:
 		void write_to_map(uint8_t **map, uint8_t *area, uint16_t address, uint16_t length);
 
 		std::unique_ptr<MOS::MOS6560> _mos6560;
-		UserPortVIA _userPortVIA;
-		KeyboardVIA _keyboardVIA;
+		std::shared_ptr<UserPortVIA> _userPortVIA;
+		std::shared_ptr<KeyboardVIA> _keyboardVIA;
+		std::shared_ptr<SerialPort> _serialPort;
 
 		// Tape
 		Tape _tape;