1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 16:31:31 +00:00

Merge pull request #39 from TomHarte/1540

Starts work on the serial bus and preparatory work for a 1540 emulation
This commit is contained in:
Thomas Harte 2016-07-10 08:06:04 -04:00 committed by GitHub
commit cf83258eaf
25 changed files with 987 additions and 182 deletions

View File

@ -10,6 +10,7 @@
#define _522_hpp
#include <cstdint>
#include <typeinfo>
#include <cstdio>
namespace MOS {
@ -52,14 +53,14 @@ template <class T> class MOS6522 {
inline void set_register(int address, uint8_t value)
{
address &= 0xf;
// printf("6522 %p: %d <- %02x\n", this, address, value);
// printf("6522 [%s]: %0x <- %02x\n", typeid(*this).name(), address, value);
switch(address)
{
case 0x0:
_registers.output[1] = value;
static_cast<T *>(this)->set_port_output(Port::B, value, _registers.data_direction[1]); // TODO: handshake
_registers.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | InterruptFlag::CB2ActiveEdge);
_registers.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | ((_registers.peripheral_control&0x20) ? 0 : InterruptFlag::CB2ActiveEdge));
reevaluate_interrupts();
break;
case 0xf:
@ -67,7 +68,7 @@ template <class T> class MOS6522 {
_registers.output[0] = value;
static_cast<T *>(this)->set_port_output(Port::A, value, _registers.data_direction[0]); // TODO: handshake
_registers.interrupt_flags &= ~(InterruptFlag::CA1ActiveEdge | InterruptFlag::CA2ActiveEdge);
_registers.interrupt_flags &= ~(InterruptFlag::CA1ActiveEdge | ((_registers.peripheral_control&0x02) ? 0 : InterruptFlag::CB2ActiveEdge));
reevaluate_interrupts();
break;
// // No handshake, so write directly
@ -114,17 +115,25 @@ template <class T> class MOS6522 {
case 0xc:
// printf("Peripheral control %02x\n", value);
_registers.peripheral_control = value;
switch(value & 0x0e)
// TODO: simplify below; trying to avoid improper logging of unimplemented warnings in input mode
if(value & 0x08)
{
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 & 0x0e)
{
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)
if(value & 0x80)
{
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;
switch(value & 0xe0)
{
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;
}
}
break;
@ -207,7 +216,16 @@ template <class T> class MOS6522 {
break;
case Line::Two:
// TODO
// TODO: output modes, but probably elsewhere?
if( value != _control_inputs[port].line_two && // i.e. value has changed ...
!(_registers.peripheral_control & (port ? 0x80 : 0x08)) && // ... and line is input ...
value == !!(_registers.peripheral_control & (port ? 0x40 : 0x04)) // ... and it's either high or low, as required
)
{
_registers.interrupt_flags |= port ? InterruptFlag::CB2ActiveEdge : InterruptFlag::CA2ActiveEdge;
reevaluate_interrupts();
}
_control_inputs[port].line_two = value;
break;
}
}
@ -284,9 +302,8 @@ template <class T> class MOS6522 {
// Expected to be overridden
uint8_t get_port_input(Port port) { return 0xff; }
void set_port_output(Port port, uint8_t value, uint8_t direction_mask) {}
bool get_control_line(Port port, Line line) { return true; }
void set_control_line_output(Port port, Line line, bool value) {}
void set_interrupt_status(bool status) {}
void set_interrupt_status(bool status) {}
// Input/output multiplexer
uint8_t get_port_input(Port port, uint8_t output_mask, uint8_t output)

View File

@ -0,0 +1,100 @@
//
// Commodore1540.cpp
// Clock Signal
//
// Created by Thomas Harte on 05/07/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "C1540.hpp"
#include <string.h>
using namespace Commodore::C1540;
Machine::Machine()
{
// create a serial port and a VIA to run it
_serialPortVIA.reset(new SerialPortVIA);
_serialPort.reset(new SerialPort);
// attach the serial port to its VIA and vice versa
_serialPort->set_serial_port_via(_serialPortVIA);
_serialPortVIA->set_serial_port(_serialPort);
// set this instance as the delegate to receive interrupt requests from both VIAs
_serialPortVIA->set_delegate(this);
_driveVIA.set_delegate(this);
}
void Machine::set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bus)
{
Commodore::Serial::AttachPortAndBus(_serialPort, serial_bus);
}
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
{
// if(operation == CPU6502::BusOperation::ReadOpcode && (address >= 0xF556 && address <= 0xF56D)) printf("%04x\n", address);
// if(operation == CPU6502::BusOperation::ReadOpcode && (address == 0xE887)) printf("A: %02x\n", get_value_of_register(CPU6502::Register::A));
/* static bool log = false;
if(operation == CPU6502::BusOperation::ReadOpcode)
{
log = (address >= 0xE85B && address <= 0xE907) || (address >= 0xE9C9 && address <= 0xEA2D);
if(log) printf("\n%04x: ", address);
}
if(log) printf("[%c %04x] ", isReadOperation(operation) ? 'r' : 'w', address);*/
/*
Memory map (given that I'm unsure yet on any potential mirroring):
0x00000x07ff RAM
0x18000x180f the serial-port VIA
0x1c000x1c0f the drive VIA
0xc0000xffff ROM
*/
if(address < 0x800)
{
if(isReadOperation(operation))
*value = _ram[address];
else
_ram[address] = *value;
}
else if(address >= 0xc000)
{
if(isReadOperation(operation))
*value = _rom[address & 0x3fff];
}
else if(address >= 0x1800 && address <= 0x180f)
{
if(isReadOperation(operation))
*value = _serialPortVIA->get_register(address);
else
_serialPortVIA->set_register(address, *value);
}
else if(address >= 0x1c00 && address <= 0x1c0f)
{
if(isReadOperation(operation))
*value = _driveVIA.get_register(address);
else
_driveVIA.set_register(address, *value);
}
_serialPortVIA->run_for_half_cycles(2);
_driveVIA.run_for_half_cycles(2);
return 1;
}
void Machine::set_rom(const uint8_t *rom)
{
memcpy(_rom, rom, sizeof(_rom));
}
#pragma mark - 6522 delegate
void Machine::mos6522_did_change_interrupt_status(void *mos6522)
{
// both VIAs are connected to the IRQ line
set_irq_line(_serialPortVIA->get_interrupt_line() || _driveVIA.get_interrupt_line());
}

View File

@ -0,0 +1,194 @@
//
// Commodore1540.hpp
// Clock Signal
//
// Created by Thomas Harte on 05/07/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Commodore1540_hpp
#define Commodore1540_hpp
#include "../../../Processors/6502/CPU6502.hpp"
#include "../../../Components/6522/6522.hpp"
#include "../SerialBus.hpp"
namespace Commodore {
namespace C1540 {
/*!
An implementation of the serial-port VIA in a Commodore 1540 the VIA that facilitates all
IEC bus communications.
It is wired up such that Port B contains:
Bit 0: data input; 1 if the line is low, 0 if it is high;
Bit 1: data output; 1 if the line should be low, 0 if it should be high;
Bit 2: clock input; 1 if the line is low, 0 if it is high;
Bit 3: clock output; 1 if the line is low, 0 if it is high;
Bit 4: attention acknowledge output; exclusive ORd with the attention input and ORd onto the data output;
Bits 5/6: device select input; the 1540 will act as device 8 + [value of bits]
Bit 7: attention input; 1 if the line is low, 0 if it is high
The attention input is also connected to CA1, similarly inverted the CA1 wire will be high when the bus is low and vice versa.
*/
class SerialPortVIA: public MOS::MOS6522<SerialPortVIA>, public MOS::MOS6522IRQDelegate {
public:
using MOS6522IRQDelegate::set_interrupt_status;
SerialPortVIA() : _portB(0x00), _attention_acknowledge_level(false), _attention_level_input(true), _data_level_output(false) {}
uint8_t get_port_input(Port port) {
if(port) {
return _portB;
}
return 0xff;
}
void set_port_output(Port port, uint8_t value, uint8_t mask) {
if(port) {
std::shared_ptr<::Commodore::Serial::Port> serialPort = _serialPort.lock();
if(serialPort) {
_attention_acknowledge_level = !(value&0x10);
_data_level_output = (value&0x02);
serialPort->set_output(::Commodore::Serial::Line::Clock, (::Commodore::Serial::LineLevel)!(value&0x08));
update_data_line();
}
}
}
void set_serial_line_state(::Commodore::Serial::Line line, bool value) {
switch(line) {
default: break;
case ::Commodore::Serial::Line::Data: _portB = (_portB & ~0x01) | (value ? 0x00 : 0x01); break;
case ::Commodore::Serial::Line::Clock: _portB = (_portB & ~0x04) | (value ? 0x00 : 0x04); break;
case ::Commodore::Serial::Line::Attention:
_attention_level_input = !value;
_portB = (_portB & ~0x80) | (value ? 0x00 : 0x80);
set_control_line_input(Port::A, Line::One, !value);
update_data_line();
break;
}
}
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort) {
_serialPort = serialPort;
}
private:
uint8_t _portB;
std::weak_ptr<::Commodore::Serial::Port> _serialPort;
bool _attention_acknowledge_level, _attention_level_input, _data_level_output;
void update_data_line()
{
std::shared_ptr<::Commodore::Serial::Port> serialPort = _serialPort.lock();
if(serialPort) {
// "ATN (Attention) is an input on pin 3 of P2 and P3 that is sensed at PB7 and CA1 of UC3 after being inverted by UA1"
serialPort->set_output(::Commodore::Serial::Line::Data,
(::Commodore::Serial::LineLevel)(!_data_level_output
&& (_attention_level_input != _attention_acknowledge_level)));
}
}
};
/*!
An implementation of the drive VIA in a Commodore 1540 the VIA that is used to interface with the disk.
It is wired up such that Port B contains:
Bits 0/1: head step direction (TODO)
Bit 2: motor control (TODO)
Bit 3: LED control (TODO)
Bit 4: write protect photocell status (TODO)
Bits 5/6: write density (TODO)
Bit 7: 0 if sync marks are currently being detected, 1 otherwise;
... and Port A contains the byte most recently read from the disk or the byte next to write to the disk, depending on data direction.
It is implied that CA2 might be used to set processor overflow, CA1 a strobe for data input, and one of the CBs being definitive on
whether the disk head is being told to read or write, but it's unclear and I've yet to investigate. So, TODO.
*/
class DriveVIA: public MOS::MOS6522<DriveVIA>, public MOS::MOS6522IRQDelegate {
public:
using MOS6522IRQDelegate::set_interrupt_status;
uint8_t get_port_input(Port port) {
if(port)
{
return 0xff; // imply not sync, write protect tab uncovered
}
return 0xff;
}
void set_port_output(Port port, uint8_t value, uint8_t direction_mask) {
if(port)
{
// if(value&4)
// {
// printf("Head step: %d\n", value&3);
// printf("Motor: %s\n", value&4 ? "On" : "Off");
// printf("LED: %s\n", value&8 ? "On" : "Off");
// printf("Density: %d\n", (value >> 5)&3);
// }
}
}
};
/*!
An implementation of the C1540's serial port; this connects incoming line levels to the serial-port VIA.
*/
class SerialPort : public ::Commodore::Serial::Port {
public:
void set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level) {
std::shared_ptr<SerialPortVIA> serialPortVIA = _serialPortVIA.lock();
if(serialPortVIA) serialPortVIA->set_serial_line_state(line, (bool)level);
}
void set_serial_port_via(std::shared_ptr<SerialPortVIA> serialPortVIA) {
_serialPortVIA = serialPortVIA;
}
private:
std::weak_ptr<SerialPortVIA> _serialPortVIA;
};
/*!
Provides an emulation of the C1540.
*/
class Machine:
public CPU6502::Processor<Machine>,
public MOS::MOS6522IRQDelegate::Delegate {
public:
Machine();
/*!
Sets the ROM image to use for this drive; it is assumed that the buffer provided will be at least 16 kb in size.
*/
void set_rom(const uint8_t *rom);
/*!
Sets the serial bus to which this drive should attach itself.
*/
void set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bus);
// to satisfy CPU6502::Processor
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);
// to satisfy MOS::MOS6522::Delegate
virtual void mos6522_did_change_interrupt_status(void *mos6522);
private:
uint8_t _ram[0x800];
uint8_t _rom[0x4000];
std::shared_ptr<SerialPortVIA> _serialPortVIA;
std::shared_ptr<SerialPort> _serialPort;
DriveVIA _driveVIA;
};
}
}
#endif /* Commodore1540_hpp */

