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

// 疑似 STOP について。
//	m88k には、m68k の STOP 状態に相当するものは存在せず、同じところを
//	ぐるぐる回って時間を潰す方法が取られる。実機であれば (電力が無駄な
//	こと以外は) これでも特段困らないが、エミュレータ、特に高速モードでは
//	ホストの CPU がぶん回る状況になるため、これを検出して等速モードに
//	落としたい。
//	この無限ループの命令列は OS/コンパイラによってバリエーションがあるので
//	勝手に分類してそれぞれ対応する。
//
//	  STOP#1)
//		or r0,rS1,rS2 命令のみで構成されたループの場合は、or 命令を
//		カウントして DoBranch() で検出している。
//		XXX どこで使われてたっけ
//
//	  STOP#2)
//		OpenBSD 6.6(?) 〜 7.7 では以下の命令列が使用されている。
//		  L1: tb1    #1,  r0, #0xff
//		      ld     r13, r21, #0x168
//		      bcnd.n eq0, r13, L1
//		      or     r2,  r25, #0x5ea0
//		tb1 は volatile 変数をアクセスするための同期命令として
//		gcc 3.3.6 により出力されている(*1)。スーパバイザ状態において、
//		同じ位置の tb1 命令に後方から直接 3 回ジャンプしてきたら、
//		それは volatile 変数を観測しながら変化を待っているループだと
//		思われるので、STOP と判断する。
//		これは tb1 命令内で検出している。
//		3回という数字に確たる理由はない。また念の為、4命令後方以内
//		からのブランチに限定しておく。
//		*1: https://misskey.io/notes/aervliztx3l308r2 参照
//
//	  STOP#3)
//		OpenBSD 7.8 以降は tb1 命令の代わりに fldcr r0, fpcr 命令が
//		出力されるようになったが基本構造は STOP#2 と同じ。
//		fldcr r0, fpcr 命令内で対応。
//		   L1: fldcr  r0, fpcr
//		       ld     r13, r24, #0x0204
//		       bcnd.n eq0, r13, L1
//		       or     r2,  r21, #0x7b18

#include "m88100acc.h"
#include "bitops.h"
#include <cfenv>
#include <cmath>

// 命令のフィールド取り出し
#define FLD_D	m88100opf_D(reg.opX)
#define FLD_S1	m88100opf_S1(reg.opX)
#define FLD_S2	m88100opf_S2(reg.opX)
#define FLD_CR	m88100opf_CR(reg.opX)
#define B5		m88100opf_B5(reg.opX)
#define M5		m88100opf_M5(reg.opX)
#define W5		m88100opf_W5(reg.opX)
#define O5		m88100opf_O5(reg.opX)
#define IMM16	m88100opf_IMM16(reg.opX)
#define VEC9	m88100opf_VEC9(reg.opX)
#define IsCO	((reg.opX & 0x100))
#define IsCI	((reg.opX & 0x200))

// 符号拡張・シフト済みのディスプレースメント
// XXX: (負数の右シフト)GCC only
#define D16		(((int32)(int16)reg.opX) << 2)
#define D26		(((int32)(reg.opX << 6) >> 4))

// レジスタアクセス
#define rD	(reg.r[FLD_D])
#define rS1	(reg.r[FLD_S1])
#define rS2	(reg.r[FLD_S2])
// ダブルワードの2ワード目
#define FLD_D2	((FLD_D + 1) & 0x1f)
#define rD2	(reg.r[FLD_D2])

// FP フィールド
#define FP_T1	m88100opf_FP_T1(reg.opX)
#define FP_T2	m88100opf_FP_T2(reg.opX)
#define FP_TD	m88100opf_FP_TD(reg.opX)

// XXX t(サイズ)が原因で Reserved Operand になった時の S1, S2 はどうなる?
#define FP_GET(v, t, n)	\
do {	\
	if (t == 0) {	\
		v = u2f(reg.r[n]);	\
	} else if (t == 1) {	\
		uint64 tmp = (uint64)reg.r[n] << 32;	\
		if (__predict_true(n != 31))	\
			tmp |= reg.r[n + 1];	\
		v = u2d(tmp);	\
	} else {	\
		FPPreciseException(FPECR_FROP);	\
		return;	\
	}	\
} while (0)

// n==0 は事前に弾いてある。
#define FP_SET(t, n, v)	\
do {	\
	if (t == 0) {	\
		reg.r[n] = f2u(v);	\
	} else if (t == 1) {	\
		uint64 tmp = d2u(v);	\
		reg.r[n] = tmp >> 32;	\
		if (__predict_true(n != 31))	\
			reg.r[n + 1] = (uint32)tmp;	\
	} else {	\
		FPPreciseException(FPECR_FROP);	\
		return;	\
	}	\
} while (0)

#define CHECK_FPU	do {	\
	if (IsFPUEnable() == false) {	\
		FPPreciseException(FPECR_FUNIMP);	\
		return;	\
	}	\
} while (0)

// ブランチ命令
void
MPU88xx0Device::DoBranch(uint32 toaddr, bool next_exec)
{
	// ジャンプ先アドレスの下位2bitはマスクされる
	toaddr &= ~3;
	// ブランチヒストリに登録しつつ、
	// ブランチヒストリからブランチ状況を取得
	auto e = brhist.AddEntry(reg.xip | (IsSuper() ? 1 : 0), toaddr, reg.opX);
	reg.fip = toaddr;

	// 通常のブランチで来る回数がはるかに多いと考えられるので、
	// 先にカウント数をチェック対象かどうか調べる。
	// また m68k の STOP 命令に倣ってここでも特権状態のみを対象とする。
	if (e.count < 3 && IsSuper()) {
		if (e.count == 1) {
			// 新規ブランチ
			// 初期化
			nop_counter = 0;
		} else if (pseudo_stop_enable) {
			// STOP#1 の検出。
			// ループ内がすべて NOP (or r0, rS1, rS2) だったら STOP 状態。
			// そのためループ2周目に入ったら nop_counter を初期化し、
			// ループ3周目に入ったところで命令数を数える。
			// ブランチ間の命令がすべて NOP なら STOP ということにする。

			// d はブランチ間の命令数
			uint32 d = (reg.xip - toaddr) / sizeof(uint32);
			// 遅延ブランチなら 1 命令余分に実行するはずなので 1 足す
			if (next_exec) {
				d++;
			}
			if (nop_counter == d) {
				ChangeState(CPU_STATE_STOP);
			}
		}
		// STOP ではなかったら、あとは放置していい
	}

	if (next_exec == false) {
		// 通常(即時)ブランチ出口
		// re-fetch branched instruction
		fetch();
	}
}

