mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-28 07:29:45 +00:00
Merge pull request #197 from TomHarte/MoreFDC
Improves the 8272 and the whole disk infrastructure behind it
This commit is contained in:
commit
8a37a0ff2e
@ -502,7 +502,7 @@ void WD1770::posit_event(int new_event_type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set_data_mode(DataMode::Writing);
|
set_data_mode(DataMode::Writing);
|
||||||
begin_writing();
|
begin_writing(false);
|
||||||
for(int c = 0; c < (get_is_double_density() ? 12 : 6); c++) {
|
for(int c = 0; c < (get_is_double_density() ? 12 : 6); c++) {
|
||||||
write_byte(0);
|
write_byte(0);
|
||||||
}
|
}
|
||||||
@ -694,7 +694,7 @@ void WD1770::posit_event(int new_event_type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WAIT_FOR_EVENT(Event1770::IndexHoleTarget);
|
WAIT_FOR_EVENT(Event1770::IndexHoleTarget);
|
||||||
begin_writing();
|
begin_writing(true);
|
||||||
index_hole_count_ = 0;
|
index_hole_count_ = 0;
|
||||||
|
|
||||||
write_track_write_loop:
|
write_track_write_loop:
|
||||||
|
@ -52,6 +52,29 @@ using namespace Intel::i8272;
|
|||||||
#define SetBadCylinder() (status_[2] |= 0x02)
|
#define SetBadCylinder() (status_[2] |= 0x02)
|
||||||
#define SetMissingDataAddressMark() (status_[2] |= 0x01)
|
#define SetMissingDataAddressMark() (status_[2] |= 0x01)
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const uint8_t CommandReadData = 0x06;
|
||||||
|
const uint8_t CommandReadDeletedData = 0x0c;
|
||||||
|
|
||||||
|
const uint8_t CommandWriteData = 0x05;
|
||||||
|
const uint8_t CommandWriteDeletedData = 0x09;
|
||||||
|
|
||||||
|
const uint8_t CommandReadTrack = 0x02;
|
||||||
|
const uint8_t CommandReadID = 0x0a;
|
||||||
|
const uint8_t CommandFormatTrack = 0x0d;
|
||||||
|
|
||||||
|
const uint8_t CommandScanLow = 0x11;
|
||||||
|
const uint8_t CommandScanLowOrEqual = 0x19;
|
||||||
|
const uint8_t CommandScanHighOrEqual = 0x1d;
|
||||||
|
|
||||||
|
const uint8_t CommandRecalibrate = 0x07;
|
||||||
|
const uint8_t CommandSeek = 0x0f;
|
||||||
|
|
||||||
|
const uint8_t CommandSenseInterruptStatus = 0x08;
|
||||||
|
const uint8_t CommandSpecify = 0x03;
|
||||||
|
const uint8_t CommandSenseDriveStatus = 0x04;
|
||||||
|
}
|
||||||
|
|
||||||
i8272::i8272(BusHandler &bus_handler, Cycles clock_rate, int clock_rate_multiplier, int revolutions_per_minute) :
|
i8272::i8272(BusHandler &bus_handler, Cycles clock_rate, int clock_rate_multiplier, int revolutions_per_minute) :
|
||||||
Storage::Disk::MFMController(clock_rate, clock_rate_multiplier, revolutions_per_minute),
|
Storage::Disk::MFMController(clock_rate, clock_rate_multiplier, revolutions_per_minute),
|
||||||
bus_handler_(bus_handler),
|
bus_handler_(bus_handler),
|
||||||
@ -170,25 +193,28 @@ void i8272::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
|
|||||||
#define CONCAT(x, y) PASTE(x, y)
|
#define CONCAT(x, y) PASTE(x, y)
|
||||||
|
|
||||||
#define FIND_HEADER() \
|
#define FIND_HEADER() \
|
||||||
|
set_data_mode(DataMode::Scanning); \
|
||||||
CONCAT(find_header, __LINE__): WAIT_FOR_EVENT((int)Event::Token | (int)Event::IndexHole); \
|
CONCAT(find_header, __LINE__): WAIT_FOR_EVENT((int)Event::Token | (int)Event::IndexHole); \
|
||||||
if(event_type == (int)Event::IndexHole) index_hole_limit_--; \
|
if(event_type == (int)Event::IndexHole) { index_hole_limit_--; } \
|
||||||
else if(get_latest_token().type == Token::ID) goto CONCAT(header_found, __LINE__); \
|
else if(get_latest_token().type == Token::ID) goto CONCAT(header_found, __LINE__); \
|
||||||
\
|
\
|
||||||
if(index_hole_limit_) goto CONCAT(find_header, __LINE__); \
|
if(index_hole_limit_) goto CONCAT(find_header, __LINE__); \
|
||||||
CONCAT(header_found, __LINE__): 0;\
|
CONCAT(header_found, __LINE__): 0;\
|
||||||
|
|
||||||
#define FIND_DATA() \
|
#define FIND_DATA() \
|
||||||
|
set_data_mode(DataMode::Scanning); \
|
||||||
CONCAT(find_data, __LINE__): WAIT_FOR_EVENT((int)Event::Token | (int)Event::IndexHole); \
|
CONCAT(find_data, __LINE__): WAIT_FOR_EVENT((int)Event::Token | (int)Event::IndexHole); \
|
||||||
if(event_type == (int)Event::Token && get_latest_token().type != Token::Data && get_latest_token().type != Token::DeletedData) goto CONCAT(find_data, __LINE__);
|
if(event_type == (int)Event::Token) { \
|
||||||
|
if(get_latest_token().type == Token::Byte || get_latest_token().type == Token::Sync) goto CONCAT(find_data, __LINE__); \
|
||||||
|
}
|
||||||
|
|
||||||
#define READ_HEADER() \
|
#define READ_HEADER() \
|
||||||
distance_into_section_ = 0; \
|
distance_into_section_ = 0; \
|
||||||
set_data_mode(Reading); \
|
set_data_mode(DataMode::Reading); \
|
||||||
CONCAT(read_header, __LINE__): WAIT_FOR_EVENT(Event::Token); \
|
CONCAT(read_header, __LINE__): WAIT_FOR_EVENT(Event::Token); \
|
||||||
header_[distance_into_section_] = get_latest_token().byte_value; \
|
header_[distance_into_section_] = get_latest_token().byte_value; \
|
||||||
distance_into_section_++; \
|
distance_into_section_++; \
|
||||||
if(distance_into_section_ < 6) goto CONCAT(read_header, __LINE__); \
|
if(distance_into_section_ < 6) goto CONCAT(read_header, __LINE__); \
|
||||||
set_data_mode(Scanning);
|
|
||||||
|
|
||||||
#define SET_DRIVE_HEAD_MFM() \
|
#define SET_DRIVE_HEAD_MFM() \
|
||||||
active_drive_ = command_[1]&3; \
|
active_drive_ = command_[1]&3; \
|
||||||
@ -198,6 +224,12 @@ void i8272::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
|
|||||||
set_is_double_density(command_[0] & 0x40); \
|
set_is_double_density(command_[0] & 0x40); \
|
||||||
invalidate_track();
|
invalidate_track();
|
||||||
|
|
||||||
|
#define WAIT_FOR_BYTES(n) \
|
||||||
|
distance_into_section_ = 0; \
|
||||||
|
CONCAT(wait_bytes, __LINE__): WAIT_FOR_EVENT(Event::Token); \
|
||||||
|
if(get_latest_token().type == Token::Byte) distance_into_section_++; \
|
||||||
|
if(distance_into_section_ < (n)) goto CONCAT(wait_bytes, __LINE__);
|
||||||
|
|
||||||
#define LOAD_HEAD() \
|
#define LOAD_HEAD() \
|
||||||
if(!drives_[active_drive_].head_is_loaded[active_head_]) { \
|
if(!drives_[active_drive_].head_is_loaded[active_head_]) { \
|
||||||
drives_[active_drive_].head_is_loaded[active_head_] = true; \
|
drives_[active_drive_].head_is_loaded[active_head_] = true; \
|
||||||
@ -218,6 +250,7 @@ void i8272::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void i8272::posit_event(int event_type) {
|
void i8272::posit_event(int event_type) {
|
||||||
|
if(event_type == (int)Event::IndexHole) index_hole_count_++;
|
||||||
if(!(interesting_event_mask_ & event_type)) return;
|
if(!(interesting_event_mask_ & event_type)) return;
|
||||||
interesting_event_mask_ &= ~event_type;
|
interesting_event_mask_ &= ~event_type;
|
||||||
|
|
||||||
@ -260,61 +293,75 @@ void i8272::posit_event(int event_type) {
|
|||||||
|
|
||||||
// If this is not clearly a command that's safe to carry out in parallel to a seek, end all seeks.
|
// If this is not clearly a command that's safe to carry out in parallel to a seek, end all seeks.
|
||||||
switch(command_[0] & 0x1f) {
|
switch(command_[0] & 0x1f) {
|
||||||
case 0x03: // specify
|
case CommandReadData:
|
||||||
case 0x04: // sense drive status
|
case CommandReadDeletedData:
|
||||||
case 0x07: // recalibrate
|
case CommandWriteData:
|
||||||
case 0x08: // sense interrupt status
|
case CommandWriteDeletedData:
|
||||||
case 0x0f: // seek
|
case CommandReadTrack:
|
||||||
|
case CommandReadID:
|
||||||
|
case CommandFormatTrack:
|
||||||
|
case CommandScanLow:
|
||||||
|
case CommandScanLowOrEqual:
|
||||||
|
case CommandScanHighOrEqual:
|
||||||
|
is_access_command_ = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
for(int c = 0; c < 4; c++) {
|
is_access_command_ = false;
|
||||||
if(drives_[c].phase == Drive::Seeking) {
|
|
||||||
drives_[c].phase = Drive::NotSeeking;
|
|
||||||
drives_seeking_--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(is_access_command_) {
|
||||||
|
for(int c = 0; c < 4; c++) {
|
||||||
|
if(drives_[c].phase == Drive::Seeking) {
|
||||||
|
drives_[c].phase = Drive::NotSeeking;
|
||||||
|
drives_seeking_--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Establishes the drive and head being addressed, and whether in double density mode; populates the internal
|
||||||
|
// cylinder, head, sector and size registers from the command stream.
|
||||||
|
if(!dma_mode_) SetNonDMAExecution();
|
||||||
|
SET_DRIVE_HEAD_MFM();
|
||||||
|
LOAD_HEAD();
|
||||||
|
}
|
||||||
|
|
||||||
// Jump to the proper place.
|
// Jump to the proper place.
|
||||||
switch(command_[0] & 0x1f) {
|
switch(command_[0] & 0x1f) {
|
||||||
case 0x06: // read data
|
case CommandReadData:
|
||||||
case 0x0c: // read deleted data
|
case CommandReadDeletedData:
|
||||||
goto read_data;
|
goto read_data;
|
||||||
|
|
||||||
case 0x05: // write data
|
case CommandWriteData:
|
||||||
case 0x09: // write deleted data
|
case CommandWriteDeletedData:
|
||||||
goto write_data;
|
goto write_data;
|
||||||
|
|
||||||
case 0x02: goto read_track;
|
case CommandReadTrack: goto read_track;
|
||||||
case 0x0a: goto read_id;
|
case CommandReadID: goto read_id;
|
||||||
case 0x0d: goto format_track;
|
case CommandFormatTrack: goto format_track;
|
||||||
case 0x11: goto scan_low;
|
|
||||||
case 0x19: goto scan_low_or_equal;
|
|
||||||
case 0x1d: goto scan_high_or_equal;
|
|
||||||
case 0x07: goto recalibrate;
|
|
||||||
case 0x08: goto sense_interrupt_status;
|
|
||||||
case 0x03: goto specify;
|
|
||||||
case 0x04: goto sense_drive_status;
|
|
||||||
case 0x0f: goto seek;
|
|
||||||
|
|
||||||
default: goto invalid;
|
case CommandScanLow: goto scan_low;
|
||||||
|
case CommandScanLowOrEqual: goto scan_low_or_equal;
|
||||||
|
case CommandScanHighOrEqual: goto scan_high_or_equal;
|
||||||
|
|
||||||
|
case CommandRecalibrate: goto recalibrate;
|
||||||
|
case CommandSeek: goto seek;
|
||||||
|
|
||||||
|
case CommandSenseInterruptStatus: goto sense_interrupt_status;
|
||||||
|
case CommandSpecify: goto specify;
|
||||||
|
case CommandSenseDriveStatus: goto sense_drive_status;
|
||||||
|
|
||||||
|
default: goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decodes drive, head and density, loads the head, loads the internal cylinder, head, sector and size registers,
|
// Decodes drive, head and density, loads the head, loads the internal cylinder, head, sector and size registers,
|
||||||
// and searches for a sector that meets those criteria. If one is found, inspects the instruction in use and
|
// and searches for a sector that meets those criteria. If one is found, inspects the instruction in use and
|
||||||
// jumps to an appropriate handler.
|
// jumps to an appropriate handler.
|
||||||
read_write_find_header:
|
read_write_find_header:
|
||||||
// Establishes the drive and head being addressed, and whether in double density mode; populates the internal
|
|
||||||
// cylinder, head, sector and size registers from the command stream.
|
|
||||||
LOAD_HEAD();
|
|
||||||
|
|
||||||
// Sets a maximum index hole limit of 2 then performs a find header/read header loop, continuing either until
|
// Sets a maximum index hole limit of 2 then performs a find header/read header loop, continuing either until
|
||||||
// the index hole limit is breached or a sector is found with a cylinder, head, sector and size equal to the
|
// the index hole limit is breached or a sector is found with a cylinder, head, sector and size equal to the
|
||||||
// values in the internal registers.
|
// values in the internal registers.
|
||||||
index_hole_limit_ = 2;
|
index_hole_limit_ = 2;
|
||||||
set_data_mode(DataMode::Scanning);
|
|
||||||
// printf("Seeking %02x %02x %02x %02x\n", cylinder_, head_, sector_, size_);
|
// printf("Seeking %02x %02x %02x %02x\n", cylinder_, head_, sector_, size_);
|
||||||
find_next_sector:
|
find_next_sector:
|
||||||
FIND_HEADER();
|
FIND_HEADER();
|
||||||
@ -322,38 +369,39 @@ void i8272::posit_event(int event_type) {
|
|||||||
// Two index holes have passed wihout finding the header sought.
|
// Two index holes have passed wihout finding the header sought.
|
||||||
// printf("Not found\n");
|
// printf("Not found\n");
|
||||||
SetNoData();
|
SetNoData();
|
||||||
goto abort_read_write;
|
goto abort;
|
||||||
}
|
}
|
||||||
|
index_hole_count_ = 0;
|
||||||
|
// printf("Header\n");
|
||||||
READ_HEADER();
|
READ_HEADER();
|
||||||
|
if(index_hole_count_) {
|
||||||
|
// This implies an index hole was sighted within the header. Error out.
|
||||||
|
SetEndOfCylinder();
|
||||||
|
goto abort;
|
||||||
|
}
|
||||||
if(get_crc_generator().get_value()) {
|
if(get_crc_generator().get_value()) {
|
||||||
// This implies a CRC error in the header; mark as such but continue.
|
// This implies a CRC error in the header; mark as such but continue.
|
||||||
SetDataError();
|
SetDataError();
|
||||||
}
|
}
|
||||||
// printf("Considering %02x %02x %02x %02x\n", header_[0], header_[1], header_[2], header_[3]);
|
// printf("Considering %02x %02x %02x %02x [%04x]\n", header_[0], header_[1], header_[2], header_[3], get_crc_generator().get_value());
|
||||||
if(header_[0] != cylinder_ || header_[1] != head_ || header_[2] != sector_ || header_[3] != size_) goto find_next_sector;
|
if(header_[0] != cylinder_ || header_[1] != head_ || header_[2] != sector_ || header_[3] != size_) goto find_next_sector;
|
||||||
|
|
||||||
// Branch to whatever is supposed to happen next
|
// Branch to whatever is supposed to happen next
|
||||||
// printf("Proceeding\n");
|
// printf("Proceeding\n");
|
||||||
switch(command_[0] & 0x1f) {
|
switch(command_[0] & 0x1f) {
|
||||||
case 0x06: // read data
|
case CommandReadData:
|
||||||
case 0x0c: // read deleted data
|
case CommandReadDeletedData:
|
||||||
goto read_data_found_header;
|
goto read_data_found_header;
|
||||||
|
|
||||||
case 0x05: // write data
|
case CommandWriteData: // write data
|
||||||
case 0x09: // write deleted data
|
case CommandWriteDeletedData: // write deleted data
|
||||||
goto write_data_found_header;
|
goto write_data_found_header;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets abnormal termination of the current command and proceeds to an ST0, ST1, ST2, C, H, R, N result phase.
|
|
||||||
abort_read_write:
|
|
||||||
SetAbnormalTermination();
|
|
||||||
goto post_st012chrn;
|
|
||||||
|
|
||||||
// Performs the read data or read deleted data command.
|
// Performs the read data or read deleted data command.
|
||||||
read_data:
|
read_data:
|
||||||
printf("Read data [%02x %02x %02x %02x ... %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[8]);
|
printf("Read [deleted] data [%02x %02x %02x %02x ... %02x %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[6], command_[8]);
|
||||||
if(!dma_mode_) SetNonDMAExecution();
|
|
||||||
SET_DRIVE_HEAD_MFM();
|
|
||||||
read_next_data:
|
read_next_data:
|
||||||
goto read_write_find_header;
|
goto read_write_find_header;
|
||||||
|
|
||||||
@ -361,14 +409,27 @@ void i8272::posit_event(int event_type) {
|
|||||||
// flag doesn't match the sort the command was looking for.
|
// flag doesn't match the sort the command was looking for.
|
||||||
read_data_found_header:
|
read_data_found_header:
|
||||||
FIND_DATA();
|
FIND_DATA();
|
||||||
if((get_latest_token().type == Token::Data) != ((command_[0]&0xf) == 0x6)) {
|
if(event_type == (int)Event::Token) {
|
||||||
if(!(command_[0]&0x20)) {
|
if(get_latest_token().type != Token::Data && get_latest_token().type != Token::DeletedData) {
|
||||||
// SK is not set; set the error flag but read this sector before finishing.
|
// Something other than a data mark came next — impliedly an ID or index mark.
|
||||||
SetControlMark();
|
SetMissingAddressMark();
|
||||||
|
SetMissingDataAddressMark();
|
||||||
|
goto abort; // TODO: or read_next_data?
|
||||||
} else {
|
} else {
|
||||||
// SK is set; skip this sector.
|
if((get_latest_token().type == Token::Data) != ((command_[0] & 0x1f) == CommandReadData)) {
|
||||||
goto read_next_data;
|
if(!(command_[0]&0x20)) {
|
||||||
|
// SK is not set; set the error flag but read this sector before finishing.
|
||||||
|
SetControlMark();
|
||||||
|
} else {
|
||||||
|
// SK is set; skip this sector.
|
||||||
|
goto read_next_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// An index hole appeared before the data mark.
|
||||||
|
SetEndOfCylinder();
|
||||||
|
goto abort; // TODO: or read_next_data?
|
||||||
}
|
}
|
||||||
|
|
||||||
distance_into_section_ = 0;
|
distance_into_section_ = 0;
|
||||||
@ -379,12 +440,14 @@ void i8272::posit_event(int event_type) {
|
|||||||
//
|
//
|
||||||
// TODO: consider DTL.
|
// TODO: consider DTL.
|
||||||
read_data_get_byte:
|
read_data_get_byte:
|
||||||
WAIT_FOR_EVENT(Event::Token);
|
WAIT_FOR_EVENT((int)Event::Token | (int)Event::IndexHole);
|
||||||
result_stack_.push_back(get_latest_token().byte_value);
|
if(event_type == (int)Event::Token) {
|
||||||
distance_into_section_++;
|
result_stack_.push_back(get_latest_token().byte_value);
|
||||||
SetDataRequest();
|
distance_into_section_++;
|
||||||
SetDataDirectionToProcessor();
|
SetDataRequest();
|
||||||
WAIT_FOR_EVENT((int)Event8272::ResultEmpty | (int)Event::Token | (int)Event::IndexHole);
|
SetDataDirectionToProcessor();
|
||||||
|
WAIT_FOR_EVENT((int)Event8272::ResultEmpty | (int)Event::Token | (int)Event::IndexHole);
|
||||||
|
}
|
||||||
switch(event_type) {
|
switch(event_type) {
|
||||||
case (int)Event8272::ResultEmpty: // The caller read the byte in time; proceed as normal.
|
case (int)Event8272::ResultEmpty: // The caller read the byte in time; proceed as normal.
|
||||||
ResetDataRequest();
|
ResetDataRequest();
|
||||||
@ -392,9 +455,11 @@ void i8272::posit_event(int event_type) {
|
|||||||
break;
|
break;
|
||||||
case (int)Event::Token: // The caller hasn't read the old byte yet and a new one has arrived
|
case (int)Event::Token: // The caller hasn't read the old byte yet and a new one has arrived
|
||||||
SetOverrun();
|
SetOverrun();
|
||||||
goto abort_read_write;
|
goto abort;
|
||||||
break;
|
break;
|
||||||
case (int)Event::IndexHole:
|
case (int)Event::IndexHole:
|
||||||
|
SetEndOfCylinder();
|
||||||
|
goto abort;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,7 +470,7 @@ void i8272::posit_event(int event_type) {
|
|||||||
// This implies a CRC error in the sector; mark as such and temrinate.
|
// This implies a CRC error in the sector; mark as such and temrinate.
|
||||||
SetDataError();
|
SetDataError();
|
||||||
SetDataFieldDataError();
|
SetDataFieldDataError();
|
||||||
goto abort_read_write;
|
goto abort;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether that's it: either the final requested sector has been read, or because
|
// check whether that's it: either the final requested sector has been read, or because
|
||||||
@ -419,26 +484,24 @@ void i8272::posit_event(int event_type) {
|
|||||||
goto post_st012chrn;
|
goto post_st012chrn;
|
||||||
|
|
||||||
write_data:
|
write_data:
|
||||||
printf("Write [deleted] data\n");
|
printf("Write [deleted] data [%02x %02x %02x %02x ... %02x %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[6], command_[8]);
|
||||||
if(!dma_mode_) SetNonDMAExecution();
|
|
||||||
SET_DRIVE_HEAD_MFM();
|
|
||||||
|
|
||||||
if(drives_[active_drive_].drive->get_is_read_only()) {
|
if(drives_[active_drive_].drive->get_is_read_only()) {
|
||||||
SetNotWriteable();
|
SetNotWriteable();
|
||||||
goto abort_read_write;
|
goto abort;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_next_data:
|
write_next_data:
|
||||||
goto read_write_find_header;
|
goto read_write_find_header;
|
||||||
|
|
||||||
write_data_found_header:
|
write_data_found_header:
|
||||||
begin_writing();
|
WAIT_FOR_BYTES(get_is_double_density() ? 22 : 11);
|
||||||
|
begin_writing(true);
|
||||||
|
|
||||||
write_id_data_joiner((command_[0] & 0x1f) == 0x09);
|
write_id_data_joiner((command_[0] & 0x1f) == CommandWriteDeletedData, true);
|
||||||
|
|
||||||
SetDataDirectionFromProcessor();
|
SetDataDirectionFromProcessor();
|
||||||
SetDataRequest();
|
SetDataRequest();
|
||||||
WAIT_FOR_EVENT(Event::DataWritten);
|
|
||||||
expects_input_ = true;
|
expects_input_ = true;
|
||||||
distance_into_section_ = 0;
|
distance_into_section_ = 0;
|
||||||
|
|
||||||
@ -447,7 +510,7 @@ void i8272::posit_event(int event_type) {
|
|||||||
if(!has_input_) {
|
if(!has_input_) {
|
||||||
SetOverrun();
|
SetOverrun();
|
||||||
end_writing();
|
end_writing();
|
||||||
goto abort_read_write;
|
goto abort;
|
||||||
}
|
}
|
||||||
write_byte(input_);
|
write_byte(input_);
|
||||||
has_input_ = false;
|
has_input_ = false;
|
||||||
@ -457,19 +520,23 @@ void i8272::posit_event(int event_type) {
|
|||||||
goto write_loop;
|
goto write_loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("Wrote %d bytes\n", distance_into_section_);
|
||||||
write_crc();
|
write_crc();
|
||||||
expects_input_ = false;
|
expects_input_ = false;
|
||||||
WAIT_FOR_EVENT(Event::DataWritten);
|
WAIT_FOR_EVENT(Event::DataWritten);
|
||||||
end_writing();
|
end_writing();
|
||||||
|
|
||||||
|
if(sector_ != command_[6]) {
|
||||||
|
sector_++;
|
||||||
|
goto write_next_data;
|
||||||
|
}
|
||||||
|
|
||||||
goto post_st012chrn;
|
goto post_st012chrn;
|
||||||
|
|
||||||
// Performs the read ID command.
|
// Performs the read ID command.
|
||||||
read_id:
|
read_id:
|
||||||
// Establishes the drive and head being addressed, and whether in double density mode.
|
// Establishes the drive and head being addressed, and whether in double density mode.
|
||||||
printf("Read ID\n");
|
printf("Read ID [%02x %02x]\n", command_[0], command_[1]);
|
||||||
SET_DRIVE_HEAD_MFM();
|
|
||||||
LOAD_HEAD();
|
|
||||||
|
|
||||||
// Sets a maximum index hole limit of 2 then waits either until it finds a header mark or sees too many index holes.
|
// Sets a maximum index hole limit of 2 then waits either until it finds a header mark or sees too many index holes.
|
||||||
// If a header mark is found, reads in the following bytes that produce a header. Otherwise branches to data not found.
|
// If a header mark is found, reads in the following bytes that produce a header. Otherwise branches to data not found.
|
||||||
@ -477,8 +544,8 @@ void i8272::posit_event(int event_type) {
|
|||||||
read_id_find_next_sector:
|
read_id_find_next_sector:
|
||||||
FIND_HEADER();
|
FIND_HEADER();
|
||||||
if(!index_hole_limit_) {
|
if(!index_hole_limit_) {
|
||||||
SetNoData();
|
SetMissingAddressMark();
|
||||||
goto abort_read_write;
|
goto abort;
|
||||||
}
|
}
|
||||||
READ_HEADER();
|
READ_HEADER();
|
||||||
|
|
||||||
@ -493,8 +560,6 @@ void i8272::posit_event(int event_type) {
|
|||||||
// Performs read track.
|
// Performs read track.
|
||||||
read_track:
|
read_track:
|
||||||
printf("Read track [%02x %02x %02x %02x]\n", command_[2], command_[3], command_[4], command_[5]);
|
printf("Read track [%02x %02x %02x %02x]\n", command_[2], command_[3], command_[4], command_[5]);
|
||||||
if(!dma_mode_) SetNonDMAExecution();
|
|
||||||
SET_DRIVE_HEAD_MFM();
|
|
||||||
|
|
||||||
// Wait for the index hole.
|
// Wait for the index hole.
|
||||||
WAIT_FOR_EVENT(Event::IndexHole);
|
WAIT_FOR_EVENT(Event::IndexHole);
|
||||||
@ -508,7 +573,7 @@ void i8272::posit_event(int event_type) {
|
|||||||
if(!index_hole_limit_) {
|
if(!index_hole_limit_) {
|
||||||
if(!sector_) {
|
if(!sector_) {
|
||||||
SetMissingAddressMark();
|
SetMissingAddressMark();
|
||||||
goto abort_read_write;
|
goto abort;
|
||||||
} else {
|
} else {
|
||||||
goto post_st012chrn;
|
goto post_st012chrn;
|
||||||
}
|
}
|
||||||
@ -536,18 +601,15 @@ void i8272::posit_event(int event_type) {
|
|||||||
// Performs format [/write] track.
|
// Performs format [/write] track.
|
||||||
format_track:
|
format_track:
|
||||||
printf("Format track\n");
|
printf("Format track\n");
|
||||||
if(!dma_mode_) SetNonDMAExecution();
|
|
||||||
SET_DRIVE_HEAD_MFM();
|
|
||||||
if(drives_[active_drive_].drive->get_is_read_only()) {
|
if(drives_[active_drive_].drive->get_is_read_only()) {
|
||||||
SetNotWriteable();
|
SetNotWriteable();
|
||||||
goto abort_read_write;
|
goto abort;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOAD_HEAD();
|
|
||||||
|
|
||||||
// Wait for the index hole.
|
// Wait for the index hole.
|
||||||
WAIT_FOR_EVENT(Event::IndexHole);
|
WAIT_FOR_EVENT(Event::IndexHole);
|
||||||
begin_writing();
|
index_hole_count_ = 0;
|
||||||
|
begin_writing(true);
|
||||||
|
|
||||||
// Write start-of-track.
|
// Write start-of-track.
|
||||||
write_start_of_track();
|
write_start_of_track();
|
||||||
@ -564,20 +626,30 @@ void i8272::posit_event(int event_type) {
|
|||||||
expects_input_ = true;
|
expects_input_ = true;
|
||||||
distance_into_section_ = 0;
|
distance_into_section_ = 0;
|
||||||
format_track_write_header:
|
format_track_write_header:
|
||||||
WAIT_FOR_EVENT(Event::DataWritten);
|
WAIT_FOR_EVENT((int)Event::DataWritten | (int)Event::IndexHole);
|
||||||
// TODO: overrun?
|
switch(event_type) {
|
||||||
header_[distance_into_section_] = input_;
|
case (int)Event::IndexHole:
|
||||||
write_byte(input_);
|
SetOverrun();
|
||||||
has_input_ = false;
|
end_writing();
|
||||||
distance_into_section_++;
|
goto abort;
|
||||||
if(distance_into_section_ < 4) {
|
break;
|
||||||
SetDataRequest();
|
case (int)Event::DataWritten:
|
||||||
goto format_track_write_header;
|
header_[distance_into_section_] = input_;
|
||||||
|
write_byte(input_);
|
||||||
|
has_input_ = false;
|
||||||
|
distance_into_section_++;
|
||||||
|
if(distance_into_section_ < 4) {
|
||||||
|
SetDataRequest();
|
||||||
|
goto format_track_write_header;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("W: %02x %02x %02x %02x, %04x\n", header_[0], header_[1], header_[2], header_[3], get_crc_generator().get_value());
|
||||||
write_crc();
|
write_crc();
|
||||||
|
|
||||||
// Write the sector body.
|
// Write the sector body.
|
||||||
write_id_data_joiner(false);
|
write_id_data_joiner(false, false);
|
||||||
write_n_bytes(128 << command_[2], command_[5]);
|
write_n_bytes(128 << command_[2], command_[5]);
|
||||||
write_crc();
|
write_crc();
|
||||||
|
|
||||||
@ -586,7 +658,7 @@ void i8272::posit_event(int event_type) {
|
|||||||
|
|
||||||
// Consider repeating.
|
// Consider repeating.
|
||||||
sector_++;
|
sector_++;
|
||||||
if(sector_ < command_[3])
|
if(sector_ < command_[3] && !index_hole_count_)
|
||||||
goto format_track_write_sector;
|
goto format_track_write_sector;
|
||||||
|
|
||||||
// Otherwise, pad out to the index hole.
|
// Otherwise, pad out to the index hole.
|
||||||
@ -717,6 +789,11 @@ void i8272::posit_event(int event_type) {
|
|||||||
result_stack_.push_back(0x80);
|
result_stack_.push_back(0x80);
|
||||||
goto post_result;
|
goto post_result;
|
||||||
|
|
||||||
|
// Sets abnormal termination of the current command and proceeds to an ST0, ST1, ST2, C, H, R, N result phase.
|
||||||
|
abort:
|
||||||
|
SetAbnormalTermination();
|
||||||
|
goto post_st012chrn;
|
||||||
|
|
||||||
// Posts ST0, ST1, ST2, C, H, R and N as a result phase.
|
// Posts ST0, ST1, ST2, C, H, R and N as a result phase.
|
||||||
post_st012chrn:
|
post_st012chrn:
|
||||||
SCHEDULE_HEAD_UNLOAD();
|
SCHEDULE_HEAD_UNLOAD();
|
||||||
@ -735,7 +812,7 @@ void i8272::posit_event(int event_type) {
|
|||||||
// Posts whatever is in result_stack_ as a result phase. Be aware that it is a stack — the
|
// Posts whatever is in result_stack_ as a result phase. Be aware that it is a stack — the
|
||||||
// last thing in it will be returned first.
|
// last thing in it will be returned first.
|
||||||
post_result:
|
post_result:
|
||||||
printf("Result to %02x: ", command_[0] & 0x1f);
|
printf("Result to %02x, main %02x: ", command_[0] & 0x1f, main_status_);
|
||||||
for(size_t c = 0; c < result_stack_.size(); c++) {
|
for(size_t c = 0; c < result_stack_.size(); c++) {
|
||||||
printf("%02x ", result_stack_[result_stack_.size() - 1 - c]);
|
printf("%02x ", result_stack_[result_stack_.size() - 1 - c]);
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,7 @@ class i8272: public Storage::Disk::MFMController {
|
|||||||
void posit_event(int type);
|
void posit_event(int type);
|
||||||
int interesting_event_mask_;
|
int interesting_event_mask_;
|
||||||
int resume_point_;
|
int resume_point_;
|
||||||
|
bool is_access_command_;
|
||||||
|
|
||||||
// The counter used for ::Timer events.
|
// The counter used for ::Timer events.
|
||||||
int delay_time_;
|
int delay_time_;
|
||||||
@ -117,7 +118,7 @@ class i8272: public Storage::Disk::MFMController {
|
|||||||
// Transient storage and counters used while reading the disk.
|
// Transient storage and counters used while reading the disk.
|
||||||
uint8_t header_[6];
|
uint8_t header_[6];
|
||||||
int distance_into_section_;
|
int distance_into_section_;
|
||||||
int index_hole_limit_;
|
int index_hole_count_, index_hole_limit_;
|
||||||
|
|
||||||
// Keeps track of the drive and head in use during commands.
|
// Keeps track of the drive and head in use during commands.
|
||||||
int active_drive_;
|
int active_drive_;
|
||||||
|
@ -121,6 +121,7 @@
|
|||||||
4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B31D327F7E00DF5BA0 /* G64.cpp */; };
|
4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B31D327F7E00DF5BA0 /* G64.cpp */; };
|
||||||
4BAB62B81D3302CA00DF5BA0 /* PCMTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */; };
|
4BAB62B81D3302CA00DF5BA0 /* PCMTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */; };
|
||||||
4BACC5B11F3DFF7C0037C015 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BACC5AF1F3DFF7C0037C015 /* CharacterMapper.cpp */; };
|
4BACC5B11F3DFF7C0037C015 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BACC5AF1F3DFF7C0037C015 /* CharacterMapper.cpp */; };
|
||||||
|
4BAD9B961F43D7E900724854 /* UnformattedTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAD9B941F43D7E900724854 /* UnformattedTrack.cpp */; };
|
||||||
4BB17D4E1ED7909F00ABD1E1 /* tests.expected.json in Resources */ = {isa = PBXBuildFile; fileRef = 4BB17D4C1ED7909F00ABD1E1 /* tests.expected.json */; };
|
4BB17D4E1ED7909F00ABD1E1 /* tests.expected.json in Resources */ = {isa = PBXBuildFile; fileRef = 4BB17D4C1ED7909F00ABD1E1 /* tests.expected.json */; };
|
||||||
4BB17D4F1ED7909F00ABD1E1 /* tests.in.json in Resources */ = {isa = PBXBuildFile; fileRef = 4BB17D4D1ED7909F00ABD1E1 /* tests.in.json */; };
|
4BB17D4F1ED7909F00ABD1E1 /* tests.in.json in Resources */ = {isa = PBXBuildFile; fileRef = 4BB17D4D1ED7909F00ABD1E1 /* tests.in.json */; };
|
||||||
4BB298F11B587D8400A49093 /* start in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E51B587D8300A49093 /* start */; };
|
4BB298F11B587D8400A49093 /* start in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E51B587D8300A49093 /* start */; };
|
||||||
@ -673,6 +674,8 @@
|
|||||||
4BAB62B71D3302CA00DF5BA0 /* PCMTrack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCMTrack.hpp; sourceTree = "<group>"; };
|
4BAB62B71D3302CA00DF5BA0 /* PCMTrack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCMTrack.hpp; sourceTree = "<group>"; };
|
||||||
4BACC5AF1F3DFF7C0037C015 /* CharacterMapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CharacterMapper.cpp; path = AmstradCPC/CharacterMapper.cpp; sourceTree = "<group>"; };
|
4BACC5AF1F3DFF7C0037C015 /* CharacterMapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CharacterMapper.cpp; path = AmstradCPC/CharacterMapper.cpp; sourceTree = "<group>"; };
|
||||||
4BACC5B01F3DFF7C0037C015 /* CharacterMapper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CharacterMapper.hpp; path = AmstradCPC/CharacterMapper.hpp; sourceTree = "<group>"; };
|
4BACC5B01F3DFF7C0037C015 /* CharacterMapper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CharacterMapper.hpp; path = AmstradCPC/CharacterMapper.hpp; sourceTree = "<group>"; };
|
||||||
|
4BAD9B941F43D7E900724854 /* UnformattedTrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UnformattedTrack.cpp; sourceTree = "<group>"; };
|
||||||
|
4BAD9B951F43D7E900724854 /* UnformattedTrack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UnformattedTrack.hpp; sourceTree = "<group>"; };
|
||||||
4BB06B211F316A3F00600C7A /* ForceInline.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ForceInline.h; sourceTree = "<group>"; };
|
4BB06B211F316A3F00600C7A /* ForceInline.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ForceInline.h; sourceTree = "<group>"; };
|
||||||
4BB17D4C1ED7909F00ABD1E1 /* tests.expected.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.expected.json; path = FUSE/tests.expected.json; sourceTree = "<group>"; };
|
4BB17D4C1ED7909F00ABD1E1 /* tests.expected.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.expected.json; path = FUSE/tests.expected.json; sourceTree = "<group>"; };
|
||||||
4BB17D4D1ED7909F00ABD1E1 /* tests.in.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.in.json; path = FUSE/tests.in.json; sourceTree = "<group>"; };
|
4BB17D4D1ED7909F00ABD1E1 /* tests.in.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.in.json; path = FUSE/tests.in.json; sourceTree = "<group>"; };
|
||||||
@ -1552,6 +1555,7 @@
|
|||||||
4B3F1B441E0388D200DB26EE /* PCMPatchedTrack.cpp */,
|
4B3F1B441E0388D200DB26EE /* PCMPatchedTrack.cpp */,
|
||||||
4B121F961E060CF000BFDA12 /* PCMSegment.cpp */,
|
4B121F961E060CF000BFDA12 /* PCMSegment.cpp */,
|
||||||
4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */,
|
4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */,
|
||||||
|
4BAD9B941F43D7E900724854 /* UnformattedTrack.cpp */,
|
||||||
4B0BE4271D3481E700D5256B /* DigitalPhaseLockedLoop.hpp */,
|
4B0BE4271D3481E700D5256B /* DigitalPhaseLockedLoop.hpp */,
|
||||||
4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */,
|
4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */,
|
||||||
4B6C73BC1D387AE500AFCFCA /* DiskController.hpp */,
|
4B6C73BC1D387AE500AFCFCA /* DiskController.hpp */,
|
||||||
@ -1560,6 +1564,7 @@
|
|||||||
4B3F1B451E0388D200DB26EE /* PCMPatchedTrack.hpp */,
|
4B3F1B451E0388D200DB26EE /* PCMPatchedTrack.hpp */,
|
||||||
4B121F971E060CF000BFDA12 /* PCMSegment.hpp */,
|
4B121F971E060CF000BFDA12 /* PCMSegment.hpp */,
|
||||||
4BAB62B71D3302CA00DF5BA0 /* PCMTrack.hpp */,
|
4BAB62B71D3302CA00DF5BA0 /* PCMTrack.hpp */,
|
||||||
|
4BAD9B951F43D7E900724854 /* UnformattedTrack.hpp */,
|
||||||
4BB697CF1D4BA44900248BDF /* Encodings */,
|
4BB697CF1D4BA44900248BDF /* Encodings */,
|
||||||
4BAB62B21D327F7E00DF5BA0 /* Formats */,
|
4BAB62B21D327F7E00DF5BA0 /* Formats */,
|
||||||
4B3FE75F1F3CF6BA00448EE4 /* Parsers */,
|
4B3FE75F1F3CF6BA00448EE4 /* Parsers */,
|
||||||
@ -2709,6 +2714,7 @@
|
|||||||
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */,
|
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */,
|
||||||
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
|
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
|
||||||
4B95FA9D1F11893B0008E395 /* ZX8081OptionsPanel.swift in Sources */,
|
4B95FA9D1F11893B0008E395 /* ZX8081OptionsPanel.swift in Sources */,
|
||||||
|
4BAD9B961F43D7E900724854 /* UnformattedTrack.cpp in Sources */,
|
||||||
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
|
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
|
||||||
4B8378DC1F336631005CA9E4 /* CharacterMapper.cpp in Sources */,
|
4B8378DC1F336631005CA9E4 /* CharacterMapper.cpp in Sources */,
|
||||||
4B8378E51F3378C4005CA9E4 /* CharacterMapper.cpp in Sources */,
|
4B8378E51F3378C4005CA9E4 /* CharacterMapper.cpp in Sources */,
|
||||||
|
@ -28,6 +28,9 @@ void Disk::set_track_at_position(unsigned int head, unsigned int position, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Track> Disk::get_track_at_position(unsigned int head, unsigned int position) {
|
std::shared_ptr<Track> Disk::get_track_at_position(unsigned int head, unsigned int position) {
|
||||||
|
if(head >= get_head_count()) return nullptr;
|
||||||
|
if(position >= get_head_position_count()) return nullptr;
|
||||||
|
|
||||||
int address = get_id_for_track_at_position(head, position);
|
int address = get_id_for_track_at_position(head, position);
|
||||||
std::map<int, std::shared_ptr<Track>>::iterator cached_track = cached_tracks_.find(address);
|
std::map<int, std::shared_ptr<Track>>::iterator cached_track = cached_tracks_.find(address);
|
||||||
if(cached_track != cached_tracks_.end()) return cached_track->second;
|
if(cached_track != cached_tracks_.end()) return cached_track->second;
|
||||||
|
@ -7,7 +7,9 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "DiskController.hpp"
|
#include "DiskController.hpp"
|
||||||
|
#include "UnformattedTrack.hpp"
|
||||||
#include "../../NumberTheory/Factors.hpp"
|
#include "../../NumberTheory/Factors.hpp"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
using namespace Storage::Disk;
|
using namespace Storage::Disk;
|
||||||
|
|
||||||
@ -29,13 +31,17 @@ Controller::Controller(Cycles clock_rate, int clock_rate_multiplier, int revolut
|
|||||||
|
|
||||||
void Controller::setup_track() {
|
void Controller::setup_track() {
|
||||||
track_ = drive_->get_track();
|
track_ = drive_->get_track();
|
||||||
|
if(!track_) {
|
||||||
|
track_.reset(new UnformattedTrack);
|
||||||
|
}
|
||||||
|
|
||||||
Time offset;
|
Time offset;
|
||||||
Time track_time_now = get_time_into_track();
|
Time track_time_now = get_time_into_track();
|
||||||
if(track_) {
|
assert(track_time_now >= Time(0) && current_event_.length <= Time(1));
|
||||||
Time time_found = track_->seek_to(track_time_now);
|
|
||||||
offset = track_time_now - time_found;
|
Time time_found = track_->seek_to(track_time_now);
|
||||||
}
|
assert(time_found >= Time(0) && time_found <= track_time_now);
|
||||||
|
offset = track_time_now - time_found;
|
||||||
|
|
||||||
get_next_event(offset);
|
get_next_event(offset);
|
||||||
}
|
}
|
||||||
@ -93,7 +99,9 @@ void Controller::get_next_event(const Time &duration_already_passed) {
|
|||||||
|
|
||||||
// divide interval, which is in terms of a single rotation of the disk, by rotation speed to
|
// divide interval, which is in terms of a single rotation of the disk, by rotation speed to
|
||||||
// convert it into revolutions per second; this is achieved by multiplying by rotational_multiplier_
|
// convert it into revolutions per second; this is achieved by multiplying by rotational_multiplier_
|
||||||
set_next_event_time_interval((current_event_.length - duration_already_passed) * rotational_multiplier_);
|
assert(current_event_.length <= Time(1) && current_event_.length >= Time(0));
|
||||||
|
Time interval = (current_event_.length - duration_already_passed) * rotational_multiplier_;
|
||||||
|
set_next_event_time_interval(interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::process_next_event()
|
void Controller::process_next_event()
|
||||||
@ -121,8 +129,9 @@ Storage::Time Controller::get_time_into_track() {
|
|||||||
|
|
||||||
#pragma mark - Writing
|
#pragma mark - Writing
|
||||||
|
|
||||||
void Controller::begin_writing() {
|
void Controller::begin_writing(bool clamp_to_index_hole) {
|
||||||
is_reading_ = false;
|
is_reading_ = false;
|
||||||
|
clamp_writing_to_index_hole_ = clamp_to_index_hole;
|
||||||
|
|
||||||
write_segment_.length_of_a_bit = bit_length_ / rotational_multiplier_;
|
write_segment_.length_of_a_bit = bit_length_ / rotational_multiplier_;
|
||||||
write_segment_.data.clear();
|
write_segment_.data.clear();
|
||||||
@ -150,7 +159,8 @@ void Controller::end_writing() {
|
|||||||
patched_track_.reset(new PCMPatchedTrack(track_));
|
patched_track_.reset(new PCMPatchedTrack(track_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
patched_track_->add_segment(write_start_time_, write_segment_);
|
patched_track_->add_segment(write_start_time_, write_segment_, clamp_writing_to_index_hole_);
|
||||||
|
cycles_since_index_hole_ %= 8000000 * clock_rate_multiplier_;
|
||||||
invalidate_track(); // TEMPORARY: to force a seek
|
invalidate_track(); // TEMPORARY: to force a seek
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,8 +215,7 @@ bool Controller::get_motor_on() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Controller::set_drive(std::shared_ptr<Drive> drive) {
|
void Controller::set_drive(std::shared_ptr<Drive> drive) {
|
||||||
if(drive_ != drive)
|
if(drive_ != drive) {
|
||||||
{
|
|
||||||
invalidate_track();
|
invalidate_track();
|
||||||
drive_ = drive;
|
drive_ = drive;
|
||||||
}
|
}
|
||||||
|
@ -72,8 +72,11 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop
|
|||||||
@c write_bit. They will be written with the length set via @c set_expected_bit_length.
|
@c write_bit. They will be written with the length set via @c set_expected_bit_length.
|
||||||
It is acceptable to supply a backlog of bits. Flux transition events will not be reported
|
It is acceptable to supply a backlog of bits. Flux transition events will not be reported
|
||||||
while writing.
|
while writing.
|
||||||
|
|
||||||
|
@param clamp_to_index_hole If @c true then writing will automatically be truncated by
|
||||||
|
the index hole. Writing will continue over the index hole otherwise.
|
||||||
*/
|
*/
|
||||||
void begin_writing();
|
void begin_writing(bool clamp_to_index_hole);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Writes the bit @c value as the next in the PCM stream initiated by @c begin_writing.
|
Writes the bit @c value as the next in the PCM stream initiated by @c begin_writing.
|
||||||
@ -129,6 +132,7 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop
|
|||||||
bool motor_is_on_;
|
bool motor_is_on_;
|
||||||
|
|
||||||
bool is_reading_;
|
bool is_reading_;
|
||||||
|
bool clamp_writing_to_index_hole_;
|
||||||
std::shared_ptr<PCMPatchedTrack> patched_track_;
|
std::shared_ptr<PCMPatchedTrack> patched_track_;
|
||||||
PCMSegment write_segment_;
|
PCMSegment write_segment_;
|
||||||
Time write_start_time_;
|
Time write_start_time_;
|
||||||
|
@ -199,15 +199,15 @@ void MFMController::write_id_joiner() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MFMController::write_id_data_joiner(bool is_deleted) {
|
void MFMController::write_id_data_joiner(bool is_deleted, bool skip_first_gap) {
|
||||||
if(get_is_double_density()) {
|
if(get_is_double_density()) {
|
||||||
write_n_bytes(22, 0x4e);
|
if(!skip_first_gap) write_n_bytes(22, 0x4e);
|
||||||
write_n_bytes(12, 0x00);
|
write_n_bytes(12, 0x00);
|
||||||
for(int c = 0; c < 3; c++) write_raw_short(Storage::Encodings::MFM::MFMSync);
|
for(int c = 0; c < 3; c++) write_raw_short(Storage::Encodings::MFM::MFMSync);
|
||||||
get_crc_generator().set_value(Storage::Encodings::MFM::MFMPostSyncCRCValue);
|
get_crc_generator().set_value(Storage::Encodings::MFM::MFMPostSyncCRCValue);
|
||||||
write_byte(is_deleted ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte);
|
write_byte(is_deleted ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte);
|
||||||
} else {
|
} else {
|
||||||
write_n_bytes(11, 0xff);
|
if(!skip_first_gap) write_n_bytes(11, 0xff);
|
||||||
write_n_bytes(6, 0x00);
|
write_n_bytes(6, 0x00);
|
||||||
get_crc_generator().reset();
|
get_crc_generator().reset();
|
||||||
get_crc_generator().add(is_deleted ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte);
|
get_crc_generator().add(is_deleted ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte);
|
||||||
@ -227,7 +227,7 @@ void MFMController::write_start_of_track() {
|
|||||||
if(get_is_double_density()) {
|
if(get_is_double_density()) {
|
||||||
write_n_bytes(80, 0x4e);
|
write_n_bytes(80, 0x4e);
|
||||||
write_n_bytes(12, 0x00);
|
write_n_bytes(12, 0x00);
|
||||||
for(int c = 0; c < 3; c++) write_raw_short(Storage::Encodings::MFM::MFMIndexSync);
|
for(int c = 0; c < 3; c++) write_raw_short(Storage::Encodings::MFM::MFMIndexSync);
|
||||||
write_byte(Storage::Encodings::MFM::IndexAddressByte);
|
write_byte(Storage::Encodings::MFM::IndexAddressByte);
|
||||||
write_n_bytes(50, 0x4e);
|
write_n_bytes(50, 0x4e);
|
||||||
} else {
|
} else {
|
||||||
|
@ -125,11 +125,12 @@ class MFMController: public Controller {
|
|||||||
void write_id_joiner();
|
void write_id_joiner();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Writes everything that should, per the spec, appear after the ID's CRC, up to and
|
Writes at most what should, per the spec, appear after the ID's CRC, up to and
|
||||||
including the mark that indicates the beginning of data, appropriately seeding
|
including the mark that indicates the beginning of data, appropriately seeding
|
||||||
the CRC generator.
|
the CRC generator; if @c skip_first_gap is set then the initial gap after the
|
||||||
|
CRC isn't written.
|
||||||
*/
|
*/
|
||||||
void write_id_data_joiner(bool is_deleted);
|
void write_id_data_joiner(bool is_deleted, bool skip_first_gap);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Writes the gap expected after a sector's data CRC and before the beginning of the
|
Writes the gap expected after a sector's data CRC and before the beginning of the
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include "PCMPatchedTrack.hpp"
|
#include "PCMPatchedTrack.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
using namespace Storage::Disk;
|
using namespace Storage::Disk;
|
||||||
|
|
||||||
PCMPatchedTrack::PCMPatchedTrack(std::shared_ptr<Track> underlying_track) :
|
PCMPatchedTrack::PCMPatchedTrack(std::shared_ptr<Track> underlying_track) :
|
||||||
@ -29,20 +31,25 @@ Track *PCMPatchedTrack::clone() {
|
|||||||
return new PCMPatchedTrack(*this);
|
return new PCMPatchedTrack(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PCMPatchedTrack::add_segment(const Time &start_time, const PCMSegment &segment) {
|
void PCMPatchedTrack::add_segment(const Time &start_time, const PCMSegment &segment, bool clamp_to_index_hole) {
|
||||||
std::shared_ptr<PCMSegmentEventSource> event_source(new PCMSegmentEventSource(segment));
|
std::shared_ptr<PCMSegmentEventSource> event_source(new PCMSegmentEventSource(segment));
|
||||||
|
|
||||||
Time zero(0);
|
Time zero(0);
|
||||||
|
Time one(1);
|
||||||
Time end_time = start_time + event_source->get_length();
|
Time end_time = start_time + event_source->get_length();
|
||||||
|
if(clamp_to_index_hole && end_time > one) {
|
||||||
|
end_time = one;
|
||||||
|
}
|
||||||
Period insertion_period(start_time, end_time, zero, event_source);
|
Period insertion_period(start_time, end_time, zero, event_source);
|
||||||
|
|
||||||
// the new segment may wrap around, so divide it up into track-length parts if required
|
// the new segment may wrap around, so divide it up into track-length parts if required
|
||||||
Time one = Time(1);
|
assert(insertion_period.start_time <= one);
|
||||||
while(insertion_period.end_time > one) {
|
while(insertion_period.end_time > one) {
|
||||||
Time next_end_time = insertion_period.end_time - one;
|
Time next_end_time = insertion_period.end_time - one;
|
||||||
insertion_period.end_time = one;
|
insertion_period.end_time = one;
|
||||||
insert_period(insertion_period);
|
insert_period(insertion_period);
|
||||||
|
|
||||||
|
insertion_period.segment_start_time += one;
|
||||||
insertion_period.start_time = zero;
|
insertion_period.start_time = zero;
|
||||||
insertion_period.end_time = next_end_time;
|
insertion_period.end_time = next_end_time;
|
||||||
}
|
}
|
||||||
@ -127,8 +134,7 @@ void PCMPatchedTrack::insert_period(const Period &period) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Track::Event PCMPatchedTrack::get_next_event()
|
Track::Event PCMPatchedTrack::get_next_event() {
|
||||||
{
|
|
||||||
const Time one(1);
|
const Time one(1);
|
||||||
const Time zero(0);
|
const Time zero(0);
|
||||||
Time extra_time(0);
|
Time extra_time(0);
|
||||||
@ -184,13 +190,19 @@ Track::Event PCMPatchedTrack::get_next_event()
|
|||||||
Storage::Time PCMPatchedTrack::seek_to(const Time &time_since_index_hole) {
|
Storage::Time PCMPatchedTrack::seek_to(const Time &time_since_index_hole) {
|
||||||
// start at the beginning and continue while segments end before reaching the time sought
|
// start at the beginning and continue while segments end before reaching the time sought
|
||||||
active_period_ = periods_.begin();
|
active_period_ = periods_.begin();
|
||||||
while(active_period_->end_time < time_since_index_hole) active_period_++;
|
while(active_period_->end_time < time_since_index_hole) {
|
||||||
|
assert(active_period_ != periods_.end());
|
||||||
|
active_period_++;
|
||||||
|
}
|
||||||
|
|
||||||
// allow whatever storage represents the period found to perform its seek
|
// allow whatever storage represents the period found to perform its seek; calculation for periods
|
||||||
|
// with an event source is, in effect: seek_to(offset_into_segment + distance_into_period) - offset_into_segment.
|
||||||
if(active_period_->event_source)
|
if(active_period_->event_source)
|
||||||
current_time_ = active_period_->event_source->seek_to(time_since_index_hole - active_period_->start_time) + active_period_->start_time;
|
current_time_ = active_period_->event_source->seek_to(active_period_->segment_start_time + time_since_index_hole - active_period_->start_time) + active_period_->start_time - active_period_->segment_start_time;
|
||||||
else
|
else
|
||||||
current_time_ = underlying_track_->seek_to(time_since_index_hole);
|
current_time_ = underlying_track_->seek_to(time_since_index_hole);
|
||||||
|
|
||||||
|
assert(current_time_ <= time_since_index_hole);
|
||||||
return current_time_;
|
return current_time_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#ifndef PCMPatchedTrack_hpp
|
#ifndef PCMPatchedTrack_hpp
|
||||||
#define PCMPatchedTrack_hpp
|
#define PCMPatchedTrack_hpp
|
||||||
|
|
||||||
#include "PCMTrack.hpp"
|
#include "Disk.hpp"
|
||||||
#include "PCMSegment.hpp"
|
#include "PCMSegment.hpp"
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
@ -34,8 +34,13 @@ class PCMPatchedTrack: public Track {
|
|||||||
/*!
|
/*!
|
||||||
Replaces whatever is currently on the track from @c start_position to @c start_position + segment length
|
Replaces whatever is currently on the track from @c start_position to @c start_position + segment length
|
||||||
with the contents of @c segment.
|
with the contents of @c segment.
|
||||||
|
|
||||||
|
@param start_time The time at which this segment begins. Must be in the range [0, 1).
|
||||||
|
@param segment The PCM segment to add.
|
||||||
|
@param clamp_to_index_hole If @c true then the new segment will be truncated if it overruns the index hole;
|
||||||
|
it will otherwise write over the index hole and continue.
|
||||||
*/
|
*/
|
||||||
void add_segment(const Time &start_time, const PCMSegment &segment);
|
void add_segment(const Time &start_time, const PCMSegment &segment, bool clamp_to_index_hole);
|
||||||
|
|
||||||
// To satisfy Storage::Disk::Track
|
// To satisfy Storage::Disk::Track
|
||||||
Event get_next_event();
|
Event get_next_event();
|
||||||
|
26
Storage/Disk/UnformattedTrack.cpp
Normal file
26
Storage/Disk/UnformattedTrack.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// UnformattedTrack.cpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 15/08/2017.
|
||||||
|
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "UnformattedTrack.hpp"
|
||||||
|
|
||||||
|
using namespace Storage::Disk;
|
||||||
|
|
||||||
|
Track::Event UnformattedTrack::get_next_event() {
|
||||||
|
Track::Event event;
|
||||||
|
event.type = Event::IndexHole;
|
||||||
|
event.length = Time(1);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
Storage::Time UnformattedTrack::seek_to(const Time &time_since_index_hole) {
|
||||||
|
return Time(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Track *UnformattedTrack::clone() {
|
||||||
|
return new UnformattedTrack;
|
||||||
|
}
|
30
Storage/Disk/UnformattedTrack.hpp
Normal file
30
Storage/Disk/UnformattedTrack.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// UnformattedTrack.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 15/08/2017.
|
||||||
|
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef UnformattedTrack_hpp
|
||||||
|
#define UnformattedTrack_hpp
|
||||||
|
|
||||||
|
#include "Disk.hpp"
|
||||||
|
|
||||||
|
namespace Storage {
|
||||||
|
namespace Disk {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A subclass of @c Track with no contents. Just an index hole.
|
||||||
|
*/
|
||||||
|
class UnformattedTrack: public Track {
|
||||||
|
public:
|
||||||
|
Event get_next_event();
|
||||||
|
Time seek_to(const Time &time_since_index_hole);
|
||||||
|
Track *clone();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* UnformattedTrack_hpp */
|
Loading…
Reference in New Issue
Block a user