mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-27 06:35:04 +00:00
Performed an about-face on how this should probably be implemented. Now forsaking the state machine in favour of a macro'd switch-for-cooperative-event-based-messaging implementation. Let's see how this ends up looking.
This commit is contained in:
parent
88bbeedad3
commit
53522e28e2
@ -7,17 +7,24 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "1770.hpp"
|
#include "1770.hpp"
|
||||||
|
#include "../../Storage/Disk/Encodings/MFM.hpp"
|
||||||
|
|
||||||
using namespace WD;
|
using namespace WD;
|
||||||
|
|
||||||
WD1770::WD1770() :
|
WD1770::WD1770() :
|
||||||
Storage::Disk::Drive(1000000, 8, 300),
|
Storage::Disk::Drive(8000000, 1, 300),
|
||||||
state_(State::Waiting), status_(0), has_command_(false) {
|
status_(0),
|
||||||
|
interesting_event_mask_(Event::Command),
|
||||||
|
resume_point_(0),
|
||||||
|
delay_time_(0)
|
||||||
|
{
|
||||||
set_is_double_density(false);
|
set_is_double_density(false);
|
||||||
|
posit_event(Event::Command);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WD1770::set_is_double_density(bool is_double_density)
|
void WD1770::set_is_double_density(bool is_double_density)
|
||||||
{
|
{
|
||||||
|
is_double_density_ = is_double_density;
|
||||||
Storage::Time bit_length;
|
Storage::Time bit_length;
|
||||||
bit_length.length = 1;
|
bit_length.length = 1;
|
||||||
bit_length.clock_rate = is_double_density ? 500000 : 250000;
|
bit_length.clock_rate = is_double_density ? 500000 : 250000;
|
||||||
@ -30,7 +37,7 @@ void WD1770::set_register(int address, uint8_t value)
|
|||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
command_ = value;
|
command_ = value;
|
||||||
has_command_ = true;
|
posit_event(Event::Command);
|
||||||
// TODO: is this force interrupt?
|
// TODO: is this force interrupt?
|
||||||
break;
|
break;
|
||||||
case 1: track_ = value; break;
|
case 1: track_ = value; break;
|
||||||
@ -52,8 +59,22 @@ uint8_t WD1770::get_register(int address)
|
|||||||
|
|
||||||
void WD1770::run_for_cycles(unsigned int number_of_cycles)
|
void WD1770::run_for_cycles(unsigned int number_of_cycles)
|
||||||
{
|
{
|
||||||
|
if(status_ & Flag::MotorOn) Storage::Disk::Drive::run_for_cycles((int)number_of_cycles);
|
||||||
|
|
||||||
|
if(delay_time_)
|
||||||
|
{
|
||||||
|
if(delay_time_ <= number_of_cycles)
|
||||||
|
{
|
||||||
|
delay_time_ = 0;
|
||||||
|
posit_event(Event::Timer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delay_time_ -= number_of_cycles;
|
||||||
|
}
|
||||||
|
}
|
||||||
// perform one step every eight cycles, arbitrariy as I can find no timing documentation
|
// perform one step every eight cycles, arbitrariy as I can find no timing documentation
|
||||||
cycles += number_of_cycles;
|
/* cycles += number_of_cycles;
|
||||||
while(cycles > 8)
|
while(cycles > 8)
|
||||||
{
|
{
|
||||||
cycles -= 8;
|
cycles -= 8;
|
||||||
@ -183,11 +204,134 @@ void WD1770::run_for_cycles(unsigned int number_of_cycles)
|
|||||||
|
|
||||||
case State::TestPause:
|
case State::TestPause:
|
||||||
// TODO: pause for 30ms if E is set
|
// TODO: pause for 30ms if E is set
|
||||||
state_ = State::TestWrite;
|
state_ = State::TestWriteProtect;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// case State::TestWrite:
|
case State::TestWriteProtect:
|
||||||
// continue;
|
if(command_ & 0x20) // TODO: && write protect
|
||||||
|
{
|
||||||
|
set_interrupt_request(true);
|
||||||
|
status_ &= ~Flag::Busy;
|
||||||
|
status_ |= Flag::WriteProtect;
|
||||||
|
state_ = State::Waiting;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
get_header_.found_id = false;
|
||||||
|
state_ = State::GetHeader;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case State::GetHeader:
|
||||||
|
if(index_hole_count_ == 5)
|
||||||
|
{
|
||||||
|
set_interrupt_request(true);
|
||||||
|
status_ &= ~Flag::Busy;
|
||||||
|
status_ |= Flag::RecordNotFound;
|
||||||
|
state_ = State::Waiting;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(get_header_.found_id)
|
||||||
|
{
|
||||||
|
if(token_counter_ > 0 && latest_token_.type != Token::Byte)
|
||||||
|
{
|
||||||
|
get_header_.found_id = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(token_counter_ == 5)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else if(token_counter_ > 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(latest_token_.type == Token::ID)
|
||||||
|
{
|
||||||
|
get_header_.found_id = true;
|
||||||
|
token_counter_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
shift_register_ = (shift_register_ << 1) | value;
|
||||||
|
bits_since_token_++;
|
||||||
|
|
||||||
|
Token::Type token_type = Token::Byte;
|
||||||
|
if(is_double_density_)
|
||||||
|
{
|
||||||
|
switch(shift_register_ & 0xffff)
|
||||||
|
{
|
||||||
|
case Storage::Encodings::MFM::FMIndexAddressMark:
|
||||||
|
token_type = Token::Index;
|
||||||
|
break;
|
||||||
|
case Storage::Encodings::MFM::FMIDAddressMark:
|
||||||
|
token_type = Token::ID;
|
||||||
|
break;
|
||||||
|
case Storage::Encodings::MFM::FMDataAddressMark:
|
||||||
|
token_type = Token::Data;
|
||||||
|
break;
|
||||||
|
case Storage::Encodings::MFM::FMDeletedDataAddressMark:
|
||||||
|
token_type = Token::DeletedData;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: MFM
|
||||||
|
}
|
||||||
|
|
||||||
|
if(token_type != Token::Byte)
|
||||||
|
{
|
||||||
|
latest_token_.type = token_type;
|
||||||
|
bits_since_token_ = 0;
|
||||||
|
posit_event(Event::Token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bits_since_token_ == 16)
|
||||||
|
{
|
||||||
|
latest_token_.type = Token::Byte;
|
||||||
|
latest_token_.byte_value = (uint8_t)(
|
||||||
|
((shift_register_ & 0x0001) >> 0) |
|
||||||
|
((shift_register_ & 0x0004) >> 1) |
|
||||||
|
((shift_register_ & 0x0010) >> 2) |
|
||||||
|
((shift_register_ & 0x0040) >> 3) |
|
||||||
|
((shift_register_ & 0x0100) >> 4) |
|
||||||
|
((shift_register_ & 0x0400) >> 5) |
|
||||||
|
((shift_register_ & 0x1000) >> 6) |
|
||||||
|
((shift_register_ & 0x4000) >> 7));
|
||||||
|
bits_since_token_ = 0;
|
||||||
|
posit_event(Event::Token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WD1770::process_index_hole()
|
||||||
|
{
|
||||||
|
index_hole_count_++;
|
||||||
|
posit_event(Event::IndexHole);
|
||||||
|
}
|
||||||
|
|
||||||
// +------+----------+-------------------------+
|
// +------+----------+-------------------------+
|
||||||
// ! ! ! BITS !
|
// ! ! ! BITS !
|
||||||
@ -206,28 +350,104 @@ void WD1770::run_for_cycles(unsigned int number_of_cycles)
|
|||||||
// ! 4 ! Forc int ! 1 1 0 1 i3 i2 i1 i0 !
|
// ! 4 ! Forc int ! 1 1 0 1 i3 i2 i1 i0 !
|
||||||
// +------+----------+-------------------------+
|
// +------+----------+-------------------------+
|
||||||
|
|
||||||
default:
|
#define WAIT_FOR_EVENT(mask) resume_point_ = __LINE__; interesting_event_mask_ = mask; return; case __LINE__:
|
||||||
{
|
#define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; interesting_event_mask_ = Event::Timer; delay_time_ = ms * 8000; if(delay_time_) return; case __LINE__:
|
||||||
static bool has_hit_error = false;
|
#define BEGIN_SECTION() switch(resume_point_) { default:
|
||||||
if(!has_hit_error)
|
#define END_SECTION() 0; }
|
||||||
printf("Unhandled state %d!\n", state_);
|
|
||||||
has_hit_error = true;
|
|
||||||
}
|
void WD1770::posit_event(Event type)
|
||||||
return;
|
{
|
||||||
|
if(!(interesting_event_mask_ & (int)type)) return;
|
||||||
|
interesting_event_mask_ &= ~type;
|
||||||
|
|
||||||
|
BEGIN_SECTION()
|
||||||
|
|
||||||
|
// Wait for a new command, branch to the appropriate handler.
|
||||||
|
wait_for_command:
|
||||||
|
WAIT_FOR_EVENT(Event::Command);
|
||||||
|
status_ |= Flag::Busy;
|
||||||
|
if(!(command_ & 0x80)) goto begin_type_1;
|
||||||
|
if(!(command_ & 0x40)) goto begin_type_2;
|
||||||
|
goto begin_type_3;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Type 1 entry point.
|
||||||
|
*/
|
||||||
|
begin_type_1:
|
||||||
|
// Set initial flags, skip spin-up if possible.
|
||||||
|
status_ &= ~(Flag::DataRequest | Flag::DataRequest);
|
||||||
|
set_interrupt_request(false);
|
||||||
|
if(!(command_&0x08)) goto test_type1_type;
|
||||||
|
|
||||||
|
// Perform spin up.
|
||||||
|
status_ |= Flag::MotorOn;
|
||||||
|
WAIT_FOR_EVENT(Event::IndexHole);
|
||||||
|
WAIT_FOR_EVENT(Event::IndexHole);
|
||||||
|
WAIT_FOR_EVENT(Event::IndexHole);
|
||||||
|
WAIT_FOR_EVENT(Event::IndexHole);
|
||||||
|
WAIT_FOR_EVENT(Event::IndexHole);
|
||||||
|
WAIT_FOR_EVENT(Event::IndexHole);
|
||||||
|
|
||||||
|
test_type1_type:
|
||||||
|
// Set step direction if this is a step in or out.
|
||||||
|
if((command_ >> 5) == 2) step_direction_ = 1;
|
||||||
|
if((command_ >> 5) == 3) step_direction_ = 0;
|
||||||
|
if((command_ >> 5) != 0) goto perform_step_command;
|
||||||
|
|
||||||
|
// This is now definitely either a seek or a restore; if it's a restore then set track to 0xff.
|
||||||
|
if(!(command_ & 0x10)) track_ = 0xff;
|
||||||
|
data_ = 0;
|
||||||
|
|
||||||
|
perform_seek_or_restore_command:
|
||||||
|
if(track_ == data_) goto verify;
|
||||||
|
step_direction_ = (data_ < track_);
|
||||||
|
|
||||||
|
adjust_track:
|
||||||
|
if(step_direction_) track_--; else track_++;
|
||||||
|
|
||||||
|
perform_step:
|
||||||
|
if(!step_direction_ && get_is_track_zero())
|
||||||
|
{
|
||||||
|
track_ = 0;
|
||||||
|
goto verify;
|
||||||
}
|
}
|
||||||
}
|
step(step_direction_);
|
||||||
}
|
int time_to_wait;
|
||||||
|
switch(command_ & 3)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case 0: time_to_wait = 6; break; // 2 on a 1772
|
||||||
|
case 1: time_to_wait = 12; break; // 3 on a 1772
|
||||||
|
case 2: time_to_wait = 20; break; // 5 on a 1772
|
||||||
|
case 3: time_to_wait = 30; break; // 6 on a 1772
|
||||||
|
}
|
||||||
|
WAIT_FOR_TIME(command_ & 3);
|
||||||
|
if(command_ >> 5) goto verify;
|
||||||
|
goto perform_seek_or_restore_command;
|
||||||
|
|
||||||
void WD1770::process_input_bit(int value, unsigned int cycles_since_index_hole)
|
perform_step_command:
|
||||||
{
|
if(command_ & 0x10) goto adjust_track;
|
||||||
}
|
goto perform_step;
|
||||||
|
|
||||||
void WD1770::process_index_hole()
|
verify:
|
||||||
{
|
if(!(command_ & 0x04))
|
||||||
index_hole_count_++;
|
{
|
||||||
|
set_interrupt_request(true);
|
||||||
|
status_ &= ~(Flag::Busy);
|
||||||
|
goto wait_for_command;
|
||||||
|
}
|
||||||
|
|
||||||
if(state_ == State::WaitForSixIndexPulses && index_hole_count_ == 6)
|
printf("!!!TODO: verify a type 1!!!\n");
|
||||||
{
|
|
||||||
state_ = wait_six_index_pulses_.next_state;
|
begin_type_2:
|
||||||
}
|
printf("!!!TODO: type 2 commands!!!\n");
|
||||||
|
|
||||||
|
|
||||||
|
begin_type_3:
|
||||||
|
printf("!!!TODO: type 3 commands!!!\n");
|
||||||
|
|
||||||
|
|
||||||
|
END_SECTION()
|
||||||
}
|
}
|
||||||
|
@ -39,16 +39,23 @@ class WD1770: public Storage::Disk::Drive {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned int cycles;
|
uint8_t status_;
|
||||||
|
uint8_t track_;
|
||||||
|
uint8_t sector_;
|
||||||
|
uint8_t data_;
|
||||||
|
uint8_t command_;
|
||||||
|
|
||||||
enum class State {
|
int index_hole_count_;
|
||||||
|
|
||||||
|
/* enum class State {
|
||||||
Waiting,
|
Waiting,
|
||||||
BeginType1, BeginType2, BeginType3,
|
BeginType1, BeginType2, BeginType3,
|
||||||
BeginType1PostSpin,
|
BeginType1PostSpin,
|
||||||
WaitForSixIndexPulses,
|
WaitForSixIndexPulses,
|
||||||
TestTrack, TestDirection, TestHead,
|
TestTrack, TestDirection, TestHead,
|
||||||
TestVerify, VerifyTrack,
|
TestVerify, VerifyTrack,
|
||||||
StepDelay, TestPause, TestWrite
|
StepDelay, TestPause, TestWriteProtect,
|
||||||
|
GetHeader,
|
||||||
} state_;
|
} state_;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
@ -58,21 +65,58 @@ class WD1770: public Storage::Disk::Drive {
|
|||||||
struct {
|
struct {
|
||||||
int count;
|
int count;
|
||||||
} step_delay_;
|
} step_delay_;
|
||||||
|
struct {
|
||||||
|
bool found_id;
|
||||||
|
uint8_t value[4];
|
||||||
|
} get_header_;
|
||||||
};
|
};
|
||||||
|
|
||||||
int index_hole_count_;
|
enum class ReadingState {
|
||||||
|
Idle,
|
||||||
|
ReadingHeader,
|
||||||
|
ReadingData
|
||||||
|
} reading_state_;
|
||||||
|
struct {
|
||||||
|
uint8_t track;
|
||||||
|
uint8_t sector;
|
||||||
|
uint8_t length;
|
||||||
|
} header;
|
||||||
|
bool crc_error_;
|
||||||
|
|
||||||
|
int shift_register_;
|
||||||
|
int shift_register_duration_;
|
||||||
|
bool is_double_density_;
|
||||||
|
|
||||||
uint8_t status_;
|
|
||||||
uint8_t track_;
|
|
||||||
uint8_t sector_;
|
|
||||||
uint8_t data_;
|
|
||||||
uint8_t command_;
|
|
||||||
bool has_command_;
|
bool has_command_;
|
||||||
|
|
||||||
|
bool is_step_in_;*/
|
||||||
|
int step_direction_;
|
||||||
void set_interrupt_request(bool interrupt_request) {}
|
void set_interrupt_request(bool interrupt_request) {}
|
||||||
bool is_step_in_;
|
|
||||||
uint8_t data_shift_register_;
|
|
||||||
|
|
||||||
|
// Tokeniser
|
||||||
|
bool is_double_density_;
|
||||||
|
int shift_register_;
|
||||||
|
struct Token {
|
||||||
|
enum Type {
|
||||||
|
Index, ID, Data, DeletedData, Byte
|
||||||
|
} type;
|
||||||
|
uint8_t byte_value;
|
||||||
|
} latest_token_;
|
||||||
|
int bits_since_token_;
|
||||||
|
|
||||||
|
// Events
|
||||||
|
enum Event: int {
|
||||||
|
Command = (1 << 0),
|
||||||
|
Token = (1 << 1),
|
||||||
|
IndexHole = (1 << 2),
|
||||||
|
Timer = (1 << 3)
|
||||||
|
};
|
||||||
|
void posit_event(Event type);
|
||||||
|
int interesting_event_mask_;
|
||||||
|
int resume_point_;
|
||||||
|
int delay_time_;
|
||||||
|
|
||||||
|
//
|
||||||
virtual void process_input_bit(int value, unsigned int cycles_since_index_hole);
|
virtual void process_input_bit(int value, unsigned int cycles_since_index_hole);
|
||||||
virtual void process_index_hole();
|
virtual void process_index_hole();
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user