|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// FDC (uPD72065B) + I/O コントローラ
//
#pragma once
#include "device.h"
#include "fixedqueue.h"
#include <array>
class DMACDevice;
class FDDDevice;
class PEDECDevice;
struct FDCCmdBuf
{
union {
uint8 buf[10];
struct {
uint8 code;
union {
// READ DATA, READ DELETED DATA
// WRITE DATA, WRITE DELETED DATA
// READ DIAGNOSTIC
struct {
uint8 hd_us;
uint8 c;
uint8 h;
uint8 r;
uint8 n;
uint8 eot;
uint8 gsl;
uint8 dtl;
} data;
// SCAN EQUAL, SCAN LOW OR EQUAL, SCAN HIGH OR EQUAL
struct {
uint8 hd_us;
uint8 c;
uint8 h;
uint8 r;
uint8 n;
uint8 eot;
uint8 gsl;
uint8 dtl;
uint8 stp;
} scan;
struct {
uint8 us;
uint8 ncn;
} seek;
struct {
uint8 us;
} recalibrate;
struct {
uint8 srt_hut;
uint8 hlt_nd;
} specify;
// READ ID
struct {
uint8 hd_us;
} read_id, sense_device_status;
struct {
uint8 hd_us;
uint8 n;
uint8 sc;
uint8 gpl;
uint8 d;
} write_id;
};
};
};
uint len;
void Clear() {
len = 0;
}
};
struct FDCResBuf
{
union {
uint8 buf[7];
struct {
uint8 st0;
uint8 st1;
uint8 st2;
uint8 c;
uint8 h;
uint8 r;
uint8 n;
} data;
struct {
uint8 st0;
uint8 pcn;
} interrupt_status;
struct {
uint8 st3;
} device_status;
struct {
uint8 st0;
} invalid;
};
int len {};
int pos {};
void Clear() {
len = 0;
pos = 0;
}
uint8 Take() {
assert(pos < len);
return buf[pos++];
}
uint8 Peek() {
assert(pos < len);
return buf[pos];
}
};
class FDCDevice : public IODevice
{
using inherited = IODevice;
static const uint32 baseaddr = 0xe94000;
public:
// サポートしているドライブ数上限
static const uint MAX_DRIVE = 4;
private:
// コマンド定義
struct FDCCmd
{
uint8 code;
uint8 mask;
int cmdlen;
void (FDCDevice::*func)();
const char *name;
};
// ユニットのポーリング時の動作状態
enum class UnitState {
Idle,
Seek,
Recalibrate,
};
// ユニットの状態
struct UnitInfo {
// FDC が記憶しているヘッドがいることになっているシリンダ番号。
// FDD の実際のヘッド位置とは必ずしも一致しない。
int pcn;
// シーク先のシリンダ番号
int ncn;
// ユニットごとの、前回調べた READY
bool ready;
// ポーリング時動作状態
UnitState state;
uint64 ready_time; // READY になった時刻
};
// FDC の動作フェーズ
enum fdc_phase_t {
PHASE_COMMAND,
PHASE_EXEC,
PHASE_RESULT,
};
// マスタステータスレジスタ
static const uint32 MSR_RQM = 0x80;
static const uint32 MSR_DIO = 0x40;
static const uint32 MSR_NDM = 0x20;
static const uint32 MSR_CB = 0x10;
static const bool DIOtoFDC = false;
static const bool DIOtoHost = true;
struct MSR {
bool rqm; // Request For Master
bool dio; // Data Input/Output
bool ndm; // Non-DMA Mode
bool cb; // FDC Busy
std::array<bool, 4> db; // FDn Busy (注:seek中/割り込み保留中)
};
// ST0 レジスタ
static const uint32 ST0_IC_NT = 0x00;
static const uint32 ST0_IC_AT = 0x40;
static const uint32 ST0_IC_IC = 0x80;
static const uint32 ST0_IC_AI = 0xc0;
static const uint32 ST0_SE = 0x20;
static const uint32 ST0_EC = 0x10;
static const uint32 ST0_NR = 0x08;
static const uint32 ST0_HD = 0x04;
static const uint32 ST0_US = 0x03;
struct ST0 {
uint8 ic; // Interrupt Code
bool se; // Seek End
bool ec; // Equipment Check
bool nr; // Not Ready
bool hd;
uint8 us;
};
// ST1 レジスタ
static const uint32 ST1_EN = 0x80;
static const uint32 ST1_DE = 0x20;
static const uint32 ST1_OR = 0x10;
static const uint32 ST1_ND = 0x04;
static const uint32 ST1_NW = 0x02;
static const uint32 ST1_MA = 0x01;
struct ST1 {
bool en; // End of Cylinder
bool de; // Data Error
bool overrun; // Over Run
bool nd; // No Data
bool nw; // Not Writable
bool ma; // Missing Address Mark
};
// ST2 レジスタ
static const uint32 ST2_CM = 0x40;
static const uint32 ST2_DD = 0x20;
static const uint32 ST2_NC = 0x10;
static const uint32 ST2_SH = 0x08;
static const uint32 ST2_SN = 0x04;
static const uint32 ST2_BC = 0x02;
static const uint32 ST2_MD = 0x01;
struct ST2 {
bool cm; // Control Mark
bool dd; // Data Error In Data Field
bool nc; // No Cylinder
bool sh; // Scan Equal Hit
bool sn; // Scan Not Satisfied
bool bc; // Bad Cylinder
bool md; // Missing Addrss Mark in Data Field
};
// ST3 レジスタ
static const uint32 ST3_FT = 0x80;
static const uint32 ST3_WP = 0x40;
static const uint32 ST3_RY = 0x20;
static const uint32 ST3_T0 = 0x10;
static const uint32 ST3_TS = 0x08;
static const uint32 ST3_HD = 0x04;
static const uint32 ST3_US = 0x03;
struct ST3 {
bool ft; // Fault
bool wp; // Write Protect
bool ry; // Ready
bool t0; // Track 0
bool ts; // Two Side
bool hd;
uint8 us;
};
public:
FDCDevice();
~FDCDevice() override;
bool Create() override;
bool Init() override;
void ResetHard(bool poweron) override;
// READY 信号が変化したことの通知 (FDD から呼ばれる)
void ReadyChanged(int unit, bool is_ready);
// 強制レディ (OPM から呼ばれる)
void SetForceReady(bool force_ready_);
// DACK と TC 信号 (DMA から呼ばれる)
void AssertDACK(bool tc);
void NegateDACK();
protected:
// BusIO インタフェース
static const uint32 NPORT = 4;
busdata ReadPort(uint32 offset);
busdata WritePort(uint32 offset, uint32 data);
busdata PeekPort(uint32 offset);
bool PokePort(uint32 offset, uint32 data);
private:
uint32 ReadData();
void WriteDataReg(uint32);
uint32 ReadDriveStatus();
void WriteCmd(uint32);
uint32 GetDriveStatus() const;
void PushDreg(uint8);
uint32 PopDreg();
void RequestDataByte();
uint8 GetMSR() const;
uint8 GetST0() const;
uint8 GetST1() const;
uint8 GetST2() const;
void CommandPhase();
void ExecPhase();
void ResultPhase();
void CommandCallback(Event *);
void ResultCallback(Event *);
void ChangeInterrupt();
// シーク終了処理
void SeekEnd();
// データ転送
void XferStart(bool is_write);
void XferStartCallback(Event *);
void XferDataCallback(Event *);
void XferEndCallback(Event *);
// FDC コマンド
void CmdInvalid();
void CmdReadDiagnostic();
void CmdSpecify();
void CmdSenseDeviceStatus();
void CmdWriteData();
void CmdReadData();
void CmdRecalibrate();
void CmdSenseInterruptStatus();
void CmdWriteDeletedData();
void CmdReadID();
void CmdReadDeletedData();
void CmdWriteID();
void CmdSeek();
void CmdVersion();
void CmdScanEqual();
void CmdScanLowOrEqual();
void CmdScanHighOrEqual();
void CmdResetStandby();
void CmdSetStandby();
void CmdSoftwareReset();
// 未実装コマンドの共通部
void CmdNotImplemented();
// ユニット US0,US1 信号線の状態を変える
void UnitSelect(uint8);
// ドライブセレクト信号線の状態を変える
void DriveSelect(uint8 sel);
// READY 入力端子
bool IsReady() const;
// ポーリング
void StartPoll();
void PollEventCallback(Event *);
bool PollReady(int);
bool ExecSeek(int);
// モータ停止用
void MotorEventCallback(Event *);
DECLARE_MONITOR_SCREEN(MonitorScreen);
void MonitorReg(TextScreen&,
int x, int y, uint8 reg, const char * const *names);
// FDC が内部で記憶しているユニットごとの状態。
// こちらは US をインデックスとしてアクセスすること。
std::array<UnitInfo, MAX_DRIVE> unitinfo {};
// 選択されている FDD
FDDDevice *fdd {};
// ドライブ数
int ndrive {};
// 接続されているドライブ。
// fdd_list[] は配列なので歯抜けをサポート出来るが、今の所そのような
// 設定をする方法がない (必要になったら考える)。
std::unique_ptr<FDDDevice> fdd_list[MAX_DRIVE] /*{}*/;
// 生ポインタを配列と同じ要領で格納したもの。
std::vector<FDDDevice *> fdd_vector {};
// SPECIFY で設定(初期化)する項目
uint8 srt {};
uint8 hut {};
uint8 hlt {};
bool nd {}; // Non DMA
bool initialized {}; // SPECIFY で初期化されたかどうか
// コマンド
const FDCCmd *cmd {}; // 現在選択中のコマンド
static const std::vector<FDCCmd> cmd_list;
// FDC 内部状態
fdc_phase_t phase {}; // 動作フェーズ
FDCCmdBuf cmdbuf {};
FDCResBuf resbuf {};
FixedQueue<uint8, 1> dreg {}; // 内部データレジスタ
bool hd {}; // Head address
uint8 us {}; // Unit Select (US0,US1) 出力信号
bool tc {}; // TC 入力信号
bool dack {}; // DACK 入力信号
bool mt {}; // Multi Track
bool mf {}; // MFM
bool sk {}; // Skip
bool xfer_start {};
bool xfer_enable {};
bool xfer_write {};
uint xfer_remain {};
uint sect_remain {};
int index_detected {};
uint seek_srt {}; // SEEK ステップレートタイマカウンタ
MSR msr {};
ST0 st0 {};
ST1 st1 {};
ST2 st2 {};
// I/O コントローラ部
uint8 drivectrl {}; // ドライブコントロールレジスタ
bool motor_on_bit {}; // アクセスドライブ選択レジスタの MOTOR_ON
// OPM
bool force_ready {}; // 強制レディ
DMACDevice *dmac {};
PEDECDevice *pedec {};
Event *phase_event {}; // 状態遷移用
Event *poll_event {}; // ポーリング用
Event *motor_event {}; // モータ停止用
Monitor *monitor {};
};
inline FDCDevice *GetFDCDevice() {
return Object::GetObject<FDCDevice>(OBJ_FDC);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.