1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-07 23:29:06 +00:00

Attempted to find a more expressive way for maintaining state — macros for all conditions, to bind both values and destinations.

This commit is contained in:
Thomas Harte 2017-08-12 17:33:52 -04:00
parent 58bad1e2a3
commit 11d0c37506

View File

@ -12,12 +12,42 @@
using namespace Intel; using namespace Intel;
namespace { #define SetDataRequest() (main_status_ |= 0x80)
const uint8_t StatusRequest = 0x80; // Set: ready to send or receive from processor. #define ResetDataRequest() (main_status_ &= ~0x80)
const uint8_t StatusDirection = 0x40; // Set: data is expected to be taken from the 8272 by the processor. #define DataRequest() (main_status_ & 0x80)
const uint8_t StatusNonDMAExecuting = 0x20; // Set: the execution phase of a data transfer command is ongoing and DMA mode is disabled.
const uint8_t StatusBusy = 0x10; // Set: the FDC is busy. #define SetDataDirectionToProcessor() (main_status_ |= 0x40)
} #define SetDataDirectionFromProcessor() (main_status_ &= ~0x40)
#define DataDirectionToProcessor() (main_status_ & 0x40)
#define SetNonDMAExecution() (main_status_ |= 0x20)
#define ResetNonDMAExecution() (main_status_ &= ~0x20)
#define SetBusy() (main_status_ |= 0x10)
#define ResetBusy() (main_status_ &= ~0x10)
#define Busy() (main_status_ & 0x10)
#define SetAbnormalTermination() (status_[0] |= 0x40)
#define SetInvalidCommand() (status_[0] |= 0x80)
#define SetReadyChanged() (status_[0] |= 0xc0)
#define SetSeekEnd() (status_[0] |= 0x20)
#define SetEquipmentCheck() (status_[0] |= 0x10)
#define SetNotReady() (status_[0] |= 0x08)
#define SetEndOfCylinder() (status_[1] |= 0x80)
#define SetDataError() (status_[1] |= 0x20)
#define SetOverrun() (status_[1] |= 0x10)
#define SetNoData() (status_[1] |= 0x04)
#define SetNotWriteable() (status_[1] |= 0x02)
#define SetMissingAddressMark() (status_[1] |= 0x01)
#define SetControlMark() (status_[2] |= 0x40)
#define SetDataFieldDataError() (status_[2] |= 0x20)
#define SetWrongCyinder() (status_[2] |= 0x10)
#define SetScanEqualHit() (status_[2] |= 0x08)
#define SetScanNotSatisfied() (status_[2] |= 0x04)
#define SetBadCylinder() (status_[2] |= 0x02)
#define SetMissingDataAddressMark() (status_[2] |= 0x01)
i8272::i8272(Cycles clock_rate, int clock_rate_multiplier, int revolutions_per_minute) : i8272::i8272(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),
@ -90,7 +120,7 @@ void i8272::set_register(int address, uint8_t value) {
if(!address) return; if(!address) return;
// if not ready for commands, do nothing // if not ready for commands, do nothing
if(!(main_status_ & StatusRequest)) return; if(!DataRequest() || DataDirectionToProcessor()) return;
// accumulate latest byte in the command byte sequence // accumulate latest byte in the command byte sequence
command_.push_back(value); command_.push_back(value);
@ -151,7 +181,6 @@ void i8272::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
status_[0] = status_[1] = status_[2] = 0; status_[0] = status_[1] = status_[2] = 0;
#define SET_DRIVE_HEAD_MFM() \ #define SET_DRIVE_HEAD_MFM() \
if(!dma_mode_) main_status_ |= StatusNonDMAExecuting; \
active_drive_ = command_[1]&3; \ active_drive_ = command_[1]&3; \
active_head_ = (command_[1] >> 2)&1; \ active_head_ = (command_[1] >> 2)&1; \
set_drive(drives_[active_drive_].drive); \ set_drive(drives_[active_drive_].drive); \
@ -188,90 +217,92 @@ void i8272::posit_event(int event_type) {
// into wait_for_complete_command_sequence. // into wait_for_complete_command_sequence.
wait_for_command: wait_for_command:
set_data_mode(Storage::Disk::MFMController::DataMode::Scanning); set_data_mode(Storage::Disk::MFMController::DataMode::Scanning);
main_status_ &= ~(StatusBusy | StatusNonDMAExecuting); ResetBusy();
ResetNonDMAExecution();
command_.clear(); command_.clear();
// Sets the data request bit, and waits for a byte. Then sets the busy bit. Continues accepting bytes // Sets the data request bit, and waits for a byte. Then sets the busy bit. Continues accepting bytes
// until it has a quantity that make up an entire command, then resets the data request bit and // until it has a quantity that make up an entire command, then resets the data request bit and
// branches to that command. // branches to that command.
wait_for_complete_command_sequence: wait_for_complete_command_sequence:
main_status_ |= StatusRequest; SetDataRequest();
SetDataDirectionFromProcessor();
WAIT_FOR_EVENT(Event8272::CommandByte) WAIT_FOR_EVENT(Event8272::CommandByte)
main_status_ |= StatusBusy; SetBusy();
switch(command_[0] & 0x1f) { switch(command_[0] & 0x1f) {
case 0x06: // read data case 0x06: // read data
case 0x0b: // read deleted data case 0x0b: // read deleted data
if(command_.size() < 9) goto wait_for_complete_command_sequence; if(command_.size() < 9) goto wait_for_complete_command_sequence;
main_status_ &= ~StatusRequest; ResetDataRequest();
goto read_data; goto read_data;
case 0x05: // write data case 0x05: // write data
if(command_.size() < 9) goto wait_for_complete_command_sequence; if(command_.size() < 9) goto wait_for_complete_command_sequence;
main_status_ &= ~StatusRequest; ResetDataRequest();
goto write_data; 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;
main_status_ &= ~StatusRequest; ResetDataRequest();
goto write_deleted_data; goto write_deleted_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;
main_status_ &= ~StatusRequest; ResetDataRequest();
goto read_track; goto read_track;
case 0x0a: // read ID case 0x0a: // read ID
if(command_.size() < 2) goto wait_for_complete_command_sequence; if(command_.size() < 2) goto wait_for_complete_command_sequence;
main_status_ &= ~StatusRequest; ResetDataRequest();
goto read_id; goto read_id;
case 0x0d: // format track case 0x0d: // format track
if(command_.size() < 6) goto wait_for_complete_command_sequence; if(command_.size() < 6) goto wait_for_complete_command_sequence;
main_status_ &= ~StatusRequest; ResetDataRequest();
goto format_track; goto format_track;
case 0x11: // scan low case 0x11: // scan low
if(command_.size() < 9) goto wait_for_complete_command_sequence; if(command_.size() < 9) goto wait_for_complete_command_sequence;
main_status_ &= ~StatusRequest; ResetDataRequest();
goto scan_low; goto scan_low;
case 0x19: // scan low or equal case 0x19: // scan low or equal
if(command_.size() < 9) goto wait_for_complete_command_sequence; if(command_.size() < 9) goto wait_for_complete_command_sequence;
main_status_ &= ~StatusRequest; ResetDataRequest();
goto scan_low_or_equal; goto scan_low_or_equal;
case 0x1d: // scan high or equal case 0x1d: // scan high or equal
if(command_.size() < 9) goto wait_for_complete_command_sequence; if(command_.size() < 9) goto wait_for_complete_command_sequence;
main_status_ &= ~StatusRequest; ResetDataRequest();
goto scan_high_or_equal; goto scan_high_or_equal;
case 0x07: // recalibrate case 0x07: // recalibrate
if(command_.size() < 2) goto wait_for_complete_command_sequence; if(command_.size() < 2) goto wait_for_complete_command_sequence;
main_status_ &= ~StatusRequest; ResetDataRequest();
goto recalibrate; goto recalibrate;
case 0x08: // sense interrupt status case 0x08: // sense interrupt status
main_status_ &= ~StatusRequest; ResetDataRequest();
goto sense_interrupt_status; goto sense_interrupt_status;
case 0x03: // specify case 0x03: // specify
if(command_.size() < 3) goto wait_for_complete_command_sequence; if(command_.size() < 3) goto wait_for_complete_command_sequence;
main_status_ &= ~StatusRequest; ResetDataRequest();
goto specify; goto specify;
case 0x04: // sense drive status case 0x04: // sense drive status
if(command_.size() < 2) goto wait_for_complete_command_sequence; if(command_.size() < 2) goto wait_for_complete_command_sequence;
main_status_ &= ~StatusRequest; ResetDataRequest();
goto sense_drive_status; goto sense_drive_status;
case 0x0f: // seek case 0x0f: // seek
if(command_.size() < 3) goto wait_for_complete_command_sequence; if(command_.size() < 3) goto wait_for_complete_command_sequence;
main_status_ &= ~StatusRequest; ResetDataRequest();
goto seek; goto seek;
default: // invalid default: // invalid
main_status_ &= ~StatusRequest; ResetDataRequest();
goto invalid; goto invalid;
} }
@ -281,6 +312,7 @@ void i8272::posit_event(int event_type) {
// 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();
SET_DRIVE_HEAD_MFM(); SET_DRIVE_HEAD_MFM();
LOAD_HEAD(); LOAD_HEAD();
CLEAR_STATUS(); CLEAR_STATUS();
@ -299,13 +331,13 @@ void i8272::posit_event(int event_type) {
FIND_HEADER(); FIND_HEADER();
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.
status_[1] |= 0x4; SetNoData();
goto abort_read; goto abort_read;
} }
READ_HEADER(); READ_HEADER();
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.
status_[1] |= 0x20; SetDataError();
} }
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;
@ -316,7 +348,7 @@ void i8272::posit_event(int event_type) {
if((get_latest_token().type == Token::Data) != ((command_[0]&0xf) == 0x6)) { if((get_latest_token().type == Token::Data) != ((command_[0]&0xf) == 0x6)) {
if(!(command_[0]&0x20)) { if(!(command_[0]&0x20)) {
// SK is not set; set the error flag but read this sector before finishing. // SK is not set; set the error flag but read this sector before finishing.
status_[2] |= 0x40; SetControlMark();
} else { } else {
// SK is set; skip this sector. // SK is set; skip this sector.
goto read_next_data; goto read_next_data;
@ -333,15 +365,16 @@ void i8272::posit_event(int event_type) {
WAIT_FOR_EVENT(Event::Token); WAIT_FOR_EVENT(Event::Token);
result_stack_.push_back(get_latest_token().byte_value); result_stack_.push_back(get_latest_token().byte_value);
distance_into_section_++; distance_into_section_++;
main_status_ |= StatusRequest | StatusDirection; SetDataRequest();
SetDataDirectionToProcessor();
WAIT_FOR_EVENT((int)Event8272::ResultEmpty | (int)Event::Token | (int)Event::IndexHole); 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.
main_status_ &= ~StatusRequest; ResetDataRequest();
if(distance_into_section_ < (128 << size_)) goto get_byte; if(distance_into_section_ < (128 << size_)) goto get_byte;
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
status_[0] |= 0x10; SetOverrun();
goto abort_read; goto abort_read;
break; break;
case (int)Event::IndexHole: case (int)Event::IndexHole:
@ -353,8 +386,8 @@ void i8272::posit_event(int event_type) {
WAIT_FOR_EVENT(Event::Token); WAIT_FOR_EVENT(Event::Token);
if(get_crc_generator().get_value()) { if(get_crc_generator().get_value()) {
// 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.
status_[1] |= 0x20; SetDataError();
status_[2] |= 0x20; SetDataFieldDataError();
goto abort_read; goto abort_read;
} }
@ -369,7 +402,7 @@ void i8272::posit_event(int event_type) {
goto post_st012chrn; goto post_st012chrn;
abort_read: abort_read:
status_[0] = 0x40; SetAbnormalTermination();
goto post_st012chrn; goto post_st012chrn;
write_data: write_data:
@ -397,7 +430,7 @@ 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_) {
status_[1] |= 0x4; SetNoData();
goto abort_read; goto abort_read;
} }
READ_HEADER(); READ_HEADER();
@ -469,7 +502,8 @@ void i8272::posit_event(int event_type) {
// If a drive was found, return its results. Otherwise return a single 0x80. // If a drive was found, return its results. Otherwise return a single 0x80.
if(found_drive != -1) { if(found_drive != -1) {
drives_[found_drive].phase = Drive::NotSeeking; drives_[found_drive].phase = Drive::NotSeeking;
status_[0] = (uint8_t)found_drive | 0x20; status_[0] = (uint8_t)found_drive;
SetSeekEnd();
main_status_ &= ~(1 << found_drive); main_status_ &= ~(1 << found_drive);
result_stack_.push_back(drives_[found_drive].head_position); result_stack_.push_back(drives_[found_drive].head_position);
@ -527,15 +561,15 @@ void i8272::posit_event(int event_type) {
// last thing in it will be returned first. // last thing in it will be returned first.
post_result: post_result:
// Set ready to send data to the processor, no longer in non-DMA execution phase. // Set ready to send data to the processor, no longer in non-DMA execution phase.
main_status_ |= StatusRequest | StatusDirection; ResetNonDMAExecution();
main_status_ &= ~StatusNonDMAExecuting; SetDataRequest();
SetDataDirectionToProcessor();
// The actual stuff of unwinding result_stack_ is handled by ::get_register; wait // The actual stuff of unwinding result_stack_ is handled by ::get_register; wait
// until the processor has read all result bytes. // until the processor has read all result bytes.
WAIT_FOR_EVENT(Event8272::ResultEmpty); WAIT_FOR_EVENT(Event8272::ResultEmpty);
// Reset data direction and end the command. // Reset data direction and end the command.
main_status_ &= ~StatusDirection;
goto wait_for_command; goto wait_for_command;
END_SECTION() END_SECTION()