File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / fdc.h
Revision 1.1.1.14 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:06:01 2026 UTC (3 months ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
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);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.