1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-25 18:30:21 +00:00

Attempts better to hide C1540 implementation details from the reader.

In this case not from the compiler, as it's desireable to keep `run_for` as a non-virtual call, and therefore everything else comes alone for the ride.
This commit is contained in:
Thomas Harte 2017-09-04 20:58:00 -04:00
parent a49594c6a3
commit 6547102511
4 changed files with 193 additions and 148 deletions

View File

@ -9,124 +9,20 @@
#ifndef Commodore1540_hpp
#define Commodore1540_hpp
#include "../../../Processors/6502/6502.hpp"
#include "../../../Components/6522/6522.hpp"
#include "../SerialBus.hpp"
#include "../../../Storage/Disk/Disk.hpp"
#include "../../../Storage/Disk/DiskController.hpp"
#include "Implementation/C1540Base.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::IRQDelegatePortHandler {
public:
SerialPortVIA(MOS::MOS6522::MOS6522<SerialPortVIA> &via);
uint8_t get_port_input(MOS::MOS6522::Port);
void set_port_output(MOS::MOS6522::Port, uint8_t value, uint8_t mask);
void set_serial_line_state(::Commodore::Serial::Line, bool);
void set_serial_port(const std::shared_ptr<::Commodore::Serial::Port> &);
private:
MOS::MOS6522::MOS6522<SerialPortVIA> via_;
uint8_t port_b_;
std::weak_ptr<::Commodore::Serial::Port> serial_port_;
bool attention_acknowledge_level_, attention_level_input_, data_level_output_;
void update_data_line();
};
/*!
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
Bit 2: motor control
Bit 3: LED control (TODO)
Bit 4: write protect photocell status (TODO)
Bits 5/6: read/write density
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::IRQDelegatePortHandler {
public:
class Delegate {
public:
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;
};
void set_delegate(Delegate *);
DriveVIA();
uint8_t get_port_input(MOS::MOS6522::Port port);
void set_sync_detected(bool);
void set_data_input(uint8_t);
bool get_should_set_overflow();
bool get_motor_enabled();
void set_control_line_output(MOS::MOS6522::Port, MOS::MOS6522::Line, bool value);
void set_port_output(MOS::MOS6522::Port, uint8_t value, uint8_t direction_mask);
private:
uint8_t port_b_, port_a_;
bool should_set_overflow_;
bool drive_motor_;
uint8_t previous_port_b_output_;
Delegate *delegate_;
};
/*!
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, ::Commodore::Serial::LineLevel);
void set_serial_port_via(const std::shared_ptr<SerialPortVIA> &);
private:
std::weak_ptr<SerialPortVIA> serial_port_VIA_;
};
/*!
Provides an emulation of the C1540.
*/
class Machine:
public CPU::MOS6502::BusHandler,
public MOS::MOS6522::IRQDelegatePortHandler::Delegate,
public DriveVIA::Delegate,
public Storage::Disk::Controller {
class Machine: public MachineBase {
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.
Sets the ROM image to use for this drive; it is asserted that the buffer provided is 16 kb in size.
*/
void set_rom(const std::vector<uint8_t> &rom);
@ -135,35 +31,11 @@ class Machine:
*/
void set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bus);
/// Advances time.
void run_for(const Cycles cycles);
/// Inserts @c disk into the drive.
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk);
// to satisfy CPU::MOS6502::Processor
Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value);
// to satisfy MOS::MOS6522::Delegate
virtual void mos6522_did_change_interrupt_status(void *mos6522);
// to satisfy DriveVIA::Delegate
void drive_via_did_step_head(void *driveVIA, int direction);
void drive_via_did_set_data_density(void *driveVIA, int density);
private:
CPU::MOS6502::Processor<Machine, false> m6502_;
uint8_t ram_[0x800];
uint8_t rom_[0x4000];
std::shared_ptr<SerialPortVIA> serial_port_VIA_port_handler_;
std::shared_ptr<SerialPort> serial_port_;
DriveVIA drive_VIA_port_handler_;
MOS::MOS6522::MOS6522<DriveVIA> drive_VIA_;
MOS::MOS6522::MOS6522<SerialPortVIA> serial_port_VIA_;
int shift_register_, bit_window_offset_;
virtual void process_input_bit(int value, unsigned int cycles_since_index_hole);
virtual void process_index_hole();
};
}

