1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-11 08:30:55 +00:00

Made a first stab at enabling multi-disk machines and thereby obeying (some of) the Plus 3's status register.

This commit is contained in:
Thomas Harte 2016-09-25 21:24:16 -04:00
parent 9bbcbd1001
commit 572d5587d9
14 changed files with 291 additions and 100 deletions

View File

@ -60,7 +60,7 @@ uint8_t WD1770::get_register(int address)
void WD1770::run_for_cycles(unsigned int number_of_cycles)
{
if(status_ & Flag::MotorOn) Storage::Disk::Controller::run_for_cycles((int)number_of_cycles);
Storage::Disk::Controller::run_for_cycles((int)number_of_cycles);
if(delay_time_)
{
@ -144,7 +144,11 @@ void WD1770::process_index_hole()
}
// motor power-down
if(index_hole_count_ == 9 && !(status_&Flag::Busy)) status_ &= ~Flag::MotorOn;
if(index_hole_count_ == 9 && !(status_&Flag::Busy))
{
status_ &= ~Flag::MotorOn;
set_motor_on(false);
}
}
// +------+----------+-------------------------+
@ -186,6 +190,7 @@ void WD1770::process_index_hole()
#define SPIN_UP() \
status_ |= Flag::MotorOn; \
set_motor_on(true); \
index_hole_count_ = 0; \
index_hole_count_target_ = 6; \
WAIT_FOR_EVENT(Event::IndexHoleTarget);

View File

@ -102,9 +102,17 @@ void Machine::set_rom(const uint8_t *rom)
memcpy(_rom, rom, sizeof(_rom));
}
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);
}
void Machine::run_for_cycles(int number_of_cycles)
{
CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles);
set_motor_on(_driveVIA.get_motor_enabled());
if(_driveVIA.get_motor_enabled()) // TODO: motor speed up/down
Storage::Disk::Controller::run_for_cycles(number_of_cycles);
}

View File

@ -232,6 +232,7 @@ class Machine:
void set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bus);
void run_for_cycles(int number_of_cycles);
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk);
// to satisfy CPU6502::Processor
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);

View File

@ -302,7 +302,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
break;
case 0xfc04: case 0xfc05: case 0xfc06: case 0xfc07:
if(_wd1770 && (address&0x00f0) == 0x00c0)
if(_plus3 && (address&0x00f0) == 0x00c0)
{
if(is_holding_shift_ && address == 0xfcc4)
{
@ -310,22 +310,17 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
set_key_state(KeyShift, false);
}
if(isReadOperation(operation))
*value = _wd1770->get_register(address);
*value = _plus3->get_register(address);
else
_wd1770->set_register(address, *value);
_plus3->set_register(address, *value);
}
break;
case 0xfc00:
if(_wd1770 && (address&0x00f0) == 0x00c0)
if(_plus3 && (address&0x00f0) == 0x00c0)
{
if(!isReadOperation(operation))
{
// TODO:
// bit 0 => enable or disable drive 1
// bit 1 => enable or disable drive 2
// bit 2 => side select
// bit 3 => single density select
// _wd1770->set_register(address, *value);
_plus3->set_control_register(*value);
}
}
break;
@ -478,7 +473,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
_tape.run_for_cycles(cycles);
if(_typer) _typer->update((int)cycles);
if(_wd1770) _wd1770->run_for_cycles(4*cycles);
if(_plus3) _plus3->run_for_cycles(4*cycles);
return cycles;
}
@ -498,7 +493,7 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target)
if(target.disks.size())
{
_wd1770.reset(new WD::WD1770);
_plus3.reset(new Plus3);
if(target.acorn.has_dfs)
{
@ -510,7 +505,7 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target)
set_rom(ROMSlot2, std::vector<uint8_t>(_adfs.begin() + 16384, _adfs.end()), true);
}
_wd1770->set_disk(target.disks.front());
_plus3->set_disk(target.disks.front(), 0);
}
ROMSlot slot = ROMSlot12;

View File

@ -10,12 +10,12 @@
#define Electron_hpp
#include "../../Processors/6502/CPU6502.hpp"
#include "../../Components/1770/1770.hpp"
#include "../../Storage/Tape/Tape.hpp"
#include "../ConfigurationTarget.hpp"
#include "../CRTMachine.hpp"
#include "../Typer.hpp"
#include "Plus3.hpp"
#include <cstdint>
#include <vector>
@ -232,7 +232,7 @@ class Machine:
bool _fast_load_is_in_data;
// Disk
std::unique_ptr<WD::WD1770> _wd1770;
std::unique_ptr<Plus3> _plus3;
bool is_holding_shift_;
// Outputs

