diff --git a/Machines/Commodore/1540/Commodore1540.cpp b/Machines/Commodore/1540/C1540.cpp similarity index 98% rename from Machines/Commodore/1540/Commodore1540.cpp rename to Machines/Commodore/1540/C1540.cpp index fa7bb49e9..69672ddb5 100644 --- a/Machines/Commodore/1540/Commodore1540.cpp +++ b/Machines/Commodore/1540/C1540.cpp @@ -6,7 +6,7 @@ // Copyright © 2016 Thomas Harte. All rights reserved. // -#include "Commodore1540.hpp" +#include "C1540.hpp" #include <string.h> using namespace Commodore::C1540; diff --git a/Machines/Commodore/1540/Commodore1540.hpp b/Machines/Commodore/1540/C1540.hpp similarity index 65% rename from Machines/Commodore/1540/Commodore1540.hpp rename to Machines/Commodore/1540/C1540.hpp index 2d0fffec3..92ceccb6e 100644 --- a/Machines/Commodore/1540/Commodore1540.hpp +++ b/Machines/Commodore/1540/C1540.hpp @@ -16,6 +16,21 @@ 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; @@ -34,28 +49,21 @@ class SerialPortVIA: public MOS::MOS6522<SerialPortVIA>, public MOS::MOS6522IRQD if(port) { std::shared_ptr<::Commodore::Serial::Port> serialPort = _serialPort.lock(); if(serialPort) { -// printf("1540 output: %02x\n", value); - // "ATNA (Attention Acknowledge) is an output from PB4 which is sensed on the serial data line after being exclusively "ored" by the attention line and inverted" _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(); } -// printf("1540 serial port VIA port B: %02x\n", value); } -// else -// printf("1540 serial port VIA port A: %02x\n", value); } void set_serial_line_state(::Commodore::Serial::Line line, bool value) { -// printf("1540 Serial port line %d: %s\n", line, value ? "on" : "off"); 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: - // "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" _attention_level_input = !value; _portB = (_portB & ~0x80) | (value ? 0x00 : 0x80); set_control_line_input(Port::A, Line::One, !value); @@ -77,6 +85,7 @@ class SerialPortVIA: public MOS::MOS6522<SerialPortVIA>, public MOS::MOS6522IRQD { 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))); @@ -84,6 +93,22 @@ class SerialPortVIA: public MOS::MOS6522<SerialPortVIA>, public MOS::MOS6522IRQD } }; +/*! + 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; @@ -110,6 +135,9 @@ class DriveVIA: public MOS::MOS6522<DriveVIA>, public MOS::MOS6522IRQDelegate { } }; +/*! + 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) { @@ -125,17 +153,29 @@ class SerialPort : public ::Commodore::Serial::Port { std::weak_ptr<SerialPortVIA> _serialPortVIA; }; +/*! + Provides an emulation of the C1540. +*/ class Machine: public CPU6502::Processor<Machine>, public MOS::MOS6522IRQDelegate::Delegate { public: Machine(); - unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value); + /*! + 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); diff --git a/Machines/Commodore/SerialBus.cpp b/Machines/Commodore/SerialBus.cpp index 13ee704b3..69b187472 100644 --- a/Machines/Commodore/SerialBus.cpp +++ b/Machines/Commodore/SerialBus.cpp @@ -22,6 +22,12 @@ const char *::Commodore::Serial::StringForLine(Line line) } } +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); diff --git a/Machines/Commodore/SerialBus.hpp b/Machines/Commodore/SerialBus.hpp index 78ee5be70..324ef235e 100644 --- a/Machines/Commodore/SerialBus.hpp +++ b/Machines/Commodore/SerialBus.hpp @@ -27,15 +27,38 @@ namespace Serial { Low = false }; + class Port; + class Bus; + + /*! + Returns a C string giving a human-readable name for the supplied line. + */ const char *StringForLine(Line line); - class Port; + /*! + 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: @@ -43,10 +66,17 @@ namespace Serial { 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) { @@ -56,12 +86,21 @@ namespace Serial { } } + /*! + 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; } @@ -71,6 +110,9 @@ namespace Serial { 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); diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 454b061ee..e417c2849 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -22,8 +22,7 @@ Machine::Machine() : _serialBus.reset(new ::Commodore::Serial::Bus); // wire up the serial bus and serial port - _serialBus->add_port(_serialPort); - _serialPort->set_serial_bus(_serialBus); + Commodore::Serial::AttachPortAndBus(_serialPort, _serialBus); // wire up 6522s and serial port _userPortVIA->set_serial_port(_serialPort); diff --git a/Machines/Commodore/Vic-20/Vic20.hpp b/Machines/Commodore/Vic-20/Vic20.hpp index a6ad74501..fcbac0fbf 100644 --- a/Machines/Commodore/Vic-20/Vic20.hpp +++ b/Machines/Commodore/Vic-20/Vic20.hpp @@ -13,7 +13,7 @@ #include "../../../Storage/Tape/Tape.hpp" #include "../../../Components/6560/6560.hpp" #include "../../../Components/6522/6522.hpp" -#include "../1540/Commodore1540.hpp" +#include "../1540/C1540.hpp" #include "../SerialBus.hpp" #include "../../CRTMachine.hpp" diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 2602afa7e..302bceebd 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -30,7 +30,7 @@ 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 /* Commodore1540.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8261D2C2470003C5BF8 /* Commodore1540.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 */; }; @@ -398,8 +398,8 @@ 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 /* Commodore1540.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Commodore1540.cpp; sourceTree = "<group>"; }; - 4B4DC8271D2C2470003C5BF8 /* Commodore1540.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Commodore1540.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>"; }; @@ -909,8 +909,8 @@ 4B4DC8251D2C2470003C5BF8 /* 1540 */ = { isa = PBXGroup; children = ( - 4B4DC8261D2C2470003C5BF8 /* Commodore1540.cpp */, - 4B4DC8271D2C2470003C5BF8 /* Commodore1540.hpp */, + 4B4DC8261D2C2470003C5BF8 /* C1540.cpp */, + 4B4DC8271D2C2470003C5BF8 /* C1540.hpp */, ); path = 1540; sourceTree = "<group>"; @@ -1836,7 +1836,7 @@ 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */, 4BBF99141C8FBA6F0075DAFB /* CRTInputBufferBuilder.cpp in Sources */, 4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */, - 4B4DC8281D2C2470003C5BF8 /* Commodore1540.cpp in Sources */, + 4B4DC8281D2C2470003C5BF8 /* C1540.cpp in Sources */, 4B1E85751D170228001EF87D /* Typer.cpp in Sources */, 4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */, 4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */, diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/C1540Bridge.mm b/OSBindings/Mac/Clock SignalTests/Bridges/C1540Bridge.mm index 72339ef4d..b062016c5 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/C1540Bridge.mm +++ b/OSBindings/Mac/Clock SignalTests/Bridges/C1540Bridge.mm @@ -7,7 +7,7 @@ // #import "C1540Bridge.h" -#include "Commodore1540.hpp" +#include "C1540.hpp" class VanillaSerialPort: public Commodore::Serial::Port { public: @@ -35,8 +35,7 @@ class VanillaSerialPort: public Commodore::Serial::Port { _serialPort.reset(new VanillaSerialPort); _c1540.set_serial_bus(_serialBus); - _serialBus->add_port(_serialPort); - _serialPort->set_serial_bus(_serialBus); + Commodore::Serial::AttachPortAndBus(_serialPort, _serialBus); } return self; }