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

#include "mpu680x0.h"
#include "mainbus.h"
#include "m68030cache.h"

// 現在の FC での通常アクセス (MPU 内部から使用する)
// ミスアライン可。
// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。
//	uint32 fetch_{2,4}()
//	uint32 read_{1,2,4}(laddr)        .. vm/mpu680x0.h にある
//	void   write_{1,2,4}(laddr, data) .. vm/mpu680x0.h にある

// reg.pc からワードをフェッチし reg.pc を進める。
uint32
MPU68030Device::fetch_2()
{
	busaddr addr = busaddr(reg.pc) | fc_inst | BusAddr::Size2;

	// まず可能ならキャッシュから探す。
	m68030Cache *cache = icache.get();
	if (__predict_true(cache->enable)) {
		uint32 tag = (addr.Get()  >> 4) & 0xfffffff0;
		uint32 idx = (addr.Addr() >> 4) & 0x0000000f;
		uint32 sel = (addr.Addr() >> 2) & 0x00000003;
		m68030CacheLine *l = &cache->line[idx];

		if (__predict_true(l->tag == tag && l->valid[sel])) {
			cache->stat_fasthit++;
			uint32 data = l->data[sel];
			if ((reg.pc & 2) == 0) {
				data >>= 16;
			} else {
				data &= 0xffff;
			}
			reg.pc += 2;
			return data;
		}
	}

	busdata bd;
	if (__predict_true(cache->enable)) {
		bd = read_cache(cache, addr);
	} else {
		bd = read_nocache(addr);
	}
	if (__predict_false(bd.IsBusErr())) {
		throw M68K::EXCEP_BUSERR;
	}
	reg.pc += 2;
	return bd.Data();
}

// reg.pc からロングワード(ミスアライン可)をフェッチし reg.pc を進める。
uint32
MPU68030Device::fetch_4()
{
	busaddr addr = busaddr(reg.pc) | fc_inst | BusAddr::Size4;

	// まず可能ならキャッシュから探す。
	m68030Cache *cache = icache.get();
	if (__predict_true(cache->enable)) {
		uint32 tag = (addr.Get()  >> 4) & 0xfffffff0;
		uint32 idx = (addr.Addr() >> 4) & 0x0000000f;
		uint32 sel = (addr.Addr() >> 2) & 0x00000003;
		m68030CacheLine *l = &cache->line[idx];

		if (__predict_true(l->tag == tag && l->valid[sel])) {
			uint32 data;
			if ((reg.pc & 2) == 0) {
				cache->stat_fasthit++;
				data = l->data[sel];
			} else {
				// 次のエントリにまたがる場合。
				if (sel < 3 && l->valid[sel + 1]) {
					cache->stat_fasthit += 2;
					data  = l->data[sel] << 16;
					data |= l->data[sel + 1] >> 16;
				} else {
					// 片方でキャッシュミスした。
					// この場合は fast* はカウントしない。
					goto fetch;
				}
			}
			reg.pc += 4;
			return data;
		}
	}

 fetch:
	busdata bd;
	if (__predict_true(cache->enable)) {
		bd = read_cache(cache, addr);
	} else {
		bd = read_nocache(addr);
	}
	if (__predict_false(bd.IsBusErr())) {
		throw M68K::EXCEP_BUSERR;
	}
	reg.pc += 4;
	return bd.Data();
}

// read_1,2,4() の共通部分。ミスアライン可。
// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。
uint32
MPU68030Device::read_data(busaddr addr)
{
	m68030Cache *cache = dcache.get();
	busdata data;
	if (__predict_true(cache->enable)) {
		data = read_cache(cache, addr);
	} else {
		data = read_nocache(addr);
	}
	if (__predict_false(data.IsBusErr())) {
		throw M68K::EXCEP_BUSERR;
	}
	return data.Data();
}

// write_1,2,4() の共通部分。ミスアライン可。
// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。
void
MPU68030Device::write_data(busaddr addr, uint32 data)
{
	busdata r = write_bus(addr, data);
	if (__predict_false(r.IsBusErr())) {
		throw M68K::EXCEP_BUSERR;
	}
}

