mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-09 05:25:01 +00:00
Edging towards implementing IWM write support, but mainly tidied up.
This commit is contained in:
@@ -105,7 +105,7 @@ uint8_t IWM::read(int address) {
|
|||||||
bit 7: 1 = write data buffer ready for data.
|
bit 7: 1 = write data buffer ready for data.
|
||||||
*/
|
*/
|
||||||
LOG("Reading write handshake");
|
LOG("Reading write handshake");
|
||||||
return 0x1f | 0x80 | 0x40;
|
return 0x1f | write_handshake_;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0xff;
|
return 0xff;
|
||||||
@@ -143,7 +143,7 @@ void IWM::write(int address, uint8_t input) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Q7|Q6|ENABLE: // Write data register.
|
case Q7|Q6|ENABLE: // Write data register.
|
||||||
LOG("Data register write\n");
|
LOG("Data register write");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,24 +233,34 @@ void IWM::run_for(const Cycles cycles) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// Activity otherwise depends on mode and motor state.
|
// Activity otherwise depends on mode and motor state.
|
||||||
const bool run_disk = drives_[active_drive_]; // drive_motor_on_ &&
|
|
||||||
int integer_cycles = cycles.as_int();
|
int integer_cycles = cycles.as_int();
|
||||||
switch(state_ & (Q6 | Q7 | ENABLE)) {
|
switch(state_ & (Q6 | Q7 | ENABLE)) {
|
||||||
case 0:
|
case 0:
|
||||||
case ENABLE: // i.e. read mode.
|
case ENABLE: // i.e. read mode.
|
||||||
while(integer_cycles--) {
|
if(drive_is_rotating_[active_drive_]) {
|
||||||
if(run_disk) {
|
while(integer_cycles--) {
|
||||||
drives_[active_drive_]->run_for(Cycles(1));
|
drives_[active_drive_]->run_for(Cycles(1));
|
||||||
|
++cycles_since_shift_;
|
||||||
|
if(cycles_since_shift_ == bit_length_ + Cycles(2)) {
|
||||||
|
propose_shift(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
++cycles_since_shift_;
|
} else {
|
||||||
if(cycles_since_shift_ == bit_length_ + Cycles(2)) {
|
while(cycles_since_shift_ + integer_cycles >= bit_length_ + Cycles(2)) {
|
||||||
propose_shift(0);
|
propose_shift(0);
|
||||||
|
integer_cycles -= bit_length_.as_int() + 2 - cycles_since_shift_.as_int();
|
||||||
}
|
}
|
||||||
|
cycles_since_shift_ += Cycles(integer_cycles);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Q6|Q7:
|
||||||
|
case Q6|Q7|ENABLE: // write mode?
|
||||||
|
printf("IWM write mode?\n");
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if(run_disk) drives_[active_drive_]->run_for(cycles);
|
if(drive_is_rotating_[active_drive_]) drives_[active_drive_]->run_for(cycles);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,4 +288,15 @@ void IWM::propose_shift(uint8_t bit) {
|
|||||||
void IWM::set_drive(int slot, IWMDrive *drive) {
|
void IWM::set_drive(int slot, IWMDrive *drive) {
|
||||||
drives_[slot] = drive;
|
drives_[slot] = drive;
|
||||||
drive->set_event_delegate(this);
|
drive->set_event_delegate(this);
|
||||||
|
drive->set_clocking_hint_observer(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IWM::set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) {
|
||||||
|
const bool is_rotating = clocking != ClockingHint::Preference::None;
|
||||||
|
|
||||||
|
if(component == static_cast<ClockingHint::Source *>(drives_[0])) {
|
||||||
|
drive_is_rotating_[0] = is_rotating;
|
||||||
|
} else {
|
||||||
|
drive_is_rotating_[1] = is_rotating;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#define IWM_hpp
|
#define IWM_hpp
|
||||||
|
|
||||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||||
|
#include "../../ClockReceiver/ClockingHintSource.hpp"
|
||||||
#include "../../Storage/Disk/Drive.hpp"
|
#include "../../Storage/Disk/Drive.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -37,11 +38,11 @@ struct IWMDrive: public Storage::Disk::Drive {
|
|||||||
virtual void set_enabled(bool) = 0;
|
virtual void set_enabled(bool) = 0;
|
||||||
virtual void set_control_lines(int) = 0;
|
virtual void set_control_lines(int) = 0;
|
||||||
virtual bool read() = 0;
|
virtual bool read() = 0;
|
||||||
virtual void write(bool value) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class IWM:
|
class IWM:
|
||||||
public Storage::Disk::Drive::EventDelegate {
|
public Storage::Disk::Drive::EventDelegate,
|
||||||
|
public ClockingHint::Observer {
|
||||||
public:
|
public:
|
||||||
IWM(int clock_rate);
|
IWM(int clock_rate);
|
||||||
|
|
||||||
@@ -81,8 +82,12 @@ class IWM:
|
|||||||
|
|
||||||
int active_drive_ = 0;
|
int active_drive_ = 0;
|
||||||
IWMDrive *drives_[2] = {nullptr, nullptr};
|
IWMDrive *drives_[2] = {nullptr, nullptr};
|
||||||
|
bool drive_is_rotating_[2] = {false, false};
|
||||||
|
|
||||||
|
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override;
|
||||||
|
|
||||||
Cycles cycles_until_motor_off_;
|
Cycles cycles_until_motor_off_;
|
||||||
|
uint8_t write_handshake_ = 0xc0;
|
||||||
|
|
||||||
void access(int address);
|
void access(int address);
|
||||||
|
|
||||||
|
@@ -8,6 +8,14 @@
|
|||||||
|
|
||||||
#include "MacintoshDoubleDensityDrive.hpp"
|
#include "MacintoshDoubleDensityDrive.hpp"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sources used pervasively:
|
||||||
|
|
||||||
|
http://members.iinet.net.au/~kalandi/apple/AUG/1991/11%20NOV.DEC/DISK.STUFF.html
|
||||||
|
Apple Guide to the Macintosh Family Hardware
|
||||||
|
Inside Macintosh III
|
||||||
|
*/
|
||||||
|
|
||||||
using namespace Apple::Macintosh;
|
using namespace Apple::Macintosh;
|
||||||
|
|
||||||
DoubleDensityDrive::DoubleDensityDrive(int input_clock_rate, bool is_800k) :
|
DoubleDensityDrive::DoubleDensityDrive(int input_clock_rate, bool is_800k) :
|
||||||
@@ -65,27 +73,27 @@ void DoubleDensityDrive::set_control_lines(int lines) {
|
|||||||
if((old_state ^ control_state_) & control_state_ & Line::LSTRB) {
|
if((old_state ^ control_state_) & control_state_ & Line::LSTRB) {
|
||||||
switch(control_state_ & (Line::CA1 | Line::CA0 | Line::SEL)) {
|
switch(control_state_ & (Line::CA1 | Line::CA0 | Line::SEL)) {
|
||||||
default:
|
default:
|
||||||
// LOG("Unhandled LSTRB");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0:
|
case 0: // Set step direction — CA2 set => step outward.
|
||||||
// LOG("LSTRB Set stepping direction: " << int(state_ & CA2));
|
|
||||||
step_direction_ = (control_state_ & Line::CA2) ? -1 : 1;
|
step_direction_ = (control_state_ & Line::CA2) ? -1 : 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Line::CA1:
|
case Line::CA1: // Set drive motor — CA2 set => motor off.
|
||||||
// LOG("LSTRB Motor");
|
|
||||||
set_motor_on(!(control_state_ & Line::CA2));
|
set_motor_on(!(control_state_ & Line::CA2));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Line::CA0:
|
case Line::CA0: // Initiate a step, if CA2 is clear.
|
||||||
// LOG("LSTRB Step");
|
if(!(control_state_ & Line::CA2))
|
||||||
step(Storage::Disk::HeadPosition(step_direction_));
|
step(Storage::Disk::HeadPosition(step_direction_));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Line::CA1 | Line::CA0:
|
case Line::SEL: // Reset has-been-ejected flag (if CA2 is set?)
|
||||||
// LOG("LSTRB Eject disk");
|
break;
|
||||||
set_disk(nullptr);
|
|
||||||
|
case Line::CA1 | Line::CA0: // Eject the disk if CA2 is set.
|
||||||
|
if(control_state_ & Line::CA2)
|
||||||
|
set_disk(nullptr); // TODO: should probably trigger the disk has been ejected bit?
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,75 +102,64 @@ void DoubleDensityDrive::set_control_lines(int lines) {
|
|||||||
bool DoubleDensityDrive::read() {
|
bool DoubleDensityDrive::read() {
|
||||||
switch(control_state_ & (CA2 | CA1 | CA0 | SEL)) {
|
switch(control_state_ & (CA2 | CA1 | CA0 | SEL)) {
|
||||||
default:
|
default:
|
||||||
// LOG("unknown)");
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Possible other meanings:
|
|
||||||
// B = ready (0 = ready)
|
|
||||||
// C = disk switched?
|
|
||||||
//
|
|
||||||
// {CA1,CA0,SEL,CA2}
|
|
||||||
|
|
||||||
case 0: // Head step direction.
|
case 0: // Head step direction.
|
||||||
// (0 = inward)
|
// (0 = inward)
|
||||||
// LOG("head step direction)");
|
|
||||||
return step_direction_ <= 0;
|
return step_direction_ <= 0;
|
||||||
|
|
||||||
case SEL: // Disk in place.
|
case SEL: // Disk in place.
|
||||||
// (0 = disk present)
|
// (0 = disk present)
|
||||||
// LOG("disk in place)");
|
|
||||||
return !has_disk();
|
return !has_disk();
|
||||||
|
|
||||||
case CA0: // Disk head step completed.
|
case CA0: // Disk head step completed.
|
||||||
// (0 = still stepping)
|
// (0 = still stepping)
|
||||||
// LOG("head stepping)");
|
|
||||||
return true; // TODO: stepping delay. But at the main Drive level.
|
return true; // TODO: stepping delay. But at the main Drive level.
|
||||||
|
|
||||||
case CA0|SEL: // Disk locked.
|
case CA0|SEL: // Disk locked.
|
||||||
// (0 = write protected)
|
// (0 = write protected)
|
||||||
// LOG("disk locked)");
|
|
||||||
return !get_is_read_only();
|
return !get_is_read_only();
|
||||||
|
|
||||||
case CA1: // Disk motor running.
|
case CA1: // Disk motor running.
|
||||||
// (0 = motor on)
|
// (0 = motor on)
|
||||||
// LOG("disk motor running)");
|
|
||||||
return !get_motor_on();
|
return !get_motor_on();
|
||||||
|
|
||||||
case CA1|SEL: // Head at track 0.
|
case CA1|SEL: // Head at track 0.
|
||||||
// (0 = at track 0)
|
// (0 = at track 0)
|
||||||
// LOG("head at track 0)");
|
// "This bit becomes valid beginning 12 msec after the step that places the head at track 0."
|
||||||
return !get_is_track_zero();
|
return !get_is_track_zero();
|
||||||
|
|
||||||
|
case CA1|CA0: // Disk has been ejected.
|
||||||
|
// (0 = user has ejected disk)
|
||||||
|
return false;
|
||||||
|
|
||||||
case CA1|CA0|SEL: // Tachometer.
|
case CA1|CA0|SEL: // Tachometer.
|
||||||
// (arbitrary)
|
// (arbitrary)
|
||||||
return get_tachometer();
|
return get_tachometer();
|
||||||
|
|
||||||
case CA2: // Read data, lower head.
|
case CA2: // Read data, lower head.
|
||||||
// LOG("data, lower head)\n");
|
|
||||||
set_head(0);
|
set_head(0);
|
||||||
return false;;
|
return false;
|
||||||
|
|
||||||
case CA2|SEL: // Read data, upper head.
|
case CA2|SEL: // Read data, upper head.
|
||||||
// LOG("data, upper head)\n");
|
|
||||||
set_head(1);
|
set_head(1);
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case CA2|CA1: // Single- or double-sided drive.
|
case CA2|CA1: // Single- or double-sided drive.
|
||||||
// (0 = single sided)
|
// (0 = single sided)
|
||||||
// LOG("single- or double-sided drive)");
|
|
||||||
return get_head_count() != 1;
|
return get_head_count() != 1;
|
||||||
|
|
||||||
case CA2|CA1|CA0: // "Present/HD" (per the Mac Plus ROM)
|
case CA2|CA1|CA0: // "Present/HD" (per the Mac Plus ROM)
|
||||||
// (0 = ??HD??)
|
// (0 = ??HD??)
|
||||||
// LOG("present/HD)");
|
//
|
||||||
|
// Alternative explanation: "Disk ready for reading?"
|
||||||
|
// (0 = ready)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case CA2|CA1|CA0|SEL: // Drive installed.
|
case CA2|CA1|CA0|SEL: // Drive installed.
|
||||||
// (0 = present, 1 = missing)
|
// (0 = present, 1 = missing)
|
||||||
// LOG("drive installed)");
|
//
|
||||||
|
// TODO: why do I need to return this the wrong way around for the Mac Plus?
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoubleDensityDrive::write(bool value) {
|
|
||||||
}
|
|
||||||
|
@@ -21,7 +21,6 @@ class DoubleDensityDrive: public IWMDrive {
|
|||||||
void set_enabled(bool) override;
|
void set_enabled(bool) override;
|
||||||
void set_control_lines(int) override;
|
void set_control_lines(int) override;
|
||||||
bool read() override;
|
bool read() override;
|
||||||
void write(bool value) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// To receive the proper notifications from Storage::Disk::Drive.
|
// To receive the proper notifications from Storage::Disk::Drive.
|
||||||
|
Reference in New Issue
Block a user