1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 01:31:42 +00:00

Eliminates the fiction of setting and getting registers.

The Disk II seems lower level than that; it will read the data bus whenever it likes, it is the programmer's responsibility to keep up with that. It also reserves the right not to load the bus regardless of whether it receives a read or write access.
This commit is contained in:
Thomas Harte 2018-05-17 21:39:11 -04:00
parent c46007332a
commit 7b7beb13a3
5 changed files with 71 additions and 64 deletions

View File

@ -39,11 +39,9 @@ void DiskII::set_control(Control control, bool on) {
case Control::Motor: case Control::Motor:
motor_is_enabled_ = on; motor_is_enabled_ = on;
drives_[active_drive_].set_motor_on(on); drives_[active_drive_].set_motor_on(on);
break; return;
} }
// printf("%0x: Set control %d %s\n", stepper_mask_, control, on ? "on" : "off");
// If the stepper magnet selections have changed, and any is on, see how // If the stepper magnet selections have changed, and any is on, see how
// that moves the head. // that moves the head.
if(previous_stepper_mask ^ stepper_mask_ && stepper_mask_) { if(previous_stepper_mask ^ stepper_mask_ && stepper_mask_) {
@ -63,14 +61,7 @@ void DiskII::set_control(Control control, bool on) {
} }
} }
void DiskII::set_mode(Mode mode) {
// printf("Set mode %d\n", mode);
inputs_ = (inputs_ & ~input_mode) | ((mode == Mode::Write) ? input_mode : 0);
set_controller_can_sleep();
}
void DiskII::select_drive(int drive) { void DiskII::select_drive(int drive) {
// printf("Select drive %d\n", drive);
if((drive&1) == active_drive_) return; if((drive&1) == active_drive_) return;
drives_[active_drive_].set_event_delegate(this); drives_[active_drive_].set_event_delegate(this);
@ -81,20 +72,6 @@ void DiskII::select_drive(int drive) {
drives_[active_drive_].set_motor_on(motor_is_enabled_); drives_[active_drive_].set_motor_on(motor_is_enabled_);
} }
void DiskII::set_data_register(uint8_t value) {
// printf("Set data register (?)\n");
inputs_ |= input_command;
shift_register_ = value;
set_controller_can_sleep();
}
uint8_t DiskII::get_shift_register() {
// if(shift_register_ & 0x80) printf("[%02x] ", shift_register_);
inputs_ &= ~input_command;
set_controller_can_sleep();
return shift_register_;
}
void DiskII::run_for(const Cycles cycles) { void DiskII::run_for(const Cycles cycles) {
if(is_sleeping()) return; if(is_sleeping()) return;
@ -124,11 +101,7 @@ void DiskII::run_for(const Cycles cycles) {
return; return;
} }
break; break;
case 0xb: case 0xb: shift_register_ = data_input_; break; // load data register from data bus
// load data register from data bus...
printf("TODO\n");
// shift_register_ = data_register_;
break; // load
} }
// TODO: surely there's a less heavyweight solution than this? // TODO: surely there's a less heavyweight solution than this?
@ -221,15 +194,11 @@ bool DiskII::is_sleeping() {
return controller_can_sleep_ && drive_is_sleeping_[0] && drive_is_sleeping_[1]; return controller_can_sleep_ && drive_is_sleeping_[0] && drive_is_sleeping_[1];
} }
void DiskII::set_register(int address, uint8_t value) { void DiskII::set_data_input(uint8_t input) {
trigger_address(address, value); data_input_ = input;
} }
uint8_t DiskII::get_register(int address) { int DiskII::read_address(int address) {
return trigger_address(address, 0xff);
}
uint8_t DiskII::trigger_address(int address, uint8_t value) {
switch(address & 0xf) { switch(address & 0xf) {
default: default:
case 0x0: set_control(Control::P0, false); break; case 0x0: set_control(Control::P0, false); break;
@ -241,22 +210,19 @@ uint8_t DiskII::trigger_address(int address, uint8_t value) {
case 0x6: set_control(Control::P3, false); break; case 0x6: set_control(Control::P3, false); break;
case 0x7: set_control(Control::P3, true); break; case 0x7: set_control(Control::P3, true); break;
case 0x8: set_control(Control::Motor, false); break; case 0x8:
shift_register_ = 0;
set_control(Control::Motor, false);
break;
case 0x9: set_control(Control::Motor, true); break; case 0x9: set_control(Control::Motor, true); break;
case 0xa: select_drive(0); break; case 0xa: select_drive(0); break;
case 0xb: select_drive(1); break; case 0xb: select_drive(1); break;
case 0xc: case 0xc: inputs_ &= ~input_command; break;
inputs_ &= ~input_command; case 0xd: inputs_ |= input_command; break;
break; case 0xe: inputs_ &= ~input_mode; break;
case 0xd: set_data_register(value); break; case 0xf: inputs_ |= input_mode; break;
case 0xe:
set_mode(Mode::Read);
break;
// return shift_register_;
case 0xf: set_mode(Mode::Write); break;
} }
set_controller_can_sleep(); set_controller_can_sleep();
return (address & 1) ? 0xff : shift_register_; return (address & 1) ? 0xff : shift_register_;

View File

@ -33,15 +33,48 @@ class DiskII:
public: public:
DiskII(); DiskII();
void set_register(int address, uint8_t value); /// Sets the current external value of the data bus.
uint8_t get_register(int address); 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.
*/
const int DidNotLoad = -1;
/// Advances the controller by @c cycles.
void run_for(const Cycles 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.
*/
void set_state_machine(const std::vector<uint8_t> &); 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); void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive);
// As per Sleeper.
bool is_sleeping() override; bool is_sleeping() override;
// The Disk II functions as a potential target for @c Activity::Sources.
void set_activity_observer(Activity::Observer *observer); void set_activity_observer(Activity::Observer *observer);
private: private:
@ -55,8 +88,6 @@ class DiskII:
void set_control(Control control, bool on); void set_control(Control control, bool on);
void set_mode(Mode mode); void set_mode(Mode mode);
void select_drive(int drive); void select_drive(int drive);
void set_data_register(uint8_t value);
uint8_t get_shift_register();
uint8_t trigger_address(int address, uint8_t value); uint8_t trigger_address(int address, uint8_t value);
void process_event(const Storage::Disk::Track::Event &event) override; void process_event(const Storage::Disk::Track::Event &event) override;
@ -78,6 +109,8 @@ class DiskII:
bool motor_is_enabled_ = false; bool motor_is_enabled_ = false;
void set_controller_can_sleep(); void set_controller_can_sleep();
uint8_t data_input_ = 0;
}; };
} }

