--- nono/vm/scsidev.cpp 2026/04/29 17:05:43 1.1.1.20 +++ nono/vm/scsidev.cpp 2026/04/29 17:05:51 1.1.1.22 @@ -20,6 +20,7 @@ #include "scsidev.h" #include "scsidomain.h" #include "config.h" +#include "event.h" #include "mainapp.h" #include "scheduler.h" #include "uimessage.h" @@ -61,6 +62,7 @@ SCSIHostDevice::SCSIHostDevice(SCSIDomai // イニシエータの ID は実行時にコントローラによって決まる。 devtype = SCSI::DevType::Initiator; + SetName(string_format("(SCSIHost.%s)", domain_->GetConfigName())); SetLogLevel(parent->loglevel); } @@ -158,6 +160,8 @@ SCSITarget::SCSITarget(SCSIDomain *domai { domain = domain_; myid = id; + + // ログは出すので名前はつけるがそれは継承先で SetName() している。 } // デストラクタ @@ -174,6 +178,7 @@ SCSITarget::ResetHard(bool poweron) cmdseq.clear(); cmdlen = 0; cmd.reset(); + msgseq.clear(); bytecount = 0; lastbyte = false; } @@ -218,11 +223,18 @@ SCSITarget::OnSelected() void SCSITarget::OnStartTransfer() { - // 最初はコマンドフェーズ。(ATN はとりあえず放置) - putlog(4, "Start Command Phase"); + // ATN が立っていればメッセージアウトフェーズ。 + // そうでなければコマンドフェーズ。 cmdseq.clear(); cmdlen = 0; - SetXfer(SCSI::XferPhase::Command); + msgseq.clear(); + if (GetATN()) { + putlog(4, "Start MsgOut Phase by ATN"); + SetXfer(SCSI::XferPhase::MsgOut); + } else { + putlog(4, "Start Command Phase"); + SetXfer(SCSI::XferPhase::Command); + } AssertREQ(); } @@ -325,6 +337,16 @@ SCSITarget::OnTransfer() AssertRST(); } } + } else if (xfer == SCSI::XferPhase::MsgOut) { + data = GetData(); + putlog(4, "%s: msgseq[%02u] = $%02x", __func__, + (uint)msgseq.size(), data); + msgseq.push_back(data); + bytecount--; + if (bytecount == 0) { + lastbyte = true; + } + } else if (GetIO() == false) { // OUT 方向 (ターゲットが受信側) // bytecount は減算方向。 @@ -368,7 +390,23 @@ SCSITarget::OnTransferAck() next = cmd->DoneDataIn(); break; case SCSI::XferPhase::MsgOut: - next = cmd->DoneMsgOut(); + if (cmd) { + next = cmd->DoneMsgOut(); + } else { + // コマンド前のメッセージアウト。 + if (msgseq.size() == 1 && (msgseq[0] & 0x80)) { + putlog(1, "%s: MsgOut $%02x Identify (NOT SUPPORTED)", + __func__, msgseq[0]); + } else { + std::string buf; + for (auto m : msgseq) { + buf += string_format(" %02x", m); + } + putlog(0, "%s: MsgOut%s (NOT SUPPORTED)", + __func__, buf.c_str()); + } + next = SCSI::XferPhase::Command; + } break; case SCSI::XferPhase::MsgIn: next = cmd->DoneMsgIn(); @@ -382,7 +420,7 @@ SCSITarget::OnTransferAck() putlog(3, "%s done", SCSI::GetXferPhaseName(xfer)); // 簡略化のため、フェーズ完了時だけ REQ を引き伸ばすことができる - if (cmd->optime != 0) { + if (cmd && cmd->optime != 0) { bus->SetOneshotReqWait(cmd->optime); cmd->optime = 0; } @@ -404,6 +442,8 @@ SCSITarget::OnTransferAck() // Command フェーズで Data フェーズの準備をすることが多いので // 悩ましい。 switch (xfer) { + case SCSI::XferPhase::Command: + break; case SCSI::XferPhase::DataOut: cmd->buf.clear(); bytecount = cmd->recvbytes; @@ -605,16 +645,15 @@ SCSIDisk::Init() // 行わないといけない。うーん。 write_ignore = gConfig->Find(wikey).AsInt(); + // writeignore が決まったので状態を初期化。 + Clear(); + // 設定時点でファイルが指定されていればオープン if (filename.empty() == false) { std::string path = gMainApp.NormalizePath(filename); if (LoadDisk(path) == false) { return false; } - } else { - // メディアがロードされないままでも起動時は必ず通知を投げる。 - // ステータスパネルはこの UIMessage でのみ状態を更新するため。 - MediaChanged(); } // 平均シークタイム。設定は [msec]、変数は [nsec]。 @@ -674,17 +713,29 @@ SCSIDisk::LoadDisk(const std::string& pa goto done; } - // 書き込みモードをここで確定 + // 書き込みモードをここで確定。 + // + // デバイス特性 イメージ write_ignore write_mode + // ------------ ---------- ------------ : ---------- + // 書込み可 書込み可 no Writeable + // 〃 〃 yes WriteIgnore + // 〃 書込み不可 no WriteProtect (A) + // 〃 〃 yes WriteIgnore (B) + // 書込み不可 * * ReadOnly + // + // (A) は「ライトプロテクトした MO を入れた」状態。 + // (B) は「書き換えたくないので書き込み権を落としてあるイメージファイルを + // あたかも書き込めるように見せたい」。(WI マークが出る) if (IsWriteableDevice()) { // 書き込み可能デバイスの場合 - if (w_ok) { - if (write_ignore) { - write_mode = RW::WriteIgnore; - } else { + if (write_ignore) { + write_mode = RW::WriteIgnore; + } else { + if (w_ok) { write_mode = RW::Writeable; + } else { + write_mode = RW::WriteProtect; } - } else { - write_mode = RW::WriteProtect; } } else { // 読み込み専用デバイスの場合 @@ -814,14 +865,23 @@ SCSIDisk::Clear() medium_loaded = false; image.Close(); pathname.clear(); - write_mode = RW::Writeable; + + if (IsWriteableDevice()) { + if (write_ignore) { + write_mode = RW::WriteIgnore; + } else { + write_mode = RW::Writeable; + } + } else { + write_mode = RW::ReadOnly; + } } // メディア状態変更を UI に通知する void SCSIDisk::MediaChanged() const { - UIMessage::Post(UIMessage::SCSI_MEDIA_CHANGE, GetMyID()); + gMainApp.GetUIMessage()->Post(UIMessage::SCSI_MEDIA_CHANGE, GetMyID()); } // メディアを挿入する。 @@ -847,7 +907,7 @@ SCSIDisk::LoadMessage(MessageID msgid, u { if (LoadDisk(new_pathname) == false) { // 失敗したら UI に通知 - UIMessage::Post(UIMessage::SCSI_MEDIA_FAILED); + gMainApp.GetUIMessage()->Post(UIMessage::SCSI_MEDIA_FAILED); } }