|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// SPC (MB89352)
//
#pragma once
#include "device.h"
#include "fixedqueue.h"
#include "mainapp.h"
#include "scsi.h"
#include "scsibus.h"
class InterruptDevice;
class SCSIDomain;
class SCSIHostDevice;
struct SPC
{
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 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 レジスタ
// 全ビットを保持。
uint8 scmd;
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 レジスタ
//
// 内部変数 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 レジスタ
uint8 psns;
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 uint SDGC_XFER_ENABLE = 0x20;
// SSTS レジスタ
// 上位4ビットを保持。下位4ビットは読み出し時に生成。
uint8 ssts;
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 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, CD, IO の3ビットは pctl_out で保持。
bool busfree_intr_enable;
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<uint8, 8> dreg; // DREG レジスタ
uint8 temp_in; // TEMP レジスタ (SCSI -> MPU)
uint8 temp_out; // TEMP レジスタ (MPU -> SCSI)
uint32 tc; // TC
uint32 GetTCL() const { return tc & 0x0000ff; } // TCL
// MBC (内部の読み込み専用レジスタ)
// TC の下位4ビットが読めるらしい
uint8 mbc() const {
return tc & 15;
}
};
class SPCDevice : public IODevice
{
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:
DECLARE_MONITOR_SCREEN(MonitorScreen);
void MonitorReg(TextScreen&,
int x, int y, uint32 reg, const char * const *names);
// バスフリーになった (SCSIHostDevice からのコールバック)
void BusFreeCallback(uint id);
// ターゲットがセレクションに応答した (SCSIHostDevice からのコールバック)
void SelectionAckCallback();
// 情報転送フェーズで REQ を立てた (SCSIHostDevice からのコールバック)
void TransferReqCallback();
// SCTL レジスタへの書き込み
void WriteSCTL(uint32);
// 割り込みマスクの再構成
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();
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 {};
// VM 種別
VMType vmtype {};
// イニシエータの SCSI ID (参照箇所が多いのでコピー)
uint myid {};
// SPC に入力されるクロック周期 [nsec]
uint64 t_CLF {};
// アクセスウェイト
busdata read_wait {};
busdata write_wait {};
// モニタ表示用
uint32 baseaddr {};
uint32 regoffset {};
// 割り込み信号線の接続先デバイス
InterruptDevice *interrupt {};
// イベント
Event *phase_event {};
Event *timer_event {};
// SCSI 管理
std::unique_ptr<SCSIDomain> domain /*{}*/;
// SCSI ホストアダプタ
SCSIHostDevice *host {};
// SCSI バス
std::unique_ptr<SCSIBus> bus /*{}*/;
Monitor *monitor {};
};
inline SPCDevice *GetSPCDevice() {
return Object::GetObject<SPCDevice>(OBJ_SPC0);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.