// キャッシュ無効時の読み込み。ミスアライン可。
// バスエラーも busdata を返す。
busdata
MPU68030Device::read_nocache(busaddr addr)
{
	// デバイスは、addr の下位ビットによらず、自身のデバイスのポートサイズ分
	// のデータと、その応答サイズ(自分のポートサイズ) を Size フィールドに
	// セットして返してくる。
	//
	// MPU(68030) は、A1-A0、残りバイト数、デバイスの応答サイズの情報から
	// 受け取ったデータバスのうち適切なところを切り出して使う。

	putlog(3, "read_nocache %u.%08x sz=%c",
		addr.GetFC(), addr.Addr(), "0BW3L"[addr.GetSize()]);

	uint32 data = 0;
	for (;;) {
		bus.laddr = addr;
		busaddr paddr = TranslateRead(addr);
		if (__predict_false(paddr.IsBusErr())) {
			return BusData::BusErr;
		}
		bus.ci = paddr.IsCInhibit();

		busdata bd = read_phys(paddr);
		if (__predict_false(bd.IsBusErr())) {
			return bd;
		}

		uint32 reqsize = addr.GetSize();
		// デバイスのポートサイズ(応答サイズ)
		uint32 portsize = bd.GetSize();
		// 今回のアドレスオフセット
		uint32 offset = addr.Addr() & (portsize - 1);
		uint32 datasize = portsize - offset;
		uint32 tmp = bd.Data();
		// 下詰めされたデータに対するマスク
		uint32 mask = 0xffffffffU >> ((4 - datasize) * 8);
		// 今回必要なところ
		tmp &= mask;
		if (datasize == reqsize) {
			data |= tmp;
			break;
		} else if (datasize > reqsize) {
			tmp >>= (datasize - reqsize) * 8;
			data |= tmp;
			break;
		} else {
			tmp <<= (reqsize - datasize) * 8;
			data |= tmp;
		}

		addr += datasize;
		reqsize -= datasize;
		addr.ChangeSize(reqsize);
	}
	return data;
}

// キャッシュ有効時の読み込み。ミスアライン可。
// バスエラーも busdata を返す。
//
// 簡略化・高速化のため実機と異なる制約を設けてある。
// 1) 実際の 68030 システムでは、「ロングワードの全バイトがキャッシュ可能なら
//	!CIIN をネゲートしなければならない」「いずれかのバイトでもキャッシュ可能
//	でないなら CIIN をアサートしなければならない」(MC68030UM.pdf p6-15、
//	日本語本 p296 ※誤訳あり) とあり、CIIN は同一ロングワード内で変化しては
//	いけないが、ロングワードごとには変化してよい。
//	nono の実装ではこれを同一ライン(16バイト)単位の制約にする。
//	現状サポートしている機種ではこれで困ることはない。
busdata
MPU68030Device::read_cache(m68030Cache *cache, busaddr addr)
{
	putlog(3, "read_cache %u.%08x sz=%c",
		addr.GetFC(), addr.Addr(), "0BW3L"[addr.GetSize()]);

	uint32 data = 0;
	for (;;) {
		uint32 entry_data = 0;

		// まず可能ならキャッシュから探す。
		uint32 tag = (addr.Get()  >> 4) & 0xfffffff0;
		uint32 idx = (addr.Addr() >> 4) & 0x0000000f;
		uint32 sel = (addr.Addr() >> 2) & 0x00000003;
		m68030CacheLine *l = &cache->line[idx];
		if (__predict_true(l->tag == tag && l->valid[sel])) {
			// タグが一致してエントリも有効 (キャッシュヒット)。
			cache->stat_hit++;
			entry_data = l->data[sel];
			putlog(3, " entry_data=$%08x (%s)", entry_data, "match");
		} else {
			// タグが違うか、エントリが無効。
			cache->stat_miss++;

			bus.laddr = addr;
			busaddr paddr = TranslateRead(addr);
			if (__predict_false(paddr.IsBusErr())) {
				putlog(3, " TranslateRead BusErr");
				return BusData::BusErr;
			}
			bus.ci = paddr.IsCInhibit();
			putlog(3, " paddr=$%08x.%c",
				paddr.Addr(), "0BW3L"[paddr.GetSize()]);

			// タグが違うか、全エントリが無効ならライン充填する。
			// ただし要求がラインをまたぐ場合 (例えば +$e からのロングワード
			// 要求) は除く。
			bool is_linefill = (l->tag != tag || l->valid_all == 0) &&
				((addr.Addr() & 0xf) + addr.GetSize() <= 0x10);

			// 可能ならバーストリードを使う。
			if (is_linefill &&
				__predict_true(bus.ci == false) &&
				__predict_true(cache->burst_enable) &&
				__predict_true(read_phys_burst(paddr, &l->data[0])))
			{
				// read_phys_burst() はライン充填したら true を返している。
				entry_data = l->data[sel];
				putlog(3, " entry_data=$%08x (%s)", entry_data, "burst");
				l->tag = tag;
				l->valid_all = -1;
			} else {
				// シングル充填。
				busdata bd = read_single(paddr);
				if (__predict_false(bd.IsBusErr())) {
					return bd;
				}
				entry_data = bd.Data();
				putlog(3, " entry_data=$%08x (%s)", entry_data, "single");

				if (__predict_false(bus.ci) || __predict_false(cache->freeze)) {
					// エントリを更新しない。
				} else {
					l->data[sel] = entry_data;
					if (l->tag != tag) {
						// タグを更新して、このラインの他のエントリは無効。
						l->tag = tag;
						l->valid_all = 0;
					}
					l->valid[sel] = 1;
				}
			}
		}

		// addr から今回対象とするロングワードエントリに対するマスクを作成。
		uint32 reqoffset = addr.Addr() & 3;
		uint32 reqsize = addr.GetSize();
		uint32 opersize = std::min(reqsize, 4 - reqoffset);

		data <<= opersize * 8;
		// このエントリから必要な部分を抜き出す。
		uint32 mask = 0xffffffffU >> ((4 - opersize) * 8);
		if (reqsize == opersize) {
			// 要求サイズがこのエントリをはみ出していない。
			entry_data >>= (4 - reqoffset - opersize) * 8;
			data |= entry_data & mask;
			putlog(3, " data=$%0*x (done)", opersize * 2, data);
			break;
		} else {
			data |= entry_data & mask;
			putlog(3, " data=$%0*x", opersize * 2, data);
		}
		addr += opersize;
		addr.ChangeSize(reqsize - opersize);
	}

	return data;
}

