--- nono/vm/fdc.h 2026/04/29 17:04:28 1.1 +++ nono/vm/fdc.h 2026/04/29 17:05:18 1.1.1.9 @@ -1,145 +1,461 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt +// + +// +// FDC (uPD72065B) + I/O コントローラ // #pragma once #include "device.h" -#include "scheduler.h" +#include "event.h" +#include "fixedqueue.h" +#include "monitor.h" +#include + +class DMACDevice; +class FDDDevice; +class PEDECDevice; -enum fdc_phase_t { - PHASE_IDLE = 0, - PHASE_COMMAND, - PHASE_EXEC, - PHASE_RESULT, -}; +struct FDCCmdBuf +{ + union { + uint8 buf[10]; -struct FDD { - bool motor; // モーターオン - bool seek; // シーク中/シーク完了割り込み保留中 + 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; + }; + }; + }; + + int len; + + void Clear() { + len = 0; + } }; -struct FDC { - int drivesel; // ドライブ選択 - fdc_phase_t phase; - uint8 cmdbuf[10]; - int cmdpos; - int cmdlen; - uint8 resbuf[10]; - int respos; - int reslen; - - bool is2DD; - int accdrive; // アクセスドライブ番号 - - // ステータスレジスタ - bool RQM; // Request For Master - int DIO; // Data Input/Output -#define DIOtoFDC (0) -#define DIOtoHost (1) - bool NDM; // Non-DMA Mode - bool CB; // FDC Busy - - uint8 srt; - uint8 hut; - uint8 hlt; - bool nd; // Non DMA - - // コマンド(下位5ビットのみにしたもの) - static const int CMD_MASK = 0x1f; // マスク - static const int CMD_READ_DIAGNOSTIC = 0x02; // %0M00_0010 - static const int CMD_SPECIFY = 0x03; // %0000_0011 - static const int CMD_SENSE_DEVICE_STATUS = 0x04; // %0000_0100 - static const int CMD_WRITE_DATA = 0x05; // %MM00_0101 - static const int CMD_READ = 0x06; // %MMS0_0110 - static const int CMD_RECALIBRATE = 0x07; // %0000_0111 - static const int CMD_SENSE_INTERRUPT_STATUS = 0x08; // %0000_1000 - static const int CMD_WRITE_DELETED_DATA = 0x09; // %MM00_1001 - static const int CMD_READ_ID = 0x0a; // %0M00_1010 - static const int CMD_READ_DELETED_DATA = 0x0c; // %MMS0_1100 - static const int CMD_WRITE_ID = 0x0d; // %0M00_1101 - static const int CMD_SEEK = 0x0f; // %0000_1111 - static const int CMD_SCAN_EQUAL = 0x11; // %MMS1_0001 - static const int CMD_RESET_STANDBY = 0x14; // %0011_0100 - static const int CMD_SET_STABNDBY = 0x15; // %0011_0101 - static const int CMD_SOFTWARE_RESET = 0x16; // %0011_0110 - static const int CMD_SCAN_LOW_OR_EQUAL = 0x19; // %MMS1_1001 - static const int CMD_SCAN_HIGH_OR_EQUAL = 0x1d; // %MMS1_1101 - - // ステータスレジスタ - static const int STAT_RQM = 0x80; // Request For Master - static const int STAT_DIO = 0x40; // Data Input/Output - static const int STAT_NDM = 0x20; // Non-DMA Mode - static const int STAT_CB = 0x10; // FDC Busy - static const int STAT_D3B = 0x08; // FD3 Busy - static const int STAT_D2B = 0x04; // FD2 Busy - static const int STAT_D1B = 0x02; // FD1 Busy - static const int STAT_D0B = 0x01; // FD0 Busy - - // リザルトステータス0 - static const int ST0_IC_MASK = 0xc0; - static const int ST0_IC_AI = 0xc0; // AI: Attention Interrupt - static const int ST0_IC_IC = 0x80; // CI: Invalid Command - static const int ST0_IC_AT = 0x40; // AT: Abnormal Terminate - static const int ST0_IC_NT = 0x00; // NT: Normal Terminate - static const int ST0_SE = 0x20; // Seek End - static const int ST0_EC = 0x10; // Equipment Check - static const int ST0_NR = 0x08; // Not Ready - static const int ST0_HD = 0x04; // Head Address - static const int ST0_US_MASK = 0x03; // Unit Select - - // リザルトステータス3 - static const int ST3_FT = 0x80; // Fault - static const int ST3_WP = 0x40; // Write Protect - static const int ST3_RY = 0x20; // Ready - static const int ST3_T0 = 0x10; // Track 0 - static const int ST3_TS = 0x08; // Two Side - static const int ST3_HD = 0x04; // HD - static const int ST3_US_MASK = 0x03; // Unit Select +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 + +class FDCDevice : public IODevice { - typedef IODevice inherited; - private: + using inherited = IODevice; static const int baseaddr = 0xe94000; public: - FDCDevice(); - virtual ~FDCDevice(); + // サポートしているドライブ数上限 + static const int MAX_DRIVE = 4; - virtual void ResetHard(); + 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 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; + }; - virtual uint64 Read8(uint32 addr); - virtual uint64 Read16(uint32 addr); - virtual uint64 Write8(uint32 addr, uint32 data); - virtual uint64 Write16(uint32 addr, uint32 data); - virtual uint64 Peek8(uint32 addr); + public: + FDCDevice(); + virtual ~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; + uint64 Read(uint32 offset); + uint64 Write(uint32 offset, uint32 data); + uint64 Peek(uint32 offset); private: - uint32 ReadStatus(); uint32 ReadData(); + void WriteDataReg(uint32); uint32 ReadDriveStatus(); void WriteCmd(uint32); - void idle_phase(); - void exec_phase(); - void result_phase(); - - struct FDC fdc {}; - struct FDD fdd[4] {}; + uint32 GetDriveStatus() const; + void PushDreg(uint8); + int PopDreg(); + void RequestDataByte(); + + uint8 GetMSR() const; + uint8 GetST0() const; + uint8 GetST1() const; + uint8 GetST2() const; + + void CommandPhase(); + void ExecPhase(); + void ResultPhase(); + + void CommandCallback(Event& ev); + 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_CALLBACK(MonitorUpdate); + void MonitorReg(TextScreen&, + int x, int y, uint8 reg, const char * const *names); + + // FDC が内部で記憶しているユニットごとの状態。 + // こちらは US をインデックスとしてアクセスすること。 + std::array unitinfo {}; + + // 選択されている FDD + FDDDevice *fdd {}; + + // ドライブ数 + int ndrive {}; + + // 接続されているドライブ。 + // fdd_list[] は配列なので歯抜けをサポート出来るが、今の所そのような + // 設定をする方法がない (必要になったら考える)。 + std::unique_ptr fdd_list[MAX_DRIVE] /*{}*/; + // 生ポインタを配列と同じ要領で格納したもの。 + std::vector fdd_vector {}; + + // SPECIFY で設定(初期化)する項目 + uint8 srt {}; + uint8 hut {}; + uint8 hlt {}; + bool nd {}; // Non DMA + bool initialized {}; // SPECIFY で初期化されたかどうか + + // コマンド + const FDCCmd *cmd {}; // 現在選択中のコマンド + static const std::vector cmd_list; + + // FDC 内部状態 + fdc_phase_t phase {}; // 動作フェーズ + FDCCmdBuf cmdbuf {}; + FDCResBuf resbuf {}; + FixedQueue dreg {}; // 内部データレジスタ + + bool hd {}; // Head address + uint8 us {}; // Unit Select + 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 {}; + + int 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 { this }; // 状態遷移用 + Event poll_event { this }; // ポーリング用 + Event motor_event { this }; // モータ停止用 - void Callback(int); - - void Interrupt(); - - Event event {}; - - // コマンドを名前に変換 - static const char *cmd2name(uint32); - - // コマンド名一覧 - static const char *cmdnames_str[]; + Monitor monitor { this }; }; + +static inline FDCDevice *GetFDCDevice() { + return Object::GetObject(OBJ_FDC); +}