mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-19 23:32:28 +00:00
Makes joined-up attempt to run data through the Disk II.
This commit is contained in:
parent
2f2390b5aa
commit
7061537ff5
@ -12,30 +12,76 @@
|
||||
|
||||
using namespace Apple;
|
||||
|
||||
namespace {
|
||||
const uint8_t input_command = 0x1;
|
||||
const uint8_t input_mode = 0x2;
|
||||
const uint8_t input_flux = 0x4;
|
||||
}
|
||||
|
||||
DiskII::DiskII() :
|
||||
drives_{{2045454, 300, 1}, {2045454, 300, 1}}
|
||||
{
|
||||
}
|
||||
|
||||
void DiskII::set_control(Control control, bool on) {
|
||||
printf("Set control %d %s\n", control, on ? "on" : "off");
|
||||
// TODO: seeking, motor control.
|
||||
|
||||
int previous_stepper_mask = stepper_mask_;
|
||||
switch(control) {
|
||||
case Control::P0: stepper_mask_ = (stepper_mask_ & 0xe) | (on ? 0x1 : 0x0); break;
|
||||
case Control::P1: stepper_mask_ = (stepper_mask_ & 0xd) | (on ? 0x2 : 0x0); break;
|
||||
case Control::P2: stepper_mask_ = (stepper_mask_ & 0xb) | (on ? 0x4 : 0x0); break;
|
||||
case Control::P3: stepper_mask_ = (stepper_mask_ & 0x7) | (on ? 0x8 : 0x0); break;
|
||||
|
||||
case Control::Motor:
|
||||
// TODO: does the motor control trigger both motors at once?
|
||||
drives_[0].set_motor_on(on);
|
||||
drives_[1].set_motor_on(on);
|
||||
break;
|
||||
}
|
||||
|
||||
// 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_) {
|
||||
// Convert from a representation of bits set to the centre of pull.
|
||||
int position = 0;
|
||||
if(stepper_mask_&2) position += 2;
|
||||
if(stepper_mask_&4) position += 4;
|
||||
if(stepper_mask_&8) position += 6;
|
||||
// TODO: both 0 and 4 turned on should produce position 7
|
||||
const int bits_set = (stepper_mask_&1) + ((stepper_mask_ >> 1)&1) + ((stepper_mask_ >> 2)&1) + ((stepper_mask_ >> 3)&1);
|
||||
position /= bits_set;
|
||||
|
||||
// Compare to the stepper position to decide whether that pulls in the current cog notch,
|
||||
// or grabs a later one.
|
||||
int change = ((position - stepper_position_ + 4)&7) - 4;
|
||||
drives_[active_drive_].step(change);
|
||||
|
||||
stepper_position_ = position;
|
||||
}
|
||||
}
|
||||
|
||||
void DiskII::set_mode(Mode mode) {
|
||||
printf("Set mode %d\n", mode);
|
||||
inputs_ = (inputs_ & ~0x08) | ((mode == Mode::Write) ? 0x2: 0x0);
|
||||
inputs_ = (inputs_ & ~input_mode) | ((mode == Mode::Write) ? input_mode : 0);
|
||||
}
|
||||
|
||||
void DiskII::select_drive(int drive) {
|
||||
printf("Select drive %d\n", drive);
|
||||
// TODO: select a drive.
|
||||
active_drive_ = drive & 1;
|
||||
drives_[active_drive_].set_event_delegate(this);
|
||||
drives_[active_drive_^1].set_event_delegate(nullptr);
|
||||
}
|
||||
|
||||
void DiskII::set_data_register(uint8_t value) {
|
||||
printf("Set data register (?)\n");
|
||||
inputs_ |= 0x1;
|
||||
inputs_ |= input_command;
|
||||
data_register_ = value;
|
||||
}
|
||||
|
||||
uint8_t DiskII::get_shift_register() {
|
||||
// printf("[%02x] ", shift_register_);
|
||||
inputs_ &= ~0x1;
|
||||
inputs_ &= ~input_command;
|
||||
return shift_register_;
|
||||
}
|
||||
|
||||
@ -63,9 +109,8 @@ The bytes in the P6 ROM has the high four bits reversed compared to the BAPD cha
|
||||
((state_&0x2) >> 1) |
|
||||
((state_&0x1) << 7) |
|
||||
((state_&0x4) << 4) |
|
||||
((state_&0x8) << 2) |
|
||||
0x10;
|
||||
// TODO: apply proper pulse state in bit 4.
|
||||
((state_&0x8) << 2);
|
||||
inputs_ |= input_flux;
|
||||
|
||||
const uint8_t update = state_machine_[static_cast<std::size_t>(address)];
|
||||
state_ = update >> 4;
|
||||
@ -84,6 +129,10 @@ The bytes in the P6 ROM has the high four bits reversed compared to the BAPD cha
|
||||
}
|
||||
|
||||
// printf(" -> %02x performing %02x (address was %02x)\n", state_, command, address);
|
||||
|
||||
// TODO: surely there's a less heavyweight solution than this?
|
||||
drives_[0].run_for(Cycles(1));
|
||||
drives_[1].run_for(Cycles(1));
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,3 +145,13 @@ void DiskII::set_state_machine(const std::vector<uint8_t> &state_machine) {
|
||||
// run_for(Cycles(15));
|
||||
// TODO: shuffle ordering here?
|
||||
}
|
||||
|
||||
void DiskII::set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive) {
|
||||
drives_[drive].set_disk(disk);
|
||||
}
|
||||
|
||||
void DiskII::process_event(const Storage::Disk::Track::Event &event) {
|
||||
if(event.type == Storage::Disk::Track::Event::FluxTransition) {
|
||||
inputs_ &= ~input_flux;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,9 @@
|
||||
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
|
||||
#include "../../Storage/Disk/Disk.hpp"
|
||||
#include "../../Storage/Disk/Drive.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
@ -19,8 +22,10 @@ namespace Apple {
|
||||
/*!
|
||||
Provides an emulation of the Apple Disk II.
|
||||
*/
|
||||
class DiskII {
|
||||
class DiskII: public Storage::Disk::Drive::EventDelegate {
|
||||
public:
|
||||
DiskII();
|
||||
|
||||
enum class Control {
|
||||
P0, P1, P2, P3,
|
||||
Motor,
|
||||
@ -37,14 +42,23 @@ class DiskII {
|
||||
void run_for(const Cycles cycles);
|
||||
void set_state_machine(const std::vector<uint8_t> &);
|
||||
|
||||
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive);
|
||||
|
||||
private:
|
||||
void process_event(const Storage::Disk::Track::Event &event) override;
|
||||
|
||||
uint8_t state_ = 0;
|
||||
uint8_t inputs_ = 0;
|
||||
uint8_t shift_register_ = 0;
|
||||
uint8_t data_register_ = 0;
|
||||
|
||||
int stepper_mask_ = 0;
|
||||
int stepper_position_ = 0;
|
||||
|
||||
bool is_write_protected();
|
||||
std::vector<uint8_t> state_machine_;
|
||||
Storage::Disk::Drive drives_[2];
|
||||
int active_drive_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -274,9 +274,14 @@ class ConcreteMachine:
|
||||
if(apple_target->has_disk) {
|
||||
cards_[6].reset(new AppleII::DiskIICard(rom_fetcher_, true));
|
||||
}
|
||||
|
||||
insert_media(apple_target->media);
|
||||
}
|
||||
|
||||
bool insert_media(const Analyser::Static::Media &media) override {
|
||||
if(!media.disks.empty() && cards_[6]) {
|
||||
dynamic_cast<AppleII::DiskIICard *>(cards_[6].get())->set_disk(media.disks[0], 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -63,3 +63,7 @@ void DiskIICard::perform_bus_operation(CPU::MOS6502::BusOperation operation, uin
|
||||
void DiskIICard::run_for(Cycles cycles, int stretches) {
|
||||
diskii_.run_for(Cycles(cycles.as_int() * 2));
|
||||
}
|
||||
|
||||
void DiskIICard::set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive) {
|
||||
diskii_.set_disk(disk, drive);
|
||||
}
|
||||
|
@ -11,9 +11,12 @@
|
||||
|
||||
#include "Card.hpp"
|
||||
#include "../ROMMachine.hpp"
|
||||
|
||||
#include "../../Components/DiskII/DiskII.hpp"
|
||||
#include "../../Storage/Disk/Disk.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace AppleII {
|
||||
@ -23,6 +26,7 @@ class DiskIICard: public Card {
|
||||
DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector);
|
||||
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) override;
|
||||
void run_for(Cycles cycles, int stretches) override;
|
||||
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive);
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> boot_;
|
||||
|
@ -47,6 +47,7 @@ bool Drive::get_is_track_zero() {
|
||||
}
|
||||
|
||||
void Drive::step(int direction) {
|
||||
printf("Step %d\n", direction);
|
||||
int old_head_position = head_position_;
|
||||
head_position_ = std::max(head_position_ + direction, 0);
|
||||
|
||||
@ -70,7 +71,7 @@ Storage::Time Drive::get_time_into_track() {
|
||||
Time result(cycles_since_index_hole_, static_cast<int>(get_input_clock_rate()));
|
||||
result /= rotational_multiplier_;
|
||||
result.simplify();
|
||||
assert(result <= Time(1));
|
||||
// assert(result <= Time(1));
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -168,7 +169,7 @@ void Drive::get_next_event(const Time &duration_already_passed) {
|
||||
void Drive::process_next_event() {
|
||||
// TODO: ready test here.
|
||||
if(current_event_.type == Track::Event::IndexHole) {
|
||||
assert(get_time_into_track() == Time(1) || get_time_into_track() == Time(0));
|
||||
// assert(get_time_into_track() == Time(1) || get_time_into_track() == Time(0));
|
||||
if(ready_index_count_ < 2) ready_index_count_++;
|
||||
cycles_since_index_hole_ = 0;
|
||||
}
|
||||
|
@ -110,10 +110,10 @@ class Drive: public Sleeper, public TimedEventLoop {
|
||||
If the drive is in write mode, announces that all queued bits have now been written.
|
||||
If the controller provides further bits now then there will be no gap in written data.
|
||||
*/
|
||||
virtual void process_write_completed() = 0;
|
||||
virtual void process_write_completed() {}
|
||||
|
||||
/// Informs the delegate of the passing of @c cycles.
|
||||
virtual void advance(const Cycles cycles) = 0;
|
||||
virtual void advance(const Cycles cycles) {}
|
||||
};
|
||||
|
||||
/// Sets the current event delegate.
|
||||
|
Loading…
Reference in New Issue
Block a user