|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
// +------ 030(without FPU)
// | +-- 030(+6888x)
// | | 040 LC040
// S o o 4 : FSAVE/FRESTORE (有効なEA)
// F F F : FSAVE/FRESTORE (無効なEA)
// F o o 4 : 040 でも実装してある命令 (有効なEA)
// F F F : 040 でも実装してある命令 (無効なEA)
// F o 2 4 : 6888x 命令 (有効なEA)
// F F F? : 6888x 命令 (無効なEA)
// F F F F : 無効な命令ビットパターン
//
// (厳密に言えばFPU命令ではないが)
// S S F? F? : cpSAVE/cpRESTORE (有効なEA)
// F F F? F? : cpSAVE/cpRESTORE (無効なEA)
// … XEiJ の flineprivilege.x によると、無効な EA のパターンは
// 68030 では F ライン例外になる。
// 040 ではコプロセッサプロトコル自体が廃止されているので
// ここはいずれも F ライン例外になるだろうか。
//
// o: 有効な命令処理
// F: F ライン例外 (Format$0)
// 2: 浮動小数点未実装例外(Format$2)
// 4: 浮動小数点未実装例外(Format$4)
// S: 特権違反例外
// 68040:
// Int .S .D .X .P
// ノーマル数 o o o o x
// Zero o o o o x
// Inf - o o o x
// NAN - o o o x
// 非正規化数 - x x x x
// アンノーマル数 - - - x x
//
// o: ハードウェアサポート
// x: ソフトウェアサポート
// 68040 の BUSY フレーム:
// MC68040UM.pdf (p.9-40)、fpsp/fpsp.h より。
//
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +---------------+---------------+---------------+---------------+
//$00 | VERSION=$41 | $60 | |
// +---------------+---------------+---------------+---------------+
//$04 | |
// +---------------+---------------+---------------+---------------+
//$08(LV-92)| CU_SAVEPC : |(FPR_DIRTY_BITS) |
// +---------------+---------------+---------------+---------------+
//$0c(LV-88)| |
//$10(LV-84)| |
//$14(LV-80)| |
// +---------------+---------------+---------------+---------------+
//$18(LV-76)| |<- WBTS WBTE[14:0] | (WBTEMP_SGN) | |
// +---------------+---------------+---------------+---------------+
// (WBTEMP_SGN) のバイトは fpsp がワークとして使っているようだ。
// +---------------+---------------+---------------+---------------+
//$1c(LV-72)| WBTM[63-32] (資料によって [65-34][33-02] 表記もあるが |
//$20(LV-68)| WBTM[31-00] ここでは仮数部のビット位置で数える) |
// +---------------+---------------+---------------+---------------+
//$24(LV-64)| (FPSR_SHADOW) |
// +---------------+---------------+---------------+---------------+
//$28(LV-60)| FPIARCU |
// +---------------+---------------+---------------+---------------+
//$2c(LV-56)| |
// +---------------+---------------+---------------+---------------+
//$30(LV-52)| (CMDREG2B.W?/.L?) |
// +---------+-----+---------------+---------------+---------------+
//$34(LV-48)| | CMDREG3B | |
// +---------+-----+---------------+---------------+---------------+
//$38(LV-44)| (NMNEXC) | (NMCEXC) | |
// | N N|N N N C C C C C| |
// | U N|O V D U S O V D| |
// +---------------+---------------+---------------+---------------+
// NMN Unsupported --+ | | | | | | | | +-- NMC Underflow
// NMN SNAN ----+ | | | | | | +---- NMC Overflow
// NMN OPErr ------+ | | | | +------ NMC OPErr
// NMN Overflow --------+ | | +-------- NMC SNAN
// NMN Underflow ----------+ +---------- NMC Unsupported
// +---------------+---------------+---------------+---------------+
//$3c(LV-40)|STAG |E| |V|G|R|S| | |
// +---------------+---------------+---------------+---------------+
// (ETEMP15) --+ | | | +-- SBIT (WBTEMP Sticky)
// | | +---- WBTM0 (WBTEMP Round)
// | +------ WBTM1 (WBTEMP Guard)
// +-------- WBTM66 (WBTEMP Overflow)
// +---------------+---------------+---------------+---------------+
//$40(LV-36)| CMDREG1B | |
// +-------+-------+-------+-------+---------------+---------------+
//$44(LV-32)|DTAG |F|. . . .:. . .|W|. . . .| |
// +-------+-------+-------+-------+---------------+---------------+
// +-- (FPTEMP15) +-- (WBTEMP15)
// +---------------+---------------+---------------+---------------+
//$48(LV-28)|. . . . .|E|E|S|X|.|U|T|. . . .| |
// | |1|3|F|F| |F|F| | |
// +---------------+---------------+---------------+---------------+
// (SFLAG) --+ | | +-- TFLAG
// (XFLAG) ----+ +---- (UFLAG)
// +---------------+---------------+---------------+---------------+
//$4c(LV-24)| |<-FPTS FPTE | |
// +---------------+---------------+---------------+---------------+
//$50(LV-20)| FPTM[63-32] |
//$54(LV-16)| FPTM[31-00] |
// +---------------+---------------+---------------+---------------+
//$58(LV-12)| |<-ETS ETE | |
// +---------------+---------------+---------------+---------------+
//$5c(LV-8) | ETM[63-32] |
//$60(LV-4) | ETM[31-00] |
// +---------------+---------------+---------------+---------------+
//$64(LV-0)
#include "mpu680x0.h"
#include "m680x0disasm.h"
#include "m680x0cycle.h"
#include "textscreen.h"
#define SIZE_L (0)
#define SIZE_S (1)
#define SIZE_X (2)
#define SIZE_P (3)
#define SIZE_W (4)
#define SIZE_D (5)
#define SIZE_B (6)
// val が単精度で非正規化数なら true を返す。
static inline bool
is_denormal_s(uint32 val)
{
uint32 e = (val >> 23) & 0xff;
uint32 m = val & 0x7f'ffff;
if (e == 0 && m != 0) {
return true;
}
return false;
}
// data[0..1] が倍精度で非正規化数なら true を返す。
static inline bool
is_denormal_d(const uint32 *data)
{
uint32 e = (data[0] >> 20) & 0x7ff;
uint64 m = ((uint64)(data[0] & 0x000f'ffff) << 32) | data[1];
if (e == 0 && m != 0) {
return true;
}
return false;
}
// data[0..2] が拡張精度で非正規化数なら true を返す。
static inline bool
is_denormal_x(const uint32 *data)
{
uint32 e = (data[0] >> 16) & 0x7fff;
uint32 i = (data[1] >> 31) & 1;
uint64 m = (((uint64)(data[1] & 0x7fff'ffff)) << 32) | data[2];
if (e == 0 && i == 0 && m != 0) {
return true;
}
return false;
}
// data[0..2] が拡張精度でアンノーマル数なら true を返す。
static inline bool
is_unnormal_x(const uint32 *data)
{
uint32 e = (data[0] >> 16) & 0x7fff;
uint32 i = (data[1] >> 31) & 1;
uint64 m = (((uint64)(data[1] & 0x7fff'ffff)) << 32) | data[2];
if (e != 0 && e != 0x7fff && i == 0 && m != 0) {
return true;
}
// XXX Unnormal zero はハード処理かソフト処理か分からない。
if (e != 0 && e != 0x7fff && i == 0 && m == 0) {
return true;
}
return false;
}
// リセット用のレジスタ初期値
/*static*/ struct fpframe
MPU680x0Device::initial_fpframe;
// 初期化
void
MPU680x0Device::fpu_init()
{
int i;
uint32_t *p;
// XXX cpu の初期化のところと見直してマージするとか
memset(&fe, 0, sizeof(fe));
memset(®.fpframe, 0, sizeof(reg.fpframe));
fe.fe_fpframe = ®.fpframe;
// FRESTORE によるリセット用に FP レジスタの初期値を用意しておく。
// FPn は NAN に。FPSR, FPCR, FPIAR はゼロクリアされる。
memset(&initial_fpframe, 0, sizeof(initial_fpframe));
p = initial_fpframe.fpf_regs;
for (i = 0; i < 8; i++) {
*p++ = 0x7fff0000;
*p++ = 0xffffffff;
*p++ = 0xffffffff;
}
}
// FPU をリセットする。
// MPU リセット時(true)と、NULL フレームのリストア時(false)に呼ばれる。
void
MPU680x0Device::ResetFPU(bool hard)
{
// 高速化のため、あらかじめ用意しておいたイメージをコピーする
memcpy(®.fpframe, &initial_fpframe, sizeof(initial_fpframe));
fpu_state = FPU_STATE_NULL;
if (__predict_false(hard)) {
// この内部情報は NULL フレームのリストア (頻繁に起きる) のたびに
// モニタから消えるよりは、直近の1つが見え続けていてほしい。
// ただしハードリセット前の状態は引き継がない。
memset(&fpu40, 0, sizeof(fpu40));
fpu40.reg1b = 0xffff;
fpu40.stag = FPU40::TAG_NONE;
fpu40.dtag = FPU40::TAG_NONE;
}
fpu40.enable = false;
}
// FPU からの呼び出し用。
//
// (An)+, -(An) は、サイズが増えているのとクロックが増加している(2->6)。
// それ以外のアドレッシングモードでもクロック数は微妙に違うのだが、
// 大きくは違わないのでそっちは本体の cea と同じということにする。
//
// このルーチンは Dn、An、#imm には対応していないので(自明)、呼び出し側で
// 事前に弾くこと。いずれも消費クロック数は 0 なので問題ない。
// またそれ以外のすべてのアドレッシングモードで反応するため、命令ごとに
// 対応していないアドレッシングモードは事前に弾くこと。
uint32
MPU680x0Device::cea_fpu(int bytes)
{
int n;
switch (eamode(ir)) {
case 0: // Dn
case 1: // An
break;
case 2: // (An)
return cea_anin(eanum(ir));
case 3: // (An)+
CYCLE3(cea_fpu_anpi);
n = eanum(ir);
save_reg_pi(n);
if (bytes == 1) {
return internal_ea_anpi_1(n);
} else {
// _anpi_{2,4} のマクロは用意されてるけど、これと同じなので
reg.A[n] += bytes;
return reg.A[n] - bytes;
}
break;
case 4: // -(An)
CYCLE3(cea_fpu_anpd);
n = eanum(ir);
save_reg_pd(n);
if (bytes == 1) {
return internal_ea_anpd_1(n);
} else {
// _anpd_{2,4} のマクロは用意されてるけど、これと同じなので
reg.A[n] -= bytes;
return reg.A[n];
}
break;
case 5: // d16(An)
return cea_andi(eanum(ir));
case 6: // (An,IX)
return cea_anix(eanum(ir));
case 7:
switch (eanum(ir)) {
case 0: // Abs.W
return cea_absw();
case 1: // Abs.L
return cea_absl();
case 2: // d16(PC)
return cea_pcdi();
case 3: // (PC,IX)
return cea_pcix();
case 4: // #imm
break;
default:
break;
}
break;
}
// 不当命令
throw M68K::EXCEP_ILLEGAL;
}
// こっちは LC040 用。
//
// Dn、An、#imm に対しては 0 を返す (MC68040UM.pdf, p.A-6)。
// それ以外のすべてのアドレッシングモードで反応するため、命令ごとに
// 対応していないアドレッシングモードは事前に弾くこと。
// 不当パターンなら EXCEP_ILLEGAL をスローする。
uint32
MPU680x0Device::cea_fpulc(int bytes)
{
uint mr = ir & 077;
if (__predict_false(mr < 16 || mr == 074)) {
return 0;
} else {
return cea_fpu(bytes);
}
}
// メモリから 64ビット値を読み込む
void
MPU680x0Device::read_8(uint32 addr, uint32 *data)
{
data[0] = read_4(addr);
data[1] = read_4(addr + 4);
}
// メモリから 96ビット値を読み込む
void
MPU680x0Device::read_12(uint32 addr, uint32 *data)
{
data[0] = read_4(addr);
data[1] = read_4(addr + 4);
data[2] = read_4(addr + 8);
}
// 64ビット値をメモリに書き込む
void
MPU680x0Device::write_8(uint32 addr, const uint32 *data)
{
write_4(addr, data[0]);
write_4(addr + 4, data[1]);
}
// 96ビット値をメモリに書き込む
void
MPU680x0Device::write_12(uint32 addr, const uint32 *data)
{
write_4(addr, data[0]);
write_4(addr + 4, data[1]);
write_4(addr + 8, data[2]);
}
// FPU 不当命令 (1ワード目で確定)
void
MPU680x0Device::fpu_op_illg()
{
putlog(1, "FPU illegal instruction %04X", ir);
Exception(M68K::EXCEP_FLINE);
}
// FPU 不当命令 (2ワード目で確定)
void
MPU680x0Device::fpu_op_illg2()
{
putlog(1, "FPU illegal instruction %04X %04X", ir, ir2);
Exception(M68K::EXCEP_FLINE);
}
// R/M=0 (レジスタ-レジスタ演算)、opclass=%000
void
MPU680x0Device::fpu_op_fgen_reg()
{
uint srcnum = (ir2 >> 10) & 7;
fgen(&RegFP(srcnum), SIZE_X);
}
// R/M=1 (メモリ-レジスタ演算)、opclass=%010
void
MPU680x0Device::fpu_op_fgen_mem()
{
uint mr = ir & 0x3f;
uint size_id = (ir2 >> 10) & 7;
uint32 ea = 0;
uint32 data[3];
// source specifier はオペランドサイズ
if (size_id == 7) {
// こんなところに FMOVECR
fpu_op_fmovecr();
return;
}
if ((mr >> 3) == 1) { // An
fpu_op_illg2();
return;
}
uint bytes;
switch (size_id) {
case SIZE_B:
bytes = 1;
break;
case SIZE_W:
bytes = 2;
break;
case SIZE_L:
case SIZE_S:
bytes = 4;
break;
case SIZE_D:
bytes = 8;
break;
case SIZE_X:
case SIZE_P:
bytes = 12;
break;
default:
__unreachable();
}
if (__predict_false(GetFPUType().Is4060LC())) {
ea = cea_fpulc(bytes);
ExceptionFPLC(ea);
return;
}
// 68881 本(p6-13) にはクロック数はこう書いてあるので
// Inst | src=FPn | memory source or dst operand format
// | | Int Sgl Dbl Ext Pakced
// ---- +---------- +------ ------- ------- ------- -------
// FABS | 35 | 62 54 60 58 872
// (差分) +27 +19 +25 +23 +837
// FADD | 51 | 80 72 78 76 888
// (差分) +29 +21 +27 +25 +837
//
// FABS.X FPn,FPm は 35 としてこれは fgen() 内で加算。
// FABS.L <ea>,FPm は 62 クロックなので、ここでは差分の 27 を加算。
// FABS.L Dn,FPm の場合 src がレジスタなので 62 から 5 引く。
// FABS.P <ea>,FPm は 872 クロックなので、ここでは差分の 837 を加算。
//
// XXX 多くの命令が FABS と同じだけの差分だが、
// FADD などいくつかは .P 以外に +2 クロックの追加があるようだ。
// どうしたもんか。
if (mr < 8) { // Dn
switch (bytes) {
case 1:
data[0] = (int32)(int8)reg.D[mr];
break;
case 2:
data[0] = (int32)(int16)reg.D[mr];
break;
case 4:
data[0] = reg.D[mr];
break;
default:
fpu_op_illg2();
return;
}
// 参照されない EA がどうなるか分からないが、LC040 からの類推。
fpu40.ea = 0;
// ソースが Dn の場合は 5クロック減
CYCLE3(fpu_src_dn);
} else if (mr == 074) { // #imm
switch (bytes) {
case 1:
data[0] = fetch_2() & 0xff;
break;
case 2:
data[0] = fetch_2();
break;
case 4:
data[0] = fetch_4();
break;
case 8:
data[0] = fetch_4();
data[1] = fetch_4();
break;
case 12:
data[0] = fetch_4();
data[1] = fetch_4();
data[2] = fetch_4();
break;
default:
__unreachable();
}
// 参照されない EA がどうなるか分からないが、LC040 からの類推。
fpu40.ea = 0;
} else {
// それ以外は EA を求めてからメモリから読み込む。
ea = cea_fpu(bytes);
switch (bytes) {
case 1:
data[0] = (int32)(int8)read_1(ea);
break;
case 2:
data[0] = (int32)(int16)read_2(ea);
break;
case 4:
data[0] = read_4(ea);
break;
case 8:
read_8(ea, data);
break;
case 12:
read_12(ea, data);
break;
default:
__unreachable();
}
fpu40.ea = ea;
}
fgen(data, size_id);
}
// 一般算術演算。
// srcdata, srcsize_id がソースの生データとサイズ識別子。
// デスティネーション(FPn) は ir2 から求まる。
void
MPU680x0Device::fgen(const uint32 *srcdata, uint srcsize_id)
{
uint opmode = ir2 & 0x3f;
uint dstnum = (ir2 >> 7) & 7;
bool discard_result = false;
struct fpn *res = NULL; // shutup gcc
// 機種別の前処理。
if (GetFPUType().Is4060FPU()) {
// 未実装命令と未実装データ型の検出など。
if (fgen040pre(opmode, dstnum, srcdata, srcsize_id) == false) {
return;
}
} else {
// fe_f1, fe_f2 にロードするだけ。
fgen881pre(dstnum, srcdata, srcsize_id);
}
switch (opmode) {
case 0x00: // FMOVE
fmove:
CYCLE3(fmove);
res = &fe.fe_f2;
fpu_round_prec(&fe, res);
break;
case 0x01: // FINT
CYCLE(55);
res = fpu_int(&fe);
break;
case 0x02: // FSINH
CYCLE(687);
res = fpu_sinh(&fe);
fpu_round_prec(&fe, res);
break;
case 0x03: // FINTRZ
CYCLE(55);
res = fpu_intrz(&fe);
break;
case 0x04: // FSQRT
fsqrt:
CYCLE3(fsqrt);
res = fpu_sqrt(&fe);
fpu_round_prec(&fe, res);
break;
case 0x06: // FLOGNP1
CYCLE(571);
res = fpu_lognp1(&fe);
fpu_round_prec(&fe, res);
break;
case 0x08: // FETOXM1
CYCLE(545);
res = fpu_etoxm1(&fe);
fpu_round_prec(&fe, res);
break;
case 0x09: // FTANH
CYCLE(661);
res = fpu_tanh(&fe);
fpu_round_prec(&fe, res);
break;
case 0x0a: // FATAN
CYCLE(403);
res = fpu_atan(&fe);
fpu_round_prec(&fe, res);
break;
case 0x0c: // FASIN
CYCLE(581);
res = fpu_asin(&fe);
fpu_round_prec(&fe, res);
break;
case 0x0d: // FATANH
CYCLE(693);
res = fpu_atanh(&fe);
fpu_round_prec(&fe, res);
break;
case 0x0e: // FSIN
CYCLE(391);
res = fpu_sin(&fe);
fpu_round_prec(&fe, res);
break;
case 0x0f: // FTAN
CYCLE(473);
res = fpu_tan(&fe);
fpu_round_prec(&fe, res);
break;
case 0x10: // FETOX
CYCLE(497);
res = fpu_etox(&fe);
fpu_round_prec(&fe, res);
break;
case 0x11: // FTWOTOX
CYCLE(567);
res = fpu_twotox(&fe);
fpu_round_prec(&fe, res);
break;
case 0x12: // FTENTOX
CYCLE(567);
res = fpu_tentox(&fe);
fpu_round_prec(&fe, res);
break;
case 0x14: // FLOGN
CYCLE(525);
res = fpu_logn(&fe);
fpu_round_prec(&fe, res);
break;
case 0x15: // FLOG10
CYCLE(581);
res = fpu_log10(&fe);
fpu_round_prec(&fe, res);
break;
case 0x16: // FLOG2
CYCLE(581);
res = fpu_log2(&fe);
fpu_round_prec(&fe, res);
break;
case 0x18: // FABS
fabs:
CYCLE3(fabs);
fe.fe_f2.fp_sign = 0;
res = &fe.fe_f2;
fpu_round_prec(&fe, res);
break;
case 0x19: // FCOSH
CYCLE(607);
res = fpu_cosh(&fe);
fpu_round_prec(&fe, res);
break;
case 0x1a: // FNEG
fneg:
CYCLE3(fneg);
res = &fe.fe_f2;
if (!ISNAN(res)) {
res->fp_sign = !res->fp_sign;
fpu_round_prec(&fe, res);
}
break;
case 0x1c: // FACOS
CYCLE(625);
res = fpu_acos(&fe);
fpu_round_prec(&fe, res);
break;
case 0x1d: // FCOS
CYCLE(391);
res = fpu_cos(&fe);
fpu_round_prec(&fe, res);
break;
case 0x1e: // FGETEXP
CYCLE(45);
res = fpu_getexp(&fe);
fpu_round_prec(&fe, res);
break;
case 0x1f: // FGETMAN
CYCLE(31);
res = fpu_getman(&fe);
fpu_round_prec(&fe, res);
break;
case 0x20: // FDIV
fdiv:
CYCLE3(fdiv);
res = fpu_div(&fe);
fpu_round_prec(&fe, res);
break;
case 0x21: // FMOD
CYCLE(67);
res = fpu_mod(&fe);
fpu_round_prec(&fe, res);
break;
case 0x22: // FADD
fadd:
CYCLE3(fadd);
res = fpu_add(&fe);
fpu_round_prec(&fe, res);
break;
case 0x23: // FMUL
fmul:
CYCLE3(fmul);
res = fpu_mul(&fe);
fpu_round_prec(&fe, res);
break;
case 0x24: // FSGLDIV
CYCLE3(fsgldiv);
res = fpu_sgldiv(&fe);
break;
case 0x25: // FREM
CYCLE(67);
res = fpu_rem(&fe);
fpu_round_prec(&fe, res);
break;
case 0x26: // FSCALE
// こいつだけ形式が違う。
// 結果は dst に直接格納済み、FPSR も演算済み。
CYCLE(41);
fpu_emul_fscale(&fe, ir2);
return;
case 0x27: // FSGLMUL
CYCLE3(fsglmul);
res = fpu_sglmul(&fe);
break;
case 0x28: // FSUB
fsub:
CYCLE3(fsub);
res = fpu_sub(&fe);
fpu_round_prec(&fe, res);
break;
case 0x30:
case 0x31:
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37: // FSINCOS
CYCLE(451);
res = fpu_sincos(&fe, opmode & 7);
fpu_round_prec(&fe, res);
break;
case 0x38: // FCMP
CYCLE3(fcmp);
res = fpu_cmp(&fe);
discard_result = 1;
break;
case 0x3a: // FTST
CYCLE3(ftst);
res = &fe.fe_f2;
discard_result = 1;
break;
#define ONLY_40 do { \
if (GetFPUType().Is40FPU() == false) { \
goto illegal; \
} \
} while (0)
// fe の精度だけ変える。
// fe_fpcr は RegFPCR (レジスタ) のほうには伝搬しないし、
// 次の命令の冒頭でまた RegFPCR から上書きされるので、
// 書き換えっぱなしでよい。
#define ROUND_FS do { \
fe.fe_fpcr &= ~FPCR_PREC; \
fe.fe_fpcr |= FPCR_SNGL; \
} while (0)
#define ROUND_FD do { \
fe.fe_fpcr &= ~FPCR_PREC; \
fe.fe_fpcr |= FPCR_DBL; \
} while (0)
case 0x40: ONLY_40; ROUND_FS; goto fmove; // FSMOVE
case 0x44: ONLY_40; ROUND_FD; goto fmove; // FDMOVE
case 0x41: ONLY_40; ROUND_FS; goto fsqrt; // FSSQRT
case 0x45: ONLY_40; ROUND_FD; goto fsqrt; // FDSQRT
case 0x58: ONLY_40; ROUND_FS; goto fabs; // FSABS
case 0x5c: ONLY_40; ROUND_FD; goto fabs; // FDABS
case 0x5a: ONLY_40; ROUND_FS; goto fneg; // FSNEG
case 0x5e: ONLY_40; ROUND_FD; goto fneg; // FDNEG
case 0x60: ONLY_40; ROUND_FS; goto fdiv; // FSDIV
case 0x64: ONLY_40; ROUND_FD; goto fdiv; // FDDIV
case 0x62: ONLY_40; ROUND_FS; goto fadd; // FSADD
case 0x66: ONLY_40; ROUND_FD; goto fadd; // FDADD
case 0x63: ONLY_40; ROUND_FS; goto fmul; // FSMUL
case 0x67: ONLY_40; ROUND_FD; goto fmul; // FDMUL
case 0x68: ONLY_40; ROUND_FS; goto fsub; // FSSUB
case 0x6c: ONLY_40; ROUND_FD; goto fsub; // FDSUB
illegal:
default:
// 不当命令
fpu_op_illg2();
return;
}
assert(res);
if (!discard_result) {
fpu_implode(&fe, res, FTYPE_EXT, &RegFP(dstnum));
}
fpu_upd_fpsr(&fe, res);
fpu_upd_excp(&fe);
}
// fgen() の 68881 での前処理。
// src, dst を fe_f2, fe_f1 にそれぞれロードするだけ。
void
MPU680x0Device::fgen881pre(uint dstnum, const uint32 *srcdata, uint srcsize_id)
{
switch (srcsize_id) {
case SIZE_B:
case SIZE_W:
case SIZE_L:
CYCLE(+27);
fpu_explode(&fe, &fe.fe_f2, FTYPE_LNG, srcdata);
break;
case SIZE_S:
CYCLE(+19);
fpu_explode(&fe, &fe.fe_f2, FTYPE_SNG, srcdata);
break;
case SIZE_D:
CYCLE(+25);
fpu_explode(&fe, &fe.fe_f2, FTYPE_DBL, srcdata);
break;
case SIZE_X:
CYCLE(+23);
fpu_explode(&fe, &fe.fe_f2, FTYPE_EXT, srcdata);
break;
case SIZE_P:
CYCLE(+837);
fpu_explode(&fe, &fe.fe_f2, FTYPE_BCD, srcdata);
break;
default:
__unreachable();
}
fpu_explode(&fe, &fe.fe_f1, FTYPE_EXT, &RegFP(dstnum));
}
// fgen() の 68040 での前処理。
// 未実装命令のチェックと該当すればその例外処理、
// 未実装データ型のチェックと該当すればその例外処理を行う。
// 例外処理を行った場合は false を返す。
bool
MPU680x0Device::fgen040pre(uint opmode, uint dstnum,
const uint32 *srcdata, uint srcsize_id)
{
// 未実装命令か調べる。
bool unimpl = false;
switch (opmode) {
case 0x01: // FINT
case 0x02: // FSINH
case 0x03: // FINTRZ
case 0x06: // FLOGNP1
case 0x08: // FETOXM1
case 0x09: // FTANH
case 0x0a: // FATAN
case 0x0c: // FASIN
case 0x0d: // FATANH
case 0x0e: // FSIN
case 0x0f: // FTAN
case 0x10: // FETOX
case 0x11: // FTWOTOX
case 0x12: // FTENTOX
case 0x14: // FLOGN
case 0x15: // FLOG10
case 0x16: // FLOG2
case 0x19: // FCOSH
case 0x1c: // FACOS
case 0x1d: // FCOS
case 0x1e: // FGETEXP
case 0x1f: // FGETMAN
case 0x21: // FMOD
case 0x25: // FREM
case 0x26: // FSCALE
case 0x30: // FSINCOS
case 0x31: // FSINCOS
case 0x32: // FSINCOS
case 0x33: // FSINCOS
case 0x34: // FSINCOS
case 0x35: // FSINCOS
case 0x36: // FSINCOS
case 0x37: // FSINCOS
unimpl = true;
break;
default:
break;
}
// ソースがサポートしている型なら拡張精度に変換して fe_f2 にセットし、
// stag は NONE のままか NORMAL or ZERO をセット。
// 未サポート型なら所定の etemp を作成して stag は DENORMAL か UNNORMAL。
//
// Int .S/.D .X .P
// Normal fe_f2,NORM fe_f2,NONE fe_f2,NONE etemp,UNNO
// Zero fe_f2,ZERO fe_f2,NONE fe_f2,NONE etemp,UNNO
// Inf - fe_f2,NONE fe_f2,NONE etemp,UNNO
// NAN - fe_f2,NONE fe_f2,NONE etemp,UNNO
// Denorm - etemp,DENO etemp,UNNO etemp,UNNO
// Unnorm - - etemp,UNNO etemp,UNNO
// サイクル数は 68040UM.pdf p10-30 の表から適当に逆算。
int32 stag = FPU40::TAG_NONE;
switch (srcsize_id) {
case SIZE_B:
case SIZE_W:
case SIZE_L:
CYCLE(+0);
if (srcdata[0] == 0) {
stag = FPU40::TAG_ZERO;
} else {
stag = FPU40::TAG_NORMAL;
}
fpu_explode(&fe, &fe.fe_f2, FTYPE_LNG, srcdata);
break;
case SIZE_S:
CYCLE(+0);
if (is_denormal_s(srcdata[0])) {
// 68040UM.pdf, p9-24. Figure 9-9 (a)
uint32 s = srcdata[0] & 0x8000'0000;
uint32 m = srcdata[0] & 0x007f'ffff;
fpu40.etemp[0] = s;
fpu40.etemp[1] = m << 8;
fpu40.etemp[2] = 0;
stag = FPU40::TAG_DENORMAL_S;
break;
}
fpu_explode(&fe, &fe.fe_f2, FTYPE_SNG, srcdata);
break;
case SIZE_D:
CYCLE(+0);
if (is_denormal_d(srcdata)) {
// 68040UM.pdf, p9-24. Figure 9-9 (b)
uint32 s = srcdata[0] & 0x8000'0000;
uint64 m = ((uint64)(srcdata[0] & 0x000f'ffff) << 32) | srcdata[1];
m <<= 11;
fpu40.etemp[0] = s;
fpu40.etemp[1] = m >> 32;
fpu40.etemp[2] = m & 0xffffffff;
stag = FPU40::TAG_DENORMAL_D;
break;
}
fpu_explode(&fe, &fe.fe_f2, FTYPE_DBL, srcdata);
break;
case SIZE_X:
CYCLE(+1);
if (is_denormal_x(srcdata)) {
memcpy(fpu40.etemp, srcdata, 12);
stag = FPU40::TAG_DENORMAL_X;
break;
} else if (is_unnormal_x(srcdata)) {
memcpy(fpu40.etemp, srcdata, 12);
stag = FPU40::TAG_UNNORMAL_X;
break;
}
fpu_explode(&fe, &fe.fe_f2, FTYPE_EXT, srcdata);
break;
case SIZE_P:
// 上位32ビットを FPTM[31-00] に、
// 下位64ビットを ETM[31-00] に置く。(68040UM.pdf, pp.9-42〜43)
fpu40.fptemp[2] = srcdata[0];
fpu40.etemp[1] = srcdata[1];
fpu40.etemp[2] = srcdata[2];
// Packed の時 stag は undefined と書いてある。
// ここではこの後の例外判定用および表示用として使う。
stag = FPU40::TAG_PACKED;
break;
default:
__unreachable();
}
// デスティネーション(レジスタ) が未サポートデータ型になるのは
// 非正規化数の時だけのはず。
int32 dtag = FPU40::TAG_NONE;
if (is_denormal_x(&RegFP(dstnum))) {
dtag = FPU40::TAG_DENORMAL_X;
}
fpu_explode(&fe, &fe.fe_f1, FTYPE_EXT, &RegFP(dstnum));
if (dtag == FPU40::TAG_NONE) {
dtag = fpu_gettag(&fe.fe_f1);
}
// 例外を起こす必要があるか。
if (unimpl ||
stag >= FPU40::TAG_UNNORMAL ||
dtag >= FPU40::TAG_UNNORMAL)
{
// stag がまだならここで fpn から決まる。
if (stag == FPU40::TAG_NONE) {
stag = fpu_gettag(&fe.fe_f2);
}
// stag が UNNORMAL, DENORMAL なら etemp はセット済み。
if (stag < FPU40::TAG_UNNORMAL) {
fpu_implode(&fe, &fe.fe_f2, FTYPE_EXT, fpu40.etemp);
}
// Packed なら E1 を立てる、
// また FPTEMP にはソースを展開済みなので dst を書き込まない。
// XXX 68040UM.pdf p.9-44 Table 9-16 Unimplemented Instruction には
// E1 は always 1 とあるが、p.9-42 の E1 の説明のほうが正しそう。
if (srcsize_id == SIZE_P) {
fpu40.eflag = FPU40::E1;
} else {
fpu40.eflag = 0;
memcpy(&fpu40.fptemp, &RegFP(dstnum), 12);
}
fpu40.stag = stag;
fpu40.dtag = dtag;
if (unimpl) {
ExceptionFP(M68K::EXCEP_FP_UNIMPL);
} else {
ExceptionFP(M68K::EXCEP_FP_UNSUPP);
}
return false;
}
// ハードウェア処理可能。
// 68040 では条件命令以外 (たぶん実質クラス0命令) を実行すると
// NULL -> IDLE 状態になる。68040UM.pdf, p9-39
fpu_state = FPU_STATE_IDLE;
return true;
}
// FMOVECR
void
MPU680x0Device::fpu_op_fmovecr()
{
if (__predict_false(GetFPUType().Is6888x() == false)) {
if (GetFPUType().Is4060LC()) {
ExceptionFPLC(0);
} else {
ExceptionFP(M68K::EXCEP_FP_UNIMPL);
}
return;
}
CYCLE(29);
uint offset = (ir2 & 0x7f);
uint dstnum = (ir2 >> 7) & 7;
fpu_const(&fe.fe_f3, offset);
fpu_implode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(dstnum));
fpu_upd_fpsr(&fe, &fe.fe_f3);
fpu_upd_excp(&fe);
}
// FMOVE FPn,<ea>
void
MPU680x0Device::fpu_op_fmove_to_mem()
{
// サイズごとに分岐
uint size = (ir2 >> 10) & 7;
switch (size) {
case SIZE_B:
fpu_op_fmove_b_mem();
break;
case SIZE_W:
fpu_op_fmove_w_mem();
break;
case SIZE_L:
fpu_op_fmove_l_mem();
break;
case SIZE_S:
fpu_op_fmove_s_mem();
break;
case SIZE_D:
fpu_op_fmove_d_mem();
break;
case SIZE_X:
fpu_op_fmove_x_mem();
break;
case SIZE_P:
case 7:
fpu_op_fmove_p_mem();
break;
}
}
// FMOVE.B FPn,<ea>
// cf. 68000PRM.pdf p378 (5-76)
void
MPU680x0Device::fpu_op_fmove_b_mem()
{
uint mr = ir & 0x3f;
uint srcnum = (ir2 >> 7) & 7;
uint32 ea;
uint32 data;
if (__predict_false((mr >> 3) == 1 || mr >= 072)) { // An, PC, #imm
fpu_op_illg2();
return;
}
if (__predict_false(GetFPUType().Is4060LC())) {
ea = cea_fpulc(1);
ExceptionFPLC(ea);
return;
}
fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum));
fpu_implode(&fe, &fe.fe_f3, FTYPE_LNG, &data);
// 入力値は long か、long で表現できなければ 0x80000000 が返ってくるが、
// ここはバイトへの変換なので表現できなければ 0x80 にする。
if ((int32)data < INT8_MIN || (int32)data > INT8_MAX) {
data = 0x80;
RegFPSR |= FPSR_OPERR | FPSR_AIOP;
}
if (mr < 8) { // Dn
CYCLE3(fmove_i_fp_dn);
reg.D[mr] &= 0xffffff00;
reg.D[mr] |= (uint8)data;
} else {
CYCLE3(fmove_i_fp_mem);
ea = cea_fpu(1);
write_1(ea, (uint8)data);
}
}
// FMOVE.W FPn,<ea>
// cf. 68000PRM.pdf p378 (5-76)
void
MPU680x0Device::fpu_op_fmove_w_mem()
{
uint mr = ir & 0x3f;
uint srcnum = (ir2 >> 7) & 7;
uint32 ea;
uint32 data;
if (__predict_false((mr >> 3) == 1 || mr >= 072)) { // An, PC, #imm
fpu_op_illg2();
return;
}
if (__predict_false(GetFPUType().Is4060LC())) {
ea = cea_fpulc(2);
ExceptionFPLC(ea);
return;
}
fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum));
fpu_implode(&fe, &fe.fe_f3, FTYPE_LNG, &data);
// 入力値は long か、long で表現できなければ 0x80000000 が返ってくるが、
// ここはワードへの変換なので表現できなければ 0x8000 にする。
if ((int32)data < INT16_MIN || (int32)data > INT16_MAX) {
data = 0x8000;
RegFPSR |= FPSR_OPERR | FPSR_AIOP;
}
if (mr < 8) { // Dn
CYCLE3(fmove_i_fp_dn);
reg.D[mr] &= 0xffff0000;
reg.D[mr] |= (uint16)data;
} else {
CYCLE3(fmove_i_fp_mem);
ea = cea_fpu(2);
write_2(ea, (uint16)data);
}
}
// FMOVE.L FPn,<ea>
// cf. 68000PRM.pdf p378 (5-76)
void
MPU680x0Device::fpu_op_fmove_l_mem()
{
uint mr = ir & 0x3f;
uint srcnum = (ir2 >> 7) & 7;
uint32 ea;
uint32 data;
if (__predict_false((mr >> 3) == 1 || mr >= 072)) { // An, PC, #imm
fpu_op_illg2();
return;
}
if (__predict_false(GetFPUType().Is4060LC())) {
ea = cea_fpulc(4);
ExceptionFPLC(ea);
return;
}
fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum));
fpu_implode(&fe, &fe.fe_f3, FTYPE_LNG, &data);
// XXX オーバーフローチェック
if (mr < 8) { // Dn
CYCLE3(fmove_i_fp_dn);
reg.D[mr] = data;
} else {
CYCLE3(fmove_i_fp_mem);
ea = cea_fpu(4);
write_4(ea, data);
}
}
// FMOVE.S FPn,<ea>
// cf. 68000PRM.pdf p378 (5-76)
void
MPU680x0Device::fpu_op_fmove_s_mem()
{
uint mr = ir & 0x3f;
uint srcnum = (ir2 >> 7) & 7;
uint32 ea;
uint32 data;
if (__predict_false((mr >> 3) == 1 || mr >= 072)) { // An, PC, #imm
fpu_op_illg2();
return;
}
if (__predict_false(GetFPUType().Is4060LC())) {
ea = cea_fpulc(4);
ExceptionFPLC(ea);
return;
}
fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum));
fpu_implode(&fe, &fe.fe_f3, FTYPE_SNG, &data);
// 変換時に検出した UNFL, OVFL だけ外部レジスタにコピー。
fe.fe_fpframe->fpf_fpsr &= ~(FPSR_UNFL | FPSR_OVFL);
fe.fe_fpframe->fpf_fpsr |= fe.fe_fpsr & (FPSR_UNFL | FPSR_OVFL);
// アクルードバイトは更新。
fpu_upd_excp(&fe);
if (mr < 8) { // Dn
CYCLE3(fmove_s_fp_dn);
reg.D[mr] = data;
} else {
CYCLE3(fmove_s_fp_mem);
ea = cea_fpu(4);
write_4(ea, data);
}
}
// FMOVE.D FPn,<ea>
// cf. 68000PRM.pdf p378 (5-76)
void
MPU680x0Device::fpu_op_fmove_d_mem()
{
uint mr = ir & 077;
uint srcnum = (ir2 >> 7) & 7;
uint32 ea;
uint32 data[2];
if (__predict_false(mr <= 017 || mr >= 072)) { // Dn, An, PC, #imm
fpu_op_illg2();
return;
}
if (__predict_false(GetFPUType().Is4060LC())) {
ea = cea_fpulc(8);
ExceptionFPLC(ea);
return;
}
CYCLE3(fmove_d_fp_ea);
fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum));
fpu_implode(&fe, &fe.fe_f3, FTYPE_DBL, data);
// 変換時に検出した UNFL, OVFL だけ外部レジスタにコピー。
fe.fe_fpframe->fpf_fpsr &= ~(FPSR_UNFL | FPSR_OVFL);
fe.fe_fpframe->fpf_fpsr |= fe.fe_fpsr & (FPSR_UNFL | FPSR_OVFL);
// アクルードバイトは更新。
fpu_upd_excp(&fe);
ea = cea_fpu(8);
write_8(ea, data);
}
// FMOVE.X FPn,<ea>
// cf. 68000PRM.pdf p378 (5-76)
void
MPU680x0Device::fpu_op_fmove_x_mem()
{
uint mr = ir & 077;
uint srcnum = (ir2 >> 7) & 7;
uint32 ea;
uint32 data[3];
if (__predict_false(mr <= 017 || mr >= 072)) { // Dn, An, PC, #imm
fpu_op_illg2();
return;
}
if (__predict_false(GetFPUType().Is4060LC())) {
ea = cea_fpulc(12);
ExceptionFPLC(ea);
return;
}
CYCLE3(fmove_x_fp_ea);
fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum));
fpu_implode(&fe, &fe.fe_f3, FTYPE_EXT, data);
// 変換時に検出した UNFL, OVFL だけ外部レジスタにコピー。
fe.fe_fpframe->fpf_fpsr &= ~(FPSR_UNFL | FPSR_OVFL);
fe.fe_fpframe->fpf_fpsr |= fe.fe_fpsr & (FPSR_UNFL | FPSR_OVFL);
// アクルードバイトは更新。
fpu_upd_excp(&fe);
ea = cea_fpu(12);
write_12(ea, data);
}
// FMOVE.P FPn,<ea>
// cf. 68000PRM.pdf p378 (5-76)
void
MPU680x0Device::fpu_op_fmove_p_mem()
{
uint mr = ir & 077;
uint srcnum = (ir2 >> 7) & 7;
uint32 ea;
uint32 data[3];
uint32 k;
if (__predict_false(mr <= 017 || mr >= 072)) { // Dn, An, PC, #imm
fpu_op_illg2();
return;
}
if (__predict_false(GetFPUType().Is6888x() == false)) {
if (GetFPUType().Is4060LC()) {
ea = cea_fpulc(12);
ExceptionFPLC(ea);
} else {
ExceptionFP(M68K::EXCEP_FP_UNIMPL);
}
return;
}
if ((ir2 & 0x1000)) {
// dynamic k-factor
CYCLE(1996+14);
k = reg.D[(ir2 >> 4) & 7];
} else {
// static k-factor
CYCLE(1996);
k = ir2;
}
// k は7ビット符号付き数
k &= 0x7f;
if ((k & 0x40)) {
k = (int32)(int8)(k | 0x80);
}
fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum));
fpu_ftop(&fe, &fe.fe_f3, data, k);
ea = cea_fpu(12);
write_12(ea, data);
}
// FMOVEM.L <ea>,<ctllist>
// 1111 ccc 000 mmmrrr | 10D RRR 0000 000000
// D=0: <ea> to FPctl
// RRR: register list
void
MPU680x0Device::fpu_op_fmovem_ea2ctl()
{
uint mr = ir & 0x3f;
uint reglist = (ir2 >> 10) & 7;
int count;
uint32 ea;
uint32 src[3];
int i;
// cache case worst case
// FMOVE Rn,FPc 28 31
// FMOVE <ea>,FPc 33 36
// FMOVE #imm,FPc 30 31
// FMOVEM <ea>,FPcs 27+6n 30+6n
// FMOVEM #imm,FPcs 25+6n 29+6n
//
// FMOVE #imm と FMOVEM #imm (n==1) は cache case が 1クロックだけ違う
// ようだけどもう無視する。
// 040 で複数レジスタのケースの記述が見当たらない。
// 転送するレジスタ数。
// nofpc の場合は現在の 68881 の実装では FPIAR が選択される。
if (reglist == 0)
reglist = 1;
count = __builtin_popcount(reglist);
if (mr < 8) { // Dn
CYCLE3(fmovem_rn_ctl);
// 対データレジスタは転送数 1 のみ有効
if (count > 1) {
fpu_op_illg2();
return;
}
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(0);
return;
}
src[0] = reg.R[mr];
} else if (mr < 16) { // An
CYCLE3(fmovem_rn_ctl);
// 対アドレスレジスタは FPIAR のみ有効
if (reglist != 1) {
fpu_op_illg2();
return;
}
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(0);
return;
}
src[0] = reg.R[mr];
} else if (mr < 0x3c) { // 対メモリ
CYCLE3(fmovem_mem_ctl);
CYCLE(count * 6);
// EA を取得。
// -(An) の時も先にレジスタ数分一気に減算して、そこからプラス方向に
// 読み込んでいくので、ロジックは全部同じになる。
// (68000PRM.pdf p393)
ea = cea_fpu(count * 4);
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(ea);
return;
}
for (i = 0; i < count; i++) {
src[i] = read_4(ea);
ea += 4;
}
} else if (mr == 0x3c) { // #imm
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(0);
return;
}
CYCLE3(fmovem_imm_ctl);
CYCLE(count * 6);
for (i = 0; i < count; i++) {
src[i] = fetch_4();
}
} else {
fpu_op_illg2();
return;
}
// 該当レジスタへ読み込み
i = 0;
if ((reglist & 4)) {
RegFPCR = src[i++] & M68K::FPCR_MASK;
}
if ((reglist & 2)) {
RegFPSR = src[i++] & M68K::FPSR_MASK;
}
if ((reglist & 1)) {
RegFPIAR = src[i++];
}
}
// FMOVEM.L <ctllist>,<ea>
// 1111 ccc 000 mmmrrr | 10D RRR 0000 000000
// D=1: FPctl to <ea>
// RRR: register list
void
MPU680x0Device::fpu_op_fmovem_ctl2ea()
{
uint mr = ir & 0x3f;
uint reglist = (ir2 >> 10) & 7;
int count;
uint32 ea;
// cache case worst case
// FMOVE FPc,Rn 31 34
// FMOVE FPc,<ea> 33 36
// FMOVEM FPcs,<ea> 27+6n 30+6n
//
// 040 で複数レジスタのケースの記述が見当たらない。
// 転送するレジスタ数。
// nofpc の場合は現在の 68881 の実装では FPIAR が選択される。
if (reglist == 0)
reglist = 1;
count = __builtin_popcount(reglist);
if (mr < 8) { // Dn
CYCLE3(fmovem_ctl_rn);
// 対データレジスタは転送数は 1 のみ有効
if (count > 1) {
fpu_op_illg2();
return;
}
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(0);
return;
}
if ((reglist & 4)) {
reg.R[mr] = RegFPCR;
}
if ((reglist & 2)) {
reg.R[mr] = RegFPSR;
}
if ((reglist & 1)) {
reg.R[mr] = RegFPIAR;
}
return;
}
if (mr < 16) { // An
CYCLE3(fmovem_ctl_rn);
// 対アドレスレジスタは FPIAR のみ有効
if (reglist != 1) {
fpu_op_illg2();
return;
}
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(0);
return;
}
reg.R[mr] = RegFPIAR;
return;
}
if (mr >= 0x3a) { // #imm, d16(PC), (PC,IX)
fpu_op_illg2();
return;
}
// それ以外、対メモリの場合。
// EA を取得して...
ea = cea_fpu(count * 4);
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(ea);
return;
}
CYCLE3(fmovem_ctl_mem);
CYCLE(count * 6);
// 該当レジスタをメモリへ書き出し
if ((reglist & 4)) {
write_4(ea, RegFPCR);
ea += 4;
}
if ((reglist & 2)) {
write_4(ea, RegFPSR);
ea += 4;
}
if ((reglist & 1)) {
write_4(ea, RegFPIAR);
ea += 4;
}
}
// FMOVEM.X <ea>,<reglist>
// FMOVEM.X <ea>,Dn
// 1111 ccc 000 mmmrrr | 11D MM000 RRRRRRRR
// D=0: <ea> to FPn
void
MPU680x0Device::fpu_op_fmovem_ea2reg()
{
uint mr = ir & 0x3f;
bool predec = !(ir2 & 0x1000); // 第2ワードの mode が predec か
bool dynamic = (ir2 & 0x0800);
uint8 reglist = ir2 & 0xff;
int i;
uint32 ea;
// cache worst
// FMOVEM.X <ea>,<list> 35+23n 38+23n
// FMOVEM.X <ea>,Dn 49+23n 52+23n
// 不当パターンを弾く。
switch (mr) {
case 000 ... 007: // Dn
case 010 ... 017: // An
case 040 ... 047: // -(An)
case 074: // #imm
case 075 ... 077:
fpu_op_illg2();
return;
}
// ダイナミックモードなら reglist の %0nnn0000 にレジスタ番号が
// 入っているので、この Dn の下位8ビットを取り出す。レジスタ番号以外の
// ビットは無視、Dn の上位ビットも無視。
// ついでにここでベースサイクル数を加算。
if (dynamic) {
reglist = reg.D[(reglist >> 4) & 7];
CYCLE3(fmovem_ea2reg_dyn);
} else {
CYCLE3(fmovem_ea2reg_sta);
}
// EA は最初に一度取得しておいて...
int count = __builtin_popcount(reglist);
ea = cea_fpu(12 * count);
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(ea);
return;
}
if (predec) {
// predec ならビット7から FP7..FP0 で、bit7 側から順に転送。
for (i = 7; reglist; reglist <<=1, i--) {
if ((int8)reglist < 0) {
read_12(ea, &RegFP(i));
ea += 12;
}
}
} else {
// ctrl/postinc ならビット7から FP0..FP7 で、bit7 側から順に転送。
for (i = 0; reglist; reglist <<= 1, i++) {
if ((int8)reglist < 0) {
read_12(ea, &RegFP(i));
ea += 12;
}
}
}
CYCLE3(fmovem_ea2reg_n) * count; // 副作用上等!
}
// FMOVEM.X <reglist>,<ea>
// FMOVEM.X Dn,<ea>
// 1111 ccc 000 mmmrrr | 11D MM000 RRRRRRRR
// D=1: FPn to <ea>
void
MPU680x0Device::fpu_op_fmovem_reg2ea()
{
uint mr = ir & 0x3f;
bool anpd = false; // 第1ワード EA が -(An) か
bool predec = !(ir2 & 0x1000); // 第2ワードの mode が predec か
bool dynamic = (ir2 & 0x0800);
uint8 reglist = ir2 & 0xff;
int i;
uint32 ea;
// cache worst
// FMOVEM.X <list>,<ea> 37+25n 40+25n
// FMOVEM.X Dn,<ea> 51+25n 54+25n
// 不当パターンを弾く。
// ついでに -(An) だけ覚えておく。
switch (mr) {
case 000 ... 007: // Dn
case 010 ... 017: // An
case 030 ... 037: // (An)+
case 072: // d16(PC)
case 073: // d8(PC,IX)
case 074: // #imm
case 075 ... 077:
fpu_op_illg2();
return;
case 040 ... 047: // -(An)
anpd = true;
break;
}
// ダイナミックモードなら reglist の %0nnn0000 にレジスタ番号が
// 入っているので、この Dn の下位8ビットを取り出す。レジスタ番号以外の
// ビットは無視、Dn の上位ビットも無視。
// ついでにここでベースクロックを記録
if (dynamic) {
reglist = reg.D[(reglist >> 4) & 7];
CYCLE3(fmovem_reg2ea_dyn);
} else {
CYCLE3(fmovem_reg2ea_sta);
}
int count = __builtin_popcount(reglist);
// -(An) の時だけ EA を別処理。
// 現状の cea_fpu() は -(An) の時、巻き戻し用のレジスタを保存し
// デクリメントしたアドレスを返すまでを一度に行う便利関数だが、
// FMOVEM fpn,-(An) は最初に巻き戻し用レジスタを保存し (これは命令先頭
// に戻るためなので最初の1回だけ)、その後レジスタ1つずつ 12バイトを
// プリデクリメントしながら格納していく必要がある (この順に実行しないと
// バスエラーの原因箇所が異なってしまう)。
// そのため cea_fpu() がそのまま使えないので、ここだけ独自対応する。
// cea_fpu() は1命令中で EA の取得が1回である前提のあくまで便利関数
// なので、例外的である FMOVEM は仕方がない。
if (anpd) {
CYCLE3(fmovem_reg2ea_anpd);
uint n = eanum(ir);
save_reg_pd(n);
// XXX この場合 ea はどこを指すのか?
if (__predict_false(GetFPUType().Is4060LC())) {
// Dn,An,#imm は弾いてあるので cea_fpu() と等価になる。
ea = cea_fpu(12 * count);
ExceptionFPLC(ea);
return;
}
if (predec) {
// predec ならビット7から FP7..FP0 で、bit7 側から順に転送。
for (i = 7; reglist; reglist <<= 1, i--) {
if ((int8)reglist < 0) {
reg.A[n] -= 12;
write_12(reg.A[n], &RegFP(i));
}
}
} else {
// ctrl/postinc ならビット7から FP0..FP7 で、bit7 側から順に転送。
for (i = 0; reglist; reglist <<= 1, i++) {
if ((int8)reglist < 0) {
reg.A[n] -= 12;
write_12(reg.A[n], &RegFP(i));
}
}
}
} else {
// -(An) 以外は EA を最初に一度計算しておく方法が使える
ea = cea_fpu(12 * count);
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(ea);
return;
}
if (predec) {
// predec ならビット7から FP7..FP0 で、bit7 側から順に転送。
for (i = 7; reglist; reglist <<= 1, i--) {
if ((int8)reglist < 0) {
write_12(ea, &RegFP(i));
ea += 12;
}
}
} else {
// ctrl/postinc ならビット7から FP0..FP7 で、bit7 側から順に転送。
for (i = 0; reglist; reglist <<= 1, i++) {
if ((int8)reglist < 0) {
write_12(ea, &RegFP(i));
ea += 12;
}
}
}
}
CYCLE3(fmovem_reg2ea_n) * count; // 副作用上等!
}
// FScc.B <ea>
// 1111 ccc 001 mmmrrr | 0000 000 000 CCCCCC
void
MPU680x0Device::fpu_op_fscc()
{
uint mr = ir & 0x3f;
uint mm = eamode(ir);
uint rr = eanum(ir);
uint cond = ir2 & 0x3f;
uint32 data;
uint32 ea;
// XXX 条件部に1ビット不当パターンがあるがどっちの判定が先か。
if (__predict_false(GetFPUType().Is4060LC())) {
ea = cea_fpulc(1);
ExceptionFPLC(ea);
return;
}
// cache worst
// FScc.B Dn 18 21
// FScc.B (An)+/-(An) 22 25 ; condition is true
// FScc.B (An)+/-(An) 21 24 ; condition is false
// FScc.B <ea> 20 23
if (cond > 0x1f) {
fpu_op_illg2();
return;
}
data = (test_cc(&fe, cond) == -1) ? 0xff : 0x00;
switch (mm) {
case 0: // Dn
CYCLE3(fscc_dn);
reg.D[rr] &= 0xffffff00;
reg.D[rr] |= data;
return;
case 3: // (An)+
case 4: // -(An)
// ここだけ他のメモリ系 EA とはクロックが違うし、
// 条件の成立可否でも違う。
if (data == 0x00) {
CYCLE3(fscc_anp_z);
} else {
CYCLE3(fscc_anp_nz);
}
break;
case 7:
if (mr == 0x38 || mr == 0x39) { // Abs.W, Abs.L
} else {
fpu_op_illg2();
return;
}
FALLTHROUGH;
default:
CYCLE3(fscc_mem);
break;
}
ea = cea_fpu(1);
write_1(ea, data);
}
// FDBcc Dn,<label>
// 1111 ccc 001 001rrr | 0000 000 000 CCCCCC | <16bit disp>
void
MPU680x0Device::fpu_op_fdbcc()
{
// XXX 条件部に1ビット不当パターンがあるがどっちの判定が先か。
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(0);
return;
}
uint rr = ir & 7;
uint cond = ir2 & 0x3f;
if (cond > 0x1f) {
fpu_op_illg2();
return;
}
if (test_cc(&fe, cond) == -1) {
// 真なら何もしない。disp を読み飛ばして次へ
CYCLE3(fdbcc_true);
fetch_2();
} else {
// 偽の場合
uint16 data = reg.D[rr] - 1;
reg.D[rr] = (reg.D[rr] & 0xffff0000) | data;
if (data == 0xffff) {
// ループ終了。disp を読み飛ばす
CYCLE3(fdbcc_done);
fetch_2();
} else {
// 計算に使う PC はディスプレースメントのあるアドレス。
CYCLE3(fdbcc_jump);
uint32 origin = reg.pc;
int32 disp = (int32)(int16)fetch_2();
Jump(origin + disp);
}
}
}
// FTRAPcc.W #<data>
void
MPU680x0Device::fpu_op_ftrapcc_w()
{
// XXX 条件部に1ビット不当パターンがあるがどっちの判定が先か。
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(0);
return;
}
uint cond = ir2 & 0x3f;
if (cond > 0x1f) {
fpu_op_illg2();
return;
}
// 即値はいずれにしても読み捨てる必要がある。
fetch_2();
if (test_cc(&fe, cond) == -1) {
CYCLE3(ftrapccw_excep);
Exception(M68K::EXCEP_TRAPV);
} else {
CYCLE3(ftrapccw_nop);
}
}
// FTRAPcc.L #<data>
void
MPU680x0Device::fpu_op_ftrapcc_l()
{
// XXX 条件部に1ビット不当パターンがあるがどっちの判定が先か。
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(0);
return;
}
uint cond = ir2 & 0x3f;
if (cond > 0x1f) {
fpu_op_illg2();
return;
}
// 即値はいずれにしても読み捨てる必要がある。
fetch_4();
if (test_cc(&fe, cond) == -1) {
CYCLE3(ftrapccl_excep);
Exception(M68K::EXCEP_TRAPV);
} else {
CYCLE3(ftrapccl_nop);
}
}
// FTRAPcc
void
MPU680x0Device::fpu_op_ftrapcc()
{
// XXX 条件部に1ビット不当パターンがあるがどっちの判定が先か。
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(0);
return;
}
uint cond = ir2 & 0x3f;
if (cond > 0x1f) {
fpu_op_illg2();
return;
}
// 即値なし。
if (test_cc(&fe, cond) == -1) {
CYCLE3(ftrapcc_excep);
Exception(M68K::EXCEP_TRAPV);
} else {
CYCLE3(ftrapcc_nop);
}
}
// FBcc.W <label>
// 1111 ccc 01S CCCCCC | <16bit disp>
// S=0: 16bit disp
//
// FNOP
// 1111 ccc 010 000000 | 0000 0000 0000 0000
void
MPU680x0Device::fpu_op_fbcc_w()
{
// XXX 条件部に1ビット不当パターンがあるがどっちの判定が先か。
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(0);
return;
}
uint cond = ir & 0x3f;
// TODO: Illegal condition の戻り値を処理すること。他の test_cc() も同様。
if (test_cc(&fe, cond) == -1) {
CYCLE3(fbcc_jump);
uint32 origin = reg.pc;
int32 disp = (int32)(int16)fetch_2();
Jump(origin + disp);
} else {
// disp を読み飛ばす
CYCLE3(fbccw_nop);
fetch_2();
}
}
// FBcc.L <label>
// 1111 ccc 01S CCCCCC | <32bit disp>
// S=1: 32bit disp
void
MPU680x0Device::fpu_op_fbcc_l()
{
// XXX 条件部に1ビット不当パターンがあるがどっちの判定が先か。
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(0);
return;
}
uint cond = ir & 0x3f;
if (test_cc(&fe, cond) == -1) {
CYCLE3(fbcc_jump);
uint32 origin = reg.pc;
int32 disp = (int32)fetch_4();
Jump(origin + disp);
} else {
// disp を読み飛ばす
CYCLE3(fbccl_nop);
fetch_4();
}
}
// FSAVE の NULL フレーム (全機種共通で4バイト)
/*static*/ const uint32
MPU680x0Device::fsave_frame_null[] = {
0,
};
// FSAVE のアイドルフレーム (68881)
/*static*/ const uint32
MPU680x0Device::fsave_frame_idle_68881[] = {
0x1f180000,
0x0000ffff,
0,
0,
0,
0,
0x70000000,
};
// FSAVE のアイドルフレーム (68040)
/*static*/ const uint32
MPU680x0Device::fsave_frame_idle_68040[] = {
0x41000000,
};
// FSAVE
// スーパーバイザモードは判定済み
void
MPU680x0Device::fpu_op_fsave()
{
uint32 ea;
std::array<uint32, 13> frame_noimpl;
const uint32 *frame;
int framelen;
// フレームを選択。
switch (fpu_state) {
case FPU_STATE_NULL:
// NULL フレーム
CYCLE3(fsave_null);
frame = fsave_frame_null;
framelen = countof(fsave_frame_null);
break;
case FPU_STATE_IDLE:
case FPU_STATE_BUSY: // 実装してないけどとりあえず。
// IDLE フレーム
CYCLE3(fsave_idle); // XXX これは最小値らしいけど
if (fpu_type.Is4060FPU()) {
frame = fsave_frame_idle_68040;
framelen = countof(fsave_frame_idle_68040);
} else {
frame = fsave_frame_idle_68881;
framelen = countof(fsave_frame_idle_68881);
}
break;
case FPU_STATE_NOIMPL:
// 68040 で浮動小数点未実装命令を実行した。
fpu_op_fsave_frame_noimpl(frame_noimpl);
frame = frame_noimpl.data();
framelen = frame_noimpl.size();
break;
default:
PANIC("fpu_state=%u corrupted", fpu_state);
}
switch (ir & 077) {
case 020 ... 027: // (An)
case 040 ... 047: // -(An)
case 050 ... 057: // d16(An)
case 060 ... 067: // d8(An,IX)
case 070 ... 071: // Abs.[WL]
ea = cea_fpu(framelen * 4);
break;
default:
fpu_op_illg();
return;
}
// XXX 実機がどう動くか分からないが、とりあえずここでは
// LC なら fpu_state は NULL から変化しないのでフレーム長 4 として動く。
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(ea);
return;
}
// -(An) もそれ以外もどちらも、まず先頭フレームを書き出す。
// この時点で ea は先頭フレームの位置を指している。
write_4(ea, frame[0]);
// -(An) もそれ以外もどちらも、
// 残りはプリデクリメントのように高位側から順に書き出す。
ea += framelen * 4;
for (int i = framelen - 1; i > 0; i--) {
ea -= 4;
write_4(ea, frame[i]);
}
}
// FRESTORE
// スーパーバイザモードは判定済み
void
MPU680x0Device::fpu_op_frestore()
{
uint mm = eamode(ir);
uint rr = eanum(ir);
uint32 ea;
uint32 fmtword;
uint32 ver_len;
uint32 ver;
uint32 len;
bool postinc;
postinc = false;
// EA は独自体系なのでここでべたに書く
switch (mm) {
case 2: // (An)
ea = cea_anin(rr);
break;
case 3: // (An)+
ea = cea_anin(rr);
postinc = true;
break;
case 5: // d16(An)
ea = cea_andi(rr);
break;
case 6: // (An,IX)
ea = cea_anix(rr);
break;
case 7:
switch (rr) {
case 0: // Abs.W
ea = cea_absw();
break;
case 1: // Abs.L
ea = cea_absl();
break;
case 2: // d16(PC)
ea = cea_pcdi();
break;
case 3: // (PC,IX)
ea = cea_pcix();
break;
default:
fpu_op_illg();
return;
}
break;
default:
fpu_op_illg();
return;
}
if (__predict_false(GetFPUType().Is4060LC())) {
ExceptionFPLC(ea);
return;
}
// 先頭の1ロングワードを読んでみる。
// これがフォーマットワード $VVLL'xxxx で、VV がバージョン(8bit)、
// LL が(自身を含まないこれ以降の) バイト数 (8bit) を示している。
// 68881 本では下位ワードは Reserved($0000) となっているが、
// 68040 本では Reserved のみで値の言及はない。
//
// 68881:
// NULL $0000'0000
// IDLE $1f18'0000 (以降 6 ロングワード)
// BUSY $1fb4'0000 (以降 45 ロングワード) (*)
//
// 68040:
// NULL $0000'xxxx
// IDLE $4100'xxxx
// BUSY $4160'xxxx (以降 24 ロングワード) (*)
// UNIMP $4130'xxxx (以降 12 ロングワード)
//
// *: BUSY フレームは現状の FSAVE が出力しないので来ないはず。
fmtword = read_4(ea);
ea += 4;
ver_len = fmtword >> 16;
ver = ver_len >> 8;
len = ver_len & 0xff;
if (GetFPUType().Is4060FPU()) {
if (ver == 0) {
// NULL フレーム。
// NULL フレームのリストアは FPU の状態をリセットする。
CYCLE3(frestore_null);
ResetFPU(false);
goto done;
} else if (ver_len == 0x4100) {
// IDLE フレーム。
// 現状何も実装していないので、状態を戻して読み捨てるだけ。
CYCLE3(frestore_idle);
fpu_state = FPU_STATE_IDLE;
} else if (ver_len == 0x4130) {
// 浮動小数点未実装命令フレーム。
// どうなる?
putlog(0, "FRESTORE: Unsupported UNIMP frame");
} else if (ver_len == 0x4160) {
// BUSY フレーム。
putlog(0, "FRESTORE: Unsupported BUSY frame");
if (0) {
putlog(0, "[+00] %08x", fmtword);
for (int offset = 4; offset < len; offset += 4) {
uint32 m = read_4(ea + offset);
putlog(0, "[+%02x] %08x", offset, m);
}
}
} else {
Exception(M68K::EXCEP_FORMAT);
goto done;
}
} else /* 68881 */ {
if (ver == 0) {
// NULL フレーム。
// 長さフィールドは見ない。(MC68881UM.pdf 6.4.2.1)
// NULL フレームのリストアは FPU の状態をリセットする。
CYCLE3(frestore_null);
ResetFPU(false);
goto done;
} else if (ver_len == 0x1f18) {
// IDLE フレーム。
// 現状何も実装していないので、状態を戻して読み捨てるだけ。
CYCLE3(frestore_idle);
fpu_state = FPU_STATE_IDLE;
} else {
if (ver_len == 0x1fb4) {
putlog(0, "FRESTORE: Unsupported BUSY frame");
}
Exception(M68K::EXCEP_FORMAT);
goto done;
}
}
// とにかく残りを全部読み捨てる。
len /= 4;
for (int i = 0; i < len; i++) {
read_4(ea);
ea += 4;
}
done:
// (An)+ なら更新。
// フォーマットワードを読んだところでエラーが起きたら +4 だけ更新される?
if (postinc) {
reg.A[rr] = ea;
}
}
// 現在の fpu40 の状態から
// 68040 の浮動小数点未実装命令フレームを作成して frame に書き出す。
void
MPU680x0Device::fpu_op_fsave_frame_noimpl(std::array<uint32, 13>& frame)
{
uint32 cmd1;
uint32 reg3b;
uint32 data6;
// CMDREG1B 形式の Ry+CMD1 から CMDREG3B 形式の Ry+CMD3 を作る。
cmd1 = fpu40.reg1b & 0x3ff;
reg3b = (cmd1 & 0x3c3) | ((cmd1 >> 1) & 0x1c) | ((cmd1 << 3) & 0x20);
data6 = fpu40.eflag;
if (fpu40.post) {
data6 |= 0x0010'0000;
}
frame[0] = 0x41300000;
frame[1] = reg3b << 16;
frame[2] = 0;
frame[3] = (uint32)fpu40.stag << 29;
frame[4] = fpu40.reg1b << 16;
frame[5] = (uint32)fpu40.dtag << 29;
frame[6] = data6;
memcpy(&frame[7], fpu40.fptemp, 12);
memcpy(&frame[10], fpu40.etemp, 12);
// モニタ表示用。
fpu40.enable = false;
}
// 浮動小数点系の例外。
// fpu40 の stag, dtag, etemp, fptemp, eflag を必要に応じてセットしてから
// 呼ぶこと。
void
MPU680x0Device::ExceptionFP(uint vector)
{
fpu_state = FPU_STATE_NOIMPL;
// CMDREG1B は実質 2ワード目のことのようだ。
// FMOVECR のケースはドキュメントに見当たらないが、FPSP を読む限り
// CMDREG1B はここでも FMOVECR の第2ワードそのままのようだ。
// FSQRT 用のハックで cmd(offset) の $04 を $05 に書き換えるが、FPSP の
// FMOVECR はどちらも 0.0 を返すので、書き換えてしまっても問題ないはず?
uint32 reg1b = ir2;
if ((reg1b & 0x3f) == 0x04) {
// FSQRT だけマッピングが違う。
reg1b++;
}
fpu40.reg1b = reg1b;
fpu40.post = false;
fpu40.enable = true;
// この例外を起こした命令のアドレスは FPIAR に置く。
reg.fpframe.fpf_fpiar = ppc;
// EXCEP_FP_UNIMPL (未実装命令例外) はベクタが F ラインと同じで、
// フレームフォーマットが違うので、定数値に細工がしてある。
const uint32 info = BranchHistory_m680x0::InfoException(vector & 0xff);
ExceptionGeneric(vector, info);
}
// CMDREG1B からコマンド名を返す。
// FSQRT 命令の第2ワードの cmd 部分は $04 だが CMDREG1B では $05 になる。
/*static*/ std::string
MPU68040Device::GetReg1BName(uint32 opclass, uint32 sz, uint32 cmd)
{
std::string name;
if (opclass != 0 && opclass != 2) {
return "";
}
if (sz == 7) {
return "fmovecr";
}
if (__predict_false(0x30 <= cmd && cmd < 0x38)) {
name = "fsincos";
name += '0' + (cmd - 0x30);
return name;
}
if (__predict_false(cmd == 0x04)) {
// 来ないはずだけど一応。
return "fsqrt(4)";
}
if (__predict_false(cmd == 0x05)) {
cmd = 0x04;
}
name = m680x0disasm::make_fpgen(cmd);
return name;
}
/*static*/ std::string
MPU68040Device::GetFPUTagName(int32 tag)
{
const char * const tagnames[] = {
"(Normal)",
"(Zero)",
"(Inf)",
"(NAN)",
"(Unnormal)",
"(Denormal)",
};
if (__predict_true(tag < countof(tagnames))) {
return tagnames[tag];
}
if (tag == FPU40::TAG_DENORMAL_S) {
return "(Denormal.S)";
}
if (tag == FPU40::TAG_DENORMAL_D) {
return "(Denormal.D)";
}
if (tag == FPU40::TAG_DENORMAL_X) {
return "(Denormal.X)";
}
if (tag == FPU40::TAG_UNNORMAL_X) {
return "(Unnormal.X)";
}
if (tag == FPU40::TAG_NONE) {
return "(NONE)";
}
return "";
}
// モニター更新 (040 内部情報)
void
MPU68040Device::MonitorScreenFPU40(TextScreen& screen, int y, const FPU40& tmp)
{
// CMDREG1B:$xxxx OPCLASS=0 Rs=7 Rd=7 cmd=00(FTWOTOX) Flags: E3 E1 T
// ETEMP :$01234567_01234567_01234567 STAG:1(Unnormal)
// FPTEMP:
TA attr = tmp.enable ? TA::Normal : TA::Disable;
uint32 reg1b = tmp.reg1b;
uint32 opclass = reg1b >> 13;
uint32 rx = (reg1b >> 10) & 7;
uint32 ry = (reg1b >> 7) & 7;
uint32 cmd = reg1b & 0x7f;
std::string cmdname = GetReg1BName(opclass, rx, cmd);
if (__predict_true(cmdname.empty() == false)) {
cmdname = '(' + cmdname + ')';
}
std::string stagname = GetFPUTagName(tmp.stag);
std::string dtagname = GetFPUTagName(tmp.dtag);
screen.Print(0, y, attr,
"CMDREG1B:%04x OPCLASS=%u Rs=%u Rd=%u CMD=$%02x%s",
reg1b, opclass, rx, ry, cmd, cmdname.c_str());
screen.Puts(56, y, attr, "Flags:");
screen.Puts(62, y, attr, (tmp.eflag & FPU40::E1) ? "E1" : "--");
screen.Puts(65, y, attr, (tmp.eflag & FPU40::E3) ? "E3" : "--");
screen.Puts(68, y, attr, (tmp.post) ? "T" : "-");
y++;
screen.Print(0, y, attr, "ETEMP: %08x_%08x_%08x STAG:%d%s",
tmp.etemp[0], tmp.etemp[1], tmp.etemp[2],
(__predict_true(tmp.stag >= 0) ? (tmp.stag & 7) : tmp.stag),
stagname.c_str());
screen.Print(56, y, attr, "EA:%08x", tmp.ea);
y++;
screen.Print(0, y++, attr, "FPTEMP:%08x_%08x_%08x DTAG:%d%s",
tmp.fptemp[0], tmp.fptemp[1], tmp.fptemp[2],
(__predict_true(tmp.dtag >= 0) ? (tmp.dtag & 7) : tmp.dtag),
dtagname.c_str());
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.