1
0
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:
Thomas Harte 2018-04-24 19:44:45 -07:00
parent 2f2390b5aa
commit 7061537ff5
7 changed files with 100 additions and 13 deletions

View File

@ -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;
}
}

View File

@ -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;
};
}

View File

@ -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;
}
};

View File

@ -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);
}

View File

@ -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_;

View File

@ -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;
}

View File

@ -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.