|
|
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);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.