mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-23 03:32:32 +00:00
171 lines
5.8 KiB
C++
171 lines
5.8 KiB
C++
//
|
|
// 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 "../../../Numeric/CRC.hpp"
|
|
#include "../../../ClockReceiver/ClockReceiver.hpp"
|
|
#include "../Encodings/MFM/Shifter.hpp"
|
|
|
|
namespace Storage::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(Cycles clock_rate);
|
|
|
|
protected:
|
|
/// Indicates whether the controller should try to decode double-density MFM content, or single-density FM content.
|
|
void set_is_double_density(bool);
|
|
|
|
/// @returns @c true if currently decoding MFM content; @c false otherwise.
|
|
bool get_is_double_density();
|
|
|
|
enum DataMode {
|
|
/// When the controller is scanning it will obey all synchronisation marks found, even if in the middle of data.
|
|
Scanning,
|
|
/// When the controller is reading it will ignore synchronisation marks and simply return a new token every sixteen PLL clocks.
|
|
Reading,
|
|
/// When the controller is writing, it will replace the underlying data with that which has been enqueued, posting Event::DataWritten when the queue is empty.
|
|
Writing
|
|
};
|
|
/// Sets the current data mode.
|
|
void set_data_mode(DataMode);
|
|
|
|
/*!
|
|
Describes a token found in the incoming PLL bit stream. Tokens can be one of:
|
|
|
|
Index: the bit pattern usually encoded at the start of a track to denote the position of the index hole;
|
|
ID: the pattern that begins an ID section, i.e. a sector header, announcing sector number, track number, etc.
|
|
Data: the pattern that begins a data section, i.e. sector contents.
|
|
DeletedData: the pattern that begins a deleted data section, i.e. deleted sector contents.
|
|
|
|
Sync: MFM only; the same synchronisation mark is used in MFM to denote the bottom three of the four types
|
|
of token listed above; this class combines notification of that mark and the distinct index sync mark.
|
|
Both are followed by a byte to indicate type. When scanning an MFM stream, subclasses will receive an
|
|
announcement of sync followed by an announcement of one of the above four types of token.
|
|
|
|
Byte: reports reading of an ordinary byte, with expected timing bits.
|
|
|
|
When the data mode is set to 'reading', only Byte tokens are returned; detection of the other kinds of token
|
|
is suppressed. Controllers will likely want to switch data mode when receiving ID and sector contents, as
|
|
spurious sync signals can otherwise be found in ordinary data, causing framing errors.
|
|
*/
|
|
struct Token {
|
|
enum Type {
|
|
Index, ID, Data, DeletedData, Sync, Byte
|
|
} type;
|
|
uint8_t byte_value;
|
|
};
|
|
/// @returns The most-recently read token from the surface of the disk.
|
|
Token get_latest_token();
|
|
|
|
/// @returns The controller's CRC generator. This is automatically fed during reading.
|
|
CRC::CCITT &get_crc_generator();
|
|
|
|
// Events
|
|
enum class Event: int {
|
|
Token = (1 << 0), // Indicates recognition of a new token in the flux stream. Use get_latest_token() for more details.
|
|
IndexHole = (1 << 1), // Indicates the passing of a physical index hole.
|
|
DataWritten = (1 << 2), // Indicates that all queued bits have been written
|
|
};
|
|
|
|
/*!
|
|
Subclasses should implement this. It is called every time a new @c Event is discovered in the incoming data stream.
|
|
Therefore it is called to announce when:
|
|
|
|
(i) a new token is discovered in the incoming stream: an index, ID, data or deleted data, a sync mark or a new byte of data.
|
|
(ii) the index hole passes; or
|
|
(iii) the queue of data to be written has been exhausted.
|
|
*/
|
|
virtual void posit_event(int type) = 0;
|
|
|
|
/*!
|
|
Encodes @c bit according to the current single/double density mode and adds it
|
|
to the controller's write buffer.
|
|
*/
|
|
void write_bit(int bit);
|
|
|
|
/*!
|
|
Encodes @c byte according to the current single/double density mode and adds it
|
|
to the controller's write buffer.
|
|
*/
|
|
void write_byte(uint8_t byte);
|
|
|
|
/*!
|
|
Serialises @c value into the controller's write buffer without adjustment.
|
|
*/
|
|
void write_raw_short(uint16_t value);
|
|
|
|
/*!
|
|
Gets the current value of the CRC generator and makes two calls to @c write_byte, to
|
|
write first its higher-value byte and then its lower.
|
|
*/
|
|
void write_crc();
|
|
|
|
/*!
|
|
Calls @c write_byte with @c value, @c quantity times.
|
|
*/
|
|
void write_n_bytes(int quantity, uint8_t value);
|
|
|
|
/*!
|
|
Writes everything that should per the spec appear prior to the address contained
|
|
in an ID mark (i.e. proper gaps and the ID mark) and appropriate seeds the CRC generator.
|
|
*/
|
|
void write_id_joiner();
|
|
|
|
/*!
|
|
Writes at most what should, per the spec, appear after the ID's CRC, up to and
|
|
including the mark that indicates the beginning of data, appropriately seeding
|
|
the CRC generator; if @c skip_first_gap is set then the initial gap after the
|
|
CRC isn't written.
|
|
*/
|
|
void write_id_data_joiner(bool is_deleted, bool skip_first_gap);
|
|
|
|
/*!
|
|
Writes the gap expected after a sector's data CRC and before the beginning of the
|
|
next ID joiner.
|
|
*/
|
|
void write_post_data_gap();
|
|
|
|
/*!
|
|
Writes everything that should, per the spec, following the index hole and prior
|
|
to any sectors.
|
|
*/
|
|
void write_start_of_track();
|
|
|
|
private:
|
|
// Storage::Disk::Controller
|
|
virtual void process_input_bit(int value);
|
|
virtual void process_index_hole();
|
|
virtual void process_write_completed();
|
|
|
|
// Reading state.
|
|
Token latest_token_;
|
|
Encodings::MFM::Shifter shifter_;
|
|
|
|
// input configuration
|
|
bool is_double_density_;
|
|
DataMode data_mode_ = DataMode::Scanning;
|
|
|
|
// writing
|
|
int last_bit_;
|
|
|
|
// CRC generator
|
|
CRC::CCITT crc_generator_;
|
|
};
|
|
|
|
}
|
|
|
|
#endif /* MFMDiskController_hpp */
|