mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-23 20:29: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:
parent
c46007332a
commit
7b7beb13a3
@ -39,11 +39,9 @@ void DiskII::set_control(Control control, bool on) {
|
||||
case Control::Motor:
|
||||
motor_is_enabled_ = 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
|
||||
// that moves the head.
|
||||
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) {
|
||||
// printf("Select drive %d\n", drive);
|
||||
if((drive&1) == active_drive_) return;
|
||||
|
||||
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_);
|
||||
}
|
||||
|
||||
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) {
|
||||
if(is_sleeping()) return;
|
||||
|
||||
@ -124,11 +101,7 @@ void DiskII::run_for(const Cycles cycles) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0xb:
|
||||
// load data register from data bus...
|
||||
printf("TODO\n");
|
||||
// shift_register_ = data_register_;
|
||||
break; // load
|
||||
case 0xb: shift_register_ = data_input_; break; // load data register from data bus
|
||||
}
|
||||
|
||||
// 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];
|
||||
}
|
||||
|
||||
void DiskII::set_register(int address, uint8_t value) {
|
||||
trigger_address(address, value);
|
||||
void DiskII::set_data_input(uint8_t input) {
|
||||
data_input_ = input;
|
||||
}
|
||||
|
||||
uint8_t DiskII::get_register(int address) {
|
||||
return trigger_address(address, 0xff);
|
||||
}
|
||||
|
||||
uint8_t DiskII::trigger_address(int address, uint8_t value) {
|
||||
int DiskII::read_address(int address) {
|
||||
switch(address & 0xf) {
|
||||
default:
|
||||
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 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 0xa: select_drive(0); break;
|
||||
case 0xb: select_drive(1); break;
|
||||
|
||||
case 0xc:
|
||||
inputs_ &= ~input_command;
|
||||
break;
|
||||
case 0xd: set_data_register(value); break;
|
||||
|
||||
case 0xe:
|
||||
set_mode(Mode::Read);
|
||||
break;
|
||||
// return shift_register_;
|
||||
case 0xf: set_mode(Mode::Write); break;
|
||||
case 0xc: inputs_ &= ~input_command; break;
|
||||
case 0xd: inputs_ |= input_command; break;
|
||||
case 0xe: inputs_ &= ~input_mode; break;
|
||||
case 0xf: inputs_ |= input_mode; break;
|
||||
}
|
||||
set_controller_can_sleep();
|
||||
return (address & 1) ? 0xff : shift_register_;
|
||||
|
@ -33,15 +33,48 @@ class DiskII:
|
||||
public:
|
||||
DiskII();
|
||||
|
||||
void set_register(int address, uint8_t value);
|
||||
uint8_t get_register(int address);
|
||||
/// 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.
|
||||
*/
|
||||
const 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.
|
||||
*/
|
||||
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.
|
||||
bool is_sleeping() override;
|
||||
|
||||
// The Disk II functions as a potential target for @c Activity::Sources.
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
private:
|
||||
@ -55,8 +88,6 @@ class DiskII:
|
||||
void set_control(Control control, bool on);
|
||||
void set_mode(Mode mode);
|
||||
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);
|
||||
void process_event(const Storage::Disk::Track::Event &event) override;
|
||||
@ -78,6 +109,8 @@ class DiskII:
|
||||
bool motor_is_enabled_ = false;
|
||||
|
||||
void set_controller_can_sleep();
|
||||
|
||||
uint8_t data_input_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -25,10 +25,12 @@ void DiskIICard::perform_bus_operation(CPU::MOS6502::BusOperation operation, uin
|
||||
if(address < 0x100) {
|
||||
if(isReadOperation(operation)) *value = boot_[address];
|
||||
} 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)) {
|
||||
*value = diskii_.get_register(address);
|
||||
} else {
|
||||
diskii_.set_register(address, *value);
|
||||
if(disk_value != diskii_.DidNotLoad)
|
||||
*value = static_cast<uint8_t>(disk_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -405,9 +405,8 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
}
|
||||
}
|
||||
} else {
|
||||
update_diskii();
|
||||
if(isReadOperation(operation)) *value = diskii_.get_register(address);
|
||||
else diskii_.set_register(address, *value);
|
||||
const int disk_value = diskii_.read_address(address);
|
||||
if(isReadOperation(operation) && disk_value != diskii_.DidNotLoad) *value = static_cast<uint8_t>(disk_value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -436,8 +435,13 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
tape_player_.run_for(Cycles(1));
|
||||
switch(disk_interface) {
|
||||
default: break;
|
||||
case Analyser::Static::Oric::Target::DiskInterface::Microdisc: microdisc_.run_for(Cycles(8)); break;
|
||||
case Analyser::Static::Oric::Target::DiskInterface::Pravetz: cycles_since_diskii_update_ += 2; break;
|
||||
case Analyser::Static::Oric::Target::DiskInterface::Microdisc:
|
||||
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_++;
|
||||
return Cycles(1);
|
||||
@ -446,7 +450,6 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
forceinline void flush() {
|
||||
update_video();
|
||||
via_port_handler_.flush();
|
||||
if(disk_interface == Analyser::Static::Oric::Target::DiskInterface::Pravetz) update_diskii();
|
||||
}
|
||||
|
||||
// to satisfy CRTMachine::Machine
|
||||
@ -605,10 +608,10 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
Apple::DiskII diskii_;
|
||||
std::vector<uint8_t> pravetz_rom_;
|
||||
std::size_t pravetz_rom_base_pointer_ = 0;
|
||||
Cycles cycles_since_diskii_update_;
|
||||
void update_diskii() {
|
||||
diskii_.run_for(cycles_since_diskii_update_.flush());
|
||||
}
|
||||
// Cycles cycles_since_diskii_update_;
|
||||
// void update_diskii() {
|
||||
// diskii_.run_for(cycles_since_diskii_update_.flush());
|
||||
// }
|
||||
|
||||
// Overlay RAM
|
||||
uint16_t ram_top_ = basic_visible_ram_top_;
|
||||
|
@ -35,6 +35,9 @@ class AppleDSK: public DiskImage {
|
||||
HeadPosition get_maximum_head_position() override;
|
||||
std::shared_ptr<Track> get_track_at_position(Track::Address address) override;
|
||||
|
||||
// TEST!
|
||||
bool get_is_read_only() override { return false; }
|
||||
|
||||
private:
|
||||
Storage::FileHolder file_;
|
||||
int sectors_per_track_ = 16;
|
||||
|
Loading…
Reference in New Issue
Block a user