View File

@ -0,0 +1,93 @@
//
// SerialPort.cpp
// Clock Signal
//
// Created by Thomas Harte on 05/07/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "SerialBus.hpp"
using namespace Commodore::Serial;
const char *::Commodore::Serial::StringForLine(Line line)
{
switch(line)
{
case ServiceRequest: return "Service request";
case Attention: return "Attention";
case Clock: return "Clock";
case Data: return "Data";
case Reset: return "Reset";
}
}
void ::Commodore::Serial::AttachPortAndBus(std::shared_ptr<Port> port, std::shared_ptr<Bus> bus)
{
port->set_serial_bus(bus);
bus->add_port(port);
}
void Bus::add_port(std::shared_ptr<Port> port)
{
_ports.push_back(port);
for(int line = (int)ServiceRequest; line <= (int)Reset; line++)
{
// the addition of a new device may change the line output...
set_line_output_did_change((Line)line);
// ... but the new device will need to be told the current state regardless
port->set_input((Line)line, _line_levels[line]);
}
}
void Bus::set_line_output_did_change(Line line)
{
// i.e. I believe these lines to be open collector
LineLevel new_line_level = High;
for(std::weak_ptr<Port> port : _ports)
{
std::shared_ptr<Port> locked_port = port.lock();
if(locked_port)
{
new_line_level = (LineLevel)((bool)new_line_level & (bool)locked_port->get_output(line));
}
}
// post an update only if one occurred
if(new_line_level != _line_levels[line])
{
_line_levels[line] = new_line_level;
for(std::weak_ptr<Port> port : _ports)
{
std::shared_ptr<Port> locked_port = port.lock();
if(locked_port)
{
locked_port->set_input(line, new_line_level);
}
}
}
}
#pragma mark - The debug port
void DebugPort::set_input(Line line, LineLevel value)
{
_input_levels[line] = value;
printf("[Bus] %s is %s\n", StringForLine(line), value ? "high" : "low");
if(!_incoming_count)
{
_incoming_count = (!_input_levels[Line::Clock] && !_input_levels[Line::Data]) ? 8 : 0;
}
else
{
if(line == Line::Clock && value)
{
_incoming_byte = (_incoming_byte >> 1) | (_input_levels[Line::Data] ? 0x80 : 0x00);
}
_incoming_count--;
if(_incoming_count == 0) printf("[Bus] Observed %02x\n", _incoming_byte);
}
}

View File

