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

//
// MPU (M88xx0)
//

#pragma once

#include "mpu.h"
#include "branchhistory.h"
#include "m88100.h"
#include "m88200.h"
#include <array>

class MPU88xx0Device : public MainMPUDevice, private M88100
{
	using inherited = MainMPUDevice;
	friend class Luna88kPROMEmuDevice;

 public:
	MPU88xx0Device();
	~MPU88xx0Device() override;

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

	// キャッシュ禁止通知 (LUNA-88K では使っていない?)
	void SetCI() override { }

	// 現在実行中の命令の PC を取得
	uint32 GetPPC() const override { return reg.xip; }

	// 現在の VBR を取得
	uint32 GetVBR() const override { return reg.vbr; }

	// MPU 名を取得
	const char *GetMPUName() const override { return "MC88100"; }

	// スーパーバイザ/ユーザモードなら true
	bool IsSuper() const		{ return (reg.psr & PSR_SUPER) != 0; }
	bool IsUser() const			{ return (reg.psr & PSR_SUPER) == 0; }

	// 割り込みが許可なら true
	bool IsIntrEnable() const	{ return (reg.psr & PSR_IND) == 0; }

	// アクセスウェイト [clock] を加算。m88200 が呼ぶ。
	void AddCycle(int32 cycle) {
		// XXX 本来は今アクセスしてきている(= バスマスターの) CMMU に
		// 対して加算するべき?かもだが、まだその辺の概念がない。
		used_cycle += cycle;
	}

	// アクセスウェイト [nsec] を加算。m88200 が呼ぶ。
	void AddWait(uint64 nsec) {
		buswait += nsec;
	}

	// 割り込み発生を MPU に通知
	void Interrupt(int level) override;

	// DOS call エミュレーションのコールバックを指定する
	void SetFLineCallback(bool (*callback)(MPU88xx0Device *, void *),
		void *arg);

	// op が負数ならバスエラーが起きた
	static bool OpIsBusErr(uint64 op) {
		return (int64)op < 0;
	}

	m88100reg reg {};

	// 直近のデータアクセスアドレス。
	// XXX メモリブレークポイント用だがもう少しなんとかしたい
	uint64 lastaddr {};

 private:
	// リセット例外コールバック
	void ResetCallback(Event *);

	// 命令実行系コールバック
	void ExecNormal(Event *);
	void ExecTrace(Event *);

	// トレース設定。デバッガから呼び出される。
	void SetTrace(bool enable) override;

	uint64 fetch() {
		reg.nip = reg.fip;
		reg.opF = cmmu[0]->load_4(reg.fip);
		reg.fip += 4;
		return reg.opF;
	}

	void Prefetch();

	// PSR を newpsr に変更する。
	// CY ビットは特別にこの関数を経由しないで変更してよいことにする。
	void SetPSR(uint32 newpsr) {
		reg.psr = newpsr;
		SetPSR();
	}
	// 更新された psr に基づいて、PSR 変更によって必要な処理をする。
	// CY ビットを変更した場合は呼ばなくてよいことにする。
	void SetPSR();

	// キャリーフラグが立っていれば 1 を返す
	uint32 GetCY() const	{ return (reg.psr >> 28) & 1; }

	// キャリーフラグをセットする。
	// cy のうち最下位ビットのみ参照する (呼び出し側はマスクせず渡してよい)
	void SetCY(uint cy) {
		reg.psr &= ~PSR_C;
		reg.psr |= ((cy & 1) << 28);
	}

	// SFU1(FPU)が有効なら true
	bool IsFPUEnable() const	{ return (reg.psr & PSR_SFD1) == 0; }

	// MXM (Misaligned Access Enable) なら true
	bool IsMXM() const			{ return (reg.psr & PSR_MXM) != 0; }

	// FPxS1, FPxS2 レジスタを設定
	static void SetFPxS(uint32 *h, uint32 *l, double src, uint32 t);
	void SetFPS1(double src, uint32 t1);
	void SetFPS2(double src, uint32 t2);
	void SetFPS1(uint32 src);
	void SetFPS2(uint32 src);
	// FP Result を FPIT, FPRH, FPRL レジスタに設定
	void SetFPRx(double src);

	void ExceptionCore(uint32 primask);
	void Exception(uint32 pri);
	void OuterException(uint32 pri);
	void InternalException(uint32 vec);
	void TrapException(uint32 vec);
	uint32 MakeDMT(uint32 flag);
	void ReadDataException32(uint32 addr, uint32 flag);
	void ReadDataException64(uint32 addr, uint32 flag);
	void WriteDataException32(uint32 addr, uint32 flag);
	void WriteDataException64(uint32 addr, uint32 flag);
	void XmemDataException(uint32 addr, uint32 flag);
	void FPPreciseException(uint32 cause, double s2);
	void FPPreciseException(uint32 cause, double s1, double s2);
	void FPPreciseException(uint32 cause);
	void FPImpreciseException(uint32 cause);

	// ブランチ処理用
	void DoBranch(uint32 toaddr, bool next_exec);
	uint32 nop_counter {};	// STOP 検出用の nop 命令カウンタ

	// ロードストア命令用の下処理
	uint32 ldst_scale(uint32 size);

	// 浮動小数点数命令用の処理
	void ops_int(int mode);

#define OP_PROTO(name)	void __CONCAT(op_,name)()
#include "m88100ops.h"
OP_PROTO(illegal);
#undef OP_PROTO

	// float,double と整数値の相互変換
	static float u2f(uint32 u) {
		float v;
		memcpy(&v, &u, sizeof(v));
		return v;
	}
	static double u2d(uint64 u) {
		double v;
		memcpy(&v, &u, sizeof(v));
		return v;
	}
	static uint32 f2u(float v) {
		uint32 u;
		memcpy(&u, &v, sizeof(u));
		return u;
	}
	static uint64 d2u(double v) {
		uint64 u;
		memcpy(&u, &v, sizeof(u));
		return u;
	}

	// [0] 命令バス
	// [1] データバス
	std::array<std::unique_ptr<m88200>, 2> cmmu {};

	// ブランチ履歴 (例外履歴を含む)
	BranchHistory_m88xx0 brhist { OBJ_MPU_BRHIST };
	// 例外履歴のみ
	BranchHistory_m88xx0 exhist { OBJ_MPU_EXHIST };

	// 今のところサイクルを正確に実装するのは無理なので
	// 適当に全部積み上げてある。

	// 電源オンからの累積消費サイクル
	uint64 used_cycle {};

	// 今回のバスウェイト [nsec]
	uint64 buswait {};

	// 割り込み信号線の状態。
	bool intr_asserted {};

	// ペンディング中の例外優先度。EXCPRI_*
	uint32 excep_pending {};

	// 内部例外/トラップ例外のベクタ番号。EXCVEC_*
	uint32 excep_vector {};

	// 疑似 STOP 状態に入る機能を有効にする場合 true
	bool pseudo_stop_enable {};

	// DOS call エミュレーション
	bool (*fline_callback)(MPU88xx0Device *, void *) = NULL;
	void *fline_arg {};

	// レジスタモニター
	DECLARE_MONITOR_SCREEN(MonitorScreen);
	Monitor *monitor {};

	// イベントコールバック
	EventCallback_t exec_normal {};
};

// これを呼びたい人は mpu を持ってるはず。
inline MPU88xx0Device *GetMPU88xx0Device(Device *mpu_) {
	return dynamic_cast<MPU88xx0Device*>(mpu_);
}

unix.superglobalmegacorp.com

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