mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-26 15:32:04 +00:00
Merge pull request #38 from TomHarte/VicJoystick
Vic: Adds support for a toggleable joystick-via-keyboard mode and wires in one side of a serial port
This commit is contained in:
commit
1cc83dc0a0
@ -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;
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -49,27 +49,82 @@ enum Key: uint16_t {
|
||||
TerminateSequence = 0, NotMapped = 0xffff
|
||||
};
|
||||
|
||||
enum JoystickInput {
|
||||
Up = 0x04,
|
||||
Down = 0x08,
|
||||
Left = 0x10,
|
||||
Right = 0x80,
|
||||
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:
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if(input != JoystickInput::Right)
|
||||
{
|
||||
_portA = (_portA & ~input) | (value ? 0 : input);
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
public:
|
||||
KeyboardVIA() {
|
||||
KeyboardVIA() : _portB(0xff) {
|
||||
clear_all_keys();
|
||||
}
|
||||
|
||||
@ -96,7 +151,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) {
|
||||
@ -105,16 +160,36 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void set_joystick_state(JoystickInput input, bool value) {
|
||||
if(input == JoystickInput::Right)
|
||||
{
|
||||
_portB = (_portB & ~input) | (value ? 0 : input);
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -156,8 +231,12 @@ 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);
|
||||
}
|
||||
|
||||
inline void set_use_fast_tape_hack(bool activate) { _use_fast_tape_hack = activate; }
|
||||
|
||||
@ -204,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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user