File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / bus.h
Revision 1.1.1.10 (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) 2024 nono project
// Licensed under nono-license.txt
//

//
// バス
//

#pragma once

#include "tick.h"

// busaddr はアドレスバスにいくつかの情報を追加した構造。
//
// :      6        :          5    :              4:              3:3       0
//  3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1       0
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-- ... --+
// |B| |C|     |T|P|         |Size |               |R|     |S|I|D|S| Address |
// |E| |I|     |S|A|         |     |               |W|     |2| | | | (32bit) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-- ... --+
//                                                  7 6 5 4 3 2 1 0
// BE (BusError, bit63)
//	アドレス変換時のバスエラー。
//
// CI (Cache Inhibit, bit61)
//
// TS (Table Searched, bit57)
//	このアドレスを求めるのにテーブルサーチを行ったら%1。デバッガアクセス用。
//
// PA (Physical Address, bit56)
//	このアドレスを物理アドレスとする場合は %1 を指定する。
//	論理アドレスまたは区別が不要な場合は %0 にする。
//	論理アドレスと物理アドレスの両方を受け取るメモリダンプとかで使う。
//
// Size (bit50-48)
//	要求バイト数で 1 .. 4 で表す。
//
// RW (Read/Write, bit39)
//	Read なら %1。
//	上位ワードを >>1 して下位7ビットを取り出すと SSW (の FC と RW) になる。
//
// S2, I, D, S (bit35-32)
//	S と S2 はどちらも S/U で同じ値を入れる。
//	D はデータアクセスなら %1 で、m68030 の FC0 に相当。
//	I は命令(プログラム)アクセスなら %1 で、m68030 の FC1 に相当。
//	この並びは
//	 o 下位 33 ビットで S/U と32ビットアドレスが表現できる。
//	 o 下位 35 ビットで FC(順不同) と32ビットアドレスが表現できる。
//	 o 上位ワードを >>1 すると下位3ビットが FC2-0 (同順) になる。

// 定数
enum class BusAddr : uint64
{
	D				= 0x0000'0002'0000'0000ULL,	// データ空間
	I				= 0x0000'0004'0000'0000ULL,	// 命令空間
	S				= 0x0000'0009'0000'0000ULL,	// スーパーバイザ空間
	U				= 0x0000'0000'0000'0000ULL,	// ユーザ空間
	R				= 0x0000'0080'0000'0000ULL,	// 読み込み
	W				= 0x0000'0000'0000'0000ULL,	// 書き込み

	Fetch			= I | R,
	Read			= D | R,
	Write			= D | W,
	SRead			= S | Read,
	SWrite			= S | Write,
	FCMask			= 0x0000'000f'0000'0000ULL,

	Size1			= 0x0001'0000'0000'0000ULL,
	Size2			= 0x0002'0000'0000'0000ULL,
	Size3			= 0x0003'0000'0000'0000ULL,
	Size4			= 0x0004'0000'0000'0000ULL,
	SizeMask		= 0x0007'0000'0000'0000ULL,

	// デバッガ用
	Physical		= 0x0100'0000'0000'0000ULL,
	Logical			= 0x0000'0000'0000'0000ULL,
	TableSearched	= 0x0200'0000'0000'0000ULL,

	CInhibit		= 0x2000'0000'0000'0000ULL,
	BusErr			= 0x8000'0000'0000'0000ULL,
};

// BusAddr に対する OR 演算子 (まだ busaddr は出せない)
inline BusAddr operator|(const BusAddr& lhs, const BusAddr& rhs) {
	return (BusAddr)((uint64)lhs | (uint64)rhs);
}
inline BusAddr operator|(const BusAddr& lhs, const uint64& rhs) {
	return (BusAddr)((uint64)lhs | rhs);
}
inline BusAddr operator|(const uint64& lhs, const BusAddr& rhs) {
	return (BusAddr)(lhs | (uint64)rhs);
}