// MEMO: ロードストアでの usr の処理で、CMMU に対して
// usr を引数ではなくSetSuper() の呼び出しで渡すようにしている。
// データのロードストアだけがあるのなら usr を引数で渡すほうが
// 効率的だが、CMMU は 命令/データで同じものを使うため、
// 命令 CMMU では 100% 無駄な引数の引き渡しが必要になってしまう。
// そして命令アクセスのほうが何倍も比率が多い。
// .usr 指定は比率としてはレアなので、状態変更メソッドによる
// 方法にしてみた。

// ロードストアユニットの scale の処理。addr を返す。
inline uint32
MPU88xx0Device::ldst_scale(uint32 size)
{
	if ((reg.opX & (1U << 9)) != 0) {
		return rS1 + rS2 * size;
	} else {
		return rS1 + rS2;
	}
}

// ロードストアユニットの usr と scale の処理。
// 正常な場合は usr と addr を更新する。
// 例外の場合は例外を処理してこのマクロが展開してある関数から return する。
#define LDST_USR_SCALE(size) do {				\
	usr = (reg.opX & (1U << 8)) ? DM_USR : 0;	\
	if (IsUser() && usr) {						\
		Exception(EXCPRI_PRIV);					\
		return;									\
	}											\
	addr = ldst_scale(size);					\
} while (0)

// ロードストアユニットの align の処理。
// 正常な場合は addr を更新する可能性がある。
// 例外の場合は例外を処理してこのマクロが展開されてる関数から return する。
#define LDST_ALIGN(size) do {					\
	if ((addr & (size - 1)) != 0) {				\
		if (IsMXM()) {							\
			addr &= ~(size - 1);				\
		} else {								\
			InternalException(EXCVEC_MISALIGNED);	\
			return;								\
		}										\
	}											\
} while (0)

// ロードストアユニットのまとめたもの
#define LDST_USR_SCALE_ALIGN(size) do {			\
	LDST_USR_SCALE(size);						\
	LDST_ALIGN(size);							\
} while (0)

// .usr 開始。
#define ENTER_USR() do {						\
	if (__predict_false(usr))					\
		cmmu[1]->SetSuper(false);				\
} while (0)

// .usr 終了。
#define LEAVE_USR() do {						\
	if (__predict_false(usr))					\
		cmmu[1]->SetSuper(true);				\
} while (0)

// 幅規制付き算術左シフト
static inline uint32
ASL(uint32 a, int n)
{
	if (n <= 0)
		return a;
	if (n >= 32)
		return 0;
	return a << n;
}

// 幅規制付き算術右シフト
static inline uint32
ASR(uint32 a, int n)
{
	if (n <= 0)
		return a;
	if (n >= 31)
		return (int32)a < 0 ? -1 : 0;
	// XXX: 負数の右シフト
	return (int32)a >> n;
}

// 幅規制付き論理右シフト
static inline uint32
LSR(uint32 a, int n)
{
	if (n <= 0)
		return a;
	if (n >= 32)
		return 0;
	return a >> n;
}

// add でのオーバーフロー条件
static inline bool
isovf_add(uint32 s1, uint32 s2, uint32 res)
{
	return (int32)((s1 ^ res) & (s2 ^ res)) < 0;
}
// sub でのオーバーフロー条件
static inline bool
isovf_sub(uint32 s1, uint32 s2, uint32 res)
{
	return (int32)((s1 ^ s2) & (s1 ^ res)) < 0;
}

// bcnd のやつ
static inline uint
acc_cnd(uint32 r)
{
	uint rv;
	if ((r & 0x80000000) != 0) {
		if (r == 0x80000000) {
			rv = 0x8;
		} else {
			rv = 0x4;
		}
	} else {
		if (r == 0) {
			rv = 0x2;
		} else {
			rv = 0x1;
		}
	}
	return rv;
}

// bitfield
static inline uint32
acc_bf(int w, int ofs)
{
	uint32 m;
	if (w == 0) {
		m = 0xffffffffU;
	} else {
		m = ~(0xffffffffU << w);
	}
	return m << ofs;
}

// m88100 FPCR の RndMode (0..3) から <cfenv> の丸めモード値への変換。
// 大抵ここに渡す前に 3 で AND しているはずなので範囲外は考慮しない。
static int rm_to_round[4] = {
	FE_TONEAREST,
	FE_TOWARDZERO,
	FE_DOWNWARD,
	FE_UPWARD,
};


// ここから命令定義