View File

@ -25,10 +25,12 @@ void DiskIICard::perform_bus_operation(CPU::MOS6502::BusOperation operation, uin
if(address < 0x100) { if(address < 0x100) {
if(isReadOperation(operation)) *value = boot_[address]; if(isReadOperation(operation)) *value = boot_[address];
} else { } else {
// TODO: data input really shouldn't happen only upon a write.
diskii_.set_data_input(*value);
const int disk_value = diskii_.read_address(address);
if(isReadOperation(operation)) { if(isReadOperation(operation)) {
*value = diskii_.get_register(address); if(disk_value != diskii_.DidNotLoad)
} else { *value = static_cast<uint8_t>(disk_value);
diskii_.set_register(address, *value);
} }
} }
} }

View File

@ -405,9 +405,8 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
} }
} }
} else { } else {
update_diskii(); const int disk_value = diskii_.read_address(address);
if(isReadOperation(operation)) *value = diskii_.get_register(address); if(isReadOperation(operation) && disk_value != diskii_.DidNotLoad) *value = static_cast<uint8_t>(disk_value);
else diskii_.set_register(address, *value);
} }
break; break;
} }
@ -436,8 +435,13 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
tape_player_.run_for(Cycles(1)); tape_player_.run_for(Cycles(1));
switch(disk_interface) { switch(disk_interface) {
default: break; default: break;
case Analyser::Static::Oric::Target::DiskInterface::Microdisc: microdisc_.run_for(Cycles(8)); break; case Analyser::Static::Oric::Target::DiskInterface::Microdisc:
case Analyser::Static::Oric::Target::DiskInterface::Pravetz: cycles_since_diskii_update_ += 2; break; microdisc_.run_for(Cycles(8));
break;
case Analyser::Static::Oric::Target::DiskInterface::Pravetz:
diskii_.set_data_input(*value);
diskii_.run_for(Cycles(2));
break;
} }
cycles_since_video_update_++; cycles_since_video_update_++;
return Cycles(1); return Cycles(1);
@ -446,7 +450,6 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
forceinline void flush() { forceinline void flush() {
update_video(); update_video();
via_port_handler_.flush(); via_port_handler_.flush();
if(disk_interface == Analyser::Static::Oric::Target::DiskInterface::Pravetz) update_diskii();
} }
// to satisfy CRTMachine::Machine // to satisfy CRTMachine::Machine
@ -605,10 +608,10 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
Apple::DiskII diskii_; Apple::DiskII diskii_;
std::vector<uint8_t> pravetz_rom_; std::vector<uint8_t> pravetz_rom_;
std::size_t pravetz_rom_base_pointer_ = 0; std::size_t pravetz_rom_base_pointer_ = 0;
Cycles cycles_since_diskii_update_; // Cycles cycles_since_diskii_update_;
void update_diskii() { // void update_diskii() {
diskii_.run_for(cycles_since_diskii_update_.flush()); // diskii_.run_for(cycles_since_diskii_update_.flush());
} // }
// Overlay RAM // Overlay RAM
uint16_t ram_top_ = basic_visible_ram_top_; uint16_t ram_top_ = basic_visible_ram_top_;

View File

@ -35,6 +35,9 @@ class AppleDSK: public DiskImage {
HeadPosition get_maximum_head_position() override; HeadPosition get_maximum_head_position() override;
std::shared_ptr<Track> get_track_at_position(Track::Address address) override; std::shared_ptr<Track> get_track_at_position(Track::Address address) override;
// TEST!
bool get_is_read_only() override { return false; }
private: private:
Storage::FileHolder file_; Storage::FileHolder file_;
int sectors_per_track_ = 16; int sectors_per_track_ = 16;