@ -0,0 +1,131 @@
//
// SerialPort.hpp
// Clock Signal
//
// Created by Thomas Harte on 05/07/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef SerialBus_hpp
#define SerialBus_hpp
#import <vector>
namespace Commodore {
namespace Serial {
enum Line {
ServiceRequest = 0,
Attention,
Clock,
Data,
Reset
};
enum LineLevel: bool {
High = true,
Low = false
};
class Port;
class Bus;
/*!
Returns a C string giving a human-readable name for the supplied line.
*/
const char *StringForLine(Line line);
/*!
Calls both of the necessary methods to (i) set @c bus as the target for @c port outputs; and
(ii) add @c port as one of the targets to which @c bus propagates line changes.
*/
void AttachPortAndBus(std::shared_ptr<Port> port, std::shared_ptr<Bus> bus);
/*!
A serial bus is responsible for retaining a weakly-held collection of attached ports and for deciding the
current bus levels based upon the net result of each port's output, and for communicating changes in bus
levels to every port.
*/
class Bus {
public:
Bus() : _line_levels{High, High, High, High, High} {}
/*!
Adds the supplied port to the bus.
*/
void add_port(std::shared_ptr<Port> port);
/*!
Communicates to the bus that one of its attached port has changed its output level for the given line.
The bus will therefore recalculate bus state and propagate as necessary.
*/
void set_line_output_did_change(Line line);
private:
LineLevel _line_levels[5];
std::vector<std::weak_ptr<Port>> _ports;
};
/*!
A serial port is an endpoint on a serial bus; this class provides a direct setter for current line outputs and
expects to be subclassed in order for specific port-housing devices to deal with input.
*/
class Port {
public:
Port() : _line_levels{High, High, High, High, High} {}
/*!
Sets the current level of an output line on this serial port.
*/
void set_output(Line line, LineLevel level) {
if(_line_levels[line] != level)
{
_line_levels[line] = level;
std::shared_ptr<Bus> bus = _serial_bus.lock();
if(bus) bus->set_line_output_did_change(line);
}
}
/*!
Gets the previously set level of an output line.
*/
LineLevel get_output(Line line) {
return _line_levels[line];
}
/*!
Called by the bus to signal a change in any input line level. Subclasses should implement this.
*/
virtual void set_input(Line line, LineLevel value) = 0;
/*!
Sets the supplied serial bus as that to which line levels will be communicated.
*/
inline void set_serial_bus(std::shared_ptr<Bus> serial_bus) {
_serial_bus = serial_bus;
}
private:
std::weak_ptr<Bus> _serial_bus;
LineLevel _line_levels[5];
};
/*!
A debugging port, which makes some attempt to log bus activity. Incomplete. TODO: complete.
*/
class DebugPort: public Port {
public:
void set_input(Line line, LineLevel value);
DebugPort() : _incoming_count(0) {}
private:
uint8_t _incoming_byte;
int _incoming_count;
LineLevel _input_levels[5];
};
}
}
#endif /* SerialPort_hpp */

View File

@ -10,23 +10,31 @@
#include <algorithm>
using namespace Vic20;
using namespace Commodore::Vic20;
Machine::Machine() :
_rom(nullptr)
{
// create 6522s, serial port and bus
_userPortVIA.reset(new UserPortVIA);
_keyboardVIA.reset(new KeyboardVIA);
_serialPort.reset(new SerialPort);
_serialBus.reset(new ::Commodore::Serial::Bus);
// wire up the serial bus and serial port
Commodore::Serial::AttachPortAndBus(_serialPort, _serialBus);
// wire up 6522s and serial port
_userPortVIA->set_serial_port(_serialPort);
_keyboardVIA->set_serial_port(_serialPort);
_serialPort->set_vias(_userPortVIA, _keyboardVIA);
_serialPort->set_user_port_via(_userPortVIA);
// wire up the 6522s, tape and machine
_userPortVIA->set_delegate(this);
_keyboardVIA->set_delegate(this);
_tape.set_delegate(this);
// establish the memory maps
memset(_videoMemoryMap, 0, sizeof(_videoMemoryMap));
memset(_processorReadMemoryMap, 0, sizeof(_processorReadMemoryMap));
memset(_processorWriteMemoryMap, 0, sizeof(_processorWriteMemoryMap));
@ -45,6 +53,13 @@ Machine::Machine() :
write_to_map(_processorWriteMemoryMap, _userBASICMemory, 0x0000, sizeof(_userBASICMemory));
write_to_map(_processorWriteMemoryMap, _screenMemory, 0x1000, sizeof(_screenMemory));
write_to_map(_processorWriteMemoryMap, _colorMemory, 0x9400, sizeof(_colorMemory));
// TEMPORARY: attach a [diskless] 1540
// set_disc();
// _debugPort.reset(new ::Commodore::Serial::DebugPort);
// _debugPort->set_serial_bus(_serialBus);
// _serialBus->add_port(_debugPort);
}
void Machine::write_to_map(uint8_t **map, uint8_t *area, uint16_t address, uint16_t length)
@ -59,7 +74,6 @@ void Machine::write_to_map(uint8_t **map, uint8_t *area, uint16_t address, uint1
}
}
Machine::~Machine()
{
delete[] _rom;
@ -67,6 +81,13 @@ Machine::~Machine()
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
{
// static int logCount = 0;
// if(operation == CPU6502::BusOperation::ReadOpcode && address == 0xee17) logCount = 500;
// if(operation == CPU6502::BusOperation::ReadOpcode && logCount) {
// logCount--;
// printf("%04x\n", address);
// }
// run the phase-1 part of this cycle, in which the VIC accesses memory
uint16_t video_address = _mos6560->get_address();
uint8_t video_value = _videoMemoryMap[video_address >> 10] ? _videoMemoryMap[video_address >> 10][video_address & 0x3ff] : 0xff; // TODO
@ -85,7 +106,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
*value = result;
// test for PC at F92F
if(_use_fast_tape_hack && address == 0xf92f && operation == CPU6502::BusOperation::ReadOpcode)
if(_use_fast_tape_hack && _tape.has_tape() && 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())
@ -112,6 +133,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
_keyboardVIA->run_for_half_cycles(2);
if(_typer) _typer->update(1);
_tape.run_for_cycles(1);
if(_c1540) _c1540->run_for_cycles(1);
return 1;
}
@ -141,9 +163,16 @@ void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data)
size_t max_length = 0x2000;
switch(slot)
{
case ROMSlotKernel: target = _kernelROM; break;
case ROMSlotCharacters: target = _characterROM; max_length = 0x1000; break;
case ROMSlotBASIC: target = _basicROM; break;
case Kernel: target = _kernelROM; break;
case Characters: target = _characterROM; max_length = 0x1000; break;
case BASIC: target = _basicROM; break;
case Drive:
if(_c1540)
{
_c1540->set_rom(data);
_c1540->run_for_cycles(2000000); // pretend it booted a couple of seconds ago
}
return;
}
if(target)
@ -315,34 +344,13 @@ void Tape::process_input_pulse(Storage::Tape::Pulse pulse)
}
}
#pragma mark - Serial Port
#pragma mark - Disc
void SerialPort::set_clock_output(bool value)
void Machine::set_disc()
{
printf("Serial port clock output %s\n", value ? "on" : "off");
}
// construct the 1540
_c1540.reset(new ::Commodore::C1540::Machine);
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");
// attach it to the serial bus
_c1540->set_serial_bus(_serialBus);
}

View File