View File

@ -7,12 +7,15 @@
//
#include "C1540.hpp"
#include <string>
#include "../../../Storage/Disk/Encodings/CommodoreGCR.hpp"
#include <cassert>
#include "../../../../Storage/Disk/Encodings/CommodoreGCR.hpp"
using namespace Commodore::C1540;
Machine::Machine() :
MachineBase::MachineBase() :
m6502_(*this),
shift_register_(0),
Storage::Disk::Controller(1000000, 4, 300),
@ -37,7 +40,7 @@ void Machine::set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bu
Commodore::Serial::AttachPortAndBus(serial_port_, serial_bus);
}
Cycles Machine::perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
Cycles MachineBase::perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
/*
Memory map (given that I'm unsure yet on any potential mirroring):
@ -73,6 +76,7 @@ Cycles Machine::perform_bus_operation(CPU::MOS6502::BusOperation operation, uint
}
void Machine::set_rom(const std::vector<uint8_t> &rom) {
assert(rom.size() == sizeof(rom_));
memcpy(rom_, rom.data(), std::min(sizeof(rom_), rom.size()));
}
@ -84,21 +88,23 @@ void Machine::set_disk(std::shared_ptr<Storage::Disk::Disk> disk) {
void Machine::run_for(const Cycles cycles) {
m6502_.run_for(cycles);
set_motor_on(drive_VIA_port_handler_.get_motor_enabled());
if(drive_VIA_port_handler_.get_motor_enabled()) // TODO: motor speed up/down
bool drive_motor = drive_VIA_port_handler_.get_motor_enabled();
set_motor_on(drive_motor);
if(drive_motor)
Storage::Disk::Controller::run_for(cycles);
}
#pragma mark - 6522 delegate
void Machine::mos6522_did_change_interrupt_status(void *mos6522) {
void MachineBase::mos6522_did_change_interrupt_status(void *mos6522) {
// both VIAs are connected to the IRQ line
m6502_.set_irq_line(serial_port_VIA_.get_interrupt_line() || drive_VIA_.get_interrupt_line());
}
#pragma mark - Disk drive
void Machine::process_input_bit(int value, unsigned int cycles_since_index_hole) {
void MachineBase::process_input_bit(int value, unsigned int cycles_since_index_hole) {
shift_register_ = (shift_register_ << 1) | value;
if((shift_register_ & 0x3ff) == 0x3ff) {
drive_VIA_port_handler_.set_sync_detected(true);
@ -118,15 +124,15 @@ void Machine::process_input_bit(int value, unsigned int cycles_since_index_hole)
}
// the 1540 does not recognise index holes
void Machine::process_index_hole() {}
void MachineBase::process_index_hole() {}
#pragma mak - Drive VIA delegate
void Machine::drive_via_did_step_head(void *driveVIA, int direction) {
void MachineBase::drive_via_did_step_head(void *driveVIA, int direction) {
step(direction);
}
void Machine::drive_via_did_set_data_density(void *driveVIA, int density) {
void MachineBase::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));
}

View File

@ -0,0 +1,157 @@
//
// C1540Base.hpp
// Clock Signal
//
// Created by Thomas Harte on 04/09/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef C1540Base_hpp
#define C1540Base_hpp
#include "../../../../Processors/6502/6502.hpp"
#include "../../../../Components/6522/6522.hpp"
#include "../../SerialBus.hpp"
#include "../../../../Storage/Disk/Disk.hpp"
#include "../../../../Storage/Disk/DiskController.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::IRQDelegatePortHandler {
public:
SerialPortVIA(MOS::MOS6522::MOS6522<SerialPortVIA> &via);
uint8_t get_port_input(MOS::MOS6522::Port);
void set_port_output(MOS::MOS6522::Port, uint8_t value, uint8_t mask);
void set_serial_line_state(::Commodore::Serial::Line, bool);
void set_serial_port(const std::shared_ptr<::Commodore::Serial::Port> &);
private:
MOS::MOS6522::MOS6522<SerialPortVIA> via_;
uint8_t port_b_;
std::weak_ptr<::Commodore::Serial::Port> serial_port_;
bool attention_acknowledge_level_, attention_level_input_, data_level_output_;
void update_data_line();
};
/*!
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
Bit 2: motor control
Bit 3: LED control (TODO)
Bit 4: write protect photocell status (TODO)
Bits 5/6: read/write density
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::IRQDelegatePortHandler {
public:
class Delegate {
public:
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;
};
void set_delegate(Delegate *);
DriveVIA();
uint8_t get_port_input(MOS::MOS6522::Port port);
void set_sync_detected(bool);
void set_data_input(uint8_t);
bool get_should_set_overflow();
bool get_motor_enabled();
void set_control_line_output(MOS::MOS6522::Port, MOS::MOS6522::Line, bool value);
void set_port_output(MOS::MOS6522::Port, uint8_t value, uint8_t direction_mask);
private:
uint8_t port_b_, port_a_;
bool should_set_overflow_;
bool drive_motor_;
uint8_t previous_port_b_output_;
Delegate *delegate_;
};
/*!
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, ::Commodore::Serial::LineLevel);
void set_serial_port_via(const std::shared_ptr<SerialPortVIA> &);
private:
std::weak_ptr<SerialPortVIA> serial_port_VIA_;
};
class MachineBase:
public CPU::MOS6502::BusHandler,
public MOS::MOS6522::IRQDelegatePortHandler::Delegate,
public DriveVIA::Delegate,
public Storage::Disk::Controller {
public:
MachineBase();
// to satisfy CPU::MOS6502::Processor
Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value);
// to satisfy MOS::MOS6522::Delegate
virtual void mos6522_did_change_interrupt_status(void *mos6522);
// to satisfy DriveVIA::Delegate
void drive_via_did_step_head(void *driveVIA, int direction);
void drive_via_did_set_data_density(void *driveVIA, int density);
protected:
CPU::MOS6502::Processor<MachineBase, false> m6502_;
uint8_t ram_[0x800];
uint8_t rom_[0x4000];
std::shared_ptr<SerialPortVIA> serial_port_VIA_port_handler_;
std::shared_ptr<SerialPort> serial_port_;
DriveVIA drive_VIA_port_handler_;
MOS::MOS6522::MOS6522<DriveVIA> drive_VIA_;
MOS::MOS6522::MOS6522<SerialPortVIA> serial_port_VIA_;
int shift_register_, bit_window_offset_;
virtual void process_input_bit(int value, unsigned int cycles_since_index_hole);
virtual void process_index_hole();
};
}
}
#endif /* C1540Base_hpp */

