File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / scsidev.h
Revision 1.1.1.20 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:06:01 2026 UTC (2 months, 3 weeks 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
//

//
// SCSI デバイス
//

// Device                 Device
//    |                     |
//    v                     v
// IODevice             SCSIDevice         (SCSI デバイスとしての共通部分)
//    |                     |
//    v               +-----+------+
// SPCDevice          v             v
//      .scsi -> SCSIHostDevice SCSITarget (SCSI ホスト/ターゲットの共通部分)
//                                  |
//                                  v
//                               SCSIDisk  (各デバイス、各ターゲット固有部分)

#pragma once

#include "diskimage.h"
#include "message.h"
#include "scsibus.h"
#include "scsicmd.h"

class SCSIDomain;

//
// SCSI デバイスとしての共通部分。
//
class SCSIDevice : public Device
{
	using inherited = Device;
 public:
	explicit SCSIDevice(uint objid_);
	~SCSIDevice() override;

	// SCSI ID を返す
	uint GetMyID() const { return myid; }

	// デバイス種別を返す (主にデバッグ表示用)
	SCSI::DevType GetDevType() const { return devtype; }

	// バスに接続する。
	void Attach(SCSIBus *bus_);

	// 自身が接続しているバスを返す。
	// イニシエータがバスに接続するのは VM 実行後なことに注意。
	SCSIBus *GetBus() const { return bus; }

	//
	// 信号線制御
	// (ソースコード的なショートカットのため)
	//
	bool GetRST() const				{ return bus->GetRST(); }
	void AssertRST()				{ bus->AssertRST(); }
	void NegateRST()				{ bus->NegateRST(); }
	bool GetREQ() const				{ return bus->GetREQ(); }
	void AssertREQ()				{ bus->AssertREQ(); }
	void NegateREQ()				{ bus->NegateREQ(); }
	bool GetACK() const				{ return bus->GetACK(); }
	void AssertACK()				{ bus->AssertACK(); }
	void NegateACK()				{ bus->NegateACK(); }
	bool GetATN() const				{ return bus->GetATN(); }
	void AssertATN()				{ bus->AssertATN(); }
	void NegateATN()				{ bus->NegateATN(); }
	uint8 GetSEL() const			{ return bus->GetSEL(); }
	void AssertSEL()				{ bus->AssertSEL(myid); }
	void NegateSEL()				{ bus->NegateSEL(myid); }
	uint8 GetBSY() const			{ return bus->GetBSY(); }
	void AssertBSY()				{ bus->AssertBSY(myid); }
	void NegateBSY()				{ bus->NegateBSY(myid); }
	bool GetMSG() const				{ return bus->GetMSG(); }
	bool GetCD() const				{ return bus->GetCD(); }
	bool GetIO() const				{ return bus->GetIO(); }
	SCSI::XferPhase GetXfer() const { return bus->GetXfer(); }
	void SetXfer(uint8 val)			{ bus->SetXfer(val); }
	SCSI::Phase GetPhase() const	{ return bus->GetPhase(); }
	uint8 GetData() const			{ return bus->GetData(); }
	void SetData(uint8 val) const	{ bus->SetData(val); }

	//
	// SCSIBus から呼ばれる
	//

	// バスリセットされた (全員に通知される)
	virtual void OnBusReset() { }

	// バスフリーになった (全員に通知される)
	virtual void OnBusFree(uint id) { }

	// セレクションで選択された (ターゲットのみ通知される)
	virtual void OnSelected() { }

	// ターゲットがセレクションに応答した (イニシエータのみ通知される)
	virtual void OnSelectionAck() { }

	// 情報転送フェーズを開始する (ターゲットのみ通知される)
	virtual void OnStartTransfer() { }

	// 情報転送フェーズで REQ を立てた (イニシエータのみ通知される)
	virtual void OnTransferReq() { }

	// 情報転送フェーズで1バイト送受信が進んだ (ターゲットのみ通知される)
	virtual void OnTransfer() { }

	// 情報転送フェーズ(マニュアル転送)で ACK を下げた (ターゲットのみ通知)
	virtual void OnTransferAck() { }

 protected:
	// 自身の ID
	uint myid {};

	// デバイス種別
	SCSI::DevType devtype {};

	// あれば自身が繋がっているバスへのポインタ。
	// (イニシエータ自身も含めて) 所有ではない。
	SCSIBus *bus {};

	// 自身を管理しているドメイン(親) へのポインタ。
	SCSIDomain *domain {};
};

//
// ホストデバイスの共通部分。
// イニシエータもしくは HBA (ホストバスアダプタ) に相当する部分。
//
class SCSIHostDevice : public SCSIDevice
{
	using inherited = SCSIDevice;

 public:
	using SCSIHostCallback_t = void (Device::*)();
	using BusFreeCallback_t = void (Device::*)(uint);
#define ToSCSIHostCallback(f)	\
	static_cast<SCSIHostDevice::SCSIHostCallback_t>(f)
#define ToBusFreeCallback(f)	\
	static_cast<SCSIHostDevice::BusFreeCallback_t>(f)

 public:
	SCSIHostDevice(SCSIDomain *domain_, IODevice *parent_);
	~SCSIHostDevice() override;

	// SCSI ID を設定する。(SCSIDomain からのみ呼ぶこと)
	void SetID(uint myid_);

	// SCSIBus からのコールバック関数を設定する。
	void SetBusCallback(
		BusFreeCallback_t busfree_,
		SCSIHostCallback_t selack_,
		SCSIHostCallback_t xferreq_);

 private:
	// SCSIBus から呼ばれる

	// バスフリーになった (SCSIBus から全員に通知される)
	void OnBusFree(uint id) override;
	// ターゲットがセレクションに応答した (SCSIBus から呼ばれる)
	void OnSelectionAck() override;
	// 情報転送フェーズで REQ を立てた (イニシエータのみ通知される)
	void OnTransferReq() override;

	// 親クラス(コントローラ) へのコールバック。

	// バスフリーを通知する
	BusFreeCallback_t BusFreeCallback {};
	// ターゲットがセレクションに応答したことを通知する
	SCSIHostCallback_t SelectionAckCallback {};
	// 転送フェーズでターゲットが REQ を立てたことを通知する
	SCSIHostCallback_t TransferReqCallback {};

	// コントローラデバイス。
	IODevice *parent {};
};

//
// ターゲットデバイスの共通部分。
//
class SCSITarget : public SCSIDevice
{
	using inherited = SCSIDevice;
 public:
	SCSITarget(SCSIDomain *, uint id);
	~SCSITarget() override;

	void ResetHard(bool poweron) override;

	// Inquiry データを返す (Cmd から呼ばれる)
	virtual bool Inquiry(std::vector<uint8>& buf, uint lun) = 0;

	// 現在のコマンド列を返す (モニタ用)
	std::vector<uint8> GetCmdSeq() const { return cmdseq; }

	// コマンド列から SCSICmd を選択する。
	// (VirtIOSCSI と ROM30 エミュレーションから使う)
	SCSICmd *SelectCommand(const std::vector<uint8>& cmdseq_);

	// 論理ブロック長
	uint32 GetBlocksize() const { return blocksize; }
	void SetBlocksize(uint32 val) { blocksize = val; }

	// ModeSense コマンドのデバイス固有パラメータを返す
	virtual uint8 GetDeviceSpecificParam() const { return 0; }

	// センスキー
	uint8  sensekey {};
	// 上位バイトが ASC、下位バイトが ASCQ
	uint16 senseasc {};

	// センスキーをクリア
	void ClearSense() {
		sensekey = 0;
		senseasc = 0;
	}

	// SCSIBus から呼ばれる

	// バスリセットされた (全員に通知される)
	void OnBusReset() override;

	// セレクションで選択された (ターゲットのみ通知される)
	void OnSelected() override;

	// 情報転送フェーズを開始する (ターゲットのみ通知される)
	void OnStartTransfer() override;

	// 情報転送フェーズで1バイト送受信が進んだ (ターゲットのみ通知される)
	void OnTransfer() override;

	// 情報転送フェーズ(マニュアル転送)で ACK を下げた (ターゲットのみ通知)
	void OnTransferAck() override;

 protected:
	// SCSI コマンドを選択する。内部用。
	void SelectCommand();

	// SCSI コマンドのディスパッチャ
	virtual void DispatchCmd();

	// コマンド
	std::vector<uint8> cmdseq {};

	// このコマンドの長さ。
	// cmdseq の1バイト目で決まるやつのことで、受信目標バイト数。
	// 今何バイト受信しているかは cmdseq.size()。
	uint cmdlen {};

	// 現在実行中の SCSI コマンド
	std::unique_ptr<SCSICmd> cmd {};

	// コマンド前のメッセージアウトフェーズ。
	std::vector<uint8> msgseq {};

	// 現在のフェーズでの転送カウント。
	// IN と OUT でカウント方向が違うことに注意。
	uint32 bytecount {};

	// 現在処理中のバイトが(各)フェーズの最終バイトなら true
	bool lastbyte {};

	// 論理ブロック長
	uint32 blocksize {};
};

//
// SCSI Disk (HD,CD,...)
//
class SCSIDisk : public SCSITarget
{
	using inherited = SCSITarget;
 public:
	// メディアへの書き込みモード
	//
	// Device	Media	Ignore
	// RO		(RO)	*		ReadOnly
	// RW		RO		*		WriteProtect
	// RW		RW		no		Writeable
	// RW		RW		yes		WriteIgnore
	enum class RW {
		ReadOnly		= 2,	// 読み込み専用デバイス
		WriteProtect	= 1,	// 書き込み禁止メディア
		Writeable		= 0,	// 書き込み可能
		WriteIgnore		= -1,	// 書き込み可能メディアで書き込みを無視
	};

 public:
	SCSIDisk(SCSIDomain *, uint id, SCSI::DevType type);
	~SCSIDisk() override;

	bool Init() override;
	void ResetHard(bool poweron) override;

	void DispatchCmd() override;
	bool Inquiry(std::vector<uint8>& buf, uint lun) override;

	// ModeSense コマンドのデバイス固有パラメータを返す
	uint8 GetDeviceSpecificParam() const override;

	// buf にデータを読み込む。
	// 単位はセクタではなくバイトなことに注意。
	bool Read(std::vector<uint8>& buf, uint64 start);

	// buf のデータを書き込む。
	// 単位はセクタではなくバイトなことに注意。
	bool Write(const std::vector<uint8>& buf, uint64 start);

	// このデバイスのバイトサイズ
	off_t GetSize() const;

	// このデバイスをディスクイメージだと思うとして (いや実際そうだけど)
	// 先頭から start バイト目から len バイト読み出して返す。
	// 単位はセクタではなくバイトなことに注意。
	// (エミュレータ特権アクセス)
	bool Peek(void *buf, uint64 start, uint32 len) const;

	// シーク時間[tsec]
	uint64 GetSeekTime() const { return seektime; }

	// リムーバブルデバイスなら true
	bool IsRemovableDevice() const { return removable_device; }

	// 書き込み可能デバイスなら true
	bool IsWriteableDevice() const { return writeable_device; }

	// メディアが入っていれば true
	bool IsMediumLoaded() const { return medium_loaded; }

	// メディアの書き込みモードを返す
	RW GetWriteMode() const { return write_mode; }
	// メディアの書き込みモードを文字列で返す (モニタ用)
	const char *GetWriteModeStr() const;

	// メディア(ディスク)イメージを開く/閉じる。(同一スレッドから呼ぶこと)
	bool LoadDisk(const std::string& path_);
	void UnloadDisk(bool force = false);

	// メディア(ディスク)イメージを開く/閉じる。(UI スレッドから呼ぶ)
	void LoadDiskUI(const std::string& path_);
	void UnloadDiskUI(bool force);

	// メディアの取り出し可否
	bool IsMediumRemovalPrevented() const { return medium_removal_prevented; }
	bool IsMediumRemovalAllowed() const  { return !medium_removal_prevented; }
	void PreventMediumRemoval(bool val);

	// 現在ロードされているイメージファイルのフルパスを返す
	const std::string& GetPathName() const { return pathname; }

	// SCSIBus から呼ばれる

	// バスリセットされた (全員に通知される)
	void OnBusReset() override;

 private:
	// ディスクイメージを閉じる (内部用)
	bool UnloadDisk_internal();

	// ディスク状態をクリアする
	void Clear();

	// メディア状態変更を UI に通知する
	void MediaChanged() const;

	// メディア挿入/排出コールバック
	void LoadMessage(MessageID, uint32);
	void UnloadMessage(MessageID, uint32);

	// イメージファイルのフルパス
	std::string pathname {};
	// 新しいイメージファイルのフルパス (UIから)
	std::string new_pathname {};

	// イメージファイル
	DiskImage image {};

	bool removable_device {};	// リムーバブルデバイスなら true
	bool writeable_device {};	// 書き込み可能なデバイスなら true
	bool write_ignore {};		// 書き込み無視オプション
	bool medium_loaded {};		// メディアが挿入されていれば true

	// メディアの書き込みモード
	RW write_mode {};

	// メディアの取り出しを禁止するなら true。
	// XXX Bus Device Reset メッセージでも解除できるがメッセージは未実装
	bool medium_removal_prevented {};

	uint64 seektime {};
};

unix.superglobalmegacorp.com

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