View File

@ -0,0 +1,35 @@
//
// Plus3.cpp
// Clock Signal
//
// Created by Thomas Harte on 25/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Plus3.hpp"
using namespace Electron;
void Plus3::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive)
{
if(!_drives[drive]) _drives[drive].reset(new Storage::Disk::Drive);
_drives[drive]->set_disk(disk);
}
void Plus3::set_control_register(uint8_t control)
{
// TODO:
// bit 0 => enable or disable drive 1
// bit 1 => enable or disable drive 2
// bit 2 => side select
// bit 3 => single density select
switch(control&3)
{
case 0: set_drive(nullptr); break;
default: set_drive(_drives[0]); break;
case 2: set_drive(_drives[1]); break;
}
if(_drives[0]) _drives[0]->set_head((control & 0x04) ? 1 : 0);
if(_drives[1]) _drives[1]->set_head((control & 0x04) ? 1 : 0);
set_is_double_density(!(control & 0x08));
}

View File

@ -0,0 +1,28 @@
//
// Plus3.hpp
// Clock Signal
//
// Created by Thomas Harte on 25/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Plus3_hpp
#define Plus3_hpp
#include "../../Components/1770/1770.hpp"
namespace Electron {
class Plus3 : public WD::WD1770 {
public:
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive);
void set_control_register(uint8_t control);
private:
std::shared_ptr<Storage::Disk::Drive> _drives[2];
};
}
#endif /* Plus3_hpp */

View File

@ -26,6 +26,8 @@
4B2E2D951C399D1200138695 /* ElectronDocument.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B2E2D931C399D1200138695 /* ElectronDocument.xib */; };
4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D971C3A06EC00138695 /* Atari2600.cpp */; };
4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D9B1C3A070400138695 /* Electron.cpp */; };
4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B30512B1D989E2200B4FED8 /* Drive.cpp */; };
4B3051301D98ACC600B4FED8 /* Plus3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B30512E1D98ACC600B4FED8 /* Plus3.cpp */; };
4B37EE821D7345A6006A09A4 /* BinaryDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B37EE801D7345A6006A09A4 /* BinaryDump.cpp */; };
4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */; };
4B3BA0CE1D318B44005DD7A7 /* C1540Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C61D318B44005DD7A7 /* C1540Bridge.mm */; };
@ -423,6 +425,10 @@
4B2E2D991C3A06EC00138695 /* Atari2600Inputs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Atari2600Inputs.h; sourceTree = "<group>"; };
4B2E2D9B1C3A070400138695 /* Electron.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Electron.cpp; path = Electron/Electron.cpp; sourceTree = "<group>"; };
4B2E2D9C1C3A070400138695 /* Electron.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Electron.hpp; path = Electron/Electron.hpp; sourceTree = "<group>"; };
4B30512B1D989E2200B4FED8 /* Drive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Drive.cpp; sourceTree = "<group>"; };
4B30512C1D989E2200B4FED8 /* Drive.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Drive.hpp; sourceTree = "<group>"; };
4B30512E1D98ACC600B4FED8 /* Plus3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Plus3.cpp; path = Electron/Plus3.cpp; sourceTree = "<group>"; };
4B30512F1D98ACC600B4FED8 /* Plus3.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Plus3.hpp; path = Electron/Plus3.hpp; sourceTree = "<group>"; };
4B37EE801D7345A6006A09A4 /* BinaryDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BinaryDump.cpp; sourceTree = "<group>"; };
4B37EE811D7345A6006A09A4 /* BinaryDump.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BinaryDump.hpp; sourceTree = "<group>"; };
4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = C1540Tests.swift; sourceTree = "<group>"; };
@ -962,6 +968,8 @@
children = (
4B2E2D9B1C3A070400138695 /* Electron.cpp */,
4B2E2D9C1C3A070400138695 /* Electron.hpp */,
4B30512E1D98ACC600B4FED8 /* Plus3.cpp */,
4B30512F1D98ACC600B4FED8 /* Plus3.hpp */,
);
name = Electron;
sourceTree = "<group>";
@ -1116,10 +1124,12 @@
4B0BE4261D3481E700D5256B /* DigitalPhaseLockedLoop.cpp */,
4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */,
4B6C73BB1D387AE500AFCFCA /* DiskController.cpp */,
4B30512B1D989E2200B4FED8 /* Drive.cpp */,
4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */,
4B0BE4271D3481E700D5256B /* DigitalPhaseLockedLoop.hpp */,
4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */,
4B6C73BC1D387AE500AFCFCA /* DiskController.hpp */,
4B30512C1D989E2200B4FED8 /* Drive.hpp */,
4BAB62B71D3302CA00DF5BA0 /* PCMTrack.hpp */,
4BB697CF1D4BA44900248BDF /* Encodings */,
4BAB62B21D327F7E00DF5BA0 /* Formats */,
@ -2151,6 +2161,8 @@
4B2A53A31D117D36003C6002 /* CSVic20.mm in Sources */,
4B2A53A21D117D36003C6002 /* CSElectron.mm in Sources */,
4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */,
4B3051301D98ACC600B4FED8 /* Plus3.cpp in Sources */,
4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */,
4BA22B071D8817CE0008C640 /* Disk.cpp in Sources */,
4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */,
4B4C83701D4F623200CD541F /* D64.cpp in Sources */,

