mirror of
https://github.com/TomHarte/CLK.git
synced 2024-08-17 07:29:04 +00:00
commit
460f518451
@ -167,3 +167,137 @@ void Machine::drive_via_did_set_data_density(void *driveVIA, int density)
|
|||||||
{
|
{
|
||||||
set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone((unsigned int)density));
|
set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone((unsigned int)density));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - SerialPortVIA
|
||||||
|
|
||||||
|
SerialPortVIA::SerialPortVIA() :
|
||||||
|
_portB(0x00), _attention_acknowledge_level(false), _attention_level_input(true), _data_level_output(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
uint8_t SerialPortVIA::get_port_input(Port port)
|
||||||
|
{
|
||||||
|
if(port) return _portB;
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerialPortVIA::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 SerialPortVIA::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 SerialPortVIA::set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort)
|
||||||
|
{
|
||||||
|
_serialPort = serialPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerialPortVIA::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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - DriveVIA
|
||||||
|
|
||||||
|
void DriveVIA::set_delegate(Delegate *delegate)
|
||||||
|
{
|
||||||
|
_delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write protect tab uncovered
|
||||||
|
DriveVIA::DriveVIA() : _port_b(0xff), _port_a(0xff), _delegate(nullptr) {}
|
||||||
|
|
||||||
|
uint8_t DriveVIA::get_port_input(Port port) {
|
||||||
|
return port ? _port_b : _port_a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DriveVIA::set_sync_detected(bool sync_detected) {
|
||||||
|
_port_b = (_port_b & 0x7f) | (sync_detected ? 0x00 : 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DriveVIA::set_data_input(uint8_t value) {
|
||||||
|
_port_a = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DriveVIA::get_should_set_overflow() {
|
||||||
|
return _should_set_overflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DriveVIA::get_motor_enabled() {
|
||||||
|
return _drive_motor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DriveVIA::set_control_line_output(Port port, Line line, bool value) {
|
||||||
|
if(port == Port::A && line == Line::Two) {
|
||||||
|
_should_set_overflow = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DriveVIA::set_port_output(Port port, uint8_t value, uint8_t direction_mask) {
|
||||||
|
if(port)
|
||||||
|
{
|
||||||
|
// record drive motor state
|
||||||
|
_drive_motor = !!(value&4);
|
||||||
|
|
||||||
|
// check for a head step
|
||||||
|
int step_difference = ((value&3) - (_previous_port_b_output&3))&3;
|
||||||
|
if(step_difference)
|
||||||
|
{
|
||||||
|
if(_delegate) _delegate->drive_via_did_step_head(this, (step_difference == 1) ? 1 : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for a change in density
|
||||||
|
int density_difference = (_previous_port_b_output^value) & (3 << 5);
|
||||||
|
if(density_difference && _delegate)
|
||||||
|
{
|
||||||
|
_delegate->drive_via_did_set_data_density(this, (value >> 5)&3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: something with the drive LED
|
||||||
|
// printf("LED: %s\n", value&8 ? "On" : "Off");
|
||||||
|
|
||||||
|
_previous_port_b_output = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - SerialPort
|
||||||
|
|
||||||
|
void SerialPort::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 SerialPort::set_serial_port_via(std::shared_ptr<SerialPortVIA> serialPortVIA) {
|
||||||
|
_serialPortVIA = serialPortVIA;
|
||||||
|
}
|
||||||
|
@ -39,62 +39,21 @@ class SerialPortVIA: public MOS::MOS6522<SerialPortVIA>, public MOS::MOS6522IRQD
|
|||||||
public:
|
public:
|
||||||
using MOS6522IRQDelegate::set_interrupt_status;
|
using MOS6522IRQDelegate::set_interrupt_status;
|
||||||
|
|
||||||
SerialPortVIA() : _portB(0x00), _attention_acknowledge_level(false), _attention_level_input(true), _data_level_output(false) {}
|
SerialPortVIA();
|
||||||
|
|
||||||
uint8_t get_port_input(Port port) {
|
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);
|
||||||
}
|
void set_serial_line_state(::Commodore::Serial::Line line, bool value);
|
||||||
|
|
||||||
void set_port_output(Port port, uint8_t value, uint8_t mask) {
|
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort);
|
||||||
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:
|
private:
|
||||||
uint8_t _portB;
|
uint8_t _portB;
|
||||||
std::weak_ptr<::Commodore::Serial::Port> _serialPort;
|
std::weak_ptr<::Commodore::Serial::Port> _serialPort;
|
||||||
bool _attention_acknowledge_level, _attention_level_input, _data_level_output;
|
bool _attention_acknowledge_level, _attention_level_input, _data_level_output;
|
||||||
|
|
||||||
void update_data_line()
|
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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -120,68 +79,22 @@ class DriveVIA: public MOS::MOS6522<DriveVIA>, public MOS::MOS6522IRQDelegate {
|
|||||||
virtual void drive_via_did_step_head(void *driveVIA, int direction) = 0;
|
virtual void drive_via_did_step_head(void *driveVIA, int direction) = 0;
|
||||||
virtual void drive_via_did_set_data_density(void *driveVIA, int density) = 0;
|
virtual void drive_via_did_set_data_density(void *driveVIA, int density) = 0;
|
||||||
};
|
};
|
||||||
void set_delegate(Delegate *delegate)
|
void set_delegate(Delegate *delegate);
|
||||||
{
|
|
||||||
_delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
using MOS6522IRQDelegate::set_interrupt_status;
|
using MOS6522IRQDelegate::set_interrupt_status;
|
||||||
|
|
||||||
// write protect tab uncovered
|
DriveVIA();
|
||||||
DriveVIA() : _port_b(0xff), _port_a(0xff), _delegate(nullptr) {}
|
|
||||||
|
|
||||||
uint8_t get_port_input(Port port) {
|
uint8_t get_port_input(Port port);
|
||||||
return port ? _port_b : _port_a;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_sync_detected(bool sync_detected) {
|
void set_sync_detected(bool sync_detected);
|
||||||
_port_b = (_port_b & 0x7f) | (sync_detected ? 0x00 : 0x80);
|
void set_data_input(uint8_t value);
|
||||||
}
|
bool get_should_set_overflow();
|
||||||
|
bool get_motor_enabled();
|
||||||
|
|
||||||
void set_data_input(uint8_t value) {
|
void set_control_line_output(Port port, Line line, bool value);
|
||||||
_port_a = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_should_set_overflow() {
|
void set_port_output(Port port, uint8_t value, uint8_t direction_mask);
|
||||||
return _should_set_overflow;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_motor_enabled() {
|
|
||||||
return _drive_motor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_control_line_output(Port port, Line line, bool value) {
|
|
||||||
if(port == Port::A && line == Line::Two) {
|
|
||||||
_should_set_overflow = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_port_output(Port port, uint8_t value, uint8_t direction_mask) {
|
|
||||||
if(port)
|
|
||||||
{
|
|
||||||
// record drive motor state
|
|
||||||
_drive_motor = !!(value&4);
|
|
||||||
|
|
||||||
// check for a head step
|
|
||||||
int step_difference = ((value&3) - (_previous_port_b_output&3))&3;
|
|
||||||
if(step_difference)
|
|
||||||
{
|
|
||||||
if(_delegate) _delegate->drive_via_did_step_head(this, (step_difference == 1) ? 1 : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for a change in density
|
|
||||||
int density_difference = (_previous_port_b_output^value) & (3 << 5);
|
|
||||||
if(density_difference && _delegate)
|
|
||||||
{
|
|
||||||
_delegate->drive_via_did_set_data_density(this, (value >> 5)&3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: something with the drive LED
|
|
||||||
// printf("LED: %s\n", value&8 ? "On" : "Off");
|
|
||||||
|
|
||||||
_previous_port_b_output = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t _port_b, _port_a;
|
uint8_t _port_b, _port_a;
|
||||||
@ -196,14 +109,8 @@ class DriveVIA: public MOS::MOS6522<DriveVIA>, public MOS::MOS6522IRQDelegate {
|
|||||||
*/
|
*/
|
||||||
class SerialPort : public ::Commodore::Serial::Port {
|
class SerialPort : public ::Commodore::Serial::Port {
|
||||||
public:
|
public:
|
||||||
void set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level) {
|
void set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level);
|
||||||
std::shared_ptr<SerialPortVIA> serialPortVIA = _serialPortVIA.lock();
|
void set_serial_port_via(std::shared_ptr<SerialPortVIA> serialPortVIA);
|
||||||
if(serialPortVIA) serialPortVIA->set_serial_line_state(line, (bool)level);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_serial_port_via(std::shared_ptr<SerialPortVIA> serialPortVIA) {
|
|
||||||
_serialPortVIA = serialPortVIA;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::weak_ptr<SerialPortVIA> _serialPortVIA;
|
std::weak_ptr<SerialPortVIA> _serialPortVIA;
|
||||||
|
@ -474,3 +474,140 @@ bool Machine::typer_set_next_character(::Utility::Typer *typer, char character,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - UserPortVIA
|
||||||
|
|
||||||
|
uint8_t UserPortVIA::get_port_input(Port port)
|
||||||
|
{
|
||||||
|
if(!port)
|
||||||
|
{
|
||||||
|
return _portA; // TODO: bit 6 should be high if there is no tape, low otherwise
|
||||||
|
}
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserPortVIA::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");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserPortVIA::set_serial_line_state(::Commodore::Serial::Line line, bool value)
|
||||||
|
{
|
||||||
|
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 UserPortVIA::set_joystick_state(JoystickInput input, bool value)
|
||||||
|
{
|
||||||
|
if(input != JoystickInput::Right)
|
||||||
|
{
|
||||||
|
_portA = (_portA & ~input) | (value ? 0 : input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserPortVIA::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<::Commodore::Serial::Port> serialPort = _serialPort.lock();
|
||||||
|
if(serialPort)
|
||||||
|
serialPort->set_output(::Commodore::Serial::Line::Attention, (::Commodore::Serial::LineLevel)!(value&0x80));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UserPortVIA::UserPortVIA() : _portA(0xbf) {}
|
||||||
|
|
||||||
|
void UserPortVIA::set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort)
|
||||||
|
{
|
||||||
|
_serialPort = serialPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - KeyboardVIA
|
||||||
|
|
||||||
|
KeyboardVIA::KeyboardVIA() : _portB(0xff)
|
||||||
|
{
|
||||||
|
clear_all_keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardVIA::set_key_state(Key key, bool isPressed)
|
||||||
|
{
|
||||||
|
if(isPressed)
|
||||||
|
_columns[key & 7] &= ~(key >> 3);
|
||||||
|
else
|
||||||
|
_columns[key & 7] |= (key >> 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardVIA::clear_all_keys()
|
||||||
|
{
|
||||||
|
memset(_columns, 0xff, sizeof(_columns));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t KeyboardVIA::get_port_input(Port port)
|
||||||
|
{
|
||||||
|
if(!port)
|
||||||
|
{
|
||||||
|
uint8_t result = 0xff;
|
||||||
|
for(int c = 0; c < 8; c++)
|
||||||
|
{
|
||||||
|
if(!(_activation_mask&(1 << c)))
|
||||||
|
result &= _columns[c];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _portB;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardVIA::set_port_output(Port port, uint8_t value, uint8_t mask)
|
||||||
|
{
|
||||||
|
if(port)
|
||||||
|
_activation_mask = (value & mask) | (~mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardVIA::set_control_line_output(Port port, Line line, bool value)
|
||||||
|
{
|
||||||
|
if(line == Line::Two)
|
||||||
|
{
|
||||||
|
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_output(::Commodore::Serial::Line::Clock, (::Commodore::Serial::LineLevel)!value);
|
||||||
|
else
|
||||||
|
serialPort->set_output(::Commodore::Serial::Line::Data, (::Commodore::Serial::LineLevel)!value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardVIA::set_joystick_state(JoystickInput input, bool value)
|
||||||
|
{
|
||||||
|
if(input == JoystickInput::Right)
|
||||||
|
{
|
||||||
|
_portB = (_portB & ~input) | (value ? 0 : input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardVIA::set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort)
|
||||||
|
{
|
||||||
|
_serialPort = serialPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - SerialPort
|
||||||
|
|
||||||
|
void SerialPort::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 SerialPort::set_user_port_via(std::shared_ptr<UserPortVIA> userPortVIA)
|
||||||
|
{
|
||||||
|
_userPortVIA = userPortVIA;
|
||||||
|
}
|
||||||
|
@ -77,51 +77,16 @@ enum JoystickInput {
|
|||||||
|
|
||||||
class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDelegate {
|
class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDelegate {
|
||||||
public:
|
public:
|
||||||
uint8_t get_port_input(Port port) {
|
UserPortVIA();
|
||||||
if(!port) {
|
|
||||||
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");
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
_portA = (_portA & ~input) | (value ? 0 : input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<::Commodore::Serial::Port> serialPort = _serialPort.lock();
|
|
||||||
if(serialPort)
|
|
||||||
serialPort->set_output(::Commodore::Serial::Line::Attention, (::Commodore::Serial::LineLevel)!(value&0x80));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using MOS6522IRQDelegate::set_interrupt_status;
|
using MOS6522IRQDelegate::set_interrupt_status;
|
||||||
|
|
||||||
UserPortVIA() : _portA(0xbf) {}
|
uint8_t get_port_input(Port port);
|
||||||
|
void set_control_line_output(Port port, Line line, bool value);
|
||||||
|
void set_serial_line_state(::Commodore::Serial::Line line, bool value);
|
||||||
|
void set_joystick_state(JoystickInput input, bool value);
|
||||||
|
void set_port_output(Port port, uint8_t value, uint8_t mask);
|
||||||
|
|
||||||
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort) {
|
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort);
|
||||||
_serialPort = serialPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t _portA;
|
uint8_t _portA;
|
||||||
@ -130,67 +95,21 @@ class UserPortVIA: public MOS::MOS6522<UserPortVIA>, public MOS::MOS6522IRQDeleg
|
|||||||
|
|
||||||
class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDelegate {
|
class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDelegate {
|
||||||
public:
|
public:
|
||||||
KeyboardVIA() : _portB(0xff) {
|
KeyboardVIA();
|
||||||
clear_all_keys();
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_key_state(Key key, bool isPressed) {
|
|
||||||
if(isPressed)
|
|
||||||
_columns[key & 7] &= ~(key >> 3);
|
|
||||||
else
|
|
||||||
_columns[key & 7] |= (key >> 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear_all_keys() {
|
|
||||||
memset(_columns, 0xff, sizeof(_columns));
|
|
||||||
}
|
|
||||||
|
|
||||||
// to satisfy MOS::MOS6522
|
|
||||||
uint8_t get_port_input(Port port) {
|
|
||||||
if(!port) {
|
|
||||||
uint8_t result = 0xff;
|
|
||||||
for(int c = 0; c < 8; c++)
|
|
||||||
{
|
|
||||||
if(!(_activation_mask&(1 << c)))
|
|
||||||
result &= _columns[c];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _portB;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_port_output(Port port, uint8_t value, uint8_t mask) {
|
|
||||||
if(port)
|
|
||||||
_activation_mask = (value & mask) | (~mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_control_line_output(Port port, Line line, bool value) {
|
|
||||||
if(line == Line::Two) {
|
|
||||||
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_output(::Commodore::Serial::Line::Clock, (::Commodore::Serial::LineLevel)!value);
|
|
||||||
} else {
|
|
||||||
serialPort->set_output(::Commodore::Serial::Line::Data, (::Commodore::Serial::LineLevel)!value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_joystick_state(JoystickInput input, bool value) {
|
|
||||||
if(input == JoystickInput::Right)
|
|
||||||
{
|
|
||||||
_portB = (_portB & ~input) | (value ? 0 : input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using MOS6522IRQDelegate::set_interrupt_status;
|
using MOS6522IRQDelegate::set_interrupt_status;
|
||||||
|
|
||||||
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort) {
|
void set_key_state(Key key, bool isPressed);
|
||||||
_serialPort = serialPort;
|
void clear_all_keys();
|
||||||
}
|
|
||||||
|
// to satisfy MOS::MOS6522
|
||||||
|
uint8_t get_port_input(Port port);
|
||||||
|
|
||||||
|
void set_port_output(Port port, uint8_t value, uint8_t mask);
|
||||||
|
void set_control_line_output(Port port, Line line, bool value);
|
||||||
|
|
||||||
|
void set_joystick_state(JoystickInput input, bool value);
|
||||||
|
|
||||||
|
void set_serial_port(std::shared_ptr<::Commodore::Serial::Port> serialPort);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t _portB;
|
uint8_t _portB;
|
||||||
@ -201,14 +120,8 @@ class KeyboardVIA: public MOS::MOS6522<KeyboardVIA>, public MOS::MOS6522IRQDeleg
|
|||||||
|
|
||||||
class SerialPort : public ::Commodore::Serial::Port {
|
class SerialPort : public ::Commodore::Serial::Port {
|
||||||
public:
|
public:
|
||||||
void set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level) {
|
void set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level);
|
||||||
std::shared_ptr<UserPortVIA> userPortVIA = _userPortVIA.lock();
|
void set_user_port_via(std::shared_ptr<UserPortVIA> userPortVIA);
|
||||||
if(userPortVIA) userPortVIA->set_serial_line_state(line, (bool)level);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_user_port_via(std::shared_ptr<UserPortVIA> userPortVIA) {
|
|
||||||
_userPortVIA = userPortVIA;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::weak_ptr<UserPortVIA> _userPortVIA;
|
std::weak_ptr<UserPortVIA> _userPortVIA;
|
||||||
|
@ -122,3 +122,57 @@ void Machine::tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape_player
|
|||||||
// set CB1
|
// set CB1
|
||||||
_via.set_control_line_input(VIA::Port::B, VIA::Line::One, tape_player->get_input());
|
_via.set_control_line_input(VIA::Port::B, VIA::Line::One, tape_player->get_input());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - The 6522
|
||||||
|
|
||||||
|
void Machine::VIA::set_control_line_output(Port port, Line line, bool value)
|
||||||
|
{
|
||||||
|
if(line)
|
||||||
|
{
|
||||||
|
if(port) _ay_bdir = value; else _ay_bc1 = value;
|
||||||
|
update_ay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::VIA::set_port_output(Port port, uint8_t value, uint8_t direction_mask) {
|
||||||
|
if(port)
|
||||||
|
{
|
||||||
|
keyboard->row = value;
|
||||||
|
tape->set_motor_control(value & 0x40);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ay8910->set_data_input(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Machine::VIA::get_port_input(Port port) {
|
||||||
|
if(port)
|
||||||
|
{
|
||||||
|
uint8_t column = ay8910->get_port_output(false) ^ 0xff;
|
||||||
|
return (keyboard->rows[keyboard->row & 7] & column) ? 0x08 : 0x00;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ay8910->get_data_output();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::VIA::synchronise()
|
||||||
|
{
|
||||||
|
ay8910->run_for_cycles(_half_cycles_since_ay_update >> 1);
|
||||||
|
_half_cycles_since_ay_update &= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::VIA::run_for_half_cycles(unsigned int number_of_cycles)
|
||||||
|
{
|
||||||
|
_half_cycles_since_ay_update += number_of_cycles;
|
||||||
|
MOS::MOS6522<VIA>::run_for_half_cycles(number_of_cycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::VIA::update_ay()
|
||||||
|
{
|
||||||
|
ay8910->run_for_cycles(_half_cycles_since_ay_update >> 1);
|
||||||
|
_half_cycles_since_ay_update &= 1;
|
||||||
|
ay8910->set_control_lines( (GI::AY38910::ControlLines)((_ay_bdir ? GI::AY38910::BCDIR : 0) | (_ay_bc1 ? GI::AY38910::BC1 : 0) | GI::AY38910::BC2));
|
||||||
|
}
|
||||||
|
@ -102,58 +102,19 @@ class Machine:
|
|||||||
public:
|
public:
|
||||||
using MOS6522IRQDelegate::set_interrupt_status;
|
using MOS6522IRQDelegate::set_interrupt_status;
|
||||||
|
|
||||||
void set_control_line_output(Port port, Line line, bool value)
|
void set_control_line_output(Port port, Line line, bool value);
|
||||||
{
|
void set_port_output(Port port, uint8_t value, uint8_t direction_mask);
|
||||||
if(line)
|
uint8_t get_port_input(Port port);
|
||||||
{
|
inline void run_for_half_cycles(unsigned int number_of_cycles);
|
||||||
if(port) _ay_bdir = value; else _ay_bc1 = value;
|
|
||||||
update_ay();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_port_output(Port port, uint8_t value, uint8_t direction_mask) {
|
|
||||||
if(port)
|
|
||||||
{
|
|
||||||
keyboard->row = value;
|
|
||||||
tape->set_motor_control(value & 0x40);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ay8910->set_data_input(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t get_port_input(Port port) {
|
|
||||||
if(port)
|
|
||||||
{
|
|
||||||
uint8_t column = ay8910->get_port_output(false) ^ 0xff;
|
|
||||||
return (keyboard->rows[keyboard->row & 7] & column) ? 0x08 : 0x00;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ay8910->get_data_output();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void run_for_half_cycles(unsigned int number_of_cycles)
|
|
||||||
{
|
|
||||||
_half_cycles_since_ay_update += number_of_cycles;
|
|
||||||
MOS::MOS6522<VIA>::run_for_half_cycles(number_of_cycles);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<GI::AY38910> ay8910;
|
std::shared_ptr<GI::AY38910> ay8910;
|
||||||
std::shared_ptr<Storage::Tape::BinaryTapePlayer> tape;
|
std::shared_ptr<Storage::Tape::BinaryTapePlayer> tape;
|
||||||
std::shared_ptr<Keyboard> keyboard;
|
std::shared_ptr<Keyboard> keyboard;
|
||||||
|
|
||||||
inline void synchronise() { ay8910->run_for_cycles(_half_cycles_since_ay_update >> 1); _half_cycles_since_ay_update = 0; }
|
void synchronise();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void update_ay()
|
void update_ay();
|
||||||
{
|
|
||||||
ay8910->run_for_cycles(_half_cycles_since_ay_update >> 1);
|
|
||||||
_half_cycles_since_ay_update = 0;
|
|
||||||
ay8910->set_control_lines( (GI::AY38910::ControlLines)((_ay_bdir ? GI::AY38910::BCDIR : 0) | (_ay_bc1 ? GI::AY38910::BC1 : 0) | GI::AY38910::BC2));
|
|
||||||
}
|
|
||||||
bool _ay_bdir, _ay_bc1;
|
bool _ay_bdir, _ay_bc1;
|
||||||
unsigned int _half_cycles_since_ay_update;
|
unsigned int _half_cycles_since_ay_update;
|
||||||
};
|
};
|
||||||
|
@ -93,3 +93,45 @@ void TapePlayer::process_next_event()
|
|||||||
process_input_pulse(_current_pulse);
|
process_input_pulse(_current_pulse);
|
||||||
get_next_pulse();
|
get_next_pulse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Binary Player
|
||||||
|
|
||||||
|
BinaryTapePlayer::BinaryTapePlayer(unsigned int input_clock_rate) :
|
||||||
|
TapePlayer(input_clock_rate), _motor_is_running(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void BinaryTapePlayer::set_motor_control(bool enabled)
|
||||||
|
{
|
||||||
|
_motor_is_running = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BinaryTapePlayer::set_tape_output(bool set)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BinaryTapePlayer::get_input()
|
||||||
|
{
|
||||||
|
return _input_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BinaryTapePlayer::run_for_cycles(int number_of_cycles)
|
||||||
|
{
|
||||||
|
if(_motor_is_running) TapePlayer::run_for_cycles(number_of_cycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BinaryTapePlayer::set_delegate(Delegate *delegate)
|
||||||
|
{
|
||||||
|
_delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BinaryTapePlayer::process_input_pulse(Storage::Tape::Tape::Pulse pulse)
|
||||||
|
{
|
||||||
|
bool new_input_level = pulse.type == Tape::Pulse::Low;
|
||||||
|
if(_input_level != new_input_level)
|
||||||
|
{
|
||||||
|
_input_level = new_input_level;
|
||||||
|
if(_delegate) _delegate->tape_did_change_input(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -94,39 +94,32 @@ class TapePlayer: public TimedEventLoop {
|
|||||||
Tape::Pulse _current_pulse;
|
Tape::Pulse _current_pulse;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A specific subclass of the tape player for machines that sample such as to report only either a
|
||||||
|
high or a low current input level.
|
||||||
|
|
||||||
|
Such machines can use @c get_input() to get the current level of the input.
|
||||||
|
|
||||||
|
They can also provide a delegate to be notified upon any change in the input level.
|
||||||
|
*/
|
||||||
class BinaryTapePlayer: public TapePlayer {
|
class BinaryTapePlayer: public TapePlayer {
|
||||||
public:
|
public:
|
||||||
BinaryTapePlayer(unsigned int input_clock_rate) : TapePlayer(input_clock_rate), _motor_is_running(false) {}
|
BinaryTapePlayer(unsigned int input_clock_rate);
|
||||||
void set_motor_control(bool enabled) { _motor_is_running = enabled; }
|
void set_motor_control(bool enabled);
|
||||||
void set_tape_output(bool set) {} // TODO
|
void set_tape_output(bool set);
|
||||||
inline bool get_input() { return _input_level; }
|
bool get_input();
|
||||||
|
|
||||||
void run_for_cycles(int number_of_cycles) {
|
void run_for_cycles(int number_of_cycles);
|
||||||
if(_motor_is_running) {
|
|
||||||
TapePlayer::run_for_cycles(number_of_cycles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Delegate {
|
class Delegate {
|
||||||
public:
|
public:
|
||||||
virtual void tape_did_change_input(BinaryTapePlayer *tape_player) = 0;
|
virtual void tape_did_change_input(BinaryTapePlayer *tape_player) = 0;
|
||||||
};
|
};
|
||||||
void set_delegate(Delegate *delegate)
|
void set_delegate(Delegate *delegate);
|
||||||
{
|
|
||||||
_delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Delegate *_delegate;
|
Delegate *_delegate;
|
||||||
virtual void process_input_pulse(Storage::Tape::Tape::Pulse pulse)
|
virtual void process_input_pulse(Storage::Tape::Tape::Pulse pulse);
|
||||||
{
|
|
||||||
bool new_input_level = pulse.type == Tape::Pulse::Low;
|
|
||||||
if(_input_level != new_input_level)
|
|
||||||
{
|
|
||||||
_input_level = new_input_level;
|
|
||||||
if(_delegate) _delegate->tape_did_change_input(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool _input_level;
|
bool _input_level;
|
||||||
bool _motor_is_running;
|
bool _motor_is_running;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user