diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index b53fc7c7c..1522bb3df 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -396,6 +396,7 @@ 4BB73EC21B587A5100552FC2 /* Clock_SignalUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EC11B587A5100552FC2 /* Clock_SignalUITests.swift */; }; 4BBB14311CD2CECE00BDB55C /* IntermediateShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB142F1CD2CECE00BDB55C /* IntermediateShader.cpp */; }; 4BBC951E1F368D83008F4C34 /* i8272.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBC951C1F368D83008F4C34 /* i8272.cpp */; }; + 4BBC95221F36B16C008F4C34 /* MFMDiskController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBC95201F36B16C008F4C34 /* MFMDiskController.cpp */; }; 4BBF49AF1ED2880200AB3669 /* FUSETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF49AE1ED2880200AB3669 /* FUSETests.swift */; }; 4BBF99141C8FBA6F0075DAFB /* TextureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99081C8FBA6F0075DAFB /* TextureBuilder.cpp */; }; 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF990A1C8FBA6F0075DAFB /* CRTOpenGL.cpp */; }; @@ -957,6 +958,8 @@ 4BBC34241D2208B100FFC9DF /* CSFastLoading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSFastLoading.h; sourceTree = ""; }; 4BBC951C1F368D83008F4C34 /* i8272.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = i8272.cpp; path = 8272/i8272.cpp; sourceTree = ""; }; 4BBC951D1F368D83008F4C34 /* i8272.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = i8272.hpp; path = 8272/i8272.hpp; sourceTree = ""; }; + 4BBC95201F36B16C008F4C34 /* MFMDiskController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MFMDiskController.cpp; sourceTree = ""; }; + 4BBC95211F36B16C008F4C34 /* MFMDiskController.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MFMDiskController.hpp; sourceTree = ""; }; 4BBF49AE1ED2880200AB3669 /* FUSETests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FUSETests.swift; sourceTree = ""; }; 4BBF99081C8FBA6F0075DAFB /* TextureBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureBuilder.cpp; sourceTree = ""; }; 4BBF99091C8FBA6F0075DAFB /* TextureBuilder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TextureBuilder.hpp; sourceTree = ""; }; @@ -1528,6 +1531,7 @@ 4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */, 4B6C73BB1D387AE500AFCFCA /* DiskController.cpp */, 4B30512B1D989E2200B4FED8 /* Drive.cpp */, + 4BBC95201F36B16C008F4C34 /* MFMDiskController.cpp */, 4B3F1B441E0388D200DB26EE /* PCMPatchedTrack.cpp */, 4B121F961E060CF000BFDA12 /* PCMSegment.cpp */, 4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */, @@ -1535,6 +1539,7 @@ 4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */, 4B6C73BC1D387AE500AFCFCA /* DiskController.hpp */, 4B30512C1D989E2200B4FED8 /* Drive.hpp */, + 4BBC95211F36B16C008F4C34 /* MFMDiskController.hpp */, 4B3F1B451E0388D200DB26EE /* PCMPatchedTrack.hpp */, 4B121F971E060CF000BFDA12 /* PCMSegment.hpp */, 4BAB62B71D3302CA00DF5BA0 /* PCMTrack.hpp */, @@ -2745,6 +2750,7 @@ 4BF829631D8F536B001BAE39 /* SSD.cpp in Sources */, 4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */, 4B3940E71DA83C8300427841 /* AsyncTaskQueue.cpp in Sources */, + 4BBC95221F36B16C008F4C34 /* MFMDiskController.cpp in Sources */, 4BAB62B81D3302CA00DF5BA0 /* PCMTrack.cpp in Sources */, 4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */, 4B8378DF1F33675F005CA9E4 /* CharacterMapper.cpp in Sources */, diff --git a/Storage/Disk/MFMDiskController.cpp b/Storage/Disk/MFMDiskController.cpp new file mode 100644 index 000000000..299556937 --- /dev/null +++ b/Storage/Disk/MFMDiskController.cpp @@ -0,0 +1,151 @@ +// +// MFMDiskController.cpp +// Clock Signal +// +// Created by Thomas Harte on 05/08/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#include "MFMDiskController.hpp" + +#include "../../Storage/Disk/Encodings/MFM.hpp" + +using namespace Storage::Disk; + +MFMController::MFMController(int clock_rate, int clock_rate_multiplier, int revolutions_per_minute) : + Storage::Disk::Controller(clock_rate, clock_rate_multiplier, revolutions_per_minute), + crc_generator_(0x1021, 0xffff), + data_mode_(DataMode::Scanning), + is_awaiting_marker_value_(false) { +} + +void MFMController::process_index_hole() { + posit_event(Event::IndexHole); +} + +void MFMController::process_write_completed() { + posit_event(Event::DataWritten); +} + +void MFMController::set_is_double_density(bool is_double_density) { + is_double_density_ = is_double_density; + Storage::Time bit_length; + bit_length.length = 1; + bit_length.clock_rate = is_double_density ? 500000 : 250000; + set_expected_bit_length(bit_length); + + if(!is_double_density) is_awaiting_marker_value_ = false; +} + +bool MFMController::get_is_double_density() { + return is_double_density_; +} + +void MFMController::set_data_mode(DataMode mode) { + data_mode_ = mode; +} + +MFMController::Token MFMController::get_latest_token() { + return latest_token_; +} + +void MFMController::process_input_bit(int value, unsigned int cycles_since_index_hole) { + if(data_mode_ == DataMode::Writing) return; + + shift_register_ = (shift_register_ << 1) | value; + bits_since_token_++; + + if(data_mode_ == DataMode::Scanning) { + Token::Type token_type = Token::Byte; + if(!is_double_density_) { + switch(shift_register_ & 0xffff) { + case Storage::Encodings::MFM::FMIndexAddressMark: + token_type = Token::Index; + crc_generator_.reset(); + crc_generator_.add(latest_token_.byte_value = Storage::Encodings::MFM::IndexAddressByte); + break; + case Storage::Encodings::MFM::FMIDAddressMark: + token_type = Token::ID; + crc_generator_.reset(); + crc_generator_.add(latest_token_.byte_value = Storage::Encodings::MFM::IDAddressByte); + break; + case Storage::Encodings::MFM::FMDataAddressMark: + token_type = Token::Data; + crc_generator_.reset(); + crc_generator_.add(latest_token_.byte_value = Storage::Encodings::MFM::DataAddressByte); + break; + case Storage::Encodings::MFM::FMDeletedDataAddressMark: + token_type = Token::DeletedData; + crc_generator_.reset(); + crc_generator_.add(latest_token_.byte_value = Storage::Encodings::MFM::DeletedDataAddressByte); + break; + default: + break; + } + } else { + switch(shift_register_ & 0xffff) { + case Storage::Encodings::MFM::MFMIndexSync: + bits_since_token_ = 0; + is_awaiting_marker_value_ = true; + + token_type = Token::Sync; + latest_token_.byte_value = Storage::Encodings::MFM::MFMIndexSyncByteValue; + break; + case Storage::Encodings::MFM::MFMSync: + bits_since_token_ = 0; + is_awaiting_marker_value_ = true; + crc_generator_.set_value(Storage::Encodings::MFM::MFMPostSyncCRCValue); + + token_type = Token::Sync; + latest_token_.byte_value = Storage::Encodings::MFM::MFMSyncByteValue; + break; + default: + break; + } + } + + if(token_type != Token::Byte) { + latest_token_.type = token_type; + bits_since_token_ = 0; + posit_event(Event::Token); + return; + } + } + + if(bits_since_token_ == 16) { + latest_token_.type = Token::Byte; + latest_token_.byte_value = (uint8_t)( + ((shift_register_ & 0x0001) >> 0) | + ((shift_register_ & 0x0004) >> 1) | + ((shift_register_ & 0x0010) >> 2) | + ((shift_register_ & 0x0040) >> 3) | + ((shift_register_ & 0x0100) >> 4) | + ((shift_register_ & 0x0400) >> 5) | + ((shift_register_ & 0x1000) >> 6) | + ((shift_register_ & 0x4000) >> 7)); + bits_since_token_ = 0; + + if(is_awaiting_marker_value_ && is_double_density_) { + is_awaiting_marker_value_ = false; + switch(latest_token_.byte_value) { + case Storage::Encodings::MFM::IndexAddressByte: + latest_token_.type = Token::Index; + break; + case Storage::Encodings::MFM::IDAddressByte: + latest_token_.type = Token::ID; + break; + case Storage::Encodings::MFM::DataAddressByte: + latest_token_.type = Token::Data; + break; + case Storage::Encodings::MFM::DeletedDataAddressByte: + latest_token_.type = Token::DeletedData; + break; + default: break; + } + } + + crc_generator_.add(latest_token_.byte_value); + posit_event(Event::Token); + return; + } +} diff --git a/Storage/Disk/MFMDiskController.hpp b/Storage/Disk/MFMDiskController.hpp new file mode 100644 index 000000000..27e0b86c1 --- /dev/null +++ b/Storage/Disk/MFMDiskController.hpp @@ -0,0 +1,80 @@ +// +// MFMDiskController.hpp +// Clock Signal +// +// Created by Thomas Harte on 05/08/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#ifndef MFMDiskController_hpp +#define MFMDiskController_hpp + +#include "DiskController.hpp" +#include "../../NumberTheory/CRC.hpp" + +namespace Storage { +namespace Disk { + +/*! + Extends Controller with a built-in shift register and FM/MFM decoding logic, + being able to post event messages to subclasses. +*/ +class MFMController: public Controller { + public: + MFMController(int clock_rate, int clock_rate_multiplier, int revolutions_per_minute); + + protected: + void set_is_double_density(bool); + bool get_is_double_density(); + + enum DataMode { + Scanning, + Reading, + Writing + }; + void set_data_mode(DataMode); + + struct Token { + enum Type { + Index, ID, Data, DeletedData, Sync, Byte + } type; + uint8_t byte_value; + }; + Token get_latest_token(); + + // Events + enum class Event: int { + Command = (1 << 0), // Indicates receipt of a new command. + Token = (1 << 1), // Indicates recognition of a new token in the flux stream. Use get_latest_token() for more details. + IndexHole = (1 << 2), // Indicates the passing of a physical index hole. + HeadLoad = (1 << 3), // Indicates the head has been loaded. + DataWritten = (1 << 4), // Indicates that all queued bits have been written + }; + virtual void posit_event(Event type) = 0; + + private: + // Storage::Disk::Controller + virtual void process_input_bit(int value, unsigned int cycles_since_index_hole); + virtual void process_index_hole(); + virtual void process_write_completed(); + + // PLL input state + int bits_since_token_; + int shift_register_; + bool is_awaiting_marker_value_; + + // input configuration + bool is_double_density_; + DataMode data_mode_; + + // output + Token latest_token_; + + // CRC generator + NumberTheory::CRC16 crc_generator_; +}; + +} +} + +#endif /* MFMDiskController_hpp */