File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / spc.h
Revision 1.1.1.20 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:06:00 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
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);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.