--- nono/vm/scsibus.h 2026/04/29 17:05:33 1.1.1.10 +++ nono/vm/scsibus.h 2026/04/29 17:06:01 1.1.1.13 @@ -8,97 +8,14 @@ // SCSI バス // -// フェーズ遷移についてのメモ。 -// -// イニシエータ(この時点ではまだ候補だが)が BSY を立てることで -// (バスフリーフェーズから) アービトレーションフェーズに移る。 -// 現行では規定時間後に自動的にイニシエータが勝利する(バスは何もしない)。 -// イニシエータが SEL 立てて BSY を下ろすことで、セレクションフェーズに移る。 -// この間の遷移は以下の通り。 -// BSY SEL -// 0 0 BusFree -// ↓ イニシエータ(候補)が BSY を立てる -// 1 0 Arbitration (バスは何もしない) -// ↓ イニシエータが SEL を立てる -// 1 1 Arbitration -// ↓ イニシエータがデータバスにターゲットをセットして BSY を下ろす -// 0 1 Selection -// -// バスはセレクションフェーズ遷移後、規定時間でターゲットに通知。 -// ターゲットは即 BSY を立てるのでバスはこれをイニシエータに通知。 -// イニシエータは SEL を下げるのでバスはこれをターゲットに通知。 -// この間の遷移は以下の通り。 -// BSY SEL -// 0 1 Selection (ターゲットに通知) -// ↓ ターゲットが BSY を立てる -// 1 1 Selection (イニシエータに通知) -// ↓ イニシエータがデータバスと SEL を下げる -// 1 0 Transfer (ターゲットに通知) -// -// バスは Transfer 後、規定時間でターゲットに通知。 -// ターゲットは転送フェーズ(の最初のコマンドフェーズ) に移行する。 - -// 転送フェーズについてのメモ。 -// -// -----> (実線) は関数呼び出し -// - - -> (破線) は関数からの戻り -// 〜〜-> (波線) はイベントコールバックによる呼び出し -// -// マニュアル転送は以下のようになる。 -// 基本的にはターゲットが REQ を上げたことをイベントでイニシエータに通知し、 -// イニシエータは基本的に関数呼び出しでターゲットを操作することにする。 -// -// 非同期転送時の REQ/ACK シーケンスに時間規定はないが、時間を考慮せずに -// 実行してしまうと現実より速い転送が出来てしまう可能性があるので、ここでは -// AssertREQ が TransferReqCB を呼び出すところでタイミングを調整する。 -// 具体的には前回の TransferReqCB から 1usec 以上空けるというのはどうか。 -// -// イニシエータ ターゲット -// ------------ ---------- -// Event 〜〜-> StartTransferCB -// ↓ -// REQ を上げる -// TransferReqCB <-〜〜〜 Event 〜〜〜 -// ACK を上げる -// +------ AssertACK() ---> TransferCB -// 1バイト送受信 -// REQ を下げる -// < - - - - - - - - - - - return; -// (誰かが) -// ACK を下げる -// +------ NegateACK() ---> TransferAckCB -// REQ を上げる 〜〜+ -// < - - - - - - - - - - - return; : -// : -// TransferReqCB <-〜〜〜 Event 〜〜〜〜〜〜〜〜〜〜〜〜+ -// -// ハードウェア転送の場合は ACK 一往復を省略して以下のようにする。 -// Transfer() (TransferCB()) は ACK が立っていなければハードウェア転送と -// 判断する。 -// -// イニシエータ ターゲット -// ------------ ---------- -// Event 〜〜-> StartTransferCB -// ↓ -// REQ を上げる -// TransferReqCB <-〜〜〜 Event 〜〜〜 -// | -// +------- Transfer() ---> TransferCB -// 1バイト送受信 -// REQ を下げる -// REQ を上げる 〜〜+ -// < - - - - - - - - - - - return; : -// : -// TransferReqCB <-〜〜〜 Event 〜〜〜〜〜〜〜〜〜〜〜〜+ -// - #pragma once -#include "event.h" #include "scsi.h" +#include class SCSIDevice; class SCSIDisk; +class SCSIDomain; class SCSIHostDevice; class SCSITarget; @@ -121,15 +38,16 @@ class SCSIBus : public Device } public: - SCSIBus(SCSIHostDevice *parent_, const char *config_name_); + explicit SCSIBus(SCSIDomain *); ~SCSIBus() override; - bool Create() override; bool Init() override; void ResetHard(bool poweron) override; - // このバスにデバイスを接続する。 - bool AttachDevice(SCSIDevice *dev, int id); + SCSIDomain *GetDomain() const noexcept { return domain; } + + // デバイス情報を更新する。 + void Refresh(); // 信号線の操作 bool GetRST() const { return rst; } @@ -171,24 +89,10 @@ class SCSIBus : public Device // 現在のフェーズ名を返す const char *GetPhaseName() const { return SCSI::GetPhaseName(phase); } - // 設定ファイル上のこのホストデバイスを示す接頭語を返す - const char *GetConfigName() const { return config_name; } - - // 接続済みの (イニシエータを除く) デバイス数を返す。 - uint GetTargetCount() const; - - // 接続済みの (イニシエータがあれば含む) すべてのデバイスのリストを返す。 - const std::vector& GetConnectedDevices() const { - return connected_devices; - } - - // ターゲットデバイス取得 (ROM エミュレーション/UI用) - SCSITarget *GetTarget(int id) const { return target[id].get(); } - // 現在選択中のターゲットを返す、なければ NULL を返す。(モニタ用) - SCSITarget *GetSelectedTarget() const { - if (0 <= target_id && target_id < 8) { - return target[target_id].get(); + const SCSIDevice *GetSelectedTarget() const { + if (__predict_true(0 <= target_id && target_id < 8)) { + return device[target_id]; } return NULL; } @@ -196,20 +100,15 @@ class SCSIBus : public Device // 次の REQ アサートの前に指定のウェイトを入れる // (シークタイムなどを簡易的に再現するため)。 // ウェイトは一度適用されると 0 に戻る。 - void SetOneshotReqWait(uint64 nsec) { req_wait += nsec; } + void SetOneshotReqWait(uint64 tsec) { req_wait += tsec; } private: - DECLARE_MONITOR_CALLBACK(MonitorUpdate); - - // connected_devices を更新する。 - void MakeConnectedDevices(); - void BusFree(uint id); - void SelectionStart(Event& ev); - void Selected(Event& ev); - void SelectionAck(Event& ev); - void StartTransfer(Event& ev); - void TransferReq(Event& ev); + void SelectionStart(Event *); + void Selected(Event *); + void SelectionAck(Event *); + void StartTransfer(Event *); + void TransferReq(Event *); // 現在のフェーズ SCSI::Phase phase {}; @@ -231,33 +130,20 @@ class SCSIBus : public Device // 転送速度をここで律速させている。 uint64 last_req_time {}; - // 次の REQ アサートに入れるウェイト [nsec] + // 次の REQ アサートに入れるウェイト [tsec] uint64 req_wait {}; - // 設定ファイルに使われるこのホストデバイスを示す接頭語。 - // ("spc0" とか) - const char *config_name {}; - // 実際のバスではそんなことはないが、ここではバスがイニシエータと // ターゲットの ID を把握している。どちらも非選択状態なら -1。 int init_id {}; int target_id {}; - // SCSI ターゲット (所有)。イニシエータは含まない。 - std::unique_ptr target[8] /*{}*/; + // SCSI 管理 + SCSIDomain *domain {}; - // SCSI デバイス(イニシエータも含む)。非接続なら NULL。 - // 所有権を持たない。 - SCSIDevice *device[8] {}; - - // 接続中のデバイス(イニシエータ含む)を ID 順に並べたリスト。 - std::vector connected_devices {}; - - // 親(イニシエータ) - SCSIHostDevice *parent {}; + // 接続されているデバイス (イニシエータも含む) へのポインタ。 + std::array device {}; // イベント - Event event { this }; - - Monitor *monitor {}; + Event *event {}; };