mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-12 08:30:05 +00:00
128 lines
3.3 KiB
C++
128 lines
3.3 KiB
C++
//
|
|
// DiskII.hpp
|
|
// Clock Signal
|
|
//
|
|
// Created by Thomas Harte on 20/04/2018.
|
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
#pragma once
|
|
|
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
|
#include "../../ClockReceiver/ClockingHintSource.hpp"
|
|
|
|
#include "../../Storage/Disk/Disk.hpp"
|
|
#include "../../Storage/Disk/Drive.hpp"
|
|
|
|
#include "../../Activity/Observer.hpp"
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
namespace Apple {
|
|
|
|
/*!
|
|
Provides an emulation of the Apple Disk II.
|
|
*/
|
|
class DiskII :
|
|
public Storage::Disk::Drive::EventDelegate,
|
|
public ClockingHint::Source,
|
|
public ClockingHint::Observer {
|
|
public:
|
|
DiskII(int clock_rate);
|
|
|
|
/// Sets the current external value of the data bus.
|
|
void set_data_input(uint8_t input);
|
|
|
|
/*!
|
|
Submits an access to address @c address.
|
|
|
|
@returns The 8-bit value loaded to the data bus by the DiskII if any;
|
|
@c DidNotLoad otherwise.
|
|
*/
|
|
int read_address(int address);
|
|
|
|
/*!
|
|
The value returned by @c read_address if accessing that address
|
|
didn't cause the disk II to place anything onto the bus.
|
|
*/
|
|
static constexpr int DidNotLoad = -1;
|
|
|
|
/// Advances the controller by @c cycles.
|
|
void run_for(const Cycles cycles);
|
|
|
|
/*!
|
|
Supplies the image of the state machine (i.e. P6) ROM,
|
|
which dictates how the Disk II will respond to input.
|
|
|
|
To reduce processing costs, some assumptions are made by
|
|
the implementation as to the content of this ROM.
|
|
Including:
|
|
|
|
* If Q6 is set and Q7 is reset, the controller is testing
|
|
for write protect. If and when the shift register has
|
|
become full with the state of the write protect value,
|
|
no further processing is required.
|
|
|
|
* If both Q6 and Q7 are reset, the drive motor is disabled,
|
|
and the shift register is all zeroes, no further processing
|
|
is required.
|
|
*/
|
|
void set_state_machine(const std::vector<uint8_t> &);
|
|
|
|
/// Inserts @c disk into the drive @c drive.
|
|
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive);
|
|
|
|
// As per Sleeper.
|
|
ClockingHint::Preference preferred_clocking() const final;
|
|
|
|
// The Disk II functions as a potential target for @c Activity::Sources.
|
|
void set_activity_observer(Activity::Observer *observer);
|
|
|
|
// Returns the Storage::Disk::Drive in use for drive @c index.
|
|
// *NOT FOR HARDWARE EMULATION USAGE*.
|
|
Storage::Disk::Drive &get_drive(int index);
|
|
|
|
private:
|
|
enum class Control {
|
|
P0, P1, P2, P3,
|
|
Motor,
|
|
};
|
|
enum class Mode {
|
|
Read, Write
|
|
};
|
|
void set_control(Control control, bool on);
|
|
void set_mode(Mode mode);
|
|
void select_drive(int drive);
|
|
|
|
uint8_t trigger_address(int address, uint8_t value);
|
|
void process_event(const Storage::Disk::Drive::Event &event) final;
|
|
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference preference) final;
|
|
|
|
const Cycles::IntType clock_rate_ = 0;
|
|
|
|
uint8_t state_ = 0;
|
|
uint8_t inputs_ = 0;
|
|
uint8_t shift_register_ = 0;
|
|
|
|
int stepper_mask_ = 0;
|
|
int stepper_position_ = 0;
|
|
Cycles::IntType motor_off_time_ = -1;
|
|
|
|
bool is_write_protected() const;
|
|
std::array<uint8_t, 256> state_machine_;
|
|
Storage::Disk::Drive drives_[2];
|
|
bool drive_is_sleeping_[2];
|
|
int active_drive_ = 0;
|
|
bool motor_is_enabled_ = false;
|
|
|
|
void decide_clocking_preference();
|
|
ClockingHint::Preference clocking_preference_ = ClockingHint::Preference::RealTime;
|
|
|
|
uint8_t data_input_ = 0;
|
|
int flux_duration_ = 0;
|
|
};
|
|
|
|
}
|