1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-02 02:49:28 +00:00
CLK/Machines/Commodore/1540/C1540.cpp

249 lines
7.5 KiB
C++
Raw Normal View History

//
// Commodore1540.cpp
// Clock Signal
//
// Created by Thomas Harte on 05/07/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "C1540.hpp"
#include <string>
#include "../../../Storage/Disk/Encodings/CommodoreGCR.hpp"
using namespace Commodore::C1540;
Machine::Machine() :
2017-03-26 18:34:47 +00:00
shift_register_(0),
Storage::Disk::Controller(1000000, 4, 300),
serial_port_(new SerialPort),
serial_port_VIA_(new SerialPortVIA) {
2016-07-10 12:01:16 +00:00
// attach the serial port to its VIA and vice versa
serial_port_->set_serial_port_via(serial_port_VIA_);
serial_port_VIA_->set_serial_port(serial_port_);
2016-07-10 12:01:16 +00:00
// set this instance as the delegate to receive interrupt requests from both VIAs
serial_port_VIA_->set_interrupt_delegate(this);
drive_VIA_.set_interrupt_delegate(this);
drive_VIA_.set_delegate(this);
// set a bit rate
set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(3));
}
2017-03-26 18:34:47 +00:00
void Machine::set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bus) {
Commodore::Serial::AttachPortAndBus(serial_port_, serial_bus);
}
unsigned int Machine::perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
2016-07-10 12:01:16 +00:00
/*
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
*/
2017-03-26 18:34:47 +00:00
if(address < 0x800) {
if(isReadOperation(operation))
*value = ram_[address];
else
ram_[address] = *value;
2017-03-26 18:34:47 +00:00
} else if(address >= 0xc000) {
if(isReadOperation(operation))
*value = rom_[address & 0x3fff];
2017-03-26 18:34:47 +00:00
} else if(address >= 0x1800 && address <= 0x180f) {
if(isReadOperation(operation))
*value = serial_port_VIA_->get_register(address);
else
serial_port_VIA_->set_register(address, *value);
2017-03-26 18:34:47 +00:00
} else if(address >= 0x1c00 && address <= 0x1c0f) {
if(isReadOperation(operation))
*value = drive_VIA_.get_register(address);
else
drive_VIA_.set_register(address, *value);
}
serial_port_VIA_->run_for_cycles(1);
drive_VIA_.run_for_cycles(1);
return 1;
}
void Machine::set_rom(const std::vector<uint8_t> &rom) {
memcpy(rom_, rom.data(), std::min(sizeof(rom_), rom.size()));
}
2017-03-26 18:34:47 +00:00
void Machine::set_disk(std::shared_ptr<Storage::Disk::Disk> disk) {
std::shared_ptr<Storage::Disk::Drive> drive(new Storage::Disk::Drive);
drive->set_disk(disk);
set_drive(drive);
}
2017-03-26 18:34:47 +00:00
void Machine::run_for_cycles(int number_of_cycles) {
CPU::MOS6502::Processor<Machine>::run_for(Cycles(number_of_cycles));
set_motor_on(drive_VIA_.get_motor_enabled());
if(drive_VIA_.get_motor_enabled()) // TODO: motor speed up/down
Storage::Disk::Controller::run_for_cycles(number_of_cycles);
}
#pragma mark - 6522 delegate
2017-03-26 18:34:47 +00:00
void Machine::mos6522_did_change_interrupt_status(void *mos6522) {
2016-07-10 12:01:16 +00:00
// both VIAs are connected to the IRQ line
set_irq_line(serial_port_VIA_->get_interrupt_line() || drive_VIA_.get_interrupt_line());
}
#pragma mark - Disk drive
2017-03-26 18:34:47 +00:00
void Machine::process_input_bit(int value, unsigned int cycles_since_index_hole) {
shift_register_ = (shift_register_ << 1) | value;
2017-03-26 18:34:47 +00:00
if((shift_register_ & 0x3ff) == 0x3ff) {
drive_VIA_.set_sync_detected(true);
bit_window_offset_ = -1; // i.e. this bit isn't the first within a data window, but the next might be
2017-03-26 18:34:47 +00:00
} else {
drive_VIA_.set_sync_detected(false);
}
bit_window_offset_++;
2017-03-26 18:34:47 +00:00
if(bit_window_offset_ == 8) {
drive_VIA_.set_data_input((uint8_t)shift_register_);
bit_window_offset_ = 0;
2017-03-26 18:34:47 +00:00
if(drive_VIA_.get_should_set_overflow()) {
set_overflow_line(true);
}
}
2017-03-26 18:34:47 +00:00
else set_overflow_line(false);
}
// the 1540 does not recognise index holes
void Machine::process_index_hole() {}
#pragma mak - Drive VIA delegate
2017-03-26 18:34:47 +00:00
void Machine::drive_via_did_step_head(void *driveVIA, int direction) {
step(direction);
}
2017-03-26 18:34:47 +00:00
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));
}
2016-10-21 01:15:21 +00:00
#pragma mark - SerialPortVIA
SerialPortVIA::SerialPortVIA() :
2017-03-26 18:34:47 +00:00
port_b_(0x00), attention_acknowledge_level_(false), attention_level_input_(true), data_level_output_(false) {}
2016-10-21 01:15:21 +00:00
2017-03-26 18:34:47 +00:00
uint8_t SerialPortVIA::get_port_input(Port port) {
if(port) return port_b_;
2016-10-21 01:15:21 +00:00
return 0xff;
}
2017-03-26 18:34:47 +00:00
void SerialPortVIA::set_port_output(Port port, uint8_t value, uint8_t mask) {
if(port) {
std::shared_ptr<::Commodore::Serial::Port> serialPort = serial_port_.lock();
2016-10-21 01:15:21 +00:00
if(serialPort) {
attention_acknowledge_level_ = !(value&0x10);
data_level_output_ = (value&0x02);
2016-10-21 01:15:21 +00:00
serialPort->set_output(::Commodore::Serial::Line::Clock, (::Commodore::Serial::LineLevel)!(value&0x08));
update_data_line();
}
}
}
2017-03-26 18:34:47 +00:00
void SerialPortVIA::set_serial_line_state(::Commodore::Serial::Line line, bool value) {
switch(line) {
2016-10-21 01:15:21 +00:00
default: break;
case ::Commodore::Serial::Line::Data: port_b_ = (port_b_ & ~0x01) | (value ? 0x00 : 0x01); break;
case ::Commodore::Serial::Line::Clock: port_b_ = (port_b_ & ~0x04) | (value ? 0x00 : 0x04); break;
2016-10-21 01:15:21 +00:00
case ::Commodore::Serial::Line::Attention:
attention_level_input_ = !value;
port_b_ = (port_b_ & ~0x80) | (value ? 0x00 : 0x80);
2016-10-21 01:15:21 +00:00
set_control_line_input(Port::A, Line::One, !value);
update_data_line();
break;
}
}
2017-03-26 18:34:47 +00:00
void SerialPortVIA::set_serial_port(const std::shared_ptr<::Commodore::Serial::Port> &serialPort) {
serial_port_ = serialPort;
2016-10-21 01:15:21 +00:00
}
2017-03-26 18:34:47 +00:00
void SerialPortVIA::update_data_line() {
std::shared_ptr<::Commodore::Serial::Port> serialPort = serial_port_.lock();
2017-03-26 18:34:47 +00:00
if(serialPort) {
2016-10-21 01:15:21 +00:00
// "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_)));
2016-10-21 01:15:21 +00:00
}
}
#pragma mark - DriveVIA
2017-03-26 18:34:47 +00:00
void DriveVIA::set_delegate(Delegate *delegate) {
delegate_ = delegate;
2016-10-21 01:15:21 +00:00
}
// write protect tab uncovered
DriveVIA::DriveVIA() : port_b_(0xff), port_a_(0xff), delegate_(nullptr) {}
2016-10-21 01:15:21 +00:00
uint8_t DriveVIA::get_port_input(Port port) {
return port ? port_b_ : port_a_;
2016-10-21 01:15:21 +00:00
}
void DriveVIA::set_sync_detected(bool sync_detected) {
port_b_ = (port_b_ & 0x7f) | (sync_detected ? 0x00 : 0x80);
2016-10-21 01:15:21 +00:00
}
void DriveVIA::set_data_input(uint8_t value) {
port_a_ = value;
2016-10-21 01:15:21 +00:00
}
bool DriveVIA::get_should_set_overflow() {
return should_set_overflow_;
2016-10-21 01:15:21 +00:00
}
bool DriveVIA::get_motor_enabled() {
return drive_motor_;
2016-10-21 01:15:21 +00:00
}
void DriveVIA::set_control_line_output(Port port, Line line, bool value) {
if(port == Port::A && line == Line::Two) {
should_set_overflow_ = value;
2016-10-21 01:15:21 +00:00
}
}
void DriveVIA::set_port_output(Port port, uint8_t value, uint8_t direction_mask) {
2017-03-26 18:34:47 +00:00
if(port) {
2016-10-21 01:15:21 +00:00
// record drive motor state
drive_motor_ = !!(value&4);
2016-10-21 01:15:21 +00:00
// check for a head step
int step_difference = ((value&3) - (previous_port_b_output_&3))&3;
2017-03-26 18:34:47 +00:00
if(step_difference) {
if(delegate_) delegate_->drive_via_did_step_head(this, (step_difference == 1) ? 1 : -1);
2016-10-21 01:15:21 +00:00
}
// check for a change in density
int density_difference = (previous_port_b_output_^value) & (3 << 5);
2017-03-26 18:34:47 +00:00
if(density_difference && delegate_) {
delegate_->drive_via_did_set_data_density(this, (value >> 5)&3);
2016-10-21 01:15:21 +00:00
}
// TODO: something with the drive LED
// printf("LED: %s\n", value&8 ? "On" : "Off");
previous_port_b_output_ = value;
2016-10-21 01:15:21 +00:00
}
}
#pragma mark - SerialPort
void SerialPort::set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level) {
std::shared_ptr<SerialPortVIA> serialPortVIA = serial_port_VIA_.lock();
2016-10-21 01:15:21 +00:00
if(serialPortVIA) serialPortVIA->set_serial_line_state(line, (bool)level);
}
void SerialPort::set_serial_port_via(const std::shared_ptr<SerialPortVIA> &serialPortVIA) {
serial_port_VIA_ = serialPortVIA;
2016-10-21 01:15:21 +00:00
}