View File

@ -66,7 +66,6 @@
4B4A76301DB1A3FA007AAE2E /* AY38910.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */; };
4B4C83701D4F623200CD541F /* D64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4C836E1D4F623200CD541F /* D64.cpp */; };
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 */; };
4B5073071DDD3B9400C48FBD /* ArrayBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5073051DDD3B9400C48FBD /* ArrayBuilder.cpp */; };
4B50730A1DDFCFDF00C48FBD /* ArrayBuilderTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B5073091DDFCFDF00C48FBD /* ArrayBuilderTests.mm */; };
@ -95,6 +94,7 @@
4B8334861F5DA3780097E338 /* 6502Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8334851F5DA3780097E338 /* 6502Storage.cpp */; };
4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8334891F5DB94B0097E338 /* IRQDelegatePortHandler.cpp */; };
4B83348C1F5DB99C0097E338 /* 6522Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B83348B1F5DB99C0097E338 /* 6522Base.cpp */; };
4B8334951F5E25B60097E338 /* C1540.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8334941F5E25B60097E338 /* C1540.cpp */; };
4B8378DC1F336631005CA9E4 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8378DA1F336631005CA9E4 /* CharacterMapper.cpp */; };
4B8378DF1F33675F005CA9E4 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8378DD1F33675F005CA9E4 /* CharacterMapper.cpp */; };
4B8378E21F336920005CA9E4 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8378E01F336920005CA9E4 /* CharacterMapper.cpp */; };
@ -589,7 +589,6 @@
4B4C836F1D4F623200CD541F /* D64.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = D64.hpp; 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>"; };
@ -637,6 +636,8 @@
4B8334891F5DB94B0097E338 /* IRQDelegatePortHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IRQDelegatePortHandler.cpp; path = Implementation/IRQDelegatePortHandler.cpp; sourceTree = "<group>"; };
4B83348B1F5DB99C0097E338 /* 6522Base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 6522Base.cpp; path = Implementation/6522Base.cpp; sourceTree = "<group>"; };
4B83348E1F5DBA6E0097E338 /* 6522Storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = 6522Storage.hpp; path = Implementation/6522Storage.hpp; sourceTree = "<group>"; };
4B8334911F5E24FF0097E338 /* C1540Base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = C1540Base.hpp; path = Implementation/C1540Base.hpp; sourceTree = "<group>"; };
4B8334941F5E25B60097E338 /* C1540.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = C1540.cpp; path = Implementation/C1540.cpp; sourceTree = "<group>"; };
4B8378DA1F336631005CA9E4 /* CharacterMapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CharacterMapper.cpp; path = Electron/CharacterMapper.cpp; sourceTree = "<group>"; };
4B8378DB1F336631005CA9E4 /* CharacterMapper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CharacterMapper.hpp; path = Electron/CharacterMapper.hpp; sourceTree = "<group>"; };
4B8378DD1F33675F005CA9E4 /* CharacterMapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CharacterMapper.cpp; path = ZX8081/CharacterMapper.cpp; sourceTree = "<group>"; };
@ -1429,8 +1430,8 @@
4B4DC8251D2C2470003C5BF8 /* 1540 */ = {
isa = PBXGroup;
children = (
4B4DC8261D2C2470003C5BF8 /* C1540.cpp */,
4B4DC8271D2C2470003C5BF8 /* C1540.hpp */,
4B8334931F5E25030097E338 /* Implementation */,
);
path = 1540;
sourceTree = "<group>";
@ -1585,6 +1586,15 @@
name = Implementation;
sourceTree = "<group>";
};
4B8334931F5E25030097E338 /* Implementation */ = {
isa = PBXGroup;
children = (
4B8334941F5E25B60097E338 /* C1540.cpp */,
4B8334911F5E24FF0097E338 /* C1540Base.hpp */,
);
name = Implementation;
sourceTree = "<group>";
};
4B8805F11DCFC9A2003085B1 /* Parsers */ = {
isa = PBXGroup;
children = (
@ -2802,6 +2812,7 @@
4B8378E21F336920005CA9E4 /* CharacterMapper.cpp in Sources */,
4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */,
4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */,
4B8334951F5E25B60097E338 /* C1540.cpp in Sources */,
4B1497921EE4B5A800CE2596 /* ZX8081.cpp in Sources */,
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */,
4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */,
@ -2850,7 +2861,6 @@
4B6C73BD1D387AE500AFCFCA /* DiskController.cpp in Sources */,
4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */,
4B1497881EE4A1DA00CE2596 /* ZX80O81P.cpp in Sources */,
4B4DC8281D2C2470003C5BF8 /* C1540.cpp in Sources */,
4B5A12571DD55862007A2231 /* Disassembler6502.cpp in Sources */,
4BE7C9181E3D397100A5496D /* TIA.cpp in Sources */,
4B1E85751D170228001EF87D /* Typer.cpp in Sources */,