mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-11 08:30:55 +00:00
Strung together a very basic version of 8272 write [/deleted] data. Lots of cases as-yet unhandled.
This commit is contained in:
parent
eec42aa7ae
commit
b0a7208cc7
@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "i8272.hpp"
|
#include "i8272.hpp"
|
||||||
|
#include "../../Storage/Disk/Encodings/MFM.hpp"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
@ -57,7 +58,8 @@ i8272::i8272(Cycles clock_rate, int clock_rate_multiplier, int revolutions_per_m
|
|||||||
interesting_event_mask_((int)Event8272::CommandByte),
|
interesting_event_mask_((int)Event8272::CommandByte),
|
||||||
resume_point_(0),
|
resume_point_(0),
|
||||||
delay_time_(0),
|
delay_time_(0),
|
||||||
head_timers_running_(0) {
|
head_timers_running_(0),
|
||||||
|
expects_input_(false) {
|
||||||
posit_event((int)Event8272::CommandByte);
|
posit_event((int)Event8272::CommandByte);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,9 +126,15 @@ void i8272::set_register(int address, uint8_t value) {
|
|||||||
// if not ready for commands, do nothing
|
// if not ready for commands, do nothing
|
||||||
if(!DataRequest() || DataDirectionToProcessor()) return;
|
if(!DataRequest() || DataDirectionToProcessor()) return;
|
||||||
|
|
||||||
// accumulate latest byte in the command byte sequence
|
if(expects_input_) {
|
||||||
command_.push_back(value);
|
input_ = value;
|
||||||
posit_event((int)Event8272::CommandByte);
|
has_input_ = true;
|
||||||
|
ResetDataRequest();
|
||||||
|
} else {
|
||||||
|
// accumulate latest byte in the command byte sequence
|
||||||
|
command_.push_back(value);
|
||||||
|
posit_event((int)Event8272::CommandByte);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t i8272::get_register(int address) {
|
uint8_t i8272::get_register(int address) {
|
||||||
@ -218,6 +226,7 @@ void i8272::posit_event(int event_type) {
|
|||||||
// Resets busy and non-DMA execution, clears the command buffer, sets the data mode to scanning and flows
|
// Resets busy and non-DMA execution, clears the command buffer, sets the data mode to scanning and flows
|
||||||
// into wait_for_complete_command_sequence.
|
// into wait_for_complete_command_sequence.
|
||||||
wait_for_command:
|
wait_for_command:
|
||||||
|
expects_input_ = false;
|
||||||
set_data_mode(Storage::Disk::MFMController::DataMode::Scanning);
|
set_data_mode(Storage::Disk::MFMController::DataMode::Scanning);
|
||||||
ResetBusy();
|
ResetBusy();
|
||||||
ResetNonDMAExecution();
|
ResetNonDMAExecution();
|
||||||
@ -240,14 +249,10 @@ void i8272::posit_event(int event_type) {
|
|||||||
goto read_data;
|
goto read_data;
|
||||||
|
|
||||||
case 0x05: // write data
|
case 0x05: // write data
|
||||||
if(command_.size() < 9) goto wait_for_complete_command_sequence;
|
|
||||||
ResetDataRequest();
|
|
||||||
goto write_data;
|
|
||||||
|
|
||||||
case 0x09: // write deleted data
|
case 0x09: // write deleted data
|
||||||
if(command_.size() < 9) goto wait_for_complete_command_sequence;
|
if(command_.size() < 9) goto wait_for_complete_command_sequence;
|
||||||
ResetDataRequest();
|
ResetDataRequest();
|
||||||
goto write_deleted_data;
|
goto write_data;
|
||||||
|
|
||||||
case 0x02: // read track
|
case 0x02: // read track
|
||||||
if(command_.size() < 9) goto wait_for_complete_command_sequence;
|
if(command_.size() < 9) goto wait_for_complete_command_sequence;
|
||||||
@ -308,10 +313,10 @@ void i8272::posit_event(int event_type) {
|
|||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Performs the read data or read deleted data command.
|
// Decodes drive, head and density, loads the head, loads the internal cylinder, head, sector and size registers,
|
||||||
read_data:
|
// and searches for a sector that meets those criteria. If one is found, inspects the instruction in use and
|
||||||
printf("Read [deleted?] data, sector %02x %02x %02x %02x\n", command_[2], command_[3], command_[4], command_[5]);
|
// jumps to an appropriate handler.
|
||||||
|
read_write_find_header:
|
||||||
// Establishes the drive and head being addressed, and whether in double density mode; populates the internal
|
// 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.
|
// cylinder, head, sector and size registers from the command stream.
|
||||||
if(!dma_mode_) SetNonDMAExecution();
|
if(!dma_mode_) SetNonDMAExecution();
|
||||||
@ -323,8 +328,6 @@ void i8272::posit_event(int event_type) {
|
|||||||
sector_ = command_[4];
|
sector_ = command_[4];
|
||||||
size_ = command_[5];
|
size_ = command_[5];
|
||||||
|
|
||||||
read_next_data:
|
|
||||||
|
|
||||||
// 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.
|
||||||
@ -334,7 +337,7 @@ void i8272::posit_event(int event_type) {
|
|||||||
if(!index_hole_limit_) {
|
if(!index_hole_limit_) {
|
||||||
// Two index holes have passed wihout finding the header sought.
|
// Two index holes have passed wihout finding the header sought.
|
||||||
SetNoData();
|
SetNoData();
|
||||||
goto abort_read;
|
goto abort_read_write;
|
||||||
}
|
}
|
||||||
READ_HEADER();
|
READ_HEADER();
|
||||||
if(get_crc_generator().get_value()) {
|
if(get_crc_generator().get_value()) {
|
||||||
@ -343,8 +346,30 @@ void i8272::posit_event(int event_type) {
|
|||||||
}
|
}
|
||||||
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
|
||||||
|
switch(command_[0] & 0x1f) {
|
||||||
|
case 0x06: // read data
|
||||||
|
case 0x0b: // read deleted data
|
||||||
|
goto read_data_found_header;
|
||||||
|
|
||||||
|
case 0x05: // write data
|
||||||
|
case 0x09: // write deleted data
|
||||||
|
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.
|
||||||
|
read_data:
|
||||||
|
read_next_data:
|
||||||
|
goto read_write_find_header;
|
||||||
|
|
||||||
// Finds the next data block and sets data mode to reading, setting an error flag if the on-disk deleted
|
// Finds the next data block and sets data mode to reading, setting an error flag if the on-disk deleted
|
||||||
// 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:
|
||||||
FIND_DATA();
|
FIND_DATA();
|
||||||
distance_into_section_ = 0;
|
distance_into_section_ = 0;
|
||||||
if((get_latest_token().type == Token::Data) != ((command_[0]&0xf) == 0x6)) {
|
if((get_latest_token().type == Token::Data) != ((command_[0]&0xf) == 0x6)) {
|
||||||
@ -377,7 +402,7 @@ 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;
|
goto abort_read_write;
|
||||||
break;
|
break;
|
||||||
case (int)Event::IndexHole:
|
case (int)Event::IndexHole:
|
||||||
break;
|
break;
|
||||||
@ -390,7 +415,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;
|
goto abort_read_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@ -403,17 +428,64 @@ void i8272::posit_event(int event_type) {
|
|||||||
// For a final result phase, post the standard ST0, ST1, ST2, C, H, R, N
|
// For a final result phase, post the standard ST0, ST1, ST2, C, H, R, N
|
||||||
goto post_st012chrn;
|
goto post_st012chrn;
|
||||||
|
|
||||||
abort_read:
|
|
||||||
SetAbnormalTermination();
|
|
||||||
goto post_st012chrn;
|
|
||||||
|
|
||||||
write_data:
|
write_data:
|
||||||
printf("Write data unimplemented!!\n");
|
printf("Write [deleted] data\n");
|
||||||
goto wait_for_command;
|
|
||||||
|
|
||||||
write_deleted_data:
|
write_next_data:
|
||||||
printf("Write deleted data unimplemented!!\n");
|
goto read_write_find_header;
|
||||||
goto wait_for_command;
|
|
||||||
|
write_data_found_header:
|
||||||
|
begin_writing();
|
||||||
|
|
||||||
|
// Write out the requested gap between ID and data.
|
||||||
|
for(int c = 0; c < command_[7]; c++) {
|
||||||
|
write_byte(0x4e);
|
||||||
|
}
|
||||||
|
WAIT_FOR_EVENT(Event::DataWritten);
|
||||||
|
|
||||||
|
{
|
||||||
|
bool is_deleted = (command_[0] & 0x1f) == 0x09;
|
||||||
|
if(get_is_double_density()) {
|
||||||
|
get_crc_generator().set_value(Storage::Encodings::MFM::MFMPostSyncCRCValue);
|
||||||
|
write_raw_short(Storage::Encodings::MFM::MFMSync);
|
||||||
|
write_byte(is_deleted ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte);
|
||||||
|
} else {
|
||||||
|
get_crc_generator().reset();
|
||||||
|
get_crc_generator().add(is_deleted ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte);
|
||||||
|
write_raw_short(is_deleted ? Storage::Encodings::MFM::FMDeletedDataAddressMark : Storage::Encodings::MFM::FMDataAddressMark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetDataDirectionFromProcessor();
|
||||||
|
SetDataRequest();
|
||||||
|
expects_input_ = true;
|
||||||
|
distance_into_section_ = 0;
|
||||||
|
|
||||||
|
write_loop:
|
||||||
|
WAIT_FOR_EVENT(Event::DataWritten);
|
||||||
|
if(!has_input_) {
|
||||||
|
SetOverrun();
|
||||||
|
end_writing();
|
||||||
|
goto abort_read_write;
|
||||||
|
}
|
||||||
|
write_byte(input_);
|
||||||
|
has_input_ = false;
|
||||||
|
distance_into_section_++;
|
||||||
|
if(distance_into_section_ < (128 << size_)) {
|
||||||
|
SetDataRequest();
|
||||||
|
goto write_loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uint16_t crc = get_crc_generator().get_value();
|
||||||
|
write_byte(crc >> 8);
|
||||||
|
write_byte(crc & 0xff);
|
||||||
|
}
|
||||||
|
expects_input_ = false;
|
||||||
|
WAIT_FOR_EVENT(Event::DataWritten);
|
||||||
|
end_writing();
|
||||||
|
|
||||||
|
goto post_st012chrn;
|
||||||
|
|
||||||
read_track:
|
read_track:
|
||||||
printf("Read track unimplemented!!\n");
|
printf("Read track unimplemented!!\n");
|
||||||
@ -433,7 +505,7 @@ void i8272::posit_event(int event_type) {
|
|||||||
FIND_HEADER();
|
FIND_HEADER();
|
||||||
if(!index_hole_limit_) {
|
if(!index_hole_limit_) {
|
||||||
SetNoData();
|
SetNoData();
|
||||||
goto abort_read;
|
goto abort_read_write;
|
||||||
}
|
}
|
||||||
READ_HEADER();
|
READ_HEADER();
|
||||||
|
|
||||||
|
@ -36,6 +36,9 @@ class i8272: public Storage::Disk::MFMController {
|
|||||||
// A buffer for accumulating the incoming command, and one for accumulating the result.
|
// A buffer for accumulating the incoming command, and one for accumulating the result.
|
||||||
std::vector<uint8_t> command_;
|
std::vector<uint8_t> command_;
|
||||||
std::vector<uint8_t> result_stack_;
|
std::vector<uint8_t> result_stack_;
|
||||||
|
uint8_t input_;
|
||||||
|
bool has_input_;
|
||||||
|
bool expects_input_;
|
||||||
|
|
||||||
// Event stream: the 8272-specific events, plus the current event state.
|
// Event stream: the 8272-specific events, plus the current event state.
|
||||||
enum class Event8272: int {
|
enum class Event8272: int {
|
||||||
|
@ -153,3 +153,25 @@ void MFMController::process_input_bit(int value, unsigned int cycles_since_index
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MFMController::write_bit(int bit) {
|
||||||
|
if(is_double_density_) {
|
||||||
|
Controller::write_bit(!bit && !last_bit_);
|
||||||
|
Controller::write_bit(!!bit);
|
||||||
|
last_bit_ = bit;
|
||||||
|
} else {
|
||||||
|
Controller::write_bit(true);
|
||||||
|
Controller::write_bit(!!bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MFMController::write_byte(uint8_t byte) {
|
||||||
|
for(int c = 0; c < 8; c++) write_bit((byte << c)&0x80);
|
||||||
|
crc_generator_.add(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MFMController::write_raw_short(uint16_t value) {
|
||||||
|
for(int c = 0; c < 16; c++) {
|
||||||
|
Controller::write_bit(!!((value << c)&0x8000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -90,6 +90,23 @@ class MFMController: public Controller {
|
|||||||
*/
|
*/
|
||||||
virtual void posit_event(int type) = 0;
|
virtual void posit_event(int type) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Encodes @c bit according to the current single/double density mode and adds it
|
||||||
|
to the controller's write buffer.
|
||||||
|
*/
|
||||||
|
void write_bit(int bit);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Encodes @c byte according to the current single/double density mode and adds it
|
||||||
|
to the controller's write buffer.
|
||||||
|
*/
|
||||||
|
void write_byte(uint8_t byte);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Serialises @c value into the controller's write buffer without adjustment.
|
||||||
|
*/
|
||||||
|
void write_raw_short(uint16_t value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Storage::Disk::Controller
|
// Storage::Disk::Controller
|
||||||
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);
|
||||||
@ -108,6 +125,9 @@ class MFMController: public Controller {
|
|||||||
// output
|
// output
|
||||||
Token latest_token_;
|
Token latest_token_;
|
||||||
|
|
||||||
|
// writing
|
||||||
|
int last_bit_;
|
||||||
|
|
||||||
// CRC generator
|
// CRC generator
|
||||||
NumberTheory::CRC16 crc_generator_;
|
NumberTheory::CRC16 crc_generator_;
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user