@ -9,23 +9,26 @@
#ifndef Vic20_hpp
#define Vic20_hpp
#include "../../Processors/6502/CPU6502.hpp"
#include "../../Storage/Tape/Tape.hpp"
#include "../../Components/6560/6560.hpp"
#include "../../Components/6522/6522.hpp"
#include "../../../Processors/6502/CPU6502.hpp"
#include "../../../Storage/Tape/Tape.hpp"
#include "../../../Components/6560/6560.hpp"
#include "../../../Components/6522/6522.hpp"
#include "../1540/C1540.hpp"
#include "../SerialBus.hpp"
#include "../CRTMachine.hpp"
#include "../Typer.hpp"
#include "../../CRTMachine.hpp"
#include "../../Typer.hpp"
namespace Commodore {
namespace Vic20 {
enum ROMSlot {
ROMSlotKernel,
ROMSlotBASIC,
ROMSlotCharacters,
Kernel,
BASIC,
Characters,
Drive
};
#define key(line, mask) (((mask) << 3) | (line))
enum Key: uint16_t {
@ -57,29 +60,6 @@ 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:
uint8_t get_port_input(Port port) {
@ -95,6 +75,15 @@ class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDeleg
// }
}
void set_serial_line_state(::Commodore::Serial::Line line, bool value) {
// printf("VIC Serial port line %d: %s\n", line, value ? "on" : "off");
switch(line) {
default: break;
case ::Commodore::Serial::Line::Data: _portA = (_portA & ~0x02) | (value ? 0x02 : 0x00); break;
case ::Commodore::Serial::Line::Clock: _portA = (_portA & ~0x01) | (value ? 0x01 : 0x00); break;
}
}
void set_joystick_state(JoystickInput input, bool value) {
if(input != JoystickInput::Right)
{
@ -103,9 +92,11 @@ class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDeleg
}
void set_port_output(Port port, uint8_t value, uint8_t mask) {
// Line 7 of port A is inverted and output as serial ATN
if(!port) {
std::shared_ptr<SerialPort> serialPort = _serialPort.lock();
if(serialPort) serialPort->set_attention_output(!(value&0x80));
std::shared_ptr<::Commodore::Serial::Port> serialPort = _serialPort.lock();
if(serialPort)
serialPort->set_output(::Commodore::Serial::Line::Attention, (::Commodore::Serial::LineLevel)!(value&0x80));
}
}
@ -113,13 +104,13 @@ class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDeleg
UserPortVIA() : _portA(0xbf) {}
void set_serial_port(std::shared_ptr<SerialPort> serialPort) {
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort) {
_serialPort = serialPort;
}
private:
uint8_t _portA;
std::weak_ptr<SerialPort> _serialPort;
std::weak_ptr<::Commodore::Serial::Port> _serialPort;
};
class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDelegate {
@ -161,12 +152,13 @@ class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDeleg
void set_control_line_output(Port port, Line line, bool value) {
if(line == Line::Two) {
std::shared_ptr<SerialPort> serialPort = _serialPort.lock();
std::shared_ptr<::Commodore::Serial::Port> serialPort = _serialPort.lock();
if(serialPort) {
// CB2 is inverted to become serial data; CA2 is inverted to become serial clock
if(port == Port::A) {
serialPort->set_clock_output(value);
serialPort->set_output(::Commodore::Serial::Line::Clock, (::Commodore::Serial::LineLevel)!value);
} else {
serialPort->set_data_output(value);
serialPort->set_output(::Commodore::Serial::Line::Data, (::Commodore::Serial::LineLevel)!value);
}
}
}
@ -181,7 +173,7 @@ class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDeleg
using MOS6522IRQDelegate::set_interrupt_status;
void set_serial_port(std::shared_ptr<SerialPort> serialPort) {
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort) {
_serialPort = serialPort;
}
@ -189,7 +181,22 @@ class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDeleg
uint8_t _portB;
uint8_t _columns[8];
uint8_t _activation_mask;
std::weak_ptr<SerialPort> _serialPort;
std::weak_ptr<::Commodore::Serial::Port> _serialPort;
};
class SerialPort : public ::Commodore::Serial::Port {
public:
void set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level) {
std::shared_ptr<UserPortVIA> userPortVIA = _userPortVIA.lock();
if(userPortVIA) userPortVIA->set_serial_line_state(line, (bool)level);
}
void set_user_port_via(std::shared_ptr<UserPortVIA> userPortVIA) {
_userPortVIA = userPortVIA;
}
private:
std::weak_ptr<UserPortVIA> _userPortVIA;
};
class Tape: public Storage::TapePlayer {
@ -230,6 +237,7 @@ class Machine:
void set_rom(ROMSlot slot, size_t length, const uint8_t *data);
void add_prg(size_t length, const uint8_t *data);
void set_tape(std::shared_ptr<Storage::Tape> tape);
void set_disc();
void set_key_state(Key key, bool isPressed) { _keyboardVIA->set_key_state(key, isPressed); }
void clear_all_keys() { _keyboardVIA->clear_all_keys(); }
@ -286,12 +294,18 @@ class Machine:
std::shared_ptr<UserPortVIA> _userPortVIA;
std::shared_ptr<KeyboardVIA> _keyboardVIA;
std::shared_ptr<SerialPort> _serialPort;
std::shared_ptr<::Commodore::Serial::Bus> _serialBus;
// std::shared_ptr<::Commodore::Serial::DebugPort> _debugPort;
// Tape
Tape _tape;
bool _use_fast_tape_hack;
// Disc
std::shared_ptr<::Commodore::C1540::Machine> _c1540;
};
}
}
#endif /* Vic20_hpp */

View File

@ -24,7 +24,7 @@ void Typer::update(int duration)
}
_counter += duration;
while(_counter > _frequency)
while(_string && _counter > _frequency)
{
_counter -= _frequency;
type_next_character();

View File

@ -14,7 +14,6 @@
4B1414601B58885000E04248 /* WolfgangLorenzTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */; };
4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414611B58888700E04248 /* KlausDormannTests.swift */; };
4B1E85751D170228001EF87D /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E85731D170228001EF87D /* Typer.cpp */; };
4B1E857F1D17644D001EF87D /* MOS6532Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E857E1D17644D001EF87D /* MOS6532Bridge.mm */; };
4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1E85801D176468001EF87D /* 6532Tests.swift */; };
4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2409531C45AB05004DA684 /* Speaker.cpp */; };
4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53911D117D36003C6002 /* CSAudioQueue.m */; };
@ -25,6 +24,14 @@
4B2E2D951C399D1200138695 /* ElectronDocument.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B2E2D931C399D1200138695 /* ElectronDocument.xib */; };
4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D971C3A06EC00138695 /* Atari2600.cpp */; };
4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D9B1C3A070400138695 /* Electron.cpp */; };
4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */; };
4B3BA0CE1D318B44005DD7A7 /* C1540Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C61D318B44005DD7A7 /* C1540Bridge.mm */; };
4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C91D318B44005DD7A7 /* MOS6522Bridge.mm */; };
4B3BA0D01D318B44005DD7A7 /* MOS6532Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0CB1D318B44005DD7A7 /* MOS6532Bridge.mm */; };
4B3BA0D11D318B44005DD7A7 /* TestMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0CD1D318B44005DD7A7 /* TestMachine.mm */; };
4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; };
4B4DC8281D2C2470003C5BF8 /* C1540.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8261D2C2470003C5BF8 /* C1540.cpp */; };
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; };
4B55CE581C3B7D360093A61B /* Atari2600Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE561C3B7D360093A61B /* Atari2600Document.swift */; };
4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE571C3B7D360093A61B /* ElectronDocument.swift */; };
4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5C1C3B7D6F0093A61B /* CSOpenGLView.m */; };
@ -34,11 +41,9 @@
4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; };
4B73C71A1D036BD90074D992 /* Vic20Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B73C7191D036BD90074D992 /* Vic20Document.swift */; };
4B73C71D1D036C030074D992 /* Vic20Document.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B73C71B1D036C030074D992 /* Vic20Document.xib */; };
4B886FF21D03B517004291C3 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B886FF01D03B517004291C3 /* Vic20.cpp */; };
4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */; };
4BB298EE1B587D8400A49093 /* 6502_functional_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E01B587D8300A49093 /* 6502_functional_test.bin */; };
4BB298EF1B587D8400A49093 /* AllSuiteA.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E11B587D8300A49093 /* AllSuiteA.bin */; };
4BB298F01B587D8400A49093 /* TestMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BB297E31B587D8300A49093 /* TestMachine.mm */; };
4BB298F11B587D8400A49093 /* start in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E51B587D8300A49093 /* start */; };
4BB298F21B587D8400A49093 /* adca in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E61B587D8300A49093 /* adca */; };
4BB298F31B587D8400A49093 /* adcax in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E71B587D8300A49093 /* adcax */; };
@ -317,7 +322,6 @@
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 */; };
4BC91B831D1F160E00884B76 /* CommodoreTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC91B811D1F160E00884B76 /* CommodoreTAP.cpp */; };
@ -359,8 +363,6 @@
4B1E85731D170228001EF87D /* Typer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Typer.cpp; sourceTree = "<group>"; };
4B1E85741D170228001EF87D /* Typer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Typer.hpp; sourceTree = "<group>"; };
4B1E857B1D174DEC001EF87D /* 6532.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6532.hpp; sourceTree = "<group>"; };
4B1E857D1D17644D001EF87D /* MOS6532Bridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MOS6532Bridge.h; sourceTree = "<group>"; };
4B1E857E1D17644D001EF87D /* MOS6532Bridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MOS6532Bridge.mm; sourceTree = "<group>"; };
4B1E85801D176468001EF87D /* 6532Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6532Tests.swift; sourceTree = "<group>"; };
4B2409531C45AB05004DA684 /* Speaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Speaker.cpp; path = ../../Outputs/Speaker.cpp; sourceTree = "<group>"; };
4B2409541C45AB05004DA684 /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Speaker.hpp; path = ../../Outputs/Speaker.hpp; sourceTree = "<group>"; };
@ -384,6 +386,22 @@
4B2E2D991C3A06EC00138695 /* Atari2600Inputs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Atari2600Inputs.h; sourceTree = "<group>"; };
4B2E2D9B1C3A070400138695 /* Electron.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Electron.cpp; path = Electron/Electron.cpp; sourceTree = "<group>"; };
4B2E2D9C1C3A070400138695 /* Electron.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Electron.hpp; path = Electron/Electron.hpp; sourceTree = "<group>"; };
4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = C1540Tests.swift; sourceTree = "<group>"; };
4B3BA0C51D318B44005DD7A7 /* C1540Bridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = C1540Bridge.h; sourceTree = "<group>"; };
4B3BA0C61D318B44005DD7A7 /* C1540Bridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = C1540Bridge.mm; sourceTree = "<group>"; };
4B3BA0C71D318B44005DD7A7 /* Clock SignalTests-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Clock SignalTests-Bridging-Header.h"; sourceTree = "<group>"; };
4B3BA0C81D318B44005DD7A7 /* MOS6522Bridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MOS6522Bridge.h; sourceTree = "<group>"; };
4B3BA0C91D318B44005DD7A7 /* MOS6522Bridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MOS6522Bridge.mm; sourceTree = "<group>"; };
4B3BA0CA1D318B44005DD7A7 /* MOS6532Bridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MOS6532Bridge.h; sourceTree = "<group>"; };
4B3BA0CB1D318B44005DD7A7 /* MOS6532Bridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MOS6532Bridge.mm; sourceTree = "<group>"; };
4B3BA0CC1D318B44005DD7A7 /* TestMachine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestMachine.h; sourceTree = "<group>"; };
4B3BA0CD1D318B44005DD7A7 /* TestMachine.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TestMachine.mm; sourceTree = "<group>"; };
4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Vic20.cpp; sourceTree = "<group>"; };
4B4DC8201D2C2425003C5BF8 /* Vic20.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Vic20.hpp; sourceTree = "<group>"; };
4B4DC8261D2C2470003C5BF8 /* C1540.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = C1540.cpp; sourceTree = "<group>"; };
4B4DC8271D2C2470003C5BF8 /* C1540.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = C1540.hpp; sourceTree = "<group>"; };
4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SerialBus.cpp; sourceTree = "<group>"; };
4B4DC82A1D2C27A4003C5BF8 /* SerialBus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SerialBus.hpp; sourceTree = "<group>"; };
4B55CE561C3B7D360093A61B /* Atari2600Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atari2600Document.swift; sourceTree = "<group>"; };
4B55CE571C3B7D360093A61B /* ElectronDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElectronDocument.swift; sourceTree = "<group>"; };
4B55CE5B1C3B7D6F0093A61B /* CSOpenGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSOpenGLView.h; sourceTree = "<group>"; };
@ -396,14 +414,9 @@
4B69FB451C4D950F00B5F0AA /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
4B73C7191D036BD90074D992 /* Vic20Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vic20Document.swift; sourceTree = "<group>"; };
4B73C71C1D036C030074D992 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/Vic20Document.xib"; sourceTree = SOURCE_ROOT; };
4B886FF01D03B517004291C3 /* Vic20.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Vic20.cpp; path = "Vic-20/Vic20.cpp"; sourceTree = "<group>"; };
4B886FF11D03B517004291C3 /* Vic20.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Vic20.hpp; path = "Vic-20/Vic20.hpp"; sourceTree = "<group>"; };
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6502TimingTests.swift; sourceTree = "<group>"; };
4BB297DF1B587D8200A49093 /* Clock SignalTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Clock SignalTests-Bridging-Header.h"; sourceTree = "<group>"; };
4BB297E01B587D8300A49093 /* 6502_functional_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = 6502_functional_test.bin; sourceTree = "<group>"; };
4BB297E11B587D8300A49093 /* AllSuiteA.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = AllSuiteA.bin; sourceTree = "<group>"; };
4BB297E21B587D8300A49093 /* TestMachine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestMachine.h; sourceTree = "<group>"; };
4BB297E31B587D8300A49093 /* TestMachine.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TestMachine.mm; sourceTree = "<group>"; };
4BB297E51B587D8300A49093 /* start */ = {isa = PBXFileReference; lastKnownFileType = file; path = " start"; sourceTree = "<group>"; };
4BB297E61B587D8300A49093 /* adca */ = {isa = PBXFileReference; lastKnownFileType = file; path = adca; sourceTree = "<group>"; };
4BB297E71B587D8300A49093 /* adcax */ = {isa = PBXFileReference; lastKnownFileType = file; path = adcax; sourceTree = "<group>"; };
@ -699,8 +712,6 @@
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; };
@ -859,6 +870,51 @@
name = Outputs;
sourceTree = "<group>";
};
4B3BA0C41D318B44005DD7A7 /* Bridges */ = {
isa = PBXGroup;
children = (
4B3BA0C51D318B44005DD7A7 /* C1540Bridge.h */,
4B3BA0C61D318B44005DD7A7 /* C1540Bridge.mm */,
4B3BA0C71D318B44005DD7A7 /* Clock SignalTests-Bridging-Header.h */,
4B3BA0C81D318B44005DD7A7 /* MOS6522Bridge.h */,
4B3BA0C91D318B44005DD7A7 /* MOS6522Bridge.mm */,
4B3BA0CA1D318B44005DD7A7 /* MOS6532Bridge.h */,
4B3BA0CB1D318B44005DD7A7 /* MOS6532Bridge.mm */,
4B3BA0CC1D318B44005DD7A7 /* TestMachine.h */,
4B3BA0CD1D318B44005DD7A7 /* TestMachine.mm */,
);
path = Bridges;
sourceTree = "<group>";
};
4B4DC81D1D2C2425003C5BF8 /* Commodore */ = {
isa = PBXGroup;
children = (
4B4DC8251D2C2470003C5BF8 /* 1540 */,
4B4DC81E1D2C2425003C5BF8 /* Vic-20 */,
4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */,
4B4DC82A1D2C27A4003C5BF8 /* SerialBus.hpp */,
);
path = Commodore;
sourceTree = "<group>";
};
4B4DC81E1D2C2425003C5BF8 /* Vic-20 */ = {
isa = PBXGroup;
children = (
4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */,
4B4DC8201D2C2425003C5BF8 /* Vic20.hpp */,
);
path = "Vic-20";
sourceTree = "<group>";
};
4B4DC8251D2C2470003C5BF8 /* 1540 */ = {
isa = PBXGroup;
children = (
4B4DC8261D2C2470003C5BF8 /* C1540.cpp */,
4B4DC8271D2C2470003C5BF8 /* C1540.hpp */,
);
path = 1540;
sourceTree = "<group>";
};
4B55CE551C3B7D360093A61B /* Documents */ = {
isa = PBXGroup;
children = (
@ -913,15 +969,6 @@
path = Formats;
sourceTree = "<group>";
};
4B886FF61D03B632004291C3 /* Vic-20 */ = {
isa = PBXGroup;
children = (
4B886FF01D03B517004291C3 /* Vic20.cpp */,
4B886FF11D03B517004291C3 /* Vic20.hpp */,
);
name = "Vic-20";
sourceTree = "<group>";
};
4BB297E41B587D8300A49093 /* Wolfgang Lorenz 6502 test suite */ = {
isa = PBXGroup;
children = (
@ -1243,22 +1290,17 @@
4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = {
isa = PBXGroup;
children = (
4BB297DF1B587D8200A49093 /* Clock SignalTests-Bridging-Header.h */,
4BC751B41D157EB3006C31D9 /* MOS6522Bridge.h */,
4B1E857D1D17644D001EF87D /* MOS6532Bridge.h */,
4BB297E21B587D8300A49093 /* TestMachine.h */,
4B1E857E1D17644D001EF87D /* MOS6532Bridge.mm */,
4BC751B51D157EB3006C31D9 /* MOS6522Bridge.mm */,
4BB297E31B587D8300A49093 /* TestMachine.mm */,
4BB73EB81B587A5100552FC2 /* Info.plist */,
4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */,
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
4BC751B11D157E61006C31D9 /* 6522Tests.swift */,
4B1E85801D176468001EF87D /* 6532Tests.swift */,
4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */,
4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */,
4B1414611B58888700E04248 /* KlausDormannTests.swift */,
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */,
4B3BA0C41D318B44005DD7A7 /* Bridges */,
4B1414631B588A1100E04248 /* Test Binaries */,
4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */,
);
path = "Clock SignalTests";
sourceTree = "<group>";
@ -1275,10 +1317,10 @@
4BB73EDC1B587CA500552FC2 /* Machines */ = {
isa = PBXGroup;
children = (
4B4DC81D1D2C2425003C5BF8 /* Commodore */,
4B046DC31CFE651500E9E45E /* CRTMachine.hpp */,
4B2E2D961C3A06EC00138695 /* Atari2600 */,
4B2E2D9E1C3A070900138695 /* Electron */,
4B886FF61D03B632004291C3 /* Vic-20 */,
4B1E85731D170228001EF87D /* Typer.cpp */,
4B1E85741D170228001EF87D /* Typer.hpp */,
);
@ -1780,7 +1822,7 @@
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */,
4B886FF21D03B517004291C3 /* Vic20.cpp in Sources */,
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */,
4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */,
4B55CE581C3B7D360093A61B /* Atari2600Document.swift in Sources */,
4BBB14311CD2CECE00BDB55C /* IntermediateShader.cpp in Sources */,
@ -1791,8 +1833,10 @@
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */,
4B2A53A11D117D36003C6002 /* CSAtari2600.mm in Sources */,
4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */,
4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */,
4BBF99141C8FBA6F0075DAFB /* CRTInputBufferBuilder.cpp in Sources */,
4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */,
4B4DC8281D2C2470003C5BF8 /* C1540.cpp in Sources */,
4B1E85751D170228001EF87D /* Typer.cpp in Sources */,
4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */,
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */,
@ -1813,17 +1857,19 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4BC751B61D157EB3006C31D9 /* MOS6522Bridge.mm in Sources */,
4B14145E1B5887AA00E04248 /* CPU6502AllRAM.cpp in Sources */,
4B14145D1B5887A600E04248 /* CPU6502.cpp in Sources */,
4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */,
4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */,
4B3BA0CE1D318B44005DD7A7 /* C1540Bridge.mm in Sources */,
4B3BA0D11D318B44005DD7A7 /* TestMachine.mm in Sources */,
4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */,
4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */,
4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */,
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */,
4B3BA0D01D318B44005DD7A7 /* MOS6532Bridge.mm in Sources */,
4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */,
4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */,
4BB298F01B587D8400A49093 /* TestMachine.mm in Sources */,
4B1E857F1D17644D001EF87D /* MOS6532Bridge.mm in Sources */,
4B1414601B58885000E04248 /* WolfgangLorenzTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -2014,7 +2060,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-SignalTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Clock SignalTests/Clock SignalTests-Bridging-Header.h";
SWIFT_OBJC_BRIDGING_HEADER = "Clock SignalTests/Bridges/Clock SignalTests-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Clock Signal.app/Contents/MacOS/Clock Signal";
};
@ -2030,7 +2076,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-SignalTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Clock SignalTests/Clock SignalTests-Bridging-Header.h";
SWIFT_OBJC_BRIDGING_HEADER = "Clock SignalTests/Bridges/Clock SignalTests-Bridging-Header.h";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Clock Signal.app/Contents/MacOS/Clock Signal";
};
name = Release;

View File

@ -31,6 +31,10 @@ class Vic20Document: MachineDocument {
vic20.setBASICROM(basic)
vic20.setCharactersROM(characters)
}
if let drive = dataForResource("1541", ofType: "bin", inDirectory: "ROMImages/Commodore1540") {
vic20.setDriveROM(drive)
}
}
override class func autosavesInPlace() -> Bool {

View File

@ -8,7 +8,7 @@
#import "CSAtari2600.h"
#import "Atari2600.hpp"
#include "Atari2600.hpp"
#import "CSMachine+Subclassing.h"
@interface CSAtari2600 ()

View File

@ -15,6 +15,7 @@
- (void)setKernelROM:(nonnull NSData *)rom;
- (void)setBASICROM:(nonnull NSData *)rom;
- (void)setCharactersROM:(nonnull NSData *)rom;
- (void)setDriveROM:(nonnull NSData *)rom;
- (void)setPRG:(nonnull NSData *)prg;
- (BOOL)openTAPAtURL:(nonnull NSURL *)URL;

View File

@ -11,8 +11,10 @@
#include "Vic20.hpp"
#include "CommodoreTAP.hpp"
using namespace Commodore::Vic20;
@implementation CSVic20 {
Vic20::Machine _vic20;
Machine _vic20;
BOOL _joystickMode;
}
@ -20,22 +22,26 @@
return &_vic20;
}
- (void)setROM:(nonnull NSData *)rom slot:(Vic20::ROMSlot)slot {
- (void)setROM:(nonnull NSData *)rom slot:(ROMSlot)slot {
@synchronized(self) {
_vic20.set_rom(slot, rom.length, (const uint8_t *)rom.bytes);
}
}
- (void)setKernelROM:(nonnull NSData *)rom {
[self setROM:rom slot:Vic20::ROMSlotKernel];
[self setROM:rom slot:Kernel];
}
- (void)setBASICROM:(nonnull NSData *)rom {
[self setROM:rom slot:Vic20::ROMSlotBASIC];
[self setROM:rom slot:BASIC];
}
- (void)setCharactersROM:(nonnull NSData *)rom {
[self setROM:rom slot:Vic20::ROMSlotCharacters];
[self setROM:rom slot:Characters];
}
- (void)setDriveROM:(nonnull NSData *)rom {
[self setROM:rom slot:Drive];
}
- (BOOL)openTAPAtURL:(NSURL *)URL {
@ -59,50 +65,50 @@
- (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed {
static NSDictionary<NSNumber *, NSNumber *> *vicKeysByKeys = @{
@(VK_ANSI_1): @(Vic20::Key::Key1), @(VK_ANSI_2): @(Vic20::Key::Key2),
@(VK_ANSI_3): @(Vic20::Key::Key3), @(VK_ANSI_4): @(Vic20::Key::Key4),
@(VK_ANSI_5): @(Vic20::Key::Key5), @(VK_ANSI_6): @(Vic20::Key::Key6),
@(VK_ANSI_7): @(Vic20::Key::Key7), @(VK_ANSI_8): @(Vic20::Key::Key8),
@(VK_ANSI_9): @(Vic20::Key::Key9), @(VK_ANSI_0): @(Vic20::Key::Key0),
@(VK_ANSI_1): @(Key::Key1), @(VK_ANSI_2): @(Key::Key2),
@(VK_ANSI_3): @(Key::Key3), @(VK_ANSI_4): @(Key::Key4),
@(VK_ANSI_5): @(Key::Key5), @(VK_ANSI_6): @(Key::Key6),
@(VK_ANSI_7): @(Key::Key7), @(VK_ANSI_8): @(Key::Key8),
@(VK_ANSI_9): @(Key::Key9), @(VK_ANSI_0): @(Key::Key0),
@(VK_ANSI_Q): @(Vic20::Key::KeyQ), @(VK_ANSI_W): @(Vic20::Key::KeyW),
@(VK_ANSI_E): @(Vic20::Key::KeyE), @(VK_ANSI_R): @(Vic20::Key::KeyR),
@(VK_ANSI_T): @(Vic20::Key::KeyT), @(VK_ANSI_Y): @(Vic20::Key::KeyY),
@(VK_ANSI_U): @(Vic20::Key::KeyU), @(VK_ANSI_I): @(Vic20::Key::KeyI),
@(VK_ANSI_O): @(Vic20::Key::KeyO), @(VK_ANSI_P): @(Vic20::Key::KeyP),
@(VK_ANSI_A): @(Vic20::Key::KeyA), @(VK_ANSI_S): @(Vic20::Key::KeyS),
@(VK_ANSI_D): @(Vic20::Key::KeyD), @(VK_ANSI_F): @(Vic20::Key::KeyF),
@(VK_ANSI_G): @(Vic20::Key::KeyG), @(VK_ANSI_H): @(Vic20::Key::KeyH),
@(VK_ANSI_J): @(Vic20::Key::KeyJ), @(VK_ANSI_K): @(Vic20::Key::KeyK),
@(VK_ANSI_L): @(Vic20::Key::KeyL), @(VK_ANSI_Z): @(Vic20::Key::KeyZ),
@(VK_ANSI_X): @(Vic20::Key::KeyX), @(VK_ANSI_C): @(Vic20::Key::KeyC),
@(VK_ANSI_V): @(Vic20::Key::KeyV), @(VK_ANSI_B): @(Vic20::Key::KeyB),
@(VK_ANSI_N): @(Vic20::Key::KeyN), @(VK_ANSI_M): @(Vic20::Key::KeyM),
@(VK_ANSI_Q): @(Key::KeyQ), @(VK_ANSI_W): @(Key::KeyW),
@(VK_ANSI_E): @(Key::KeyE), @(VK_ANSI_R): @(Key::KeyR),
@(VK_ANSI_T): @(Key::KeyT), @(VK_ANSI_Y): @(Key::KeyY),
@(VK_ANSI_U): @(Key::KeyU), @(VK_ANSI_I): @(Key::KeyI),
@(VK_ANSI_O): @(Key::KeyO), @(VK_ANSI_P): @(Key::KeyP),
@(VK_ANSI_A): @(Key::KeyA), @(VK_ANSI_S): @(Key::KeyS),
@(VK_ANSI_D): @(Key::KeyD), @(VK_ANSI_F): @(Key::KeyF),
@(VK_ANSI_G): @(Key::KeyG), @(VK_ANSI_H): @(Key::KeyH),
@(VK_ANSI_J): @(Key::KeyJ), @(VK_ANSI_K): @(Key::KeyK),
@(VK_ANSI_L): @(Key::KeyL), @(VK_ANSI_Z): @(Key::KeyZ),
@(VK_ANSI_X): @(Key::KeyX), @(VK_ANSI_C): @(Key::KeyC),
@(VK_ANSI_V): @(Key::KeyV), @(VK_ANSI_B): @(Key::KeyB),
@(VK_ANSI_N): @(Key::KeyN), @(VK_ANSI_M): @(Key::KeyM),
@(VK_Space): @(Vic20::Key::KeySpace),
@(VK_Return): @(Vic20::Key::KeyReturn),
@(VK_Delete): @(Vic20::Key::KeyDelete),
@(VK_ANSI_Comma): @(Vic20::Key::KeyComma),
@(VK_ANSI_Period): @(Vic20::Key::KeyFullStop),
@(VK_ANSI_Minus): @(Vic20::Key::KeyDash),
@(VK_ANSI_Equal): @(Vic20::Key::KeyEquals),
@(VK_ANSI_Semicolon): @(Vic20::Key::KeyColon),
@(VK_ANSI_Quote): @(Vic20::Key::KeySemicolon),
@(VK_ANSI_Slash): @(Vic20::Key::KeySlash),
@(VK_Option): @(Vic20::Key::KeyCBM),
@(VK_Control): @(Vic20::Key::KeyControl),
@(VK_Space): @(Key::KeySpace),
@(VK_Return): @(Key::KeyReturn),
@(VK_Delete): @(Key::KeyDelete),
@(VK_ANSI_Comma): @(Key::KeyComma),
@(VK_ANSI_Period): @(Key::KeyFullStop),
@(VK_ANSI_Minus): @(Key::KeyDash),
@(VK_ANSI_Equal): @(Key::KeyEquals),
@(VK_ANSI_Semicolon): @(Key::KeyColon),
@(VK_ANSI_Quote): @(Key::KeySemicolon),
@(VK_ANSI_Slash): @(Key::KeySlash),
@(VK_Option): @(Key::KeyCBM),
@(VK_Control): @(Key::KeyControl),
@(VK_F1): @(Vic20::Key::KeyF1), @(VK_F3): @(Vic20::Key::KeyF3),
@(VK_F5): @(Vic20::Key::KeyF5), @(VK_F7): @(Vic20::Key::KeyF7),
@(VK_F1): @(Key::KeyF1), @(VK_F3): @(Key::KeyF3),
@(VK_F5): @(Key::KeyF5), @(VK_F7): @(Key::KeyF7),
@(VK_ANSI_Grave): @(Vic20::Key::KeyLeft),
@(VK_Tab): @(Vic20::Key::KeyRunStop),
@(VK_ANSI_LeftBracket): @(Vic20::Key::KeyAt),
@(VK_ANSI_RightBracket): @(Vic20::Key::KeyAsterisk),
@(VK_ANSI_Backslash): @(Vic20::Key::KeyUp),
@(VK_ANSI_Grave): @(Key::KeyLeft),
@(VK_Tab): @(Key::KeyRunStop),
@(VK_ANSI_LeftBracket): @(Key::KeyAt),
@(VK_ANSI_RightBracket): @(Key::KeyAsterisk),
@(VK_ANSI_Backslash): @(Key::KeyUp),
@(VK_RightArrow): @(Vic20::Key::KeyRight),
@(VK_DownArrow): @(Vic20::Key::KeyDown),
@(VK_RightArrow): @(Key::KeyRight),
@(VK_DownArrow): @(Key::KeyDown),
};
// Not yet mapped:
@ -120,11 +126,11 @@
{
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;
case VK_UpArrow: _vic20.set_joystick_state(JoystickInput::Up, isPressed); break;
case VK_DownArrow: _vic20.set_joystick_state(JoystickInput::Down, isPressed); break;
case VK_LeftArrow: _vic20.set_joystick_state(JoystickInput::Left, isPressed); break;
case VK_RightArrow: _vic20.set_joystick_state(JoystickInput::Right, isPressed); break;
case VK_ANSI_A: _vic20.set_joystick_state(JoystickInput::Fire, isPressed); break;
}
}
else
@ -135,16 +141,18 @@
NSNumber *targetKey = vicKeysByKeys[@(key)];
if(targetKey)
{
_vic20.set_key_state((Vic20::Key)targetKey.integerValue, isPressed);
_vic20.set_key_state((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);
_vic20.set_key_state(Key::KeyLShift, isPressed);
_vic20.set_key_state(Key::KeyRShift, isPressed);
break;
}
}

View File

@ -0,0 +1,20 @@
//
// C1540Bridge.h
// Clock Signal
//
// Created by Thomas Harte on 09/07/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface C1540Bridge : NSObject
@property (nonatomic) BOOL attentionLine;
@property (nonatomic) BOOL dataLine;
@property (nonatomic) BOOL clockLine;
- (void)runForCycles:(NSUInteger)numberOfCycles;
- (void)setROM:(NSData *)ROM;
@end

View File

@ -0,0 +1,83 @@
//
// C1540Bridge.m
// Clock Signal
//
// Created by Thomas Harte on 09/07/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#import "C1540Bridge.h"
#include "C1540.hpp"
class VanillaSerialPort: public Commodore::Serial::Port {
public:
void set_input(Commodore::Serial::Line line, Commodore::Serial::LineLevel value)
{
_input_line_levels[(int)line] = value;
}
Commodore::Serial::LineLevel _input_line_levels[5];
};
@implementation C1540Bridge
{
Commodore::C1540::Machine _c1540;
std::shared_ptr<Commodore::Serial::Bus> _serialBus;
std::shared_ptr<VanillaSerialPort> _serialPort;
}
- (instancetype)init
{
self = [super init];
if(self)
{
_serialBus.reset(new ::Commodore::Serial::Bus);
_serialPort.reset(new VanillaSerialPort);
_c1540.set_serial_bus(_serialBus);
Commodore::Serial::AttachPortAndBus(_serialPort, _serialBus);
}
return self;
}
- (void)setROM:(NSData *)ROM
{
_c1540.set_rom((uint8_t *)ROM.bytes);
}
- (void)runForCycles:(NSUInteger)numberOfCycles
{
_c1540.run_for_cycles((int)numberOfCycles);
}
- (void)setAttentionLine:(BOOL)attentionLine
{
_serialPort->set_output(Commodore::Serial::Line::Attention, attentionLine ? Commodore::Serial::LineLevel::High : Commodore::Serial::LineLevel::Low);
}
- (BOOL)attentionLine
{
return _serialPort->_input_line_levels[Commodore::Serial::Line::Attention];
}
- (void)setDataLine:(BOOL)dataLine
{
_serialPort->set_output(Commodore::Serial::Line::Data, dataLine ? Commodore::Serial::LineLevel::High : Commodore::Serial::LineLevel::Low);
}
- (BOOL)dataLine
{
return _serialPort->_input_line_levels[Commodore::Serial::Line::Data];
}
- (void)setClockLine:(BOOL)clockLine
{
_serialPort->set_output(Commodore::Serial::Line::Clock, clockLine ? Commodore::Serial::LineLevel::High : Commodore::Serial::LineLevel::Low);
}
- (BOOL)clockLine
{
return _serialPort->_input_line_levels[Commodore::Serial::Line::Clock];
}
@end

View File

@ -5,3 +5,4 @@
#import "TestMachine.h"
#import "MOS6522Bridge.h"
#import "MOS6532Bridge.h"
#import "C1540Bridge.h"

View File

@ -0,0 +1,79 @@
//
// C1540Tests.swift
// Clock Signal
//
// Created by Thomas Harte on 09/07/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
import XCTest
class C1540Tests: XCTestCase {
private func with1540(action: (C1540Bridge) -> ()) {
let bridge = C1540Bridge()
if let path = NSBundle.mainBundle().pathForResource("1541", ofType: "bin", inDirectory: "ROMImages/Commodore1540") {
let data = NSData(contentsOfFile: path)
bridge.setROM(data)
}
action(bridge)
}
private func transmit(c1540: C1540Bridge, value: Int) {
var shiftedValue = value
c1540.dataLine = true
c1540.runForCycles(256)
XCTAssert(c1540.dataLine == false, "Listener should have taken data line low for start of transmission")
c1540.clockLine = true
c1540.runForCycles(256) // this isn't time limited on real hardware
XCTAssert(c1540.dataLine == true, "Listener should have let data line go high again")
// set up for byte transfer
c1540.clockLine = false
c1540.dataLine = true
c1540.runForCycles(40)
// transmit bits
for _ in 0..<8 {
// load data line
c1540.dataLine = (shiftedValue & 1) == 1
shiftedValue >>= 1
// toggle clock
c1540.clockLine = true
c1540.runForCycles(40)
c1540.clockLine = false
c1540.runForCycles(40)
}
// check for acknowledgment
c1540.dataLine = true
c1540.runForCycles(1000)
XCTAssert(c1540.dataLine == false, "Listener should have acknowledged byte")
}
// MARK: EOI
func testTransmission() {
with1540 {
// allow some booting time
$0.runForCycles(2000000)
// I want to be talker, so hold attention and clock low with data high
$0.clockLine = false
$0.attentionLine = false
$0.dataLine = true
// proceed 1 ms and check that the 1540 pulled the data line low
$0.runForCycles(1000)
XCTAssert($0.dataLine == false, "Listener should have taken data line low")
// transmit LISTEN #8
self.transmit($0, value: 0x28)
}
}
}

View File

@ -666,6 +666,7 @@ template <class T> class Processor {
break;
case OperationDecodeOperation:
// printf("d %02x\n", _operation);
decode_operation(_operation);
break;

View File

@ -0,0 +1,5 @@
ROM files would ordinarily go here; the copyright status of these is uncertain so they have not been included in this repository.
Expected files:
1540.rom; a 16kb image of the 1540's ROM area.