mirror of
https://github.com/dingusdev/dingusppc.git
synced 2025-02-23 05:29:26 +00:00
New floppy access logic with improved timing.
This commit is contained in:
parent
447941abe0
commit
a4ff58e9ee
@ -21,6 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
/** @file Macintosh Superdrive emulation. */
|
||||
|
||||
#include <core/timermanager.h>
|
||||
#include <devices/floppy/floppyimg.h>
|
||||
#include <devices/floppy/superdrive.h>
|
||||
#include <loguru.hpp>
|
||||
@ -42,12 +43,15 @@ MacSuperDrive::MacSuperDrive()
|
||||
|
||||
void MacSuperDrive::reset_params()
|
||||
{
|
||||
this->media_kind = MediaKind::high_density;
|
||||
this->has_disk = 0; // drive is empty
|
||||
this->drive_mode = RecMethod::MFM; // assume MFM mode by default
|
||||
this->motor_stat = 0; // spindle motor is off
|
||||
this->cur_track = 0; // current head position
|
||||
this->is_ready = 0; // drive not ready
|
||||
this->media_kind = MediaKind::high_density;
|
||||
this->has_disk = 0; // drive is empty
|
||||
this->motor_stat = 0; // spindle motor is off
|
||||
this->motor_on_time = 0;
|
||||
this->cur_track = 0; // current head position
|
||||
this->is_ready = 0; // drive not ready
|
||||
|
||||
// come up in the MFM mode by default
|
||||
this->switch_drive_mode(RecMethod::MFM);
|
||||
}
|
||||
|
||||
void MacSuperDrive::command(uint8_t addr, uint8_t value)
|
||||
@ -73,9 +77,15 @@ void MacSuperDrive::command(uint8_t addr, uint8_t value)
|
||||
if (this->motor_stat != new_motor_stat) {
|
||||
this->motor_stat = new_motor_stat;
|
||||
if (new_motor_stat) {
|
||||
this->motor_on_time = TimerManager::get_instance()->current_time_ns();
|
||||
this->track_start_time = 0;
|
||||
this->sector_start_time = 0;
|
||||
this->init_track_search(-1);
|
||||
this->is_ready = 1;
|
||||
LOG_F(INFO, "Superdrive: turn spindle motor on");
|
||||
} else {
|
||||
this->motor_on_time = 0;
|
||||
this->is_ready = 0;
|
||||
LOG_F(INFO, "Superdrive: turn spindle motor off");
|
||||
}
|
||||
}
|
||||
@ -114,8 +124,7 @@ uint8_t MacSuperDrive::status(uint8_t addr)
|
||||
case StatusAddr::Eject_Latch:
|
||||
return this->eject_latch;
|
||||
case StatusAddr::Select_Head_0:
|
||||
this->cur_head = 0;
|
||||
return 1; // not sure what should be returned here
|
||||
return this->cur_head = 0;
|
||||
case StatusAddr::MFM_Support:
|
||||
return 1; // Superdrive does support MFM encoding scheme
|
||||
case StatusAddr::Double_Sided:
|
||||
@ -129,8 +138,7 @@ uint8_t MacSuperDrive::status(uint8_t addr)
|
||||
case StatusAddr::Track_Zero:
|
||||
return this->track_zero ^ 1; // reverse logic
|
||||
case StatusAddr::Select_Head_1:
|
||||
this->cur_head = 1;
|
||||
return 1; // not sure what should be returned here
|
||||
return this->cur_head = 1;
|
||||
case StatusAddr::Drive_Mode:
|
||||
return this->drive_mode;
|
||||
case StatusAddr::Drive_Ready:
|
||||
@ -216,39 +224,138 @@ void MacSuperDrive::switch_drive_mode(int mode)
|
||||
this->track2lblk[trk] = trk * sectors_per_track * 2;
|
||||
}
|
||||
|
||||
// set up disk timing parameters
|
||||
this->index_delay = MFM_INDX_MARK_DELAY;
|
||||
this->addr_mark_delay = MFM_ADR_MARK_DELAY;
|
||||
|
||||
if (this->media_kind) {
|
||||
this->sector_delay = MFM_HD_SECTOR_DELAY;
|
||||
this->eot_delay = MFM_HD_EOT_DELAY;
|
||||
} else {
|
||||
this->sector_delay = MFM_DD_SECTOR_DELAY;
|
||||
this->eot_delay = MFM_DD_EOT_DELAY;
|
||||
}
|
||||
|
||||
this->drive_mode = RecMethod::MFM;
|
||||
}
|
||||
}
|
||||
|
||||
double MacSuperDrive::get_current_track_delay()
|
||||
{
|
||||
return (1.0f / (this->rpm_per_track[this->cur_track] / 60));
|
||||
}
|
||||
|
||||
double MacSuperDrive::get_sector_delay()
|
||||
{
|
||||
return this->get_current_track_delay() / this->sectors_per_track[this->cur_track];
|
||||
return 60.0f / this->rpm_per_track[this->cur_track];
|
||||
}
|
||||
|
||||
void MacSuperDrive::init_track_search(int pos)
|
||||
{
|
||||
if (pos == -1) {
|
||||
// pick random sector number
|
||||
this->cur_sector = this->sectors_per_track[this->cur_track] / 2;
|
||||
uint64_t seed = TimerManager::get_instance()->current_time_ns() >> 8;
|
||||
this->cur_sector = seed % this->sectors_per_track[this->cur_track];
|
||||
LOG_F(9, "Superdrive: current sector number set to %d", this->cur_sector);
|
||||
} else {
|
||||
this->cur_sector = pos;
|
||||
}
|
||||
}
|
||||
|
||||
SectorHdr MacSuperDrive::next_sector_header()
|
||||
uint64_t MacSuperDrive::sync_to_disk()
|
||||
{
|
||||
this->cur_sector++;
|
||||
if (this->cur_sector >= this->sectors_per_track[this->cur_track]) {
|
||||
this->cur_sector = 0;
|
||||
uint64_t cur_time_ns, track_time_ns;
|
||||
|
||||
uint64_t track_delay = this->get_current_track_delay() * NS_PER_SEC;
|
||||
|
||||
// look how much ns have been elapsed since the last motor enabling
|
||||
cur_time_ns = TimerManager::get_instance()->current_time_ns() - this->motor_on_time;
|
||||
|
||||
if (!this->track_start_time ||
|
||||
(cur_time_ns - this->track_start_time) >= track_delay) {
|
||||
// subtract full disk revolutions
|
||||
track_time_ns = cur_time_ns % track_delay;
|
||||
|
||||
this->track_start_time = cur_time_ns - track_time_ns;
|
||||
|
||||
// return delay until the first address mark if we're currently
|
||||
// looking at the end of track
|
||||
if (track_time_ns >= this->eot_delay) {
|
||||
this->next_sector = 0;
|
||||
// CHEAT: don't account for the remaining time of the current track
|
||||
return this->index_delay + this->addr_mark_delay;
|
||||
}
|
||||
|
||||
// return delay until the first address mark
|
||||
// if the first address mark was not reached yet
|
||||
if (track_time_ns < this->index_delay) {
|
||||
this->next_sector = 0;
|
||||
return this->index_delay + this->addr_mark_delay - track_time_ns;
|
||||
}
|
||||
} else {
|
||||
track_time_ns = cur_time_ns - this->track_start_time;
|
||||
}
|
||||
|
||||
// subtract index field delay
|
||||
track_time_ns -= this->index_delay;
|
||||
|
||||
// calculate current sector number from timestamp
|
||||
int cur_sect_num = this->cur_sector = track_time_ns / this->sector_delay;
|
||||
|
||||
this->sector_start_time = this->track_start_time + cur_sect_num * this->sector_delay +
|
||||
this->index_delay;
|
||||
|
||||
if (this->cur_sector + 1 >= this->sectors_per_track[this->cur_track]) {
|
||||
uint64_t diff = track_delay - (cur_time_ns - this->track_start_time);
|
||||
this->next_sector = 0;
|
||||
this->track_start_time = 0;
|
||||
this->sector_start_time = 0;
|
||||
return diff + this->index_delay + this->addr_mark_delay;
|
||||
} else {
|
||||
this->next_sector = this->cur_sector + 1;
|
||||
uint64_t diff = cur_time_ns - this->sector_start_time;
|
||||
this->sector_start_time += this->sector_delay;
|
||||
return this->sector_delay - diff + this->addr_mark_delay;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t MacSuperDrive::next_addr_mark_delay(uint8_t *next_sect_num)
|
||||
{
|
||||
uint64_t delay;
|
||||
|
||||
if (this->cur_sector + 1 >= this->sectors_per_track[this->cur_track]) {
|
||||
this->next_sector = 0;
|
||||
delay = this->index_delay + this->addr_mark_delay;
|
||||
} else {
|
||||
this->next_sector = this->cur_sector + 1;
|
||||
delay = this->addr_mark_delay;
|
||||
}
|
||||
|
||||
if (next_sect_num) {
|
||||
*next_sect_num = this->next_sector + ((this->rec_method == RecMethod::MFM) ? 1 : 0);
|
||||
}
|
||||
|
||||
return delay;
|
||||
}
|
||||
|
||||
uint64_t MacSuperDrive::next_sector_delay()
|
||||
{
|
||||
if (this->cur_sector + 1 >= this->sectors_per_track[this->cur_track]) {
|
||||
this->next_sector = 0;
|
||||
return this->sector_delay + this->index_delay + this->addr_mark_delay;
|
||||
}
|
||||
|
||||
this->next_sector = this->cur_sector + 1;
|
||||
|
||||
return this->sector_delay - this->addr_mark_delay;
|
||||
}
|
||||
|
||||
uint64_t MacSuperDrive::sector_data_delay()
|
||||
{
|
||||
return MFM_SECT_DATA_DELAY;
|
||||
}
|
||||
|
||||
SectorHdr MacSuperDrive::current_sector_header()
|
||||
{
|
||||
this->cur_sector = this->next_sector;
|
||||
|
||||
// MFM sector numbering is 1-based so we need to bump sector number
|
||||
int sector_num = this->cur_sector + ((this->rec_method == RecMethod::MFM) ? 1 : 0);
|
||||
uint8_t sector_num = this->cur_sector + ((this->rec_method == RecMethod::MFM) ? 1 : 0);
|
||||
|
||||
return SectorHdr {
|
||||
this->cur_track,
|
||||
|
@ -24,6 +24,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#ifndef MAC_SUPERDRIVE_H
|
||||
#define MAC_SUPERDRIVE_H
|
||||
|
||||
#include <core/timermanager.h>
|
||||
#include <devices/common/hwcomponent.h>
|
||||
#include <devices/floppy/floppyimg.h>
|
||||
|
||||
@ -31,6 +32,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
// convert number of bytes to disk time = nbytes * bits_per_byte * 2 us
|
||||
#define MFM_BYTES_TO_DISK_TIME(bytes) USECS_TO_NSECS((bytes) * 8 * 2)
|
||||
|
||||
// timing constans for MFM disks
|
||||
constexpr uint32_t MFM_INDX_MARK_DELAY = MFM_BYTES_TO_DISK_TIME(146);
|
||||
constexpr uint32_t MFM_ADR_MARK_DELAY = MFM_BYTES_TO_DISK_TIME(22);
|
||||
constexpr uint32_t MFM_SECT_DATA_DELAY = MFM_BYTES_TO_DISK_TIME(514);
|
||||
constexpr uint32_t MFM_DD_SECTOR_DELAY = MFM_BYTES_TO_DISK_TIME(658);
|
||||
constexpr uint32_t MFM_HD_SECTOR_DELAY = MFM_BYTES_TO_DISK_TIME(675);
|
||||
|
||||
constexpr uint32_t MFM_DD_EOT_DELAY = MFM_BYTES_TO_DISK_TIME( 6250 - 182);
|
||||
constexpr uint32_t MFM_HD_EOT_DELAY = MFM_BYTES_TO_DISK_TIME(12500 - 204);
|
||||
|
||||
namespace MacSuperdrive {
|
||||
|
||||
/** Apple Drive status request addresses. */
|
||||
@ -74,10 +88,10 @@ enum RecMethod : int {
|
||||
};
|
||||
|
||||
typedef struct SectorHdr {
|
||||
int track;
|
||||
int side;
|
||||
int sector;
|
||||
int format;
|
||||
int track;
|
||||
int side;
|
||||
int sector;
|
||||
int format;
|
||||
} SectorHdr;
|
||||
|
||||
class MacSuperDrive : public HWComponent {
|
||||
@ -88,11 +102,16 @@ public:
|
||||
void command(uint8_t addr, uint8_t value);
|
||||
uint8_t status(uint8_t addr);
|
||||
int insert_disk(std::string& img_path, int write_flag);
|
||||
double get_current_track_delay();
|
||||
double get_sector_delay();
|
||||
void init_track_search(int pos);
|
||||
SectorHdr next_sector_header();
|
||||
uint64_t sync_to_disk();
|
||||
uint64_t next_addr_mark_delay(uint8_t *next_sect_num);
|
||||
uint64_t next_sector_delay();
|
||||
SectorHdr current_sector_header();
|
||||
char* get_sector_data_ptr(int sector_num);
|
||||
uint64_t sector_data_delay();
|
||||
|
||||
double get_current_track_delay();
|
||||
double get_address_mark_delay();
|
||||
|
||||
protected:
|
||||
void reset_params();
|
||||
@ -100,27 +119,38 @@ protected:
|
||||
void switch_drive_mode(int mode);
|
||||
|
||||
private:
|
||||
uint8_t has_disk;
|
||||
uint8_t eject_latch;
|
||||
uint8_t motor_stat; // spindle motor status: 1 - on, 0 - off
|
||||
uint8_t drive_mode; // drive mode: 0 - GCR, 1 - MFM
|
||||
uint8_t is_ready;
|
||||
uint8_t track_zero; // 1 - if head is at track zero
|
||||
int step_dir; // step direction -1/+1
|
||||
int cur_track; // track number the head is currently at
|
||||
int cur_head; // current head number: 1 - upper, 0 - lower
|
||||
int cur_sector; // current sector number
|
||||
uint8_t has_disk;
|
||||
uint8_t eject_latch;
|
||||
uint8_t motor_stat; // spindle motor status: 1 - on, 0 - off
|
||||
uint8_t drive_mode; // drive mode: 0 - GCR, 1 - MFM
|
||||
uint8_t is_ready;
|
||||
uint8_t track_zero; // 1 - if head is at track zero
|
||||
int step_dir; // step direction -1/+1
|
||||
int cur_track; // track number the head is currently at
|
||||
int cur_head; // current head number: 1 - upper, 0 - lower
|
||||
int cur_sector; // current sector number
|
||||
int next_sector; // next sector number
|
||||
|
||||
uint64_t motor_on_time = 0; // time in ns the spindle motor was switched on
|
||||
uint64_t track_start_time = 0;
|
||||
uint64_t sector_start_time = 0;
|
||||
|
||||
// physical parameters of the currently inserted disk
|
||||
uint8_t media_kind;
|
||||
uint8_t wr_protect;
|
||||
uint8_t format_byte;
|
||||
int rec_method;
|
||||
int num_tracks;
|
||||
int num_sides;
|
||||
int sectors_per_track[80];
|
||||
int rpm_per_track[80];
|
||||
int track2lblk[80]; // convert track number to first logical block number
|
||||
uint8_t media_kind;
|
||||
uint8_t wr_protect;
|
||||
uint8_t format_byte;
|
||||
int rec_method;
|
||||
int num_tracks;
|
||||
int num_sides;
|
||||
int sectors_per_track[80];
|
||||
int rpm_per_track[80];
|
||||
int track2lblk[80]; // convert track number to first logical block number
|
||||
|
||||
// timing parameters for MFM disks
|
||||
uint32_t index_delay; // number of ns needed to read the index mark
|
||||
uint32_t addr_mark_delay; // number of ns needed to read an address index mark
|
||||
uint32_t sector_delay; // number of ns needed to read a sector
|
||||
uint32_t eot_delay; // number of ns until end of track gap
|
||||
|
||||
std::unique_ptr<FloppyImgConverter> img_conv;
|
||||
|
||||
|
@ -51,6 +51,8 @@ Swim3Ctrl::Swim3Ctrl()
|
||||
this->xfer_cnt = 0;
|
||||
this->first_sec = 0xFF;
|
||||
|
||||
this->cur_state = SWIM3_IDLE;
|
||||
|
||||
// Attach virtual Superdrive to the internal drive connector
|
||||
// TODO: make SWIM3/drive wiring user selectable
|
||||
this->int_drive = std::unique_ptr<MacSuperdrive::MacSuperDrive>
|
||||
@ -75,7 +77,7 @@ int Swim3Ctrl::device_postinit()
|
||||
|
||||
uint8_t Swim3Ctrl::read(uint8_t reg_offset)
|
||||
{
|
||||
uint8_t status_addr, status_val, old_int_flags, old_error;
|
||||
uint8_t status_addr, rddata_val, old_int_flags, old_error;
|
||||
|
||||
switch(reg_offset) {
|
||||
case Swim3Reg::Error:
|
||||
@ -89,13 +91,13 @@ uint8_t Swim3Ctrl::read(uint8_t reg_offset)
|
||||
case Swim3Reg::Handshake_Mode1:
|
||||
if (this->mode_reg & 2) { // internal drive?
|
||||
status_addr = ((this->mode_reg & 0x20) >> 2) | (this->phase_lines & 7);
|
||||
status_val = this->int_drive->status(status_addr) & 1;
|
||||
rddata_val = this->int_drive->status(status_addr) & 1;
|
||||
|
||||
// transfer status_val to both bit 2 (RDDATA) and bit 3 (SENSE)
|
||||
// transfer rddata_val to both bit 2 (RDDATA) and bit 3 (SENSE)
|
||||
// because those signals seem to be historically wired together
|
||||
return ((status_val << 2) | (status_val << 3));
|
||||
return (rddata_val << 2) | (rddata_val << 3);
|
||||
}
|
||||
return 4;
|
||||
return 0xC; // report both RdData & Sense high
|
||||
case Swim3Reg::Interrupt_Flags:
|
||||
old_int_flags = this->int_flags;
|
||||
this->int_flags = 0; // read from this register clears all flags
|
||||
@ -121,21 +123,27 @@ uint8_t Swim3Ctrl::read(uint8_t reg_offset)
|
||||
|
||||
void Swim3Ctrl::write(uint8_t reg_offset, uint8_t value)
|
||||
{
|
||||
uint8_t old_mode_reg;
|
||||
uint8_t old_mode_reg, status_addr;
|
||||
|
||||
switch(reg_offset) {
|
||||
case Swim3Reg::Timer:
|
||||
LOG_F(INFO, "SWIM3: writing %d to the Timer register", value);
|
||||
break;
|
||||
case Swim3Reg::Param_Data:
|
||||
this->pram = value;
|
||||
break;
|
||||
case Swim3Reg::Phase:
|
||||
this->phase_lines = value & 0xF;
|
||||
if (value & 8) {
|
||||
if (this->mode_reg & 2) { // internal drive?
|
||||
if (this->phase_lines & 8) { // CA3 aka LSTRB high -> sending a command to the drive
|
||||
if (this->mode_reg & 2) { // if internal drive is selected
|
||||
this->int_drive->command(
|
||||
((this->mode_reg & 0x20) >> 3) | (this->phase_lines & 3),
|
||||
(value >> 2) & 1
|
||||
);
|
||||
}
|
||||
} else if (this->phase_lines == 4 && (this->mode_reg & 2)) {
|
||||
status_addr = ((this->mode_reg & 0x20) >> 2) | (this->phase_lines & 7);
|
||||
this->rd_line = this->int_drive->status(status_addr) & 1;
|
||||
}
|
||||
break;
|
||||
case Swim3Reg::Setup:
|
||||
@ -278,53 +286,66 @@ void Swim3Ctrl::start_disk_access()
|
||||
this->mode_reg |= SWIM3_GO;
|
||||
LOG_F(9, "SWIM3: disk access started!");
|
||||
|
||||
if (this->first_sec == 0xFF) {
|
||||
// $FF means no sector to match ->
|
||||
// generate ID_read interrups as long as the GO bit is set
|
||||
this->int_drive->init_track_search(-1); // start at random sector
|
||||
} else {
|
||||
this->cur_sector = this->first_sec;
|
||||
this->target_sect = this->first_sec;
|
||||
|
||||
this->access_timer_id = TimerManager::get_instance()->add_oneshot_timer(
|
||||
this->int_drive->sync_to_disk(),
|
||||
[this]() {
|
||||
this->cur_state = SWIM3_ADDR_MARK_SEARCH;
|
||||
this->disk_access();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void Swim3Ctrl::disk_access()
|
||||
{
|
||||
MacSuperdrive::SectorHdr hdr;
|
||||
uint64_t delay;
|
||||
|
||||
switch(this->cur_state) {
|
||||
case SWIM3_ADDR_MARK_SEARCH:
|
||||
hdr = this->int_drive->current_sector_header();
|
||||
// update the corresponding SWIM3 registers
|
||||
this->cur_track = ((hdr.side & 1) << 7) | (hdr.track & 0x7F);
|
||||
this->cur_sector = 0x80 /* CRC/checksum valid */ | (hdr.sector & 0x7F);
|
||||
this->format = hdr.format;
|
||||
// generate ID_read interrupt
|
||||
this->int_flags |= INT_ID_READ;
|
||||
update_irq();
|
||||
if ((this->cur_sector & 0x7F) == this->target_sect) {
|
||||
// sector matches -> transfer its data
|
||||
this->cur_state = SWIM3_DATA_XFER;
|
||||
delay = this->int_drive->sector_data_delay();
|
||||
} else {
|
||||
// move to next address mark
|
||||
this->cur_state = SWIM3_ADDR_MARK_SEARCH;
|
||||
delay = this->int_drive->next_sector_delay();
|
||||
}
|
||||
break;
|
||||
case SWIM3_DATA_XFER:
|
||||
// transfer sector data over DMA
|
||||
this->dma_ch->push_data(this->int_drive->get_sector_data_ptr(this->cur_sector & 0x7F), 512);
|
||||
if (--this->xfer_cnt == 0) {
|
||||
this->stop_disk_access();
|
||||
// generate sector_done interrupt
|
||||
this->int_flags |= INT_SECT_DONE;
|
||||
update_irq();
|
||||
return;
|
||||
}
|
||||
this->cur_state = SWIM3_ADDR_MARK_SEARCH;
|
||||
delay = this->int_drive->next_addr_mark_delay(&this->target_sect);
|
||||
break;
|
||||
default:
|
||||
LOG_F(ERROR, "SWIM3: unknown disk access phase 0x%X", this->cur_state);
|
||||
return;
|
||||
}
|
||||
|
||||
// HACK: figure out from bits in int_mask register which kind of disk access is requested
|
||||
if (this->int_mask & INT_ID_READ) { // read address header
|
||||
this->access_timer_id = TimerManager::get_instance()->add_cyclic_timer(
|
||||
static_cast<uint64_t>(this->int_drive->get_sector_delay() * NS_PER_SEC + 0.5f),
|
||||
[this]() {
|
||||
// get next sector's address field
|
||||
MacSuperdrive::SectorHdr addr = this->int_drive->next_sector_header();
|
||||
// set up the corresponding SWIM3 registers
|
||||
this->cur_track = ((addr.side & 1) << 7) | (addr.track & 0x7F);
|
||||
this->cur_sector = 0x80 /* CRC/checksum valid */ | (addr.sector & 0x7F);
|
||||
this->format = addr.format;
|
||||
// generate ID_read interrupt
|
||||
this->int_flags |= INT_ID_READ;
|
||||
update_irq();
|
||||
}
|
||||
);
|
||||
} else { // otherwise, read sector data
|
||||
this->access_timer_id = TimerManager::get_instance()->add_cyclic_timer(
|
||||
static_cast<uint64_t>(this->int_drive->get_sector_delay() * NS_PER_SEC + 0.5f),
|
||||
[this]() {
|
||||
// transfer sector data over DMA
|
||||
this->dma_ch->push_data(this->int_drive->get_sector_data_ptr(this->cur_sector), 512);
|
||||
|
||||
// get next address field
|
||||
MacSuperdrive::SectorHdr addr = this->int_drive->next_sector_header();
|
||||
// set up the corresponding SWIM3 registers
|
||||
this->cur_track = ((addr.side & 1) << 7) | (addr.track & 0x7F);
|
||||
this->cur_sector = 0x80 /* CRC/checksum valid */ | (addr.sector & 0x7F);
|
||||
this->format = addr.format;
|
||||
|
||||
if (--this->xfer_cnt == 0) {
|
||||
this->stop_disk_access();
|
||||
// generate sector_done interrupt
|
||||
this->int_flags |= INT_SECT_DONE;
|
||||
update_irq();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
this->access_timer_id = TimerManager::get_instance()->add_oneshot_timer(
|
||||
delay,
|
||||
[this]() {
|
||||
this->disk_access();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void Swim3Ctrl::stop_disk_access()
|
||||
|
@ -69,6 +69,13 @@ enum {
|
||||
INT_SECT_DONE = 0x08,
|
||||
};
|
||||
|
||||
// SWIM3 internal states.
|
||||
enum {
|
||||
SWIM3_IDLE,
|
||||
SWIM3_ADDR_MARK_SEARCH,
|
||||
SWIM3_DATA_XFER,
|
||||
};
|
||||
|
||||
class Swim3Ctrl : public HWComponent {
|
||||
public:
|
||||
Swim3Ctrl();
|
||||
@ -94,6 +101,7 @@ protected:
|
||||
void do_step();
|
||||
void stop_stepping();
|
||||
void start_disk_access();
|
||||
void disk_access();
|
||||
void stop_disk_access();
|
||||
|
||||
private:
|
||||
@ -112,10 +120,13 @@ private:
|
||||
uint8_t step_count;
|
||||
uint8_t cur_track;
|
||||
uint8_t cur_sector;
|
||||
uint8_t target_sect;
|
||||
uint8_t format; // format byte from the last GCR/MFM address field
|
||||
uint8_t first_sec;
|
||||
uint8_t xfer_cnt;
|
||||
uint8_t gap_size;
|
||||
uint8_t rd_line;
|
||||
int cur_state;
|
||||
|
||||
int step_timer_id = 0;
|
||||
int access_timer_id = 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user