File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / scsibus.h
Revision 1.1.1.13 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:06:01 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
//

//
// SCSI バス
//

#pragma once

#include "scsi.h"
#include <array>

class SCSIDevice;
class SCSIDisk;
class SCSIDomain;
class SCSIHostDevice;
class SCSITarget;

//
// SCSI バス
//
class SCSIBus : public Device
{
	using inherited = Device;

 public:
	// (1<<id) が1つだけ立ててあるビットマップから ID を返す。
	// ビットが立っていなければ -1 を返す? 複数立ってたら?
	static inline int DecodeID(uint32 mask)
	{
		mask &= 0xff;
		if (mask == 0)
			return -1;
		return 31 - __builtin_clz(mask);
	}

 public:
	explicit SCSIBus(SCSIDomain *);
	~SCSIBus() override;

	bool Init() override;
	void ResetHard(bool poweron) override;

	SCSIDomain *GetDomain() const noexcept { return domain; }

	// デバイス情報を更新する。
	void Refresh();

	// 信号線の操作
	bool GetRST() const { return rst; }
	void AssertRST();
	void NegateRST();
	bool GetREQ() const { return req; }
	void AssertREQ();
	void NegateREQ();
	bool GetACK() const { return ack; }
	void AssertACK();
	void NegateACK();
	bool GetATN() const { return atn; }
	void AssertATN();
	void NegateATN();
	uint8 GetSEL() const { return sel; }
	void AssertSEL(uint id);
	void NegateSEL(uint id);
	uint8 GetBSY() const { return bsy; }
	void AssertBSY(uint id);
	void NegateBSY(uint id);
	bool GetMSG() const	{ return (xfer & SCSI::MSG); }
	bool GetCD() const	{ return (xfer & SCSI::CD); }
	bool GetIO() const	{ return (xfer & SCSI::IO); }

	// MSG,CD,IO で示されるビットを val で指定した状態に変更する。
	void SetXfer(uint8 val);

	// 現在の情報転送フェーズ(の信号線の状態) を返す。
	// 現在のフェーズが Transfer の時のみ戻り値の内容は有効。
	SCSI::XferPhase GetXfer() const { return (SCSI::XferPhase)xfer; }

	uint8 GetData() const { return data; }
	void SetData(uint8 val) { data = val; }

	// 現在のフェーズを返す
	SCSI::Phase GetPhase() const { return phase; }
	// フェーズを変更する
	void SetPhase(SCSI::Phase val) { phase = val; }
	// 現在のフェーズ名を返す
	const char *GetPhaseName() const { return SCSI::GetPhaseName(phase); }

	// 現在選択中のターゲットを返す、なければ NULL を返す。(モニタ用)
	const SCSIDevice *GetSelectedTarget() const {
		if (__predict_true(0 <= target_id && target_id < 8)) {
			return device[target_id];
		}
		return NULL;
	}

	// 次の REQ アサートの前に指定のウェイトを入れる
	// (シークタイムなどを簡易的に再現するため)。
	// ウェイトは一度適用されると 0 に戻る。
	void SetOneshotReqWait(uint64 tsec) { req_wait += tsec; }

 private:
	void BusFree(uint id);
	void SelectionStart(Event *);
	void Selected(Event *);
	void SelectionAck(Event *);
	void StartTransfer(Event *);
	void TransferReq(Event *);

	// 現在のフェーズ
	SCSI::Phase phase {};

	// SEL, BSY は同時に複数が重畳出来るので、アサートしたデバイスがそれぞれ
	// 自身の ID を立てる。ネゲートは 0 かどうかで判断する。
	// 現在の実装では実際には同時に立つことはないけど。
	bool rst {};
	bool req {};
	bool ack {};
	bool atn {};
	uint8 sel {};
	uint8 bsy {};
	uint8 xfer {};	// MSG,CD,IO
	uint8 data {};
	// bool parity; 未実装

	// 前回 ACK を立てた時刻。
	// 転送速度をここで律速させている。
	uint64 last_req_time {};

	// 次の REQ アサートに入れるウェイト [tsec]
	uint64 req_wait {};

	// 実際のバスではそんなことはないが、ここではバスがイニシエータと
	// ターゲットの ID を把握している。どちらも非選択状態なら -1。
	int init_id {};
	int target_id {};

	// SCSI 管理
	SCSIDomain *domain {};

	// 接続されているデバイス (イニシエータも含む) へのポインタ。
	std::array<SCSIDevice *, 8> device {};

	// イベント
	Event *event {};
};

unix.superglobalmegacorp.com

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