// 000000_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(xmem_bu_imm)
{
	uint32 addr;
	busdata data;

	addr = rS1 + IMM16;
	LDST_ALIGN(1);

	AddCycle(4);	// Table.7-3

	lastaddr = addr;
	data = cmmu[1]->xmem_1(addr, rD & 0xff);
	if (__predict_false(data.IsBusErr())) {
		XmemDataException(addr, DM_BU);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = data.Data();
}

// 000001_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(xmem_w_imm)
{
	uint32 addr;
	busdata data;

	addr = rS1 + IMM16;
	LDST_ALIGN(4);

	AddCycle(4);	// Table.7-3

	lastaddr = addr;
	data = cmmu[1]->xmem_4(addr, rD);
	if (__predict_false(data.IsBusErr())) {
		XmemDataException(addr, DM_W);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = data.Data();
}

// 000010_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(ld_hu_imm)
{
	uint32 addr;
	busdata data;

	addr = rS1 + IMM16;
	LDST_ALIGN(2);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;
	data = cmmu[1]->load_2(addr);
	if (__predict_false(data.IsBusErr())) {
		ReadDataException32(addr, DM_HU);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = data.Data();
}

// 000011_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(ld_bu_imm)
{
	uint32 addr;
	busdata data;

	addr = rS1 + IMM16;
	LDST_ALIGN(1);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;
	data = cmmu[1]->load_1(addr);
	if (__predict_false(data.IsBusErr())) {
		ReadDataException32(addr, DM_BU);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = data.Data();
}

// 000100_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(ld_d_imm)
{
	uint32 addr;	// ReadDataException64() に渡すのは常に先頭アドレス。
	busdata data;

	addr = rS1 + IMM16;
	LDST_ALIGN(8);

	AddCycle(4);	// Table.7-3

	lastaddr = addr;

	data = cmmu[1]->load_4(addr);
	if (__predict_false(data.IsBusErr())) {
		ReadDataException64(addr, DM_D1);
		return;
	}
	if (__predict_true(FLD_D != 0)) {
		rD = data.Data();
	}

	data = cmmu[1]->load_4(addr + 4);
	if (__predict_false(data.IsBusErr())) {
		ReadDataException64(addr, DM_D2);
		return;
	}

	if (__predict_true(FLD_D2 != 0)) {
		rD2 = data.Data();
	}
}

// 000101_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(ld_w_imm)
{
	uint32 addr;
	busdata data;

	addr = rS1 + IMM16;
	LDST_ALIGN(4);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;
	data = cmmu[1]->load_4(addr);
	if (__predict_false(data.IsBusErr())) {
		ReadDataException32(addr, DM_W);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = data.Data();
}

// 000110_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(ld_h_imm)
{
	uint32 addr;
	busdata data;

	addr = rS1 + IMM16;
	LDST_ALIGN(2);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;
	data = cmmu[1]->load_2(addr);
	if (__predict_false(data.IsBusErr())) {
		ReadDataException32(addr, DM_H);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = (uint32)(int32)(int16)data.Data();
}

// 000111_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(ld_b_imm)
{
	uint32 addr;
	busdata data;

	addr = rS1 + IMM16;
	LDST_ALIGN(1);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;
	data = cmmu[1]->load_1(addr);
	if (__predict_false(data.IsBusErr())) {
		ReadDataException32(addr, DM_B);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = (uint32)(int32)(int8)data.Data();
}

// 001000_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(st_d_imm)
{
	uint32 addr;	// WriteDataException64() に渡すのは常に先頭アドレス。
	busdata r;

	addr = rS1 + IMM16;
	LDST_ALIGN(8);

	AddCycle(1 + 4);	// Table.7-3

	lastaddr = addr;

	r = cmmu[1]->store_4(addr, rD);
	if (__predict_false(r.IsBusErr())) {
		WriteDataException64(addr, DM_D1);
		return;
	}

	r = cmmu[1]->store_4(addr + 4, rD2);
	if (__predict_false(r.IsBusErr())) {
		WriteDataException64(addr, DM_D2);
		return;
	}
}

// 001001_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(st_w_imm)
{
	uint32 addr;
	busdata r;

	addr = rS1 + IMM16;
	LDST_ALIGN(4);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;
	r = cmmu[1]->store_4(addr, rD);
	if (__predict_false(r.IsBusErr())) {
		WriteDataException32(addr, DM_W);
		return;
	}
}

// 001010_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(st_h_imm)
{
	uint32 addr;
	busdata r;

	addr = rS1 + IMM16;
	LDST_ALIGN(2);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;
	r = cmmu[1]->store_2(addr, rD & 0xffff);
	if (__predict_false(r.IsBusErr())) {
		WriteDataException32(addr, DM_H);
		return;
	}
}

// 001011_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(st_b_imm)
{
	uint32 addr;
	busdata r;

	addr = rS1 + IMM16;
	LDST_ALIGN(1);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;
	r = cmmu[1]->store_1(addr, rD & 0xff);
	if (__predict_false(r.IsBusErr())) {
		WriteDataException32(addr, DM_B);
		return;
	}
}

// 001100_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(lda_d_imm)
{
	if (FLD_D == 0)
		return;
	rD = rS1 + IMM16;
}

// 001101_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(lda_w_imm)
{
	if (FLD_D == 0)
		return;
	rD = rS1 + IMM16;
}

// 001110_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(lda_h_imm)
{
	if (FLD_D == 0)
		return;
	rD = rS1 + IMM16;
}

// 001111_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(lda_b_imm)
{
	if (FLD_D == 0)
		return;
	rD = rS1 + IMM16;
}

// 010000_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(and_imm)
{
	if (FLD_D == 0)
		return;
	rD = rS1 & (IMM16 | 0xffff0000U);
}

// 010001_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(and_u_imm)
{
	if (FLD_D == 0)
		return;
	rD = rS1 & ((IMM16 << 16) | 0x0000ffffU);
}

// 010010_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(mask_imm)
{
	if (FLD_D == 0)
		return;
	rD = rS1 & IMM16;
}

// 010011_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(mask_u_imm)
{
	if (FLD_D == 0)
		return;
	rD = rS1 & (IMM16 << 16);
}

// 010100_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(xor_imm)
{
	if (FLD_D == 0)
		return;
	rD = rS1 ^ IMM16;
}

// 010101_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(xor_u_imm)
{
	if (FLD_D == 0)
		return;
	rD = rS1 ^ (IMM16 << 16);
}

// 010110_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(or_imm)
{
	if (FLD_D == 0)
		return;
	rD = rS1 | IMM16;
}

// 010111_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(or_u_imm)
{
	if (FLD_D == 0)
		return;
	rD = rS1 | (IMM16 << 16);
}

// 011000_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(addu_imm)
{
	if (FLD_D == 0)
		return;
	rD = rS1 + IMM16;
}

// 011001_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(subu_imm)
{
	if (FLD_D == 0)
		return;
	rD = rS1 - IMM16;
}

// 011010_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(divu_imm)
{
	CHECK_FPU;

	AddCycle(38);	// Table.7-6 (ただしざっくり)

	if (IMM16 == 0) {
		InternalException(EXCVEC_INT_DIV);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = rS1 / IMM16;
}

// 011011_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(mul_imm)
{
	CHECK_FPU;

	AddCycle(4);	// Table.7-6 (ただしざっくり)

	if (FLD_D == 0)
		return;
	rD = rS1 * IMM16;
}

// 011100_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(add_imm)
{
	uint32 imm = IMM16;
	uint32 res = rS1 + imm;
	if (isovf_add(rS1, imm, res)) {
		InternalException(EXCVEC_INT_OVF);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = res;
}

// 011101_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(sub_imm)
{
	uint32 imm = IMM16;
	uint32 res = rS1 - imm;
	if (isovf_sub(rS1, imm, res)) {
		InternalException(EXCVEC_INT_OVF);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = res;
}

// 011110_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(div_imm)
{
	CHECK_FPU;

	AddCycle(38);	// Table.7-6 (ただしざっくり)

	if ((int32)rS1 < 0 || IMM16 == 0) {
		InternalException(EXCVEC_INT_DIV);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = rS1 / IMM16;
}

// 011111_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(cmp_imm)
{
	if (FLD_D == 0)
		return;
	rD = acc_cmp(rS1, IMM16);
}

// 100000_DDDDDzzzzz_01000n_nnnnnzzzzz
OP_DEF(ldcr)
{
	if (__predict_false(IsUser())) {
		Exception(EXCPRI_PRIV);
		return;
	}
	if (FLD_CR > countof(reg.cr)) {
		// 制御レジスタ番号が範囲外の時は
		// 88100 では undocumented、
		// 88110 では Unimplemented Opcode 例外。
		Exception(EXCPRI_UNIMPL_OP);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = reg.cr[FLD_CR];
}

// 100000_DDDDDzzzzz_01001n_nnnnnzzzzz
OP_DEF(fldcr)
{
	int n = FLD_CR;

	//         	88100	    	88110(Only for reference)
	//         	Super	User	Super	User
	//         	-----	-----	-----	-----
	// FCR0		read	FPRV	read	FPRV
	// FCR1..8	read	FPRV	FUNIMP	FPRV
	// FCR9..61 read0	FPRV	FUNIMP	FPRV
	// FCR62,63 read	read	read	read

	if (n >= 62) {
		n = n - 62 + 9;
	} else {
		if (__predict_false(IsUser())) {
			FPPreciseException(FPECR_FPRV);
			return;
		}
		if (n > 8) {
			rD = 0;
			return;
		}
	}
	if (FLD_D == 0) {
		// STOP#3。OpenBSD 7.8 以降の STOP エミュレーション。
		// 4命令以内の後方から fldcr r0, fpcr (何もしない命令) への分岐が
		// 3回連続すれば STOP 状態とする。
		if (__predict_true(pseudo_stop_enable)) {
			if (__predict_true(IsSuper())) {
				const auto& e = brhist.entry[brhist.top];
				if (e.count == 3 && e.to == reg.xip && e.from - e.to < 16) {
					ChangeState(CPU_STATE_STOP);
				}
			}
		}
		return;
	}

	// XXX 内部がバグってなければ読み出す時にマスクする必要はないが、一応
	rD = reg.fcr[n] & reg.fcr_mask[n];
}

// 100000_zzzzzSSSSS_10000n_nnnnnsssss
OP_DEF(stcr)
{
	if (__predict_false(IsUser())) {
		Exception(EXCPRI_PRIV);
		return;
	}

	uint n = FLD_CR;
	if (n > countof(reg.cr)) {
		// 制御レジスタ番号が範囲外の時は
		// 88100 では undocumented、
		// 88110 では Unimplemented Opcode 例外。
		Exception(EXCPRI_UNIMPL_OP);
		return;
	}
	// PID, SXIP は Read Only。
	if (__predict_true(n != 0 && n != 4)) {
		reg.cr[n] = rS1;
	}
	if (n == 1) {
		SetPSR();
	}
}

// 100000_zzzzzSSSSS_10001n_nnnnnsssss
OP_DEF(fstcr)
{
	int n = FLD_CR;

	//         	88100	    	88110(Only for reference)
	//         	Super	User	Super	User
	//         	-----	-----	-----	-----
	// FCR0		write	FPRV	write	FPRV
	// FCR1..8	noop	FPRV	FUNIMP	FPRV
	// FCR9..61 noop	FPRV	FUNIMP	FPRV
	// FCR62,63 write	write	write	write

	if (n >= 62) {
		n = n - 62 + 9;
	} else {
		if (__predict_false(IsUser())) {
			FPPreciseException(FPECR_FPRV);
			return;
		}
		// FCR1..8(読み込み専用レジスタ)、9..61(未実装レジスタ) ともに noop
		if (n > 0) {
			return;
		}
	}

	reg.fcr[n] = rS1 & reg.fcr_mask[n];

	// FPCR が変更されたら、ホストの丸めモードも更新
	if (n == 10) {
		uint32 rm = (reg.fpcr >> 14) & 3;
		std::fesetround(rm_to_round[rm]);
	}
}

// 100000_DDDDDSSSSS_11000n_nnnnnsssss
OP_DEF(xcr)
{
	uint32 tmp;

	if (__predict_false(IsUser())) {
		Exception(EXCPRI_PRIV);
		return;
	}

	uint n = FLD_CR;
	if (n > countof(reg.cr)) {
		// 制御レジスタ番号が範囲外の時は
		// 88100 では undocumented、
		// 88110 では Unimplemented Opcode 例外。
		Exception(EXCPRI_UNIMPL_OP);
		return;
	}
	tmp = rS1;
	rD = reg.cr[n];
	// PID, SXIP は Read Only。
	if (__predict_true(n != 0 && n != 4)) {
		reg.cr[n] = tmp;
	}
	reg.r[0] = 0;
	if (n == 1) {
		SetPSR();
	}
}

// 100000_DDDDDSSSSS_11001n_nnnnnsssss
OP_DEF(fxcr)
{
	uint n = FLD_CR;

	//         	88100	    	88110(Only for reference)
	//         	Super	User	Super	User
	//         	-----	-----	-----	-----
	// FCR0		xchg	FPRV	xchg	FPRV
	// FCR1..8	read	FPRV	FUNIMP	FPRV
	// FCR9..61 read0	FPRV	FUNIMP	FPRV
	// FCR62,63 xchg	xchg	xchg	xchg

	if (__predict_false(IsUser()) && n < 62) {
		FPPreciseException(FPECR_FPRV);
		return;
	}

	if (n == 0) {
		goto exchange;
	} else if (__predict_false(n < 9)) {
		// FCR1..8 は読み込み専用レジスタなので、読み出す側のみ
		rD = reg.fcr[n] & reg.fcr_mask[n];
		reg.r[0] = 0;
		return;
	} else if (__predict_false(n < 62)) {
		// FCR9..61 は未実装レジスタなので、0 が読み出せるのみ
		rD = 0;
		return;
	} else {
		n = n - 62 + 9;
	}

 exchange:
	uint32 tmp = rS1;
	rD = reg.fcr[n] & reg.fcr_mask[n];
	reg.fcr[n] = tmp & reg.fcr_mask[n];

	reg.r[0] = 0;
}

// 100001_DDDDDSSSSS_00000n_nnnnnsssss
OP_DEF(fmul)
{
	double s1;
	double s2;

	CHECK_FPU;
	if (FLD_D == 0) {
		FPPreciseException(FPECR_FUNIMP);
		return;
	}

	FP_GET(s1, FP_T1, FLD_S1);
	FP_GET(s2, FP_T2, FLD_S2);
	FP_SET(FP_TD, FLD_D, s1 * s2);
}

// 100001_DDDDD00000_001000_000nnsssss
OP_DEF(flt)
{
	CHECK_FPU;
	if (FLD_D == 0) {
		FPPreciseException(FPECR_FUNIMP);
		return;
	}

	FP_SET(FP_TD, FLD_D, (double)(int32)rS2);
}


// 100001_DDDDDSSSSS_00101n_nnnnnsssss
OP_DEF(fadd)
{
	double s1;
	double s2;

	CHECK_FPU;
	if (FLD_D == 0) {
		FPPreciseException(FPECR_FUNIMP);
		return;
	}

	FP_GET(s1, FP_T1, FLD_S1);
	FP_GET(s2, FP_T2, FLD_S2);
	FP_SET(FP_TD, FLD_D, s1 + s2);
}

// 100001_DDDDDSSSSS_00110n_nnnnnsssss
OP_DEF(fsub)
{
	double s1;
	double s2;

	CHECK_FPU;
	if (FLD_D == 0) {
		FPPreciseException(FPECR_FUNIMP);
		return;
	}

	FP_GET(s1, FP_T1, FLD_S1);
	FP_GET(s2, FP_T2, FLD_S2);
	FP_SET(FP_TD, FLD_D, s1 - s2);
}

// 100001_DDDDDSSSSS_00111n_nnnnnsssss
OP_DEF(fcmp)
{
	double s1;
	double s2;

	CHECK_FPU;
	if (FLD_D == 0) {
		FPPreciseException(FPECR_FUNIMP);
		return;
	}

	FP_GET(s1, FP_T1, FLD_S1);
	FP_GET(s2, FP_T2, FLD_S2);

	// XXX テストが出来てないので Inf と非正規化数は未対応
	// 非正規化数もここのはず
	if (std::isnan(s1) || std::isnan(s2) ||
	    std::isinf(s1) || std::isinf(s2)   )
	{
		FPPreciseException(FPECR_FROP, s1, s2);
		return;
	}

	//
	//  31 .. 12  11   10    9    8    7    6    5    4    3    2    1    0
	// +---- ---+----+----+----+----+----+----+----+----+----+----+----+----+
	// |    0   | ob | in | ib | ou | ge | lt | le | gt | ne | eq | cp | nc |
	// +---- ---+----+----+----+----+----+----+----+----+----+----+----+----+

	uint32 res = 0;
	if (s2 >= 0) {
		if (s1 <= 0 || s1 >= s2) res |= 0x0800;	// ob
		if (0 <  s1 && s1 <  s2) res |= 0x0400;	// in
		if (0 <= s1 && s1 <= s2) res |= 0x0200;	// ib
		if (s1 < 0  || s1 >  s2) res |= 0x0100;	// ou
	}
	if (s1 >= s2) res |= 0x0080;		// ge
	if (s1 <  s2) res |= 0x0040;		// lt
	if (s1 <= s2) res |= 0x0020;		// le
	if (s1 >  s2) res |= 0x0010;		// gt
	if (s1 != s2) res |= 0x0008;		// ne
	if (s1 == s2) res |= 0x0004;		// eq
	// cp; if and only if the two operands are comparable
	if (!std::isnan(s1) && !std::isnan(s2)) {
		res |= 0x0002;
	}
	// nc; if, and only if, the two operands are not comparable
	if (std::isnan(s1) || std::isnan(s2)) {
		res |= 0x0001;
	}

	rD = res;
}

// int, nint, trnc 命令の共通部
void
MPU88xx0Device::ops_int(int rndmode)
{
	double src;
	double res;
	int exponent;

	CHECK_FPU;
	if (FLD_D == 0) {
		FPPreciseException(FPECR_FUNIMP);
		return;
	}

	if (FP_T2 == 0) {
		float f;
		FP_GET(f, FP_T2, FLD_S2);
		src = f;
	} else {
		FP_GET(src, FP_T2, FLD_S2);
	}

	int saved_rndmode = std::fegetround();

	std::fesetround(rndmode);
	res = std::nearbyint(src);
	std::fesetround(saved_rndmode);

	exponent = 0;
	std::frexp(res, &exponent);
	if (std::isinf(res) || std::isnan(res) || exponent >= 30) {
		// 整数で表現出来ない(かも知れない)ので例外
		FPPreciseException(FPECR_FIOV, src);
		return;
	}
	rD = (uint32)(int32)res;
}

// 100001_DDDDD00000_010010_0nn00sssss
OP_DEF(int)
{
	// 現在のモードのまま (ちょっと無駄だけど…)
	ops_int(std::fegetround());
}

// 100001_DDDDD00000_010100_0nn00sssss
OP_DEF(nint)
{
	ops_int(FE_TONEAREST);
}

// 100001_DDDDD00000_010110_0nn00sssss
OP_DEF(trnc)
{
	ops_int(FE_TOWARDZERO);
}

// 100001_DDDDDSSSSS_01110n_nnnnnsssss
OP_DEF(fdiv)
{
	double s1;
	double s2;

	CHECK_FPU;
	if (FLD_D == 0) {
		FPPreciseException(FPECR_FUNIMP);
		return;
	}

	FP_GET(s1, FP_T1, FLD_S1);
	FP_GET(s2, FP_T2, FLD_S2);
	FP_SET(FP_TD, FLD_D, s1 / s2);
}

// 110000_nnnnnnnnnn_nnnnnn_nnnnnnnnnn
OP_DEF(br)
{
	AddCycle(1);	// Table.7-5
	DoBranch(reg.xip + D26, false);
}

// 110001_nnnnnnnnnn_nnnnnn_nnnnnnnnnn
OP_DEF(br_n)
{
	AddCycle(0);	// Table.7-5
	DoBranch(reg.xip + D26, true);
}

// 110010_nnnnnnnnnn_nnnnnn_nnnnnnnnnn
OP_DEF(bsr)
{
	AddCycle(1);	// Table.7-5
	reg.r[1] = reg.nip;
	DoBranch(reg.xip + D26, false);
}

// 110011_nnnnnnnnnn_nnnnnn_nnnnnnnnnn
OP_DEF(bsr_n)
{
	AddCycle(0);	// Table.7-5
	reg.r[1] = reg.nip + 4;
	DoBranch(reg.xip + D26, true);
}

// 110100_nnnnnSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(bb0)
{
	if ((rS1 & (1U << B5)) == 0) {
		AddCycle(1);	// Table.7-5
		DoBranch(reg.xip + D16, false);
	}
}

// 110101_nnnnnSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(bb0_n)
{
	if ((rS1 & (1U << B5)) == 0) {
		DoBranch(reg.xip + D16, true);
	}
}

// 110110_nnnnnSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(bb1)
{
	if ((rS1 & (1U << B5)) != 0) {
		AddCycle(1);	// Table.7-5
		DoBranch(reg.xip + D16, false);
	}
}

// 110111_nnnnnSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(bb1_n)
{
	if ((rS1 & (1U << B5)) != 0) {
		DoBranch(reg.xip + D16, true);
	}
}

// 111010_MMMMMSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(bcnd)
{
	if ((acc_cnd(rS1) & M5) != 0) {
		AddCycle(1);	// Table.7-5
		DoBranch(reg.xip + D16, false);
	}
}

// 111011_MMMMMSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(bcnd_n)
{
	if ((acc_cnd(rS1) & M5) != 0) {
		DoBranch(reg.xip + D16, true);
	}
}

// 111100_DDDDDSSSSS_100000_wwwwwooooo
OP_DEF(clr_1)
{
	if (FLD_D == 0)
		return;
	rD = rS1 & ~acc_bf(W5, O5);
}

// 111100_DDDDDSSSSS_100010_wwwwwooooo
OP_DEF(set_1)
{
	if (FLD_D == 0)
		return;
	rD = rS1 | acc_bf(W5, O5);
}

// 111100_DDDDDSSSSS_100100_wwwwwooooo
OP_DEF(ext_1)
{
	int w, o, left;
	uint32 res;

	if (FLD_D == 0)
		return;
	w = W5;
	if (w == 0)
		w = 32;
	o = O5;
	left = 32 - w - o;
	res = ASL(rS1, left);
	res = ASR(res, left);
	rD = ASR(res, o);
}

// 111100_DDDDDSSSSS_100110_wwwwwooooo
OP_DEF(extu_1)
{
	int w, o, left;
	uint32 res;

	if (FLD_D == 0)
		return;
	w = W5;
	if (w == 0)
		w = 32;
	o = O5;
	left = 32 - w - o;
	res = ASL(rS1, left);
	res = LSR(res, left);
	rD = res >> o;
}

// 111100_DDDDDSSSSS_101000_wwwwwooooo
OP_DEF(mak_1)
{
	if (FLD_D == 0)
		return;
	rD = (rS1 << O5) & acc_bf(W5, O5);
}

// 111100_DDDDDSSSSS_101010_zzzzzooooo
OP_DEF(rot_1)
{
	if (FLD_D == 0)
		return;
	rD = ROR32(rS1, O5);
}

// 111100_bbbbbSSSSS_110100_0vvvvvvvvv
OP_DEF(tb0)
{
	if (IsUser() && VEC9 < 128) {
		Exception(EXCPRI_PRIV);
		return;
	}
	if ((rS1 & (1U << B5)) == 0) {
		TrapException(VEC9);
	}
}

// 111100_bbbbbSSSSS_110110_0vvvvvvvvv
OP_DEF(tb1)
{
	if (IsUser() && VEC9 < 128) {
		Exception(EXCPRI_PRIV);
		return;
	}
	if ((rS1 & (1U << B5)) != 0) {
		TrapException(VEC9);
	} else if (pseudo_stop_enable) {
		// STOP#2。OpenBSD 6.6 〜 7.7 の STOP エミュレーション。
		// 4命令以内の後方から tb1 false, 0xff 命令への分岐が 3回連続すれば
		// STOP 状態とする。
		if (IsSuper() && VEC9 == 0xff) {
			const auto& e = brhist.entry[brhist.top];
			if (e.count == 3
			 && e.to == reg.xip
			 && e.from - e.to < 16) {
				ChangeState(CPU_STATE_STOP);
			}
		}
	}
}

// 111100_MMMMMSSSSS_111010_0vvvvvvvvv
OP_DEF(tcnd)
{
	if (IsUser() && VEC9 < 128) {
		Exception(EXCPRI_PRIV);
		return;
	}
	if ((acc_cnd(rS1) & M5) != 0) {
		TrapException(VEC9);
	}
}

// 111101_DDDDDSSSSS_000000_xU000sssss
OP_DEF(xmem_bu)
{
	busdata data;
	uint32 addr;
	uint32 usr;

	LDST_USR_SCALE_ALIGN(1);

	AddCycle(4);	// Table.7-3

	lastaddr = addr;

	// 例外安全のために都度 S/U ビットを制御
	ENTER_USR();
	data = cmmu[1]->xmem_1(addr, rD & 0xff);
	LEAVE_USR();

	if (__predict_false(data.IsBusErr())) {
		XmemDataException(addr, DM_BU | usr);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = data.Data();
}

// 111101_DDDDDSSSSS_000001_xU000sssss
OP_DEF(xmem_w)
{
	busdata data;
	uint32 addr;
	uint32 usr;

	LDST_USR_SCALE_ALIGN(4);

	AddCycle(4);	// Table.7-3

	lastaddr = addr;

	// 例外安全のために都度 S/U ビットを制御
	ENTER_USR();
	data = cmmu[1]->xmem_4(addr, rD);
	LEAVE_USR();

	if (__predict_false(data.IsBusErr())) {
		XmemDataException(addr, DM_W | usr);
		return;
	}

	if (FLD_D == 0)
		return;
	rD = data.Data();
}

// 111101_DDDDDSSSSS_000010_xU000sssss
OP_DEF(ld_hu)
{
	busdata data;
	uint32 addr;
	uint32 usr;

	LDST_USR_SCALE_ALIGN(2);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;

	ENTER_USR();
	data = cmmu[1]->load_2(addr);
	LEAVE_USR();

	if (__predict_false(data.IsBusErr())) {
		ReadDataException32(addr, DM_HU | usr);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = data.Data();
}

// 111101_DDDDDSSSSS_000011_xU000sssss
OP_DEF(ld_bu)
{
	uint32 addr;
	busdata data;
	uint32 usr;

	LDST_USR_SCALE_ALIGN(1);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;

	ENTER_USR();
	data = cmmu[1]->load_1(addr);
	LEAVE_USR();

	if (__predict_false(data.IsBusErr())) {
		ReadDataException32(addr, DM_BU | usr);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = data.Data();
}

// 111101_DDDDDSSSSS_000100_xU000sssss
OP_DEF(ld_d)
{
	uint32 addr;	// ReadDataException64() に渡すのは常に先頭アドレス。
	busdata data;
	uint32 usr;

	LDST_USR_SCALE_ALIGN(8);

	AddCycle(4);	// Table.7-3

	lastaddr = addr;

	// 例外安全のために都度 S/U ビットを制御
	ENTER_USR();
	data = cmmu[1]->load_4(addr);
	LEAVE_USR();

	if (__predict_false(data.IsBusErr())) {
		ReadDataException64(addr, DM_D1 | usr);
		return;
	}
	if (__predict_true(FLD_D != 0)) {
		rD = data.Data();
	}

	ENTER_USR();
	data = cmmu[1]->load_4(addr + 4);
	LEAVE_USR();

	if (__predict_false(data.IsBusErr())) {
		ReadDataException64(addr, DM_D2 | usr);
		return;
	}
	if (__predict_true(FLD_D2 != 0)) {
		rD2 = data.Data();
	}
}

// 111101_DDDDDSSSSS_000101_xU000sssss
OP_DEF(ld_w)
{
	uint32 addr;
	busdata data;
	uint32 usr;

	LDST_USR_SCALE_ALIGN(4);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;

	ENTER_USR();
	data = cmmu[1]->load_4(addr);
	LEAVE_USR();

	if ((int64)data < 0) {
		ReadDataException32(addr, DM_W | usr);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = data;
}

// 111101_DDDDDSSSSS_000110_xU000sssss
OP_DEF(ld_h)
{
	uint32 addr;
	busdata data;
	uint32 usr;

	LDST_USR_SCALE_ALIGN(2);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;

	ENTER_USR();
	data = cmmu[1]->load_2(addr);
	LEAVE_USR();

	if (__predict_false(data.IsBusErr())) {
		ReadDataException32(addr, DM_H | usr);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = (uint32)(int32)(int16)data.Data();
}

// 111101_DDDDDSSSSS_000111_xU000sssss
OP_DEF(ld_b)
{
	uint32 addr;
	busdata data;
	uint32 usr;

	LDST_USR_SCALE_ALIGN(1);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;

	ENTER_USR();
	data = cmmu[1]->load_1(addr);
	LEAVE_USR();

	if (__predict_false(data.IsBusErr())) {
		ReadDataException32(addr, DM_B | usr);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = (uint32)(int32)(int8)data.Data();
}

// 111101_DDDDDSSSSS_001000_xU000sssss
OP_DEF(st_d)
{
	uint32 addr;	// WriteDataException64() に渡すのは常に先頭アドレス。
	busdata r;
	uint32 usr;

	LDST_USR_SCALE_ALIGN(8);

	AddCycle(1 + 4);	// Table.7-3

	lastaddr = addr;

	ENTER_USR();
	r = cmmu[1]->store_4(addr, rD);
	LEAVE_USR();

	if (__predict_false(r.IsBusErr())) {
		WriteDataException64(addr, DM_D1 | usr);
		return;
	}

	ENTER_USR();
	r = cmmu[1]->store_4(addr + 4, rD2);
	LEAVE_USR();

	if (__predict_false(r.IsBusErr())) {
		WriteDataException64(addr, DM_D2 | usr);
		return;
	}
}

// 111101_DDDDDSSSSS_001001_xU000sssss
OP_DEF(st_w)
{
	uint32 addr;
	busdata r;
	uint32 usr;

	LDST_USR_SCALE_ALIGN(4);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;

	ENTER_USR();
	r = cmmu[1]->store_4(addr, rD);
	LEAVE_USR();

	if (__predict_false(r.IsBusErr())) {
		WriteDataException32(addr, DM_W | usr);
		return;
	}
}

// 111101_DDDDDSSSSS_001010_xU000sssss
OP_DEF(st_h)
{
	uint32 addr;
	busdata r;
	uint32 usr;

	LDST_USR_SCALE_ALIGN(2);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;

	ENTER_USR();
	r = cmmu[1]->store_2(addr, rD & 0xffff);
	LEAVE_USR();

	if (__predict_false(r.IsBusErr())) {
		WriteDataException32(addr, DM_H | usr);
		return;
	}
}

// 111101_DDDDDSSSSS_001011_xU000sssss
OP_DEF(st_b)
{
	uint32 addr;
	busdata r;
	uint32 usr;

	LDST_USR_SCALE_ALIGN(1);

	AddCycle(3);	// Table.7-3

	lastaddr = addr;

	ENTER_USR();
	r = cmmu[1]->store_1(addr, rD & 0xff);
	LEAVE_USR();

	if (__predict_false(r.IsBusErr())) {
		WriteDataException32(addr, DM_B | usr);
		return;
	}
}

// 111101_DDDDDSSSSS_001100_xU000sssss
OP_DEF(lda_d)
{
	uint32 addr;
	uint32 usr;

	LDST_USR_SCALE(8);

	if (FLD_D == 0)
		return;
	rD = addr;
}

// 111101_DDDDDSSSSS_001101_xU000sssss
OP_DEF(lda_w)
{
	uint32 addr;
	uint32 usr;

	LDST_USR_SCALE(4);

	if (FLD_D == 0)
		return;
	rD = addr;
}

// 111101_DDDDDSSSSS_001110_xU000sssss
OP_DEF(lda_h)
{
	uint32 addr;
	uint32 usr;

	LDST_USR_SCALE(2);

	if (FLD_D == 0)
		return;
	rD = addr;
}

// 111101_DDDDDSSSSS_001111_xU000sssss
OP_DEF(lda_b)
{
	uint32 addr;
	uint32 usr;

	LDST_USR_SCALE(1);

	if (FLD_D == 0)
		return;
	rD = addr;
}

// 111101_DDDDDSSSSS_010000_00000sssss
OP_DEF(and)
{
	if (FLD_D == 0)
		return;
	rD = rS1 & rS2;
}

// 111101_DDDDDSSSSS_010001_00000sssss
OP_DEF(and_c)
{
	if (FLD_D == 0)
		return;
	rD = rS1 & ~rS2;
}

// 111101_DDDDDSSSSS_010100_00000sssss
OP_DEF(xor)
{
	if (FLD_D == 0)
		return;
	rD = rS1 ^ rS2;
}

// 111101_DDDDDSSSSS_010101_00000sssss
OP_DEF(xor_c)
{
	if (FLD_D == 0)
		return;
	rD = rS1 ^ ~rS2;
}

// 111101_DDDDDSSSSS_010110_00000sssss
OP_DEF(or)
{
	if (FLD_D == 0)	{
		nop_counter++;
		return;
	}
	rD = rS1 | rS2;
}

// 111101_DDDDDSSSSS_010111_00000sssss
OP_DEF(or_c)
{
	if (FLD_D == 0)
		return;
	rD = rS1 | ~rS2;
}

// 111101_DDDDDSSSSS_011000_nn000sssss
OP_DEF(addu)
{
	uint64 res = (uint64)rS1 + rS2;
	if (IsCI) {
		res += GetCY();
	}
	if (IsCO) {
		SetCY(res >> 32);
	}
	if (FLD_D == 0)
		return;
	rD = res;
}

// 111101_DDDDDSSSSS_011001_nn000sssss
OP_DEF(subu)
{
	// sub* 命令の CY ビットはボローが発生*していない*時セット。

	// 1 の補数をとって足す。
	uint64 res = (uint64)rS1 + (~rS2);
	if (IsCI) {
		res += GetCY();
	} else {
		res += 1;
	}
	if (IsCO) {
		SetCY(res >> 32);
	}
	if (FLD_D == 0)
		return;
	rD = res;
}

// 111101_DDDDDSSSSS_011010_zz000sssss
OP_DEF(divu)
{
	CHECK_FPU;

	AddCycle(38);	// Table.7-6 (ただしざっくり)

	if (rS2 == 0) {
		InternalException(EXCVEC_INT_DIV);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = rS1 / rS2;
}

// 111101_DDDDDSSSSS_011011_zz000sssss
OP_DEF(mul)
{
	CHECK_FPU;

	AddCycle(4);	// Table.7-6 (ただしざっくり)

	if (FLD_D == 0)
		return;
	rD = rS1 * rS2;
}

// 111101_DDDDDSSSSS_011100_nn000sssss
OP_DEF(add)
{
	uint64 res = (uint64)rS1 + rS2;
	if (IsCI) {
		res += GetCY();
	}
	if (isovf_add(rS1, rS2, res)) {
		InternalException(EXCVEC_INT_OVF);
		return;
	}

	// rD, Cy の更新は例外が起きなかった時だけ (p.6-19)
	if (IsCO) {
		SetCY(res >> 32);
	}
	if (FLD_D == 0)
		return;
	rD = res;
}

// 111101_DDDDDSSSSS_011101_nn000sssss
OP_DEF(sub)
{
	// sub* 命令の CY ビットはボローが発生*していない*時セット。

	// 1 の補数をとって足す。
	uint64 res = (uint64)rS1 + (~rS2);
	if (IsCI) {
		res += GetCY();
	} else {
		res += 1;
	}
	if (isovf_sub(rS1, rS2, res)) {
		InternalException(EXCVEC_INT_OVF);
		return;
	}

	// rD, Cy の更新は例外が起きなかった時だけ (p.6-19)
	if (IsCO) {
		SetCY(res >> 32);
	}
	if (FLD_D == 0)
		return;
	rD = res;
}

// 111101_DDDDDSSSSS_011110_zz000sssss
OP_DEF(div)
{
	CHECK_FPU;

	AddCycle(38);	// Table.7-6 (ただしざっくり)

	if ((int32)rS1 < 0 || (int32)rS2 <= 0) {
		InternalException(EXCVEC_INT_DIV);
		return;
	}
	if (FLD_D == 0)
		return;
	rD = rS1 / rS2;
}

// 111101_DDDDDSSSSS_011111_zz000sssss
OP_DEF(cmp)
{
	if (FLD_D == 0)
		return;
	rD = acc_cmp(rS1, rS2);
}

// 111101_DDDDDSSSSS_100000_00000sssss
OP_DEF(clr_2)
{
	if (FLD_D == 0)
		return;
	rD = rS1 & ~acc_bf((rS2 >> 5) & 0x1f, rS2 & 0x1f);
}

// 111101_DDDDDSSSSS_100010_00000sssss
OP_DEF(set_2)
{
	if (FLD_D == 0)
		return;
	rD = rS1 | acc_bf((rS2 >> 5) & 0x1f, rS2 & 0x1f);
}

// 111101_DDDDDSSSSS_100100_00000sssss
OP_DEF(ext_2)
{
	int w, o, left;
	uint32 res;

	if (FLD_D == 0)
		return;
	w = (rS2 >> 5) & 0x1f;
	if (w == 0)
		w = 32;
	o = (rS2     ) & 0x1f;
	left = 32 - w - o;
	res = ASL(rS1, left);
	res = ASR(res, left);
	rD = ASR(res, o);
}

// 111101_DDDDDSSSSS_100110_00000sssss
OP_DEF(extu_2)
{
	int w, o, left;
	uint32 res;

	if (FLD_D == 0)
		return;
	w = (rS2 >> 5) & 0x1f;
	if (w == 0)
		w = 32;
	o = (rS2     ) & 0x1f;
	left = 32 - w - o;
	res = ASL(rS1, left);
	res = LSR(res, left);
	rD = res >> o;
}

// 111101_DDDDDSSSSS_101000_00000sssss
OP_DEF(mak_2)
{
	if (FLD_D == 0)
		return;
	int w = (rS2 >> 5) & 0x1f;
	int o = (rS2     ) & 0x1f;
	rD = (rS1 << o) & acc_bf(w, o);
}

// 111101_DDDDDSSSSS_101010_00000sssss
OP_DEF(rot_2)
{
	if (FLD_D == 0)
		return;
	int o = (rS2    ) & 0x1f;
	rD = ROR32(rS1, o);
}

// 111101_zzzzzzzzzz_110000_00000sssss
OP_DEF(jmp)
{
	AddCycle(1);	// Table.7-5
	DoBranch(rS2, false);
}

// 111101_zzzzzzzzzz_110001_00000sssss
OP_DEF(jmp_n)
{
	AddCycle(0);	// Table.7-5
	DoBranch(rS2, true);
}

// 111101_zzzzzzzzzz_110010_00000sssss
OP_DEF(jsr)
{
	AddCycle(1);	// Table.7-5
	uint32 toaddr = rS2;
	reg.r[1] = reg.nip;
	DoBranch(toaddr, false);
}

// 111101_zzzzzzzzzz_110011_00000sssss
OP_DEF(jsr_n)
{
	AddCycle(0);	// Table.7-5
	uint32 toaddr = rS2;
	reg.r[1] = reg.nip + 4;
	DoBranch(toaddr, true);
}

// 111101_DDDDDzzzzz_111010_00000sssss
OP_DEF(ff1)
{
	int i;
	uint32 m = 0x80000000U;

	if (FLD_D == 0)
		return;

	if (rS2 == 0) {
		rD = 32;
	} else {
		for (i = 31; i >= 0; i--, m >>= 1) {
			if ((rS2 & m) != 0) {
				break;
			}
		}
		rD = i;
	}
}

// 111101_DDDDDzzzzz_111011_00000sssss
OP_DEF(ff0)
{
	int i;
	uint32 m = 0x80000000U;

	if (FLD_D == 0)
		return;

	if (rS2 == 0xffffffffU) {
		rD = 32;
	} else {
		for (i = 31; i >= 0; i--, m >>= 1) {
			if ((rS2 & m) == 0) {
				break;
			}
		}
		rD = i;
	}
}

// 111101_zzzzzzzzzz_111111_0000000000
OP_DEF(rte)
{
	if (__predict_false(IsUser())) {
		Exception(EXCPRI_PRIV);
		return;
	}

	AddCycle(2);	// Table.7-5

	SetPSR(reg.epsr);

	// 本当は有効な SxIP から順次パイプラインを埋めるだけで、
	// こんな条件分けになるわけではないのだが、分岐履歴とかのため。

	if (__predict_false((reg.sfip & SIP_V) == 0)) {
		// どうなる?
		putlog(0, "rte: SFIP.V==0 (NOT IMPLEMENTED)");
	}

	if ((reg.snip & SIP_V)) {
		DoBranch(reg.snip & SIP_MASK, false);
		reg.fip = reg.sfip & SIP_MASK;
	} else {
		// PROM 1.20 の DAE ハンドラが snip の VALID を落として rte を呼ぶ。
		DoBranch(reg.sfip & SIP_MASK, false);
	}
}

// 111101_zzzzzSSSSS_111110_00000SSSSS
OP_DEF(tbnd_1)
{
	if (rS1 > rS2) {
		InternalException(EXCVEC_BOUNDS);
	}
}

// 111110_zzzzzSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(tbnd_2)
{
	if (rS1 > IMM16) {
		InternalException(EXCVEC_BOUNDS);
	}
}

// 111111_DDDDDSSSSS_000001_nnnnnnnnnn
OP_DEF(doscall)
{
	// Human68k の DOS call エミュレーションのような何か。
	// 独自ニーモニックでは doscall rD, rS1, #nn と表記することにする。
	//
	// Human68k の DOS コール #nn 番を実行し、戻り値を rD に書き込む。
	// 戻り値のない DOS コールでは rD に 0 を指定すること。
	// 引数は rS1 以降に一つずつ置く。m68k でのワードやバイト値の場合は
	// 下位詰めするだけで上位ビットのクリア等は不要。
	// 処理するこちら側が必要なビット幅だけを取り出すこと。
	//
	// また、ここでのコンソールは VM 内のコンソールではなくホストの標準入出力。
	//
	// コールバックがセットされていて特権状態の時のみ発動する。
	// それ以外では現物同様不当命令扱い。

	if (fline_callback && IsSuper()) {
		if (fline_callback(this, fline_arg)) {
			return;
		}
	}

	OP_FUNC(illegal);
}

unix.superglobalmegacorp.com

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