File:  [Isaki's NoNo m68k/m88k emulator] / nono / m680x0 / m68040bus.cpp
Revision 1.1.1.2 (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) 2024 nono project
// Licensed under nono-license.txt
//

#include "mpu680x0.h"
#include "m68040mmu.h"

uint32
MPU68040Device::fetch_2()
{
	busaddr addr = busaddr(reg.pc) | fc_inst | BusAddr::Size2;
	busdata bd = read_data(addr);
	reg.pc += 2;
	return bd.Data();
}

uint32
MPU68040Device::fetch_4()
{
	busaddr addr = busaddr(reg.pc) | fc_inst | BusAddr::Size4;
	busdata bd = read_data(addr);
	reg.pc += 4;
	return bd.Data();
}

// read_1,2,4() の共通部分。ミスアライン可。
// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。
uint32
MPU68040Device::read_data(busaddr addr)
{
	busdata data = read_nocache(addr);
	if (__predict_false(data.IsBusErr())) {
		bus.laddr = addr;
		throw M68K::EXCEP_BUSERR;
	}
	return data.Data();
}

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

	bool span_page = false;
	uint32 data = 0;
	for (;;) {
		bus.laddr = addr;
		busaddr paddr = TranslateRead(addr);
		if (__predict_false(paddr.IsBusErr())) {
			bus.atcfault = true;
			// ATC フォールトが2ページ目で起きたか。
			bus.atcfault2 = span_page;
			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;
		}

		uint32 oldaddr = addr.Addr();
		addr += datasize;
		reqsize -= datasize;
		addr.ChangeSize(reqsize);

		if (__predict_false(((addr.Addr() ^ oldaddr) & ~mmu_tc->page_mask))) {
			span_page = true;
		}
	}
	return data;
}

// write_1,2,4() の共通部分。ミスアライン可。
// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。
void
MPU68040Device::write_data(busaddr addr, uint32 data)
{
	busaddr origaddr = addr;
	uint32  origdata = data;

	bool span_page = false;
	for (;;) {
		bus.laddr = addr;
		busaddr paddr = TranslateWrite(addr);
		if (__predict_false(paddr.IsBusErr())) {
			bus.atcfault = true;
			// ATC フォールトが2ページ目で起きたか。
			bus.atcfault2 = span_page;
			break;
		}
		bus.ci = paddr.IsCInhibit();
		busdata bd = write_phys(paddr, data);
		if (__predict_false(bd.IsBusErr())) {
			break;
		}
		uint32 reqsize = addr.GetSize();
		uint32 portsize = bd.GetSize();
		uint32 offset = addr.Addr() & (portsize - 1);
		uint32 datasize = portsize - offset;
		if (datasize >= reqsize) {
			// ここが成功して終了。
			return;
		}

		addr += datasize;
		reqsize -= datasize;
		addr.ChangeSize(reqsize);
		data &= (1U << (reqsize * 8)) - 1;

		if (((addr.Addr() ^ origaddr.Addr()) & ~mmu_tc->page_mask) != 0) {
			span_page = true;
		}
	}

	// 報告するのは先頭のアドレス。
	bus.laddr = origaddr;
	bus.data = origdata;
	throw M68K::EXCEP_BUSERR;
}

// CACR レジスタ書き込み。
void
MPU68040Device::SetCACR(uint32 data)
{
	// とりあえず
	reg.cacr = data & M68040::CACR_MASK;
}

// リセット例外の CPU 固有部分。
void
MPU68040Device::ResetCache()
{
	SetCACR(0);
}

unix.superglobalmegacorp.com

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