// シングル充填を行い、(CI でなければ) 完全なロングワードを返す。
// CI なら第1サイクルだけを行い、ロングワードの所定の位置においたものを返す。
// バスエラーなら BusErr を返す。
busdata
MPU68030Device::read_single(busaddr paddr)
{
	// 一発目。
	busdata bd = read_phys(paddr);
	if (__predict_true(bd.IsOK()) && bd.GetSize() != 4) {
		// 必要なら残りのサイクル。
		bd = fill_single(paddr, bd);
	}
	// いずれにしても bd を返す。
	return bd;
}

// シングル充填の第2サイクル以降を行って、(CI でなければ) 完全なロングワードを
// 返す。
// 引数 paddr に第1サイクルの物理アドレス (インクリメントなどする前)。
// 引数 bd は第1サイクルの結果の busdata。
//
// 第1サイクルがバスエラーになっていれば事前に弾いておくこと。
// 第1サイクルでロングワード全部埋まったケースも事前に弾いておくこと。
busdata
MPU68030Device::fill_single(busaddr paddr, busdata bd)
{
	busaddr addr_h(paddr.Get() & ~0x3ULL);
	uint32 data;

	//assert(bd.IsOK());
	//assert(bd.GetSize() < 4);

	if (bd.GetSize() == 2) {
		// ワードポートアクセスだったなら、第1アクセスが上位か下位かだけ。
		if ((paddr.Addr() & 2) == 0) {
			data = bd.Data() << 16;
		} else {
			data = bd.Data();
		}
		if (__predict_false(bus.ci)) {
			goto done;
		}
		// 次のアクセスはワード境界にアラインされるので
		// 0x3 じゃなく0x2 でマスク。
		uint32 offset = (paddr.Addr() + 2) & 0x2;
		paddr = addr_h | offset;

		bd = read_phys(paddr);
		// CI はもう起きないはず。バスエラーは?
		if (offset == 0) {
			data |= bd.Data() << 16;
		} else {
			data |= bd.Data();
		}
	} else {
		// バイトポートアクセスの場合。

		// 第1サイクルのデータを所定のバイト位置に。
		uint32 offset = paddr.Addr() & 0x3;
		data = bd.Data() << ((3 - offset) * 8);
		if (__predict_false(bus.ci)) {
			goto done;
		}
		for (uint i = 1; i < 4; i++) {
			// インクリメント。
			offset = (offset + 1) & 0x3;
			paddr = addr_h | offset;

			bd = read_phys(paddr);
			// CI はもう起きないはず。バスエラーは?
			data |= bd.Data() << ((3 - offset) * 8);
		}
	}

 done:
	return data;
}