View File

@ -16,18 +16,21 @@ using namespace StaticAnalyser::Acorn;
class FMParser: public Storage::Disk::Controller {
public:
std::shared_ptr<Storage::Disk::Drive> drive;
FMParser(bool is_mfm) :
Storage::Disk::Controller(4000000, 1, 300),
crc_generator_(0x1021, 0xffff),
shift_register_(0), track_(0), is_mfm_(is_mfm)
{
// Make sure this drive really is at track '1'.
while(!get_is_track_zero()) step(-1);
Storage::Time bit_length;
bit_length.length = 1;
bit_length.clock_rate = is_mfm ? 500000 : 250000; // i.e. 250 kbps (including clocks)
set_expected_bit_length(bit_length);
drive.reset(new Storage::Disk::Drive);
set_drive(drive);
set_motor_on(true);
}
/*!
@ -184,7 +187,7 @@ std::unique_ptr<Catalogue> StaticAnalyser::Acorn::GetDFSCatalogue(const std::sha
// c.f. http://beebwiki.mdfs.net/Acorn_DFS_disc_format
std::unique_ptr<Catalogue> catalogue(new Catalogue);
FMParser parser(false);
parser.set_disk(disk);
parser.drive->set_disk(disk);
std::shared_ptr<Storage::Encodings::MFM::Sector> names = parser.get_sector(0, 0);
std::shared_ptr<Storage::Encodings::MFM::Sector> details = parser.get_sector(0, 1);
@ -246,7 +249,7 @@ std::unique_ptr<Catalogue> StaticAnalyser::Acorn::GetADFSCatalogue(const std::sh
{
std::unique_ptr<Catalogue> catalogue(new Catalogue);
FMParser parser(true);
parser.set_disk(disk);
parser.drive->set_disk(disk);
std::shared_ptr<Storage::Encodings::MFM::Sector> free_space_map_second_half = parser.get_sector(0, 1);
if(!free_space_map_second_half) return nullptr;

View File

@ -19,10 +19,12 @@ using namespace StaticAnalyser::Commodore;
class CommodoreGCRParser: public Storage::Disk::Controller {
public:
std::shared_ptr<Storage::Disk::Drive> drive;
CommodoreGCRParser() : Storage::Disk::Controller(4000000, 1, 300), shift_register_(0), track_(1)
{
// Make sure this drive really is at track '1'.
while(!get_is_track_zero()) step(-1);
drive.reset(new Storage::Disk::Drive);
set_drive(drive);
}
struct Sector
@ -186,7 +188,7 @@ std::list<File> StaticAnalyser::Commodore::GetFiles(const std::shared_ptr<Storag
{
std::list<File> files;
CommodoreGCRParser parser;
parser.set_disk(disk);
parser.drive->set_disk(disk);
// find any sector whatsoever to establish the current track
std::shared_ptr<CommodoreGCRParser::Sector> sector;

View File

@ -13,56 +13,25 @@ using namespace Storage::Disk;
Controller::Controller(unsigned int clock_rate, unsigned int clock_rate_multiplier, unsigned int revolutions_per_minute) :
_clock_rate(clock_rate * clock_rate_multiplier),
_clock_rate_multiplier(clock_rate_multiplier),
_head_position(0),
TimedEventLoop(clock_rate * clock_rate_multiplier)
{
_rotational_multiplier.length = 60;
_rotational_multiplier.clock_rate = revolutions_per_minute;
_rotational_multiplier.simplify();
// seed this class with a PLL, any PLL, so that it's safe to assume non-nullptr later
Time one;
set_expected_bit_length(one);
}
void Controller::set_expected_bit_length(Time bit_length)
void Controller::setup_track() // Time initial_offset
{
_bit_length = bit_length;
_track = _drive->get_track();
// _track = _disk->get_track_at_position(0, (unsigned int)_head_position);
// this conversion doesn't need to be exact because there's a lot of variation to be taken
// account of in rotation speed, air turbulence, etc, so a direct conversion will do
int clocks_per_bit = (int)((bit_length.length * _clock_rate) / bit_length.clock_rate);
_pll.reset(new DigitalPhaseLockedLoop(clocks_per_bit, clocks_per_bit / 5, 3));
_pll->set_delegate(this);
}
void Controller::set_disk(std::shared_ptr<Disk> disk)
{
_disk = disk;
set_track(Time());
}
bool Controller::has_disk()
{
return (bool)_disk;
}
bool Controller::get_is_track_zero()
{
return _head_position == 0;
}
void Controller::step(int direction)
{
_head_position = std::max(_head_position + direction, 0);
Time extra_time = get_time_into_next_event() / _rotational_multiplier;
extra_time.simplify();
_time_into_track += extra_time;
set_track(_time_into_track);
}
void Controller::set_track(Time initial_offset)
{
_track = _disk->get_track_at_position(0, (unsigned int)_head_position);
// TODO: probably a better implementation of the empty track?
Time offset;
/* Time offset;
if(_track && _time_into_track.length > 0)
{
Time time_found = _track->seek_to(_time_into_track).simplify();
@ -73,17 +42,18 @@ void Controller::set_track(Time initial_offset)
{
offset = _time_into_track;
_time_into_track.set_zero();
}
}*/
reset_timer();
get_next_event();
reset_timer_to_offset(offset * _rotational_multiplier);
// reset_timer_to_offset(offset * _rotational_multiplier);
}
void Controller::run_for_cycles(int number_of_cycles)
{
if(has_disk())
if(_drive && _drive->has_disk() && _motor_is_on)
{
if(!_track) setup_track();
number_of_cycles *= _clock_rate_multiplier;
while(number_of_cycles)
{
@ -132,9 +102,50 @@ void Controller::process_next_event()
get_next_event();
}
#pragma mark - PLL delegate
#pragma mark - PLL control and delegate
void Controller::set_expected_bit_length(Time bit_length)
{
_bit_length = bit_length;
// this conversion doesn't need to be exact because there's a lot of variation to be taken
// account of in rotation speed, air turbulence, etc, so a direct conversion will do
int clocks_per_bit = (int)((bit_length.length * _clock_rate) / bit_length.clock_rate);
_pll.reset(new DigitalPhaseLockedLoop(clocks_per_bit, clocks_per_bit / 5, 3));
_pll->set_delegate(this);
}
void Controller::digital_phase_locked_loop_output_bit(int value)
{
process_input_bit(value, _cycles_since_index_hole);
}
#pragma mark - Drive actions
bool Controller::get_is_track_zero()
{
if(!_drive) return false;
return _drive->get_is_track_zero();
}
void Controller::step(int direction)
{
if(_drive) _drive->step(direction);
invalidate_track();
}
void Controller::set_motor_on(bool motor_on)
{
_motor_is_on = motor_on;
}
void Controller::set_drive(std::shared_ptr<Drive> drive)
{
_drive = drive;
invalidate_track();
}
void Controller::invalidate_track()
{
_track = nullptr;
}

View File

@ -9,7 +9,7 @@
#ifndef Storage_Disk_Controller_hpp
#define Storage_Disk_Controller_hpp
#include "Disk.hpp"
#include "Drive.hpp"
#include "DigitalPhaseLockedLoop.hpp"
#include "../TimedEventLoop.hpp"
@ -17,18 +17,16 @@ namespace Storage {
namespace Disk {
/*!
Provides the shell for emulating a disk drive something that takes a disk and has a drive head
that steps between tracks, using a phase locked loop ('PLL') to decode a bit stream from the surface of
the disk.
Provides the shell for emulating a disk controller something that is connected to a disk drive and uses a
phase locked loop ('PLL') to decode a bit stream from the surface of the disk.
Partly abstract; it is expected that subclasses will provide methods to deal with receiving a newly-recognised
bit from the PLL and with crossing the index hole.
TODO: double sided disks, communication of head size and permissible stepping extents, appropriate
simulation of gain.
TODO: communication of head size and permissible stepping extents, appropriate simulation of gain.
*/
class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop {
public:
protected:
/*!
Constructs a @c DiskDrive that will be run at @c clock_rate and runs its PLL at @c clock_rate*clock_rate_multiplier,
spinning inserted disks at @c revolutions_per_minute.
@ -40,41 +38,22 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop
*/
void set_expected_bit_length(Time bit_length);
/*!
Inserts @c disk into the drive.
*/
void set_disk(std::shared_ptr<Disk> disk);
/*!
@returns @c true if a disk is currently inserted; @c false otherwise.
*/
bool has_disk();
/*!
Advances the drive by @c number_of_cycles cycles.
*/
void run_for_cycles(int number_of_cycles);
/*!
@returns @c true if the drive head is currently at track zero; @c false otherwise.
Sets the current drive.
*/
bool get_is_track_zero();
/*!
Steps the disk head the specified number of tracks. Positive numbers step inwards, negative numbers
step outwards.
*/
void step(int direction);
void set_drive(std::shared_ptr<Drive> drive);
void invalidate_track();
/*!
Enables or disables the disk motor.
*/
void set_motor_on(bool motor_on);
// to satisfy DigitalPhaseLockedLoop::Delegate
void digital_phase_locked_loop_output_bit(int value);
protected:
/*!
Should be implemented by subclasses; communicates each bit that the PLL recognises, also specifying
the amount of time since the index hole was last seen.
@ -89,6 +68,12 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop
// for TimedEventLoop
virtual void process_next_event();
// to satisfy DigitalPhaseLockedLoop::Delegate
void digital_phase_locked_loop_output_bit(int value);
bool get_is_track_zero();
void step(int direction);
private:
Time _bit_length;
unsigned int _clock_rate;
@ -96,15 +81,16 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop
Time _rotational_multiplier;
std::shared_ptr<DigitalPhaseLockedLoop> _pll;
std::shared_ptr<Disk> _disk;
std::shared_ptr<Drive> _drive;
std::shared_ptr<Track> _track;
int _head_position;
unsigned int _cycles_since_index_hole;
void set_track(Time initial_offset);
inline void get_next_event();
Track::Event _current_event;
Time _time_into_track;
bool _motor_is_on;
void setup_track();
};
}

46
Storage/Disk/Drive.cpp Normal file
View File

@ -0,0 +1,46 @@
//
// Drive.cpp
// Clock Signal
//
// Created by Thomas Harte on 25/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Drive.hpp"
#include <algorithm>
using namespace Storage::Disk;
Drive::Drive()
: _head_position(0), _head(0) {}
void Drive::set_disk(std::shared_ptr<Disk> disk)
{
_disk = disk;
}
bool Drive::has_disk()
{
return (bool)_disk;
}
bool Drive::get_is_track_zero()
{
return _head_position == 0;
}
void Drive::step(int direction)
{
_head_position = std::max(_head_position + direction, 0);
}
void Drive::set_head(unsigned int head)
{
_head = head;
}
std::shared_ptr<Track> Drive::get_track()
{
if(_disk) return _disk->get_track_at_position(_head, (unsigned int)_head_position);
return nullptr;
}

59
Storage/Disk/Drive.hpp Normal file
View File

@ -0,0 +1,59 @@
//
// Drive.hpp
// Clock Signal
//
// Created by Thomas Harte on 25/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Drive_hpp
#define Drive_hpp
#include <memory>
#include "Disk.hpp"
namespace Storage {
namespace Disk {
class Drive {
public:
Drive();
/*!
Inserts @c disk into the drive.
*/
void set_disk(std::shared_ptr<Disk> disk);
/*!
@returns @c true if a disk is currently inserted; @c false otherwise.
*/
bool has_disk();
/*!
@returns @c true if the drive head is currently at track zero; @c false otherwise.
*/
bool get_is_track_zero();
/*!
Steps the disk head the specified number of tracks. Positive numbers step inwards, negative numbers
step outwards.
*/
void step(int direction);
/*!
*/
void set_head(unsigned int head);
std::shared_ptr<Track> get_track();
private:
std::shared_ptr<Disk> _disk;
int _head_position;
unsigned int _head;
};
}
}
#endif /* Drive_hpp */