class busaddr
{
 private:
	uint64 value {};

 public:
	// ファンクションコードから busaddr を作成。fc_ は 0-7 にすること。
	static constexpr BusAddr FC(uint32 fc_) noexcept {
		return (BusAddr)(((uint64)fc_ << 33) | ((uint64)(fc_ & 4) << 30));
	}
	// アクセスサイズ
	static constexpr BusAddr Size(uint n) noexcept {
		return (BusAddr)((uint64)n << 48);
	}
	// S/U
	static constexpr BusAddr SU(bool super_) noexcept {
		return super_ ? BusAddr::S : BusAddr::U;
	}

 public:
	busaddr() noexcept {
	}

	explicit busaddr(uint64 value_) noexcept {
		value = value_;
	}

	// enum からの自動変換のため explicit つけない。
	busaddr(BusAddr value_) noexcept {
		value = (uint64)value_;
	}

	// コピーコンストラクタを自前で定義すると、
	// このクラスがレジスタ渡しから参照渡しになるようなので、
	// コピーコンストラクタはデフォルトのまま使う。

	uint64 Get() const noexcept		{ return value; }
	uint32 GetH() const noexcept	{ return (uint32)(value >> 32); }
	uint32 GetL() const noexcept	{ return (uint32)value; }
	uint32 Addr() const noexcept	{ return (uint32)value; }
	// S/U ビットを足した計33ビット
	uint64 GetSAddr() const noexcept
									{ return value & 0x1'ffff'ffffULL; }
	uint32 GetFC() const noexcept	{ return (value >> 33) & 7; }
	uint32 GetSize() const noexcept	{ return (value >> 48) & 7; }
	bool IsSuper() const noexcept	{ return (value & (uint64)BusAddr::S); }
	bool IsData() const noexcept	{ return (value & (uint64)BusAddr::D); }
	bool IsRead() const noexcept	{ return (value & (uint64)BusAddr::R); }
	bool IsWrite() const noexcept	{ return !IsRead(); }
	bool IsPhysical() const noexcept
							{ return (value & (uint64)BusAddr::Physical); }
	bool IsTableSearched() const noexcept
							{ return (value & (uint64)BusAddr::TableSearched); }
	bool IsCInhibit() const noexcept
							{ return (value & (uint64)BusAddr::CInhibit); }
	bool IsOK() const noexcept		{ return !IsBusErr(); }
	bool IsBusErr() const noexcept
							{ return (value & (uint64)BusAddr::BusErr); }

	// Change*() は特定のフィールドだけ差し替える

	void ChangeAddr(uint32 addr_) noexcept {
		value &= 0xffffffff'00000000;
		value |= addr_;
	}
	void ChangeSuper(bool super_) noexcept {
		if (super_) {
			value |= (uint64)BusAddr::S;
		} else {
			value &= ~(uint64)BusAddr::S;
		}
	}
	void ChangeFC(uint32 fc_) noexcept {
		value &= ~(uint64)BusAddr::FCMask;
		value |= (uint64)busaddr::FC(fc_);
	}
	void ChangeData(bool data_) noexcept {
		value &= ~(uint64)(BusAddr::I | BusAddr::D);
		if (data_) {
			value |= (uint64)BusAddr::D;
		} else {
			value |= (uint64)BusAddr::I;
		}
	}
	void ChangeWrite(bool write_) noexcept {
		if (write_) {
			value &= ~(uint64)BusAddr::R;
		} else {
			value |= (uint64)BusAddr::R;
		}
	}
	void ChangeSize(uint32 n) noexcept {
		value &= ~(uint64)BusAddr::SizeMask;
		value |= (uint64)Size(n);
	}

