mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-06 20:54:31 +00:00
240 lines
5.4 KiB
C++
240 lines
5.4 KiB
C++
//
|
|
// 1770.cpp
|
|
// Clock Signal
|
|
//
|
|
// Created by Thomas Harte on 17/09/2016.
|
|
// Copyright © 2016 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
#include "1770.hpp"
|
|
|
|
|
|
using namespace WD;
|
|
|
|
WD1770::WD1770() :
|
|
Storage::Disk::Drive(1000000, 8, 300),
|
|
state_(State::Waiting), status_(0), has_command_(false) {
|
|
Storage::Time bit_length;
|
|
// TODO: this should be a function of the double density line
|
|
bit_length.length = 1;
|
|
bit_length.clock_rate = 500000;
|
|
set_expected_bit_length(bit_length);
|
|
}
|
|
|
|
//void WD1770::set_disk(std::shared_ptr<Storage::Disk::Disk> disk)
|
|
//{
|
|
// drive_.reset(new Storage::Disk::Drive(1000000, 8, 300));
|
|
//}
|
|
|
|
void WD1770::set_is_double_density(bool is_double_density)
|
|
{
|
|
}
|
|
|
|
void WD1770::set_register(int address, uint8_t value)
|
|
{
|
|
switch(address&3)
|
|
{
|
|
case 0:
|
|
command_ = value;
|
|
has_command_ = true;
|
|
// TODO: is this force interrupt?
|
|
break;
|
|
case 1: track_ = value; break;
|
|
case 2: sector_ = value; break;
|
|
case 3: data_ = value; break;
|
|
}
|
|
}
|
|
|
|
uint8_t WD1770::get_register(int address)
|
|
{
|
|
switch(address&3)
|
|
{
|
|
default: return status_;
|
|
case 1: return track_;
|
|
case 2: return sector_;
|
|
case 3: return data_;
|
|
}
|
|
}
|
|
|
|
void WD1770::run_for_cycles(unsigned int number_of_cycles)
|
|
{
|
|
// perform one step every eight cycles, arbitrariy as I can find no timing documentation
|
|
cycles += number_of_cycles;
|
|
while(cycles > 8)
|
|
{
|
|
cycles -= 8;
|
|
if(status_ & Flag::MotorOn) Storage::Disk::Drive::run_for_cycles(1);
|
|
|
|
switch(state_)
|
|
{
|
|
case State::Waiting:
|
|
if(has_command_)
|
|
{
|
|
has_command_ = false;
|
|
if(command_ & 0x80)
|
|
state_ = (command_ & 0x40) ? State::BeginType3 : State::BeginType2;
|
|
else
|
|
state_ = State::BeginType1;
|
|
}
|
|
continue;
|
|
|
|
case State::WaitForSixIndexPulses:
|
|
status_ |= Flag::MotorOn;
|
|
// deliberately empty; logic is in ::process_index_hole
|
|
continue;
|
|
|
|
#pragma mark - Type 1
|
|
|
|
case State::BeginType1:
|
|
status_ |= Flag::Busy;
|
|
status_ &= ~(Flag::DataRequest | Flag::CRCError);
|
|
set_interrupt_request(false);
|
|
state_ = State::BeginType1PostSpin;
|
|
if(command_ & 0x08)
|
|
{
|
|
wait_six_index_pulses_.next_state = state_;
|
|
index_hole_count_ = 0;
|
|
state_ = State::WaitForSixIndexPulses;
|
|
}
|
|
continue;
|
|
|
|
case State::BeginType1PostSpin:
|
|
switch(command_ >> 4)
|
|
{
|
|
case 0: // restore
|
|
track_ = 0xff; // deliberate fallthrough
|
|
case 1: // seek
|
|
data_ = 0x00;
|
|
break;
|
|
case 2: case 3: // step
|
|
break;
|
|
case 4: case 5: // step in
|
|
is_step_in_ = true;
|
|
break;
|
|
case 6: case 7: // step out
|
|
is_step_in_ = false;
|
|
break;
|
|
}
|
|
|
|
if(!(command_ >> 5))
|
|
state_ = State::TestTrack;
|
|
else
|
|
state_ = (command_ & 0x10) ? State::TestDirection : State::TestHead;
|
|
continue;
|
|
|
|
case State::TestTrack:
|
|
data_shift_register_ = data_;
|
|
if(track_ == data_shift_register_)
|
|
state_ = State::TestVerify;
|
|
else
|
|
{
|
|
is_step_in_ = (data_shift_register_ < track_);
|
|
state_ = State::TestDirection;
|
|
}
|
|
continue;
|
|
|
|
case State::TestDirection:
|
|
track_ += is_step_in_ ? -1 : +1;
|
|
state_ = State::TestHead;
|
|
continue;
|
|
|
|
case State::TestHead:
|
|
if(get_is_track_zero() && !is_step_in_)
|
|
{
|
|
track_ = 0;
|
|
state_ = State::TestVerify;
|
|
}
|
|
else
|
|
{
|
|
step(is_step_in_ ? 1 : -1);
|
|
state_ = State::StepDelay;
|
|
step_delay_.count = 0;
|
|
}
|
|
continue;
|
|
|
|
case State::StepDelay:
|
|
if(step_delay_.count == (command_&3))
|
|
{
|
|
state_ = (command_ >> 5) ? State::TestVerify : State::TestTrack;
|
|
}
|
|
step_delay_.count++;
|
|
continue;
|
|
|
|
case State::TestVerify:
|
|
if(command_ & 0x04)
|
|
{
|
|
state_ = State::VerifyTrack;
|
|
}
|
|
else
|
|
{
|
|
set_interrupt_request(true);
|
|
status_ &= ~Flag::Busy;
|
|
state_ = State::Waiting;
|
|
}
|
|
continue;
|
|
|
|
#pragma mark - Type 2
|
|
|
|
case State::BeginType2:
|
|
status_ |= Flag::Busy;
|
|
status_ &= ~(Flag::DataRequest | Flag::LostData | Flag::RecordNotFound | 0x60);
|
|
state_ = State::TestPause;
|
|
if(!(command_&0x08))
|
|
{
|
|
wait_six_index_pulses_.next_state = state_;
|
|
index_hole_count_ = 0;
|
|
state_ = State::WaitForSixIndexPulses;
|
|
}
|
|
continue;
|
|
|
|
case State::TestPause:
|
|
// TODO: pause for 30ms if E is set
|
|
state_ = State::TestWrite;
|
|
continue;
|
|
|
|
// case State::TestWrite:
|
|
// continue;
|
|
|
|
// +------+----------+-------------------------+
|
|
// ! ! ! BITS !
|
|
// ! TYPE ! COMMAND ! 7 6 5 4 3 2 1 0 !
|
|
// +------+----------+-------------------------+
|
|
// ! 1 ! Restore ! 0 0 0 0 h v r1 r0 !
|
|
// ! 1 ! Seek ! 0 0 0 1 h v r1 r0 !
|
|
// ! 1 ! Step ! 0 0 1 u h v r1 r0 !
|
|
// ! 1 ! Step-in ! 0 1 0 u h v r1 r0 !
|
|
// ! 1 ! Step-out ! 0 1 1 u h v r1 r0 !
|
|
// ! 2 ! Rd sectr ! 1 0 0 m h E 0 0 !
|
|
// ! 2 ! Wt sectr ! 1 0 1 m h E P a0 !
|
|
// ! 3 ! Rd addr ! 1 1 0 0 h E 0 0 !
|
|
// ! 3 ! Rd track ! 1 1 1 0 h E 0 0 !
|
|
// ! 3 ! Wt track ! 1 1 1 1 h E P 0 !
|
|
// ! 4 ! Forc int ! 1 1 0 1 i3 i2 i1 i0 !
|
|
// +------+----------+-------------------------+
|
|
|
|
default:
|
|
{
|
|
static bool has_hit_error = false;
|
|
if(!has_hit_error)
|
|
printf("Unhandled state %d!\n", state_);
|
|
has_hit_error = true;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WD1770::process_input_bit(int value, unsigned int cycles_since_index_hole)
|
|
{
|
|
}
|
|
|
|
void WD1770::process_index_hole()
|
|
{
|
|
index_hole_count_++;
|
|
|
|
if(state_ == State::WaitForSixIndexPulses && index_hole_count_ == 6)
|
|
{
|
|
state_ = wait_six_index_pulses_.next_state;
|
|
}
|
|
}
|