// キャッシュ/バスへの書き込み。ミスアライン可。
// バスエラーも busdata を返す。
busdata
MPU68030Device::write_bus(busaddr addr, uint32 data)
{
	m68030Cache *cache = dcache.get();

	putlog(3, "write_bus %u.%08x sz=%c data=$%0*x",
		addr.GetFC(), addr.Addr(), "0BW3L"[addr.GetSize()],
		addr.GetSize() * 2, data);
	for (;;) {
		uint32 nextdata;
		uint32 reqoffset = addr.Addr() & 3;
		uint32 reqsize = addr.GetSize();

		// ロングワード境界をはみ出す場合は持ち越し分を覚えておく。
		int nextsize = reqoffset + reqsize - 4;
		if (nextsize > 0) {
			// 後ろにはみ出すケース。
			nextdata = data & ((1U << (nextsize * 8)) - 1);
			data >>= nextsize * 8;
			reqsize -= nextsize;
			addr.ChangeSize(reqsize);
		} else {
			nextsize = 0;
		}

		// まず外部バスを駆動して CI を得る。
		// バスエラーなら記録だけしておく。キャッシュへは書き込むため。
		busdata bd = BusData::BusErr;
		bus.laddr = addr;
		busaddr paddr = TranslateWrite(addr);
		bus.ci = paddr.IsCInhibit();
		if (__predict_true(paddr.IsOK())) {
			uint32 outdata = data;
			for (;;) {
				bd = write_phys(paddr, outdata);
				if (__predict_false(bd.IsBusErr())) {
					putlog(3, " write_phys -> BusErr");
					break;
				}
				uint32 breqsize = paddr.GetSize();
				putlog(3, " write_phys $%08x data=$%0*x",
					paddr.Addr(), breqsize * 2, outdata);
				uint32 portsize = bd.GetSize();
				uint32 offset = paddr.Addr() & (portsize - 1);
				uint32 datasize = portsize - offset;
				if (datasize >= breqsize) {
					break;
				}
				// 同一ロングワード内なので paddr の更新だけでいい。
				paddr += datasize;
				breqsize -= datasize;
				paddr.ChangeSize(breqsize);
				// データは下詰めで、不要な上位ビットはクリアする仕様。
				outdata &= (1U << (breqsize * 8)) - 1;
			}
		}

		bool cache_writable = (cache->enable && bus.ci == false);
		if (cache_writable) {
			uint32 tag = (addr.Get() >> 4) & 0xfffffff0;
			uint32 idx = (addr.Addr() & 0x000000f0) >> 4;
			uint32 sel = (addr.Addr() & 0x0000000c) >> 2;
			auto *l = &cache->line[idx];

			// Hit Frz WA  Tag	LA
			//	1	0	-	-	- : UpdateEntry
			//	1	1	-	-	- : UpdateEntry (ex1, ex2のb8-b9
			//	0	1	-	-	- : Nop
			//	0	0	0	-	- : Nop
			//	0	0	1	1	1 : (ex3
			//	0	0	1	1	0 : Nop (ex2のb6-b7
			//	0	0	1	0	1 : (ex4
			//	0	0	1	0	0 : (ex5
			bool tag_match = (l->tag == tag);
			bool cache_hit = (tag_match && l->valid[sel]);
			if (cache_hit) {
				// エントリの更新。
				// reqsize はロングワード境界を超えないよう調整済み。
				// data は下詰め(上はクリア)なので、所定の位置まで左シフト。
				// 整列ロングワードもここに来ることに注意。
				// Fig.6-4 の ex1, ex2のb8-b9
				int lshift = (4 - reqoffset - reqsize) * 8;
				uint32 mask = (0xffffffffU >> ((4 - reqsize) * 8)) << lshift;
				l->data[sel] = (data << lshift) | (l->data[sel] & ~mask);
			} else if (cache->freeze == false && cache->wa) {
				bool long_aligned = (reqoffset == 0 && reqsize == 4);
				// Fig.6-4
				if (tag_match) {
					if (long_aligned) {
						// ex3
						l->valid[sel] = 1;
						l->data[sel] = data;
					} else {
						// ex2 の b6-b7
						// nop
					}
				} else {
					if (long_aligned) {
						// ex4
						l->tag = tag;
						l->valid_all = 0;
						l->valid[sel] = 1;
						l->data[sel] = data;
					} else {
						// ex5
						l->valid[sel] = 0;
					}
				}
			}
		}

		// バスアクセスでバスエラーが出たらキャッシュ更新後に発報。
		if (__predict_false(bd.IsBusErr())) {
			return bd;
		}

		// ロングワード境界をまたいだ余りがあればもう一回。
		if (nextsize == 0) {
			break;
		}
		putlog(3, " nextsize=%u", nextsize);
		addr += addr.GetSize();
		addr.ChangeSize(nextsize);
		data = nextdata;
	}

	return 0;
}

