File:  [Isaki's NoNo m68k/m88k emulator] / nono / m680x0 / m68030mmu.h
Revision 1.1.1.11 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:57 2026 UTC (3 months 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
//

#pragma once

#include <array>
#include <mutex>
#include "fixedqueue.h"

// ATC 実装種別。
//#define M68030_CUSTOM_ATC
#define ATC_ORDER64

// ATC エントリ数。
#define M68030_ATC_LINES (22)

#if defined(M68030_CUSTOM_ATC)
#else
# if defined(ATC_ORDER64)
#  if M68030_ATC_LINES > 24
#   error M68030_ATC_LINES must be <= 24 on ATC_ORDER64.
#  endif
#  if M68030_ATC_LINES < 13
#   error M68030_ATC_LINES must be >= 13 on ATC_ORDER64.
#  endif
# endif
#endif

class MPU68030Device;

// SRP, CRP
class m68030RP
{
	//        6                   5                   4               3
	//  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
	// +-+-----------------------------+---------------------------+---+
	// |U|           LIMIT             |              0            |DT |
	// +-+-----------------------------+---------------------------+---+
	//
	//    3                   2                   1                   0
	//  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
	// +-------------------------------------------------------+-------+
	// |           TABLE ADDRESS (PA31-PA4)                    |unused |
	// +-------------------------------------------------------+-------+
 public:
	static const uint32 H_MASK	= 0xffff0003;
};

// TT レジスタの各フィールドを束ねた構造体のようなもの。
// MPU68030Device::SetTT(), GetTT() で読み書きする。
class m68030TT
{
	//    3                   2                   1                   0
	//  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
	// +---------------+---------------+-+-+-+-+-+-+-+-+-+-----+-+-----+
	// |LogicalAddrBase|LogicalAddrMask|E|       |C|R|M| | FCB | | FCM |
	// +---------------+---------------+-+-+-+-+-+-+-+-+-+-----+-+-----+

 public:
	static const uint32 MASK		= 0xffff8777;

	static const uint32 LBASE_MASK	= 0xff000000;
	static const uint32 LMASK_MASK	= 0x00ff0000;
	static const uint32 E			= 0x00008000;
	static const uint32 CI			= 0x00000400;
	static const uint32 RW			= 0x00000200;
	static const uint32 RWM			= 0x00000100;
	static const uint32 FCBASE_MASK	= 0x00000070;
	static const uint32 FCMASK_MASK	= 0x00000007;

	void Set(uint32 data);

	// アクセスがこの TT と一致するか調べる。
	bool Match(busaddr addr) const;

	bool e {};				// イネーブル
	bool ci {};				// キャッシュ禁止
 private:
	uint64 base {};			// ベース
	uint64 mask {};			// マスク
};

// TC レジスタの各フィールドを束ねた構造体のようなもの。
// m68kcpu::SetTC(), GetTC() で読み書きする。
class m68030TC
{
	//    3                   2                   1                   0
	//  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
	// +-+---------+-+-+-------+-------+-------+-------+-------+-------+
	// |E|    0    |S|F|  PS   |  IS   |  TIA  |  TIB  |  TIC  |  TID  |
	// +-+---------+-+-+-------+-------+-------+-------+-------+-------+
	//  |           | +-- FCL (Function Code Lookup Enable)
	//  |           +---- SRE (Supervisor Root Pointer Enable)
	//  +-- Enable
 public:
	static const uint32 MASK	= 0x83ffffff;
	static const uint32 TC_E	= 0x80000000;
	static const uint32 TC_SRE	= 0x02000000;
	static const uint32 TC_FCL	= 0x01000000;

	// レジスタの各フィールド
	uint32 e {};		// TC_E   の位置
	uint32 sre {};		// TC_SRE の位置
	uint32 fcl {};		// TC_FCL の位置
	uint ps {};
	uint is {};

	// 68030 のテーブルは最大4階層 (TIA 〜 TID) で、
	// TIA から termlv 段までが有効。
	union {
		uint tix[4] {};
		struct {
			uint tia;
			uint tib;
			uint tic;
			uint tid;
		} __packed;
	};

	int termlv {};			// 有効な段数 (例えば TIB までなら 2)
	int shift[4] {};		// TIA-TID の各部分のシフト量
	uint32 mask[4] {};		// TIA-TID の各部分のマスク
	uint32 lmask {};		// TIA-TID 全体のマスク
	uint32 pgmask {};		// PS 部分のマスク

 public:
	// 値を代入し、二次変数も用意する。
	// 構成例外を起こすなら false を返す。
	bool Set(uint32);

	// (E に関わらず) 構成が有効なら true を返す。
	bool ConfigOK() const	{ return config_ok; }

 private:
	bool Check() const;
	void MakeMask();

	bool config_ok {};		// (E に関わらず) 構成が有効なら true
};

class m68030MMUSR
{
 public:
	static const uint16 MASK = 0xee43;
	static const uint16 B    = 0x8000;	// バスエラー
	static const uint16 L    = 0x4000;	// リミット違反
	static const uint16 S    = 0x2000;	// スーパバイザ専用
	static const uint16 W    = 0x0800;	// 書き込み保護
	static const uint16 I    = 0x0400;	// 無効
	static const uint16 M    = 0x0200;	// 修正
	static const uint16 T    = 0x0040;	// 透過アクセス
	static const uint16 N    = 0x0007;	// レベル数
};

// ATC の1エントリ
struct m68030ATCLine
{
	// tag:
	//     3                   2                   1                   0
	//   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
	//  +---------------+---------------+---------------+-------+-----+-+
	//  |              Logical Address [31:8]           |   0   | FC  |I|
	//  +---------------+---------------+---------------+-------+-----+-+
	//   I:  Invalid (%1で無効)。
	//   FC: busaddr 形式の FC (210 の順) で、_Motorola のみで使用。
	//       _Custom のほうは FC 別のテーブルなのでここは使わない。
	//
	// paddr:
	//   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
	//  +---------------+---------------+---------------+---------------+
	//  |             Physical Address [31:8]           |       0       |
	//  +---------------+---------------+---------------+---------------+
	//
	// data:
	//   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
	//  +---------------+---------------+---------------+-------+-+-+-+-+
	//  |                         0                             |B|C|P|M|
	//  +---------------+---------------+---------------+-------+-+-+-+-+
	//   B: Bus Error
	//   C: Cache Inhibit
	//   P: Write Protect
	//   M: Modified

	static const uint32 LADDR_MASK	= 0xffffff00;
	static const uint32 INVALID		= 0x00000001;
	static const uint32 PADDR_MASK	= 0xffffff00;
	static const uint32 BUSERROR	= 0x00000008;
	static const uint32 CINHIBIT	= 0x00000004;
	static const uint32 WPROTECT	= 0x00000002;
	static const uint32 MODIFIED    = 0x00000001;

	uint32 tag;
	uint32 paddr;
	uint32 data;
#if defined(M68030_CUSTOM_ATC)
	uint32 age;
#endif

	uint32 GetTag() const		{ return tag; }

	void   Invalidate()			{ tag |= INVALID; }
	bool   IsInvalid() const	{ return tag & INVALID; }
	bool   IsValid() const		{ return !IsInvalid(); }

	uint32 GetLAddr() const		{ return tag & LADDR_MASK; }
#if !defined(M68030_CUSTOM_ATC)
	uint32 GetFC() const		{ return (tag >> 1) & 7; }
#endif
	uint32 GetPAddr() const		{ return paddr; }
	bool   IsBusError() const	{ return data & BUSERROR; }
	bool   IsCInhibit() const	{ return data & CINHIBIT; }
	bool   IsWProtect() const	{ return data & WPROTECT; }
	bool   IsModified() const	{ return data & MODIFIED; }
};

// ATCTable_* の共通部分だが、ポリモーフィズム (virtual) は使わずに書く。
class m68030ATCTable
{
 public:
	static constexpr int LINES = M68030_ATC_LINES;

	// 論理アドレスマスクを設定する。
	void SetMask(uint32 lmask_) {
		lmask = lmask_;
	}

 protected:
	// デバッグ表示用
	static std::string LineToStrCommon(const m68030ATCLine *, uint fc);

 public:
	int loglevel {};

 protected:
	// cpu->mmu_tc.lmask のコピー。
	uint32 lmask {};

	MPU68030Device *cpu {};
};

#if defined(M68030_CUSTOM_ATC)
//
// カスタム ATC。FC を分離し、TT と ATC を同時に検索する魔改造版。
//

struct m68030ATCStat
{
	uint32 total {};	// アドレス変換した回数 (TC と TT 両方)
	uint32 tthit {};	// TT にヒットした回数
	uint32 miss {};		// ATC ミスした回数

	void Clear() {
		total = 0;
		tthit = 0;
		miss = 0;
	}
};

class m68030ATCTable_Custom : public m68030ATCTable
{
 public:
	// ATC ハッシュ
	enum : uint8 {
		HASH_ATC_BASE	= 0,		// ATC #0-
		HASH_NONE		= 0xff,		// 対応なし
		HASH_TT			= 0xfe,		// TT
		HASH_SUB		= 0xfd,		// PS<4KB
	};

	m68030ATCTable_Custom(MPU68030Device *cpu_, uint fc_);

	// 統計情報、移動平均等をリセットする。
	void ResetStat();

	// ハッシュを完全に作り直す。
	void Rehash();

	// 論理アドレスからタグを作成する。
	uint32 MakeTag(const busaddr laddr) const {
		return (laddr.Addr() & lmask);
	}

	// フラッシュ
	// Flush() はこのテーブルのすべてのエントリが対象。
	// Flush(addr) はこのテーブルのアドレスが一致するエントリが対象。
	void Flush();
	void Flush(uint32 addr);

	m68030ATCLine *Pop();

	// PS<4KB の時に ATC を検索。
	m68030ATCLine *FindSub(const busaddr laddr);

	void Invalidate(m68030ATCLine *a);
	void InvalidateSub(const m68030ATCLine *);

	void ClearHash();
	void MakeHash(const m68030ATCLine *);
	void MoveHead(m68030ATCLine *);

	// デバッグ表示
	std::string LineToStr(const m68030ATCLine *a) const;

	// エントリの実体。
	std::array<m68030ATCLine, LINES> line {};

	// 統計情報。
	m68030ATCStat stat {};

	// 移動平均用バッファ。(こっちは排他制御が必要)
	FixedQueue<m68030ATCStat, 3> hist {};
	bool hist_tt_once_hit {};
	std::mutex hist_mtx {};

 private:
	uint32 latest_age {};

	// このテーブルの FC
	uint fc {};

	// PS(ページのビット長) - 12。負数なら PS<4KB
	int ps4k {};

 public:
	// ATC ハッシュ (他のメンバより後ろに置く?)
	std::array<uint8, 1024 * 1024> hash {};
};

#else
//
// Motorola ATC
//

struct m68030ATCStat
{
	// 先頭からn番目のヒット回数
	std::array<uint64, M68030_ATC_LINES> hit {};
	// ATC 検索ミス回数
	uint64 miss {};

	void Clear() {
		hit = {};
		miss = 0;
	}
};

class m68030ATCTable_Motorola : public m68030ATCTable
{
	friend class MPU68030Device;

#if defined(ATC_ORDER64)
	static constexpr int ORDER0_COUNT = 12;
	static constexpr int ORDER1_COUNT = M68030_ATC_LINES - ORDER0_COUNT;
#endif

 public:
	explicit m68030ATCTable_Motorola(MPU68030Device *cpu_);

	// 統計情報、移動平均等をリセットする。
	void ResetStat();

	// 論理アドレスからタグを作成する。
	uint32 MakeTag(const busaddr laddr) const {
		return (laddr.Addr() & lmask) | (laddr.GetFC() << 1);
	}

	// エントリのフラッシュ。(pflush* 命令から呼ばれる)
	void Flush();
	void Flush(uint32 fc, uint32 mask);
	void Flush(uint32 fc, uint32 mask, uint32 addr);

	// laddr を検索して見付かれば先頭に移動。
	m68030ATCLine *LookupAndMove(const busaddr laddr);

	// laddr を検索して見付かったのを返す。(PTEST 用)
	const m68030ATCLine *LookupOnly(const busaddr laddr);

	// laddr を検索して返す。なければ NULL を返す。(デバッガ用)
	const m68030ATCLine *LookupByDebugger(const busaddr laddr) {
		// 今は Only と同じ。
		return LookupOnly(laddr);
	}

	// 空きもしくは最も古いエントリを先頭に移動。
	m68030ATCLine *Pop();

	// pri 番目の idx を返す。
	uint GetIdx(uint pri) const
	{
#if defined(ATC_ORDER64)
		if (__predict_true(pri < ORDER0_COUNT)) {
			return (order[0] >> (pri * 5)) & 0x1f;
		} else {
			return (order[1] >> ((pri - ORDER0_COUNT) * 5)) & 0x1f;
		}
#else
		return order[pri];
#endif
	}

	// pri 番目のエントリを返す。
	m68030ATCLine *GetLine(uint pri) {
		return &line[GetIdx(pri)];
	}

	// デバッグ表示
	static std::string LineToStr(const m68030ATCLine *);

 private:
	// pri 番目の値を idx にする。
	void Set(uint pri, uint idx);

	int Lookup(const busaddr);

	// pri 番目の要素を先頭に移動。
	void MoveHead(uint pri);
	// pri 番目の要素を末尾に移動。
	void MoveTail(uint pri);

	// エントリの実体。
	std::array<m68030ATCLine, LINES> line {};

	// 順序管理。
#if defined(ATC_ORDER64)
	std::array<uint64, 2> order {};
#else
	std::array<uint8, LINES> order {};
#endif

	// 統計情報。
	m68030ATCStat stat {};
};

#endif // M68030_CUSTOM_ATC

class m68030ATC
{
 public:
	explicit m68030ATC(MPU68030Device *cpu_);

	// ログレベルを設定する。Object のとは同じ使い方だけど別物。
	void SetLogLevel(int loglevel_);

	// TC の設定変更に追従する。
	void SetTC(const m68030TC&);

#if defined(M68030_CUSTOM_ATC)
	// ハッシュを完全に作り直す。
	void Rehash();
#endif

	// テーブルを取得する。
#if defined(M68030_CUSTOM_ATC)
	m68030ATCTable_Custom *GetTable(uint fc) {
		assert((bool)fctables[fc]);
		return fctables[fc];
	}
#else
	m68030ATCTable_Motorola *GetTable() {
		return table.get();
	}
#endif

	// エントリのフラッシュ。(pflush* 命令から呼ばれる)
	void FlushAll();
	void Flush(uint32 fc, uint32 mask);
	void Flush(uint32 fc, uint32 mask, uint32 addr);

	// 統計情報、移動平均等をリセットする。
	void ResetStat();

	// 統計情報、移動平均等を更新する。
	void CalcStat();

 private:
#if defined(M68030_CUSTOM_ATC)
	// FC 別テーブルは 4つ。こっちはループで回せる。
	std::array<std::unique_ptr<m68030ATCTable_Custom>, 4> alltables {};
	// fc で検索する場合。実際にはこのうち 4つだけが有効。
	std::array<m68030ATCTable_Custom *, 8> fctables {};
#else
	// テーブルは1つ。
	std::unique_ptr<m68030ATCTable_Motorola> table {};
#endif

	int loglevel {};

	MPU68030Device *cpu {};
};

unix.superglobalmegacorp.com

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