 public:
	// addr |= (busaddr) で FC とかのビットが足せると楽。
	busaddr& operator|=(const busaddr& rhs) {
		value |= rhs.value;
		return *this;
	}
	busaddr& operator|=(const uint32& mask_) {
		ChangeAddr(Addr() | mask_);
		return *this;
	}
	// addr &= mask でマスクが適用できると楽。
	busaddr& operator&=(const uint32& mask_) {
		ChangeAddr(Addr() & mask_);
		return *this;
	}
	busaddr& operator|=(const BusAddr& rhs) {
		value |= (uint64)rhs;
		return *this;
	}
	busaddr& operator&=(const BusAddr& rhs) {
		value &= (uint64)rhs;
		return *this;
	}

	// addr += n, -= n でアドレス進められると楽。
	busaddr& operator+=(const uint32& val) {
		ChangeAddr(Addr() + val);
		return *this;
	}
	busaddr& operator-=(const uint32& val) {
		ChangeAddr(Addr() - val);
		return *this;
	}
	// addr++ でアドレス進められると楽。
	busaddr operator++(int n) {
		busaddr old(value);
		ChangeAddr(Addr() + 1);
		return old;
	}
	// ++addr でアドレス進められると楽。
	busaddr& operator++() {
		ChangeAddr(Addr() + 1);
		return *this;
	}
};

// busaddr の等価演算子
inline bool operator==(const busaddr& lhs, const busaddr& rhs) {
	return (lhs.Get() == rhs.Get());
}
inline bool operator!=(const busaddr& lhs, const busaddr& rhs) {
	return !(lhs == rhs);
}

// busaddr に対する OR 演算子
inline busaddr operator|(const busaddr& lhs, const uint64& rhs) {
	return busaddr(lhs.Get() | rhs);
}
inline busaddr operator|(const busaddr& lhs, const busaddr& rhs) {
	return lhs | rhs.Get();
}
inline busaddr operator|(const busaddr& lhs, const BusAddr& rhs) {
	return lhs | (busaddr)rhs;
}
inline busaddr operator|(const BusAddr& lhs, const busaddr& rhs) {
	return (busaddr)lhs | rhs;
}


// busdata はデータバスにいくつかの応答情報を追加した構造。
//
// :      6        :          5    :              4:              3:3       0
//  3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1       0
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-- ... --+
// |B|R|                     |Size |        Wait [nsec]            | Data    |
// |E|T|                     |     |                               | (32bit) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-- ... --+
//
// BE (BusError, bit63)
//	バスエラー。
//
// RT (Retry, bit62)
//	リトライ。SPC で使う。
//
// Size (bit50-48)
//	デバイスが処理したバイト数で、1 .. 4 で表す。
//
// Wait (bit47-32)
//	ウェイトは [tsec] で設定・取得するが、ここに格納するときは [nsec] にする。
//	XXX あとで Size を移動してから [tsec] に統一するかも。

// 定数
enum class BusData : uint64
{
	OK				= 0,
	// BusErr は Data() 部分を $ffffffff に見せておく。Peek*() でも使う。
	BusErr			= 0x8000'0000'ffff'ffffULL,
	Retry			= 0x4000'0000'0000'0000ULL,

	Size1			= (uint64)BusAddr::Size1,
	Size2			= (uint64)BusAddr::Size2,
	Size3			= (uint64)BusAddr::Size3,
	Size4			= (uint64)BusAddr::Size4,
	SizeMask		= (uint64)BusAddr::SizeMask,
};

// BusData に対する OR 演算子 (まだ busdata は出せない)
inline BusData operator|(const BusData& lhs, const BusData& rhs) {
	return (BusData)((uint64)lhs | (uint64)rhs);
}
inline BusData operator|(const BusData& lhs, const uint64& rhs) {
	return (BusData)((uint64)lhs | rhs);
}
inline BusData operator|(const uint64& lhs, const BusData& rhs) {
	return (BusData)(lhs | (uint64)rhs);
}

class busdata
{
 private:
	uint64 value {};