// mainbus の paddr から指定サイズで1回読み込む。
// ウェイトはこちらで加算して、バスエラーも含めた bd を返す。
busdata
MPU680x0Device::read_phys(const busaddr paddr)
{
	busdata bd = mainbus->Read(paddr);
	buswait += bd.GetWait();
	return bd;
}

// mainbus の paddr に指定サイズで1回書き込む。
// ウェイトはこちらで加算して、バスエラーも含めた bd を返す。
busdata
MPU680x0Device::write_phys(const busaddr paddr, uint32 data)
{
	busdata bd = mainbus->Write(paddr, data);
	buswait += bd.GetWait();
	return bd;
}

// バースト転送。
// paddr の下位4ビットは無視する。
// paddr がバースト転送可能なら dst に 16 バイト転送して true を返す。
// paddr がバースト転送不可なら dst は触らず false を返す。
// VM (バス) がバースト転送対応している場合のみ呼ぶこと。
bool
MPU680x0Device::read_phys_burst(busaddr paddr, uint32 *dst)
{
	paddr &= 0xfffffff0U;
	busdata bd = mainbus->ReadBurst16(paddr, dst);
	buswait += bd.GetWait();

	// BusErr ビットをバースト転送不可フラグとして使っている。
	return !bd.IsBusErr();
}

// 物理メモリからのロングワード読み込み。
// 現在の bus に影響を与えず指定のアドレスを読み出す。MMU 専用。
busdata
MPU680x0Device::read_phys_4(uint32 addr)
{
	m68kbus backup;
	busdata bd;

	// バス情報を一旦退避
	backup = bus;

	// 物理バスアクセス
	bus.laddr = busaddr(addr) | BusAddr::SRead | BusAddr::Size4;
	bd = read_phys(bus.laddr);
	if (__predict_false(bd.IsBusErr())) {
		return bd;
	}

	// バス情報を復元
	bus = backup;
	return bd;
}

// 物理メモリへのロングワード書き込み。
// 現在の bus に影響を与えず指定のアドレスに書き出す。MMU 専用。
busdata
MPU680x0Device::write_phys_4(uint32 addr, uint32 data)
{
	m68kbus backup;
	busdata bd;

	// バス情報を一旦退避
	backup = bus;

	// 物理バスアクセス
	bus.laddr = busaddr(addr) | BusAddr::SWrite | BusAddr::Size4;
	bd = write_phys(bus.laddr, data);
	if (__predict_false(bd.IsBusErr())) {
		return bd;
	}

	// バス情報を復元
	bus = backup;
	return bd;
}

