--- nono/vm/scsibus.cpp 2026/04/29 17:05:17 1.1.1.11 +++ nono/vm/scsibus.cpp 2026/04/29 17:05:42 1.1.1.15 @@ -8,8 +8,93 @@ // 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 〜〜〜〜〜〜〜〜〜〜〜〜+ +// + #include "scsibus.h" #include "scsidev.h" +#include "scsidomain.h" #include "scheduler.h" // time 時間後に呼び出すコールバックをセットする。 @@ -22,10 +107,10 @@ } while (0) // コンストラクタ -SCSIBus::SCSIBus() +SCSIBus::SCSIBus(SCSIDomain *domain_) : inherited(OBJ_SCSIBUS) { - event.Regist("SCSIBus"); // 複数出来たら名前どうするべか + domain = domain_; } // デストラクタ @@ -33,6 +118,20 @@ SCSIBus::~SCSIBus() { } +// 初期化 +bool +SCSIBus::Init() +{ + // 現時点で管理下にあるデバイス(ターゲット)をバスに接続。 + // イニシエータはこの時点では ID 未定なのでバスには繋がらない。 + Refresh(); + + event.SetName("SCSIBus"); // 複数出来たら名前どうするべか + scheduler->RegistEvent(event); + + return true; +} + // 電源オン/リセット void SCSIBus::ResetHard(bool poweron) @@ -64,21 +163,12 @@ SCSIBus::ResetHard(bool poweron) scheduler->StopEvent(event); } -// このバスにデバイスを接続する (初期化時に使う) -bool -SCSIBus::Attach(SCSIDevice *dev, int id) +// デバイス情報を更新する。 +// domain の device[] が変更されたら呼ぶこと。 +void +SCSIBus::Refresh() { - if (device[id] != NULL) { - putlog(0, "id=%d already exist?", id); - return false; - } - putlog(3, "Attach #%d", id); - - // バスとデバイスは相互に接続する。どちらも所有のないただのポインタ。 - device[id] = dev; - dev->Attach(this, id); - - return true; + domain->GetDevices(&device); } // RST を上げる @@ -95,9 +185,9 @@ SCSIBus::AssertRST() rst = true; // 即バスリセットを全員に通知 - for (int i = 0; i < 8; i++) { + for (int i = 0; i < device.size(); i++) { if (device[i]) { - device[i]->BusResetCB(); + device[i]->OnBusReset(); } } @@ -121,7 +211,7 @@ SCSIBus::NegateRST() void SCSIBus::AssertSEL(uint id) { - putlog(4, "%s(#%d)", __func__, id); + putlog(4, "%s(#%u)", __func__, id); // すでに上がってたら何もしない if ((sel & (1 << id))) { @@ -195,7 +285,7 @@ SCSIBus::AssertSEL(uint id) #else // ターゲットデバイスは賢いので、全員イニシエータ様に忖度して // 即座にバスフリーへ移行するというのはどうか。 - for (uint i = 0; i < 8; i++) { + for (uint i = 0; i < device.size(); i++) { if (i != id) { NegateBSY(i); } @@ -208,7 +298,7 @@ SCSIBus::AssertSEL(uint id) void SCSIBus::NegateSEL(uint id) { - putlog(4, "%s(#%d)", __func__, id); + putlog(4, "%s(#%u)", __func__, id); // すでに下りてたら何もしない if ((sel & (1 << id)) == 0) { @@ -251,7 +341,7 @@ SCSIBus::NegateSEL(uint id) void SCSIBus::AssertBSY(uint id) { - putlog(4, "%s(#%d)", __func__, id); + putlog(4, "%s(#%u)", __func__, id); // すでに上がってたら何もしない if ((bsy & (1 << id))) { @@ -279,7 +369,7 @@ SCSIBus::AssertBSY(uint id) } else if (GetPhase() == SCSI::Phase::Arbitration) { // アービトレーションフェーズだったら、アービトレーションに参加。 // 今は起きない。 - VMPANIC("AssertBSY(#%d) in arbitration", id); + VMPANIC("AssertBSY(#%u) in arbitration", id); } else if (GetPhase() == SCSI::Phase::Selection) { // セレクションフェーズだったら、転送フェーズに移行中。 @@ -289,7 +379,7 @@ SCSIBus::AssertBSY(uint id) CallAfter(SelectionAck, "SCSIBus Selection Ack", 90_nsec); } else { // どうする? - VMPANIC("AssertBSY(#%d) in %s phase", id, GetPhaseName()); + VMPANIC("AssertBSY(#%u) in %s phase", id, GetPhaseName()); } } @@ -297,7 +387,7 @@ SCSIBus::AssertBSY(uint id) void SCSIBus::NegateBSY(uint id) { - putlog(4, "%s(#%d)", __func__, id); + putlog(4, "%s(#%u)", __func__, id); // すでに下りてたら何もしない if ((bsy & (1 << id)) == 0) { @@ -323,7 +413,7 @@ SCSIBus::NegateBSY(uint id) } else { // どうする? - VMPANIC("NegateBSY(#%d) in %s phase", id, GetPhaseName()); + VMPANIC("NegateBSY(#%u) in %s phase", id, GetPhaseName()); } } @@ -375,7 +465,7 @@ SCSIBus::AssertREQ() } // ターゲットが REQ を立てようとして所定時間経ったので REQ を立てて -// イニシエータの TransferReqCB を呼び出すところ。 +// イニシエータの OnTransferReq を呼び出すところ。 void SCSIBus::TransferReq(Event& ev) { @@ -383,7 +473,7 @@ SCSIBus::TransferReq(Event& ev) req = true; assert(device[init_id]); - device[init_id]->TransferReqCB(); + device[init_id]->OnTransferReq(); } // REQ を下げる @@ -403,7 +493,7 @@ SCSIBus::AssertACK() if (GetPhase() == SCSI::Phase::Transfer) { assert(device[target_id]); - device[target_id]->TransferCB(); + device[target_id]->OnTransfer(); } } @@ -416,7 +506,7 @@ SCSIBus::NegateACK() if (GetPhase() == SCSI::Phase::Transfer) { assert(device[target_id]); - device[target_id]->TransferAckCB(); + device[target_id]->OnTransferAck(); } } @@ -439,16 +529,16 @@ SCSIBus::NegateATN() // バスフリーフェーズに移行。 // id はこのバスフリーを行ったデバイス (0..7) void -SCSIBus::BusFree(int id) +SCSIBus::BusFree(uint id) { putlog(4, "%s", __func__); SetPhase(SCSI::Phase::BusFree); // 即全デバイスに通知。 - for (int i = 0; i < countof(device); i++) { + for (int i = 0; i < device.size(); i++) { if (device[i]) { - device[i]->BusFreeCB(id); + device[i]->OnBusFree(id); } } @@ -486,11 +576,11 @@ SCSIBus::BusFree(int id) // 200usec 以内…なので適当に間をとって 100usec 後に Selection() コールバック // を呼び出す(1+2)。 // -// Selection() コールバック(が呼び出す device->SelectionCB()) は通常 BSY を +// Selection() コールバック(が呼び出す device->OnSelected()) は通常 BSY を // アサートするはずであり、そこから 90nsec 後に SelectionAck() コールバック // を呼び出す(3)。 // -// SelectionAck() コールバック(が呼び出す device->SelectionAckCB()) は通常 +// SelectionAck() コールバック(が呼び出す device->OnSelectionAck()) は通常 // SEL をネゲートするはずであり、そこから情報転送フェーズへの移行には特に // 規定はなさそうなので、400nsec 後に StartTransfer() をコールバックして // デバイスを情報転送フェーズに移行させる。 @@ -531,7 +621,7 @@ SCSIBus::Selected(Event& ev) { // ターゲットに通知。ターゲットがいることは確定している。 assert(device[target_id]); - device[target_id]->SelectionCB(); + device[target_id]->OnSelected(); } // ターゲットが BSY を立てて規定時間経ったので @@ -541,7 +631,7 @@ SCSIBus::SelectionAck(Event& ev) { // イニシエータに通知。 assert(device[init_id]); - device[init_id]->SelectionAckCB(); + device[init_id]->OnSelectionAck(); } // 情報転送フェーズ開始を通知する。 @@ -552,5 +642,6 @@ SCSIBus::StartTransfer(Event& ev) SetPhase(SCSI::Phase::Transfer); assert(device[target_id]); - device[target_id]->StartTransferCB(); + device[target_id]->OnStartTransfer(); } +