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:
parent
9bbcbd1001
commit
572d5587d9
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
35
Machines/Electron/Plus3.cpp
Normal file
35
Machines/Electron/Plus3.cpp
Normal 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));
|
||||
}
|
28
Machines/Electron/Plus3.hpp
Normal file
28
Machines/Electron/Plus3.hpp
Normal 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 */
|
||||
|
@ -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 */,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
46
Storage/Disk/Drive.cpp
Normal 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
59
Storage/Disk/Drive.hpp
Normal 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 */
|
Loading…
x
Reference in New Issue
Block a user