--- nono/vm/scsibus.cpp 2026/04/29 17:05:17 1.1.1.11 +++ nono/vm/scsibus.cpp 2026/04/29 17:05:29 1.1.1.12 @@ -10,6 +10,7 @@ #include "scsibus.h" #include "scsidev.h" +#include "config.h" #include "scheduler.h" // time 時間後に呼び出すコールバックをセットする。 @@ -22,10 +23,15 @@ } while (0) // コンストラクタ -SCSIBus::SCSIBus() +SCSIBus::SCSIBus(SCSIHostDevice *parent_, const char *config_name_) : inherited(OBJ_SCSIBUS) { - event.Regist("SCSIBus"); // 複数出来たら名前どうするべか + parent = parent_; + config_name = config_name_; + + monitor.func = ToMonitorCallback(&SCSIBus::MonitorUpdate); + // サイズは Create で決まる + monitor.Regist(ID_MONITOR_SCSIDEVS); } // デストラクタ @@ -33,6 +39,75 @@ SCSIBus::~SCSIBus() { } +// 動的なコンストラクション +bool +SCSIBus::Create() +{ + const char *name = GetConfigName(); + + // 設定を読み込む。 + for (int id = 0; id < 8; id++) { + const std::string key = string_format("%s-id%u-image", name, id); + const ConfigItem& item = gConfig->Find(key); + const std::string& image = item.AsString(); + + // 空ならデバイスなし + if (image.empty()) { + // 未接続部分のパラメータは減らしておくか。 + // --show-config するとさすがに無駄に多くて邪魔なので。 + const std::string wikey = string_format("%s-id%u-writeignore", + name, id); + const std::string wpkey = string_format("%s-id%u-writeprotect", + name, id); + const std::string stkey = string_format("%s-id%u-seektime", + name, id); + gConfig->Delete(wikey); + gConfig->Delete(wpkey); + gConfig->Delete(stkey); + continue; + } + + // ',' より前がデバイス種別 (ここではパスは不要) + auto v = string_split(image, ','); + // 比較のため前後の空白を取り除いて小文字にする + std::string type = string_trim(v[0]); + type = string_tolower(type); + + SCSI::DevType target_devtype; + if (type == "hd") { + target_devtype = SCSI::DevType::HD; + } else if (type == "cd") { + target_devtype = SCSI::DevType::CD; + } else if (type == "mo") { + target_devtype = SCSI::DevType::MO; + } else { + item.Err("Invaild device type '%s'", type.c_str()); + return false; + } + target[id].reset(new SCSIDisk(this, id, target_devtype)); + AttachDevice(target[id].get(), id); + } + + // ここの connected_devices はイニシエータを含んでいない数 + monitor.SetSize(75, connected_devices.size() * 4 + 1); + + return true; +} + +// 初期化 +bool +SCSIBus::Init() +{ + if (inherited::Init() == false) { + return false; + } + + event.SetName("SCSIBus"); // 複数出来たら名前どうするべか + scheduler->RegistEvent(event); + + return true; +} + // 電源オン/リセット void SCSIBus::ResetHard(bool poweron) @@ -64,23 +139,55 @@ SCSIBus::ResetHard(bool poweron) scheduler->StopEvent(event); } -// このバスにデバイスを接続する (初期化時に使う) +// デバイスを接続する。 +// id に先客がいれば false を返す。成功すれば true を返す。 bool -SCSIBus::Attach(SCSIDevice *dev, int id) +SCSIBus::AttachDevice(SCSIDevice *dev, int id) { if (device[id] != NULL) { - putlog(0, "id=%d already exist?", id); return false; } - putlog(3, "Attach #%d", id); + putlog(3, "Attach #%u", id); - // バスとデバイスは相互に接続する。どちらも所有のないただのポインタ。 device[id] = dev; - dev->Attach(this, id); + MakeConnectedDevices(); + + // 紐付けをデバイスにも通知。 + dev->OnAttached(this, id); return true; } +// device[] から connected_device を作り直す。 +// device[] はイニシエータを含む固定長の配列。 +// connected_device はイニシエータを (あれば) 含む ID 順の可変長リスト。 +void +SCSIBus::MakeConnectedDevices() +{ + connected_devices.clear(); + + for (int id = 0; id < 8; id++) { + if (device[id]) { + connected_devices.push_back(device[id]); + } + } +} + +// 接続済みの (イニシエータを除く) デバイス数を返す。 +uint +SCSIBus::GetTargetCount() const +{ + uint count = 0; + + for (int id = 0; id < 8; id++) { + if ((bool)target[id]) { + count++; + } + } + + return count; +} + // RST を上げる void SCSIBus::AssertRST() @@ -97,7 +204,7 @@ SCSIBus::AssertRST() // 即バスリセットを全員に通知 for (int i = 0; i < 8; i++) { if (device[i]) { - device[i]->BusResetCB(); + device[i]->OnBusReset(); } } @@ -121,7 +228,7 @@ SCSIBus::NegateRST() void SCSIBus::AssertSEL(uint id) { - putlog(4, "%s(#%d)", __func__, id); + putlog(4, "%s(#%u)", __func__, id); // すでに上がってたら何もしない if ((sel & (1 << id))) { @@ -208,7 +315,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 +358,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 +386,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 +396,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 +404,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 +430,7 @@ SCSIBus::NegateBSY(uint id) } else { // どうする? - VMPANIC("NegateBSY(#%d) in %s phase", id, GetPhaseName()); + VMPANIC("NegateBSY(#%u) in %s phase", id, GetPhaseName()); } } @@ -375,7 +482,7 @@ SCSIBus::AssertREQ() } // ターゲットが REQ を立てようとして所定時間経ったので REQ を立てて -// イニシエータの TransferReqCB を呼び出すところ。 +// イニシエータの OnTransferReq を呼び出すところ。 void SCSIBus::TransferReq(Event& ev) { @@ -383,7 +490,7 @@ SCSIBus::TransferReq(Event& ev) req = true; assert(device[init_id]); - device[init_id]->TransferReqCB(); + device[init_id]->OnTransferReq(); } // REQ を下げる @@ -403,7 +510,7 @@ SCSIBus::AssertACK() if (GetPhase() == SCSI::Phase::Transfer) { assert(device[target_id]); - device[target_id]->TransferCB(); + device[target_id]->OnTransfer(); } } @@ -416,7 +523,7 @@ SCSIBus::NegateACK() if (GetPhase() == SCSI::Phase::Transfer) { assert(device[target_id]); - device[target_id]->TransferAckCB(); + device[target_id]->OnTransferAck(); } } @@ -439,7 +546,7 @@ SCSIBus::NegateATN() // バスフリーフェーズに移行。 // id はこのバスフリーを行ったデバイス (0..7) void -SCSIBus::BusFree(int id) +SCSIBus::BusFree(uint id) { putlog(4, "%s", __func__); @@ -448,7 +555,7 @@ SCSIBus::BusFree(int id) // 即全デバイスに通知。 for (int i = 0; i < countof(device); i++) { if (device[i]) { - device[i]->BusFreeCB(id); + device[i]->OnBusFree(id); } } @@ -486,11 +593,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 +638,7 @@ SCSIBus::Selected(Event& ev) { // ターゲットに通知。ターゲットがいることは確定している。 assert(device[target_id]); - device[target_id]->SelectionCB(); + device[target_id]->OnSelected(); } // ターゲットが BSY を立てて規定時間経ったので @@ -541,7 +648,7 @@ SCSIBus::SelectionAck(Event& ev) { // イニシエータに通知。 assert(device[init_id]); - device[init_id]->SelectionAckCB(); + device[init_id]->OnSelectionAck(); } // 情報転送フェーズ開始を通知する。 @@ -552,5 +659,82 @@ SCSIBus::StartTransfer(Event& ev) SetPhase(SCSI::Phase::Transfer); assert(device[target_id]); - device[target_id]->StartTransferCB(); + device[target_id]->OnStartTransfer(); +} + +// SCSI デバイスモニタ +// (どこでやるのがいいか分からんけど、SCSI デバイスを一括して表示したいので +// とりあえずここで) +void +SCSIBus::MonitorUpdate(Monitor *, TextScreen& screen) +{ + int y; + +// 012345678901234567890123456789012345678901234567890123456789012345678901234 +// ID6: HD(2048MB) +// MediumLoaded Off LogicalBlock 2048 ImageSize 2,147,483,648 +// RemovalPrevent Off WriteProtected + + + screen.Clear(); + + y = 0; + for (const auto dev : connected_devices) { + std::string desc = SCSI::GetDevTypeName(dev->GetDevType()); + std::string pathname; + + const SCSIDisk *disk = dynamic_cast(dev); + if (disk && disk->IsMediumLoaded()) { + desc += string_format("(%uMB)", + (uint)(disk->GetSize() / 1024 / 1024)); + pathname = disk->GetPathName(); + } + screen.Print(0, y, "ID%c: %s", '0' + dev->GetMyID(), desc.c_str()); + +#if 1 + (void)pathname; +#else + // パス名の非ASCIIどうするか + if (!pathname.empty()) { + if (pathname.size() > screen.GetCol() - 16 - 3) { + pathname = pathname.substr(0, screen.GetCol() - 16 - 3); + pathname += "..."; + } + screen.Puts(16, y, pathname.c_str()); + } +#endif + y++; + + // イニシエータならここまで。(今の所イニシエータとディスクしかない) + if (disk == NULL) { + continue; + } + + const int x1 = 5; + const int x2 = 29; + const int x3 = 50; + + // 1行目 + screen.Print(x1, y, + (disk->IsRemovableDevice() ? TA::Normal : TA::Disable), + "MediumLoaded %s", + disk->IsMediumLoaded() ? "On" : "Off"); + screen.Print(x2, y, "LogicalBlock %u", disk->GetBlocksize()); + std::string imagesize = format_number(disk->GetSize()); + screen.Print(x3, y, "ImageSize %13s", imagesize.c_str()); + y++; + + // 2行目 + screen.Print(x1, y, + (disk->IsRemovableDevice() ? TA::Normal : TA::Disable), + "RemovalPrevent %s", + disk->IsMediumRemovalPrevented() ? "On" : "Off"); + + screen.Puts(x2, y, + (disk->IsMediumLoaded() ? TA::Normal : TA::Disable), + disk->GetWriteModeStr()); + y++; + + y++; + } }