 public:
	// ウェイトを [tsec] で指定する
	static constexpr BusData Wait(uint64 tsec) noexcept {
		return (BusData)(tsec_to_nsec(tsec) << 32);
	}
	// データサイズ
	static constexpr BusData Size(uint n) noexcept {
		return (BusData)((uint64)n << 48);
	}

 private:
	// バスエラーかどうか判定するのに使うのは BusErrBit のほう。
	enum : uint64 {
		BusErrBit	= 0x8000'0000'0000'0000ULL,
		RetryBit	= (uint64)BusData::Retry,
		Mask		= BusErrBit | RetryBit,
	};

 public:
	busdata() noexcept {
	}

	// uint32 の値をそのまま busdata 型に代入したいので explicit はつけない。
	busdata(uint64 value_) noexcept {
		value = value_;
	}

	// enum からの自動変換のため explicit つけない。
	busdata(BusData value_) noexcept {
		value = (uint64)value_;
	}

	// コピーコンストラクタを自前で定義すると、
	// このクラスがレジスタ渡しから参照渡しになるようなので、
	// コピーコンストラクタはデフォルトのまま使う。

	// uint64 にキャストできる
	operator uint64() const noexcept { return Get(); }

	uint64 Get() const noexcept		{ return value; }
	uint32 GetH() const noexcept	{ return (uint32)(value >> 32); }
	uint32 GetL() const noexcept	{ return (uint32)value; }
	uint32 Data() const noexcept	{ return (uint32)value; }
	// ウェイトを [tsec] で取得。
	uint64 GetWait() const noexcept	{
		return nsec_to_tsec((value >> 32) & 0xffff);
	}
	uint32 GetSize() const noexcept	{ return (uint32)((value >> 48) & 7); }
	bool IsOK() const noexcept		{ return (value & Mask) == 0; }
	bool IsBusErr() const noexcept	{ return (value & BusErrBit); }
	bool IsRetry() const noexcept	{ return (value & RetryBit); }

	void ChangeData(uint32 data_) noexcept {
		value &= 0xffffffff'00000000;
		value |= data_;
	}
	// ウェイトを tsec に変更。
	void ChangeWait(uint64 tsec) noexcept {
		value &= 0xffff0000'ffffffff;
		value |= (uint64)Wait(tsec);
	}
	void ChangeSize(uint32 n) noexcept {
		value &= ~(uint64)BusData::SizeMask;
		value |= (uint64)Size(n);
	}
	void SetBusErr() noexcept		{ value |= (uint64)BusData::BusErr; }
	void SetRetry() noexcept		{ value |= (uint64)BusData::Retry; }

 public:
	// data |= (busdata) で別途用意してあるウェイトが足せると楽。
	busdata& operator|=(const busdata& rhs) {
		value |= rhs.value;
		return *this;
	}

	// data &= (uint32) でデータ部分のみ演算できると楽。
	busdata& operator&=(const uint32& rhs) {
		ChangeData(Data() & rhs);
		return *this;
	}

	// data <<= (uint32) でデータ部分のみシフトできると楽。
	busdata& operator<<=(const uint32& rhs) {
		ChangeData(Data() << rhs);
		return *this;
	}

	// data >>= (uint32) でデータ部分のみシフトできると楽。
	busdata& operator>>=(const uint32& rhs) {
		ChangeData(Data() >> rhs);
		return *this;
	}
};

// busdata に対する OR 演算子
inline busdata operator|(const busdata& lhs, const uint64& rhs) {
	return busdata(lhs.Get() | rhs);
}
inline busdata operator|(const busdata& lhs, const busdata& rhs) {
	return lhs | rhs.Get();
}
inline busdata operator|(const busdata& lhs, const BusData& rhs) {
	return lhs | (busdata)rhs;
}
inline busdata operator|(const BusData& lhs, const busdata& rhs) {
	return (busdata)lhs | rhs;
}
inline busdata operator|(const uint64& lhs, const busdata& rhs) {
	return busdata(lhs | rhs.Get());
}

unix.superglobalmegacorp.com

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