--- nono/vm/spc.h 2026/04/29 17:04:28 1.1 +++ nono/vm/spc.h 2026/04/29 17:05:50 1.1.1.19 @@ -1,114 +1,145 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt +// + +// +// SPC (MB89352) // #pragma once #include "device.h" -#include "scsi.h" -#include "scheduler.h" #include "fixedqueue.h" +#include "mainapp.h" +#include "scsi.h" +#include "scsibus.h" + +class InterruptDevice; +class SCSIDomain; +class SCSIHostDevice; struct SPC { - static const int BDID = 0x0; // $E96021 RW $E100_0000 - static const int SCTL = 0x1; // $E96023 RW $E100_0004 - static const int SCMD = 0x2; // $E96025 RW $E100_0008 - static const int TMOD = 0x3; // (MB89352 にはない) - static const int INTS = 0x4; // $E96029 RW $E100_0010 - static const int PSNS = 0x5; // $E9602B R- $E100_0014 - static const int SDGC = 0x5; // $E9602B -W $E100_0014 - static const int SSTS = 0x6; // $E9602D R- $E100_0018 - static const int SERR = 0x7; // $E9602F R- $E100_001C - static const int PCTL = 0x8; // $E96031 RW $E100_0020 - static const int MBC = 0x9; // $E96033 R- $E100_0024 - static const int DREG = 0xa; // $E96035 RW $E100_0028 - static const int TEMP = 0xb; // $E96037 RW $E100_002C - static const int TCH = 0xc; // $E96039 RW $E100_0030 - static const int TCM = 0xd; // $E9603B RW $E100_0034 - static const int TCL = 0xe; // $E9603D RW $E100_0038 - static const int EXBF = 0xf; // (MB89352 にはない) + static const uint BDID = 0x0; // $E96021 RW $E100_0000 + static const uint SCTL = 0x1; // $E96023 RW $E100_0004 + static const uint SCMD = 0x2; // $E96025 RW $E100_0008 + static const uint TMOD = 0x3; // (MB89352 にはない) + static const uint INTS = 0x4; // $E96029 RW $E100_0010 + static const uint PSNS = 0x5; // $E9602B R- $E100_0014 + static const uint SDGC = 0x5; // $E9602B -W $E100_0014 + static const uint SSTS = 0x6; // $E9602D R- $E100_0018 + static const uint SERR = 0x7; // $E9602F R- $E100_001C + static const uint PCTL = 0x8; // $E96031 RW $E100_0020 + static const uint MBC = 0x9; // $E96033 R- $E100_0024 + static const uint DREG = 0xa; // $E96035 RW $E100_0028 + static const uint TEMP = 0xb; // $E96037 RW $E100_002C + static const uint TCH = 0xc; // $E96039 RW $E100_0030 + static const uint TCM = 0xd; // $E9603B RW $E100_0034 + static const uint TCL = 0xe; // $E9603D RW $E100_0038 + static const uint EXBF = 0xf; // (MB89352 にはない) // SCTL レジスタ // 全ビットを保持。 uint8 sctl; - static const int SCTL_RESET = 0x80; // Reset & Disable - static const int SCTL_CTL_RESET = 0x40; // Control Reset - static const int SCTL_DIAG_MODE = 0x20; // Diag Mode - static const int SCTL_ARBIT_EN = 0x10; // Arbitration Enable - static const int SCTL_PARITY_EN = 0x08; // Parity Enable - static const int SCTL_SEL_EN = 0x04; // Select Enable - static const int SCTL_RESEL_EN = 0x02; // Reselect Enable - static const int SCTL_INTR_EN = 0x01; // Interrupt Enable + static const uint SCTL_RESET = 0x80; // Reset & Disable + static const uint SCTL_CTL_RESET = 0x40; // Control Reset + static const uint SCTL_DIAG_MODE = 0x20; // Diag Mode + static const uint SCTL_ARBIT_EN = 0x10; // Arbitration Enable + static const uint SCTL_PARITY_EN = 0x08; // Parity Enable + static const uint SCTL_SEL_EN = 0x04; // Select Enable + static const uint SCTL_RESEL_EN = 0x02; // Reselect Enable + static const uint SCTL_INTR_EN = 0x01; // Interrupt Enable // SCMD レジスタ - // RST は読み出し時に GetRST() から生成。 + // 全ビットを保持。 uint8 scmd; - static const int SCMD_CMD_MASK = 0xe0; // Command code - static const int SCMD_RST_OUT = 0x10; // RST Out - static const int SCMD_INTERCEPT = 0x08; // Intersept Transfer - static const int SCMD_PROGRAM = 0x04; // Program Transfer - static const int SCMD_TERM_MODE = 0x01; // Termination Mode + static const uint SCMD_CMD_MASK = 0xe0; // Command code + static const uint SCMD_RST_OUT = 0x10; // RST Out + static const uint SCMD_INTERCEPT = 0x08; // Intersept Transfer + static const uint SCMD_PROGRAM = 0x04; // Program Transfer + static const uint SCMD_TERM_MODE = 0x01; // Termination Mode // INTS レジスタ - // 全ビットを保持。 - uint8 ints; - static const int INTS_SELECTED = 0x80; // Selected - static const int INTS_RESELECTED = 0x40; // Reselected - static const int INTS_DISCONNECTED = 0x20; // Disconnected - static const int INTS_COMPLETE = 0x10; // Command Complete - static const int INTS_SERV_REQ = 0x08; // Service Required - static const int INTS_TIMEOUT = 0x04; // Time Out - static const int INTS_HARD_ERR = 0x02; // SPC Hard Error - static const int INTS_RESET_COND = 0x01; // Reset Condition + // + // 内部変数 ints は割り込み状態を一度に判別するため SERR レジスタの + // xfer ビット(SERR_XFER_OUT) もここの bit8 に含めている。 + // 残りの下位8ビットは INTS と同じ。 + // + // b8 b7 b6 b5 b4 b3 b2 b1 b0 + // +----+----+----+----+----+----+----+----+ + // INTS |SEL |RESL|DIS |CMPL|SERV|TOUT|HERR|REST| + // +----+----+----+----+----+----+----+----+ + // + // +----+----+----+----+----+----+----+----+----+ + // ints変数 |xfer|SEL |RESL|DIS |CMPL|SERV|TOUT|HERR|REST| + // +----+----+----+----+----+----+----+----+----+ + uint16 ints; + static const uint INTS_SELECTED = 0x80; // Selected + static const uint INTS_RESELECTED = 0x40; // Reselected + static const uint INTS_DISCONNECTED = 0x20; // Disconnected + static const uint INTS_COMPLETE = 0x10; // Command Complete + static const uint INTS_SERV_REQ = 0x08; // Service Required + static const uint INTS_TIMEOUT = 0x04; // Time Out + static const uint INTS_HARD_ERR = 0x02; // SPC Hard Error + static const uint INTS_RESET_COND = 0x01; // Reset Condition + + static const uint INTS_XFER_OUT = 0x100; // SERR_XFER_OUT + + // 割り込みマスク (INTS_RESET_COND はノンマスカブル割り込み) + uint16 ints_mask; + static const uint INTS_ENABLE_MASK = 0x01ff; + static const uint INTS_DISABLE_MASK = 0x0001; // PSNS レジスタ - // REQ, ACK, ATN は今のところ保持? uint8 psns; - static const int PSNS_REQ = 0x80; - static const int PSNS_ACK = 0x40; - static const int PSNS_ATN = 0x20; - static const int PSNS_SEL = 0x10; - static const int PSNS_BSY = 0x08; - static const int PSNS_MSG = 0x04; - static const int PSNS_CD = 0x02; - static const int PSNS_IO = 0x01; + static const uint PSNS_REQ = 0x80; + static const uint PSNS_ACK = 0x40; + static const uint PSNS_ATN = 0x20; + static const uint PSNS_SEL = 0x10; + static const uint PSNS_BSY = 0x08; + static const uint PSNS_MSG = 0x04; + static const uint PSNS_CD = 0x02; + static const uint PSNS_IO = 0x01; // SDGC レジスタ uint8 sdgc; - static const int SDGC_XFER_ENABLE = 0x20; + static const uint SDGC_XFER_ENABLE = 0x20; // SSTS レジスタ // 上位4ビットを保持。下位4ビットは読み出し時に生成。 uint8 ssts; - static const int SSTS_CONN_INIT = 0x80; // Connected (Initiator) - static const int SSTS_CONN_TARG = 0x40; // Connected (Target) - static const int SSTS_SPC_BUSY = 0x20; // SPC Busy - static const int SSTS_IN_PROGRESS = 0x10; // Transfer in Progress - static const int SSTS_RST_IN = 0x08; // SCSI RST In - static const int SSTS_TC_ZERO = 0x04; // TC=0 - static const int SSTS_DREG_FULL = 0x02; // DREG Status (Full) - static const int SSTS_DREG_EMPTY = 0x01; // DREG Status (Empty) + static const uint SSTS_CONN_INIT = 0x80; // Connected (Initiator) + static const uint SSTS_CONN_TARG = 0x40; // Connected (Target) + static const uint SSTS_SPC_BUSY = 0x20; // SPC Busy + static const uint SSTS_IN_PROGRESS = 0x10; // Transfer in Progress + static const uint SSTS_RST_IN = 0x08; // SCSI RST In + static const uint SSTS_TC_ZERO = 0x04; // TC=0 + static const uint SSTS_DREG_FULL = 0x02; // DREG Status (Full) + static const uint SSTS_DREG_EMPTY = 0x01; // DREG Status (Empty) // SERR レジスタ + // XFER_OUT 以外の4つの有効ビットを保持。 + // XFER_OUT は ints が担当している。 uint8 serr; - static const int SERR_DATAERR_SCSI = 0x80; // Data Error: SCSI - static const int SERR_DATAERR_SPC = 0x40; // Data Error: SPC - static const int SERR_XFER_OUT = 0x20; // Xfer Out - static const int SERR_TC_PARITY_ERR = 0x08; // TC Parity Error - static const int SERR_SHORT_XFER = 0x02; // Short Transfer Period + static const uint SERR_DATAERR_SCSI = 0x80; // Data Error: SCSI + static const uint SERR_DATAERR_SPC = 0x40; // Data Error: SPC + static const uint SERR_XFER_OUT = 0x20; // Xfer Out + static const uint SERR_TC_PARITY_ERR= 0x08; // TC Parity Error + static const uint SERR_SHORT_XFER = 0x02; // Short Transfer Period // PCTL レジスタ - // ここでは BusFree INT Enable ビットだけを保持。 - // 残りの MSG, C/D, I/O は親クラスの信号線フラグから生成。 + // BusFree INT Enable は独立して保持。 + // MSG, CD, IO の3ビットは pctl_out で保持。 bool busfree_intr_enable; - static const int PCTL_MASK = 0x87; // 書き込みマスク - static const int PCTL_BFINT_EN = 0x80; // BusFree INT Enable - static const int PCTL_MSG = 0x04; - static const int PCTL_CD = 0x02; - static const int PCTL_IO = 0x01; + uint8 pctl_out; + static const uint PCTL_MASK = 0x87; // 書き込みマスク + static const uint PCTL_BFINT_EN = 0x80; // BusFree INT Enable + static const uint PCTL_MSG = 0x04; + static const uint PCTL_CD = 0x02; + static const uint PCTL_IO = 0x01; FixedQueue dreg; // DREG レジスタ @@ -123,105 +154,186 @@ struct SPC uint8 mbc() const { return tc & 15; } - - // Set/Reset ACK/REQ 内部状態 - bool ackreq; }; -class SPCDevice : public SCSIHostDevice +class SPCDevice : public IODevice { - typedef SCSIHostDevice inherited; + using inherited = IODevice; + public: + SPCDevice(); + ~SPCDevice() override; + + void SetLogLevel(int loglevel_) override; + bool Create() override; + bool Init() override; + void ResetHard(bool poweron) override; + + SCSIDomain *GetDomain() const noexcept { return domain.get(); } + + // BusIO インタフェース + // (内蔵 ROM からのアクセス用に特別に public にしてある) + busdata WritePort(uint32 offset, uint32 data); + protected: + static const uint32 NPORT = 16; + busdata ReadPort(uint32 offset); + busdata PeekPort(uint32 offset); + bool PokePort(uint32 offset, uint32 data); + private: - // X680x0 では $E96000〜 - // LUNA では $E100_0000〜 + DECLARE_MONITOR_CALLBACK(MonitorUpdate); - public: - SPCDevice(uint32); - virtual ~SPCDevice(); + void MonitorReg(TextScreen&, + int x, int y, uint32 reg, const char * const *names); - virtual bool Init(); - virtual void ResetHard(); - virtual void ResetSoft(); - void Reset(); - - virtual uint64 Read8(uint32 addr); - virtual uint64 Read16(uint32 addr); - virtual uint64 Write8(uint32 addr, uint32 data); - virtual uint64 Write16(uint32 addr, uint32 data); - virtual uint64 Peek8(uint32 addr); - virtual bool MonitorUpdate(); - - // イベントコールバック - void BusFreeCallback(int); - void SelectionCompleteCallback(int); - void SelectionFailedCallback(int); + // バスフリーになった (SCSIHostDevice からのコールバック) + void BusFreeCallback(uint id); - // ターゲットがバスフリーフェーズに移行した - virtual void BusRelease(); + // ターゲットがセレクションに応答した (SCSIHostDevice からのコールバック) + void SelectionAckCallback(); - private: - void MonitorReg(int x, int y, uint32 reg, const char * const *names); + // 情報転送フェーズで REQ を立てた (SCSIHostDevice からのコールバック) + void TransferReqCallback(); // SCTL レジスタへの書き込み void WriteSCTL(uint32); - - // SCMD レジスタ値の取得 - uint32 GetSCMD() const; + // 割り込みマスクの再構成 + void MakeIntsMask(); // SCMD レジスタへの書き込み void WriteSCMD(uint32); // INTS レジスタへの書き込み void WriteINTS(uint32); + // 表示のための前回値 + uint32 prev_ints {}; // INTS 設定 void SetINTS(uint32); + // INTS レジスタ値の取得 + uint32 GetINTS() const; + // 割り込み信号線の状態を変える + void ChangeInterrupt(); // PSNS レジスタ値の取得 uint32 GetPSNS() const; // SSTS レジスタ値の取得 uint32 GetSSTS() const; + // 表示のための前回値 + uint32 prev_ssts {}; + + // SERR レジスタ値の取得 + uint32 GetSERR() const; + // PCTL レジスタへの書き込み + void WritePCTL(uint32); // PCTL レジスタ値の取得 uint32 GetPCTL() const; + // DREG レジスタアクセス + busdata ReadDREG(); + busdata WriteDREG(uint32 data); + // バスフリーフェーズに移行する void BusFree(); + // 直近のバスフリーフェーズになった時刻 + // (アービトレーション/セレクション開始時に使う) + uint64 last_busfree_time {}; + // バスをリリースする + void BusRelease(); // Select コマンド実行 void SelectCommand(); + void Arbitration1(Event *); + void Arbitration2(Event *); + void Selection1(Event *); + void SelectionTimeout(Event *); + + // SetATN コマンド実行 + void SetATNCommand(); + // セレクションフェーズで ATN を立てる + bool set_atn_in_selection {}; // Transfer コマンド実行 void TransferCommand(); - - // DREG へのデータの充填 - void EnqueueToDREG(); + void HardwareTransfer(Event *); + void TransferComplete(); + // Transfer コマンド実行中なら true + bool transfer_command_running {}; // SCSI コマンド void ExecCommand(); + // + // 信号線制御 + // (ソースコード的なショートカットのため) + // + 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); } + // レジスタ struct SPC spc {}; - uint32 regbase = 0; // レジスタの先頭アドレス - int regstride = 0; // レジスタ間の距離 + // VM 種別 + VMType vmtype {}; - // SPC に入力されるクロック周期 [nsec] - int t_CLF = 0; + // イニシエータの SCSI ID (参照箇所が多いのでコピー) + uint myid {}; - // 現在のフェーズ - uint phase = 0; + // SPC に入力されるクロック周期 [nsec] + uint64 t_CLF {}; - // Command フェーズで受け取ったコマンド列 - std::vector cmds {}; + // アクセスウェイト + busdata read_wait {}; + busdata write_wait {}; + + // モニタ表示用 + uint32 baseaddr {}; + uint32 regoffset {}; - // Data{In,Out} フェーズのバッファ - uint8 *dataptr = NULL; - uint datalen = 0; + // 割り込み信号線の接続先デバイス + InterruptDevice *interrupt {}; // イベント - Event event {}; + Event *phase_event {}; + Event *timer_event {}; + + // SCSI 管理 + std::unique_ptr domain /*{}*/; + + // SCSI ホストアダプタ + SCSIHostDevice *host {}; + + // SCSI バス + std::unique_ptr bus /*{}*/; + + Monitor *monitor {}; }; -extern SPCDevice *gSPC; +static inline SPCDevice *GetSPCDevice() { + return Object::GetObject(OBJ_SPC); +}