// CACR レジスタ書き込み。
void
MPU68030Device::SetCACR(uint32 newval)
{
	newval &= M68030::CACR_MASK;
	uint32 oldval = reg.cacr;
	uint32 chgval = oldval ^ newval;

	// EI (命令キャッシュ有効)
	if ((chgval & M68030::CACR_EI) != 0) {
		if ((newval & M68030::CACR_EI) != 0) {
			if (force_disable_icache) {
				putlog(1, "I-Cache not enabled (mpu-force-disable-icache)");
				newval &= ~M68030::CACR_EI;
			} else {
				// 命令キャッシュ有効
				icache->enable = true;
				icache->hist.Clear();
				cycle_table = cycle_table_030cc;
				putlog(1, "I-Cache enabled");
			}
		} else {
			// 命令キャッシュ無効
			icache->enable = false;
			cycle_table = cycle_table_030ncc;
			putlog(1, "I-Cache disabled");
		}
	}

	// FI (命令キャッシュ凍結)
	if ((chgval & M68030::CACR_FI) != 0) {
		icache->freeze = (newval & M68030::CACR_FI);
		putlog(1, "CACR:FI <- %u", icache->freeze);
	}

	// CEI (命令キャッシュの指定エントリをクリア)
	if ((chgval & M68030::CACR_CEI) != 0) {
		// CAAR の b7-2 で示されるロングワードを無効にする。
		uint32 idx = (reg.caar & 0x000000f0) >> 4;
		uint32 sel = (reg.caar & 0x0000000c) >> 2;
		auto& l = icache->line[idx];
		l.valid[sel] = 0;

		newval &= ~M68030::CACR_CEI;
	}

	// CI (命令キャッシュクリア)
	if ((chgval & M68030::CACR_CI) != 0) {
		for (auto& l : icache->line) {
			l.valid_all = 0;
		}

		newval &= ~M68030::CACR_CI;
	}

	// IBE (命令バースト有効)
	if ((chgval & M68030::CACR_IBE) != 0) {
		icache->burst_enable =
			(newval & M68030::CACR_IBE) &&
			(icache->freeze == false) &&
			has_burst_access;
	}

	// ED (データキャッシュ有効)
	if ((chgval & M68030::CACR_ED) != 0) {
		if ((newval & M68030::CACR_ED) != 0) {
			if (force_disable_dcache) {
				putlog(1, "D-Cache not enabled (mpu-force-disable-dcache)");
				newval &= ~M68030::CACR_ED;
			} else {
				// データキャッシュ有効。
				dcache->enable = true;
				dcache->hist.Clear();
				putlog(1, "D-Cache enabled");
			}
		} else {
			// データキャッシュ無効。
			dcache->enable = false;
			putlog(1, "D-Cache disabled");
		}
	}

	// FD (データキャッシュ凍結)
	if ((chgval & M68030::CACR_FD) != 0) {
		dcache->freeze = (newval & M68030::CACR_FD);
		putlog(1, "CACR:FD <- %u", dcache->freeze);
	}

	// CED (データキャッシュの指定エントリをクリア)
	if ((chgval & M68030::CACR_CED) != 0) {
		// CAAR の b7-2 で示されるロングワードを無効にする。
		uint32 idx = (reg.caar & 0x000000f0) >> 4;
		uint32 sel = (reg.caar & 0x0000000c) >> 2;
		auto& l = dcache->line[idx];
		l.valid[sel] = 0;

		newval &= ~M68030::CACR_CED;
	}

	// CD (データキャッシュクリア)
	if ((chgval & M68030::CACR_CD) != 0) {
		for (auto& l : dcache->line) {
			l.valid_all = 0;
		}

		newval &= ~M68030::CACR_CD;
	}

	// DBE (データバースト有効)
	if ((chgval & M68030::CACR_DBE) != 0) {
		dcache->burst_enable =
			(newval & M68030::CACR_DBE) &&
			(dcache->freeze == false) &&
			has_burst_access;
	}

	// WA (ライトアロケート)
	if ((chgval & M68030::CACR_WA) != 0) {
		dcache->wa = (newval & M68030::CACR_WA);
	}

	// とりあえずレジスタイメージも保存。
	reg.cacr = newval;
}

// リセット例外の CPU 固有部分。
void
MPU68030Device::ResetCache()
{
	SetCACR(M68030::CACR_CD | M68030::CACR_CI);
	icache->hist.Clear();
	dcache->hist.Clear();
}

// キャッシュの統計情報を計算。
void
m68030Cache::CalcStat()
{
	m68030CacheStat s;

	memset(&s, 0, sizeof(s));

	// fasthit はファストパスでヒットした回数。
	uint32 total_read = stat_fasthit + stat_hit + stat_miss;
	if (__predict_true(total_read != 0)) {
		s.hit = (float)(stat_fasthit + stat_hit) * 100 / total_read;
		stat_fasthit = 0;
		stat_hit = 0;
		stat_miss = 0;
	}

	// 前回から今回までの間に disable になったのなら有効な値がある。
	if (__predict_true(enable || total_read != 0)) {
		// ここはロックする。
		std::lock_guard<std::mutex> lock(hist_mtx);
		hist.EnqueueForce(s);
	}
}

unix.superglobalmegacorp.com

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