|
|
nono 1.0.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
#include "mpu680x0.h"
// リセット用のレジスタ初期値
/*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 レジスタの初期値を用意しておく
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 リセット時と、NULL フレームのリストア時に呼ばれる。
void
MPU680x0Device::fpu_reset()
{
// 高速化のため、あらかじめ用意しておいたイメージをコピーする
memcpy(®.fpframe, &initial_fpframe, sizeof(initial_fpframe));
fpu_dirty = false;
}
// FPU からの呼び出し用。
//
// (An)+, -(An) は、サイズが増えているのとクロックが増加している(2->6)。
// それ以外のアドレッシングモードでもクロック数は微妙に違うのだが、
// 大きくは違わないのでそっちは本体の cea と同じということにする。
//
// このルーチンは Dn、An、#imm には対応していないので(自明)、呼び出し側で
// 事前に弾くこと。いずれも消費クロック数は 0 なので問題ない。
// またそれ以外のすべてのアドレッシングモードで反応するため、命令ごとに
// 対応していないアドレッシングモードは事前に弾くこと。
inline 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)+
CYCLE(6);
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)
CYCLE(6);
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;
}
// メモリから 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ビット値(.D)をメモリに書き込む
void
MPU680x0Device::write_8(uint32 addr, const uint32 *data)
{
write_4(addr, data[0]);
write_4(addr + 4, data[1]);
}
// 96ビット値(.D)をメモリに書き込む
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]);
}
// ソースを読み込む。
// 不当命令パターンなら false を返す。
// size_id は SIZE_*。
bool
MPU680x0Device::readSRC(int size_id, struct fpn *fpn)
{
uint mr = ir & 0x3f;
uint32 ea;
uint32 data[3];
// 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 (size_id) {
case SIZE_B:
data[0] = (int32)(int8)reg.D[mr];
break;
case SIZE_W:
data[0] = (int32)(int16)reg.D[mr];
break;
case SIZE_L:
case SIZE_S:
data[0] = reg.D[mr];
break;
default:
return false;
}
// ソースが Dn の場合は 5クロック減
CYCLE(-5);
} else if (mr == 0x3c) { // #imm
switch (size_id) {
case SIZE_B:
data[0] = fetch_2() & 0xff;
break;
case SIZE_W:
data[0] = fetch_2();
break;
case SIZE_L:
case SIZE_S:
data[0] = fetch_4();
break;
case SIZE_D:
data[0] = fetch_4();
data[1] = fetch_4();
break;
case SIZE_X:
case SIZE_P:
data[0] = fetch_4();
data[1] = fetch_4();
data[2] = fetch_4();
break;
default:
return false;
}
} else {
// それ以外は EA を求めてからメモリから読み込む
switch (size_id) {
case SIZE_B:
ea = cea_fpu(1);
data[0] = (int32)(int8)read_1(ea);
break;
case SIZE_W:
ea = cea_fpu(2);
data[0] = (int32)(int16)read_2(ea);
break;
case SIZE_L:
case SIZE_S:
ea = cea_fpu(4);
data[0] = read_4(ea);
break;
case SIZE_D:
ea = cea_fpu(8);
read_8(ea, data);
break;
case SIZE_X:
case SIZE_P:
ea = cea_fpu(12);
read_12(ea, data);
break;
default:
return false;
}
}
// 型別に fpn に変換
switch (size_id) {
case SIZE_B:
case SIZE_W:
case SIZE_L:
CYCLE(+27);
fpu_explode(&fe, fpn, FTYPE_LNG, data);
break;
case SIZE_S:
CYCLE(+19);
fpu_explode(&fe, fpn, FTYPE_SNG, data);
break;
case SIZE_D:
CYCLE(+25);
fpu_explode(&fe, fpn, FTYPE_DBL, data);
break;
case SIZE_X:
CYCLE(+23);
fpu_explode(&fe, fpn, FTYPE_EXT, data);
break;
case SIZE_P:
CYCLE(+837);
fpu_explode(&fe, fpn, FTYPE_BCD, data);
break;
}
return true;
}
// 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 (レジスタ-レジスタ演算)
void
MPU680x0Device::fpu_op_fgen_reg()
{
uint srcnum = (ir2 >> 10) & 7;
uint dstnum = (ir2 >> 7) & 7;
fpu_explode(&fe, &fe.fe_f2, FTYPE_EXT, &RegFP(srcnum * 3));
fpu_explode(&fe, &fe.fe_f1, FTYPE_EXT, &RegFP(dstnum * 3));
fgen();
}
// R/M=1 (メモリ-レジスタ演算)
void
MPU680x0Device::fpu_op_fgen_mem()
{
uint size_id = (ir2 >> 10) & 7;
uint dstnum = (ir2 >> 7) & 7;
// source specifier はオペランドサイズ
if (size_id == 7) {
// こんなところに FMOVECR
fpu_op_fmovecr();
return;
}
// 第1ワードの EA がソース EA
if (!readSRC(size_id, &fe.fe_f2)) {
fpu_op_illg2();
return;
}
fpu_explode(&fe, &fe.fe_f1, FTYPE_EXT, &RegFP(dstnum * 3));
fgen();
}
// 一般算術演算
void
MPU680x0Device::fgen()
{
uint opmode = ir2 & 0x3f;
uint dstnum = (ir2 >> 7) & 7;
bool discard_result = false;
struct fpn *res = NULL; // shutup gcc
switch (opmode) {
case 0x00: // FMOVE
CYCLE(33);
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
CYCLE(107);
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
CYCLE(35);
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
CYCLE(35);
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
CYCLE(103);
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
CYCLE(51);
res = fpu_add(&fe);
fpu_round_prec(&fe, res);
break;
case 0x23: // FMUL
CYCLE(71);
res = fpu_mul(&fe);
fpu_round_prec(&fe, res);
break;
case 0x24: // FSGLDIV
CYCLE(69);
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
CYCLE(59);
res = fpu_sglmul(&fe);
break;
case 0x28: // FSUB
CYCLE(51);
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
CYCLE(33);
res = fpu_cmp(&fe);
discard_result = 1;
break;
case 0x3a: // FTST
CYCLE(33);
res = &fe.fe_f2;
discard_result = 1;
break;
default:
// 不当命令
fpu_op_illg2();
return;
}
assert(res);
if (!discard_result) {
fpu_implode(&fe, res, FTYPE_EXT, &RegFP(dstnum * 3));
}
fpu_upd_fpsr(&fe, res);
fpu_upd_excp(&fe);
}
// FMOVECR
void
MPU680x0Device::fpu_op_fmovecr()
{
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 * 3));
fpu_upd_fpsr(&fe, &fe.fe_f3);
fpu_upd_excp(&fe);
}
// 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;
fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum * 3));
fpu_implode(&fe, &fe.fe_f3, FTYPE_LNG, &data);
if ((int32)data < INT8_MIN || (int32)data > INT8_MAX) {
RegFPSR |= FPSR_OPERR | FPSR_AIOP;
}
if (mr < 8) { // Dn
CYCLE(100-2);
reg.D[mr] &= 0xffffff00;
reg.D[mr] |= (uint8)data;
} else {
CYCLE(100);
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;
fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum * 3));
fpu_implode(&fe, &fe.fe_f3, FTYPE_LNG, &data);
if ((int32)data < INT16_MIN || (int32)data > INT16_MAX) {
RegFPSR |= FPSR_OPERR | FPSR_AIOP;
}
if (mr < 8) { // Dn
CYCLE(100-2);
reg.D[mr] &= 0xffff0000;
reg.D[mr] |= (uint16)data;
} else {
CYCLE(100);
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;
fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum * 3));
fpu_implode(&fe, &fe.fe_f3, FTYPE_LNG, &data);
// XXX オーバーフローチェック
if (mr < 8) { // Dn
CYCLE(100-2);
reg.D[mr] = data;
} else {
CYCLE(100);
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;
fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum * 3));
fpu_implode(&fe, &fe.fe_f3, FTYPE_SNG, &data);
if (mr < 8) { // Dn
CYCLE(80-2);
reg.D[mr] = data;
} else {
CYCLE(80);
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()
{
CYCLE(86);
uint srcnum = (ir2 >> 7) & 7;
uint32 ea;
uint32 data[2];
fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum * 3));
fpu_implode(&fe, &fe.fe_f3, FTYPE_DBL, data);
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()
{
CYCLE(72);
uint srcnum = (ir2 >> 7) & 7;
uint32 ea;
ea = cea_fpu(12);
write_12(ea, &RegFP(srcnum * 3));
}
// FMOVE.P FPn,<ea>
// cf. 68000PRM.pdf p378 (5-76)
void
MPU680x0Device::fpu_op_fmove_p_mem()
{
uint srcnum = (ir2 >> 7) & 7;
uint32 ea;
uint32 data[3];
uint32 k;
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 * 3));
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クロックだけ違う
// ようだけどもう無視する。
// 転送するレジスタ数
// ただし nofpc は FPIAR とみなされるようだ。see exp/fmovem.php
if (reglist == 0)
reglist = 1;
count = __builtin_popcount(reglist);
if (mr < 8) { // Dn
CYCLE2(28, 31);
// 対データレジスタは転送数 1 のみ有効
if (count > 1) {
fpu_op_illg2();
return;
}
src[0] = reg.R[mr];
} else if (mr < 16) { // An
CYCLE2(28, 31);
// 対アドレスレジスタは FPIAR のみ有効
if (reglist != 1) {
fpu_op_illg2();
return;
}
src[0] = reg.R[mr];
} else if (mr < 0x3c) { // 対メモリ
CYCLE2(28, 30);
CYCLE(count * 6);
// EA を取得。
// -(An) の時も先にレジスタ数分一気に減算して、そこからプラス方向に
// 読み込んでいくので、ロジックは全部同じになる。
// (68000PRM.pdf p393)
ea = cea_fpu(count * 4);
for (i = 0; i < count; i++) {
src[i] = read_4(ea);
ea += 4;
}
} else if (mr == 0x3c) { // #imm
CYCLE2(25, 29);
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
// 転送するレジスタ数
// ただし nofpc は FPIAR とみなされるようだ。see exp/fmovem.php
if (reglist == 0)
reglist = 1;
count = __builtin_popcount(reglist);
if (mr < 8) { // Dn
CYCLE2(31, 34);
// 対データレジスタは転送数は 1 のみ有効
if (count > 1) {
fpu_op_illg2();
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
CYCLE2(31, 34);
// 対アドレスレジスタは FPIAR のみ有効
if (reglist != 1) {
fpu_op_illg2();
return;
}
reg.R[mr] = RegFPIAR;
return;
}
if (mr >= 0x3a) { // #imm, d16(PC), (PC,IX)
fpu_op_illg2();
return;
}
// それ以外、対メモリの場合。
CYCLE2(27, 30);
CYCLE(count * 6);
// EA を取得して...
ea = cea_fpu(count * 4);
// 該当レジスタをメモリへ書き出し
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);
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 0x00 ... 0x07: // Dn
case 0x08 ... 0x0f: // An
case 0x20 ... 0x27: // -(An)
case 0x3c: // #imm
case 0x3d ... 0x3f:
fpu_op_illg2();
return;
}
// ダイナミックモードなら reglist の %0nnn0000 にレジスタ番号が
// 入っているので、この Dn の下位8ビットを取り出す。レジスタ番号以外の
// ビットは無視、Dn の上位ビットも無視。
// ついでにここでベースクロックを記録
if (dynamic) {
reglist = reg.D[(reglist >> 4) & 7];
CYCLE2(49, 52);
} else {
CYCLE2(35, 38);
}
// EA は最初に一度取得しておいて...
int count = __builtin_popcount(reglist);
ea = cea_fpu(12 * count);
if (predec) {
// predec ならビット7から FP7..FP0 で、bit7 側から順に転送。
for (i = 7; reglist; reglist <<=1, i--) {
if ((int8)reglist < 0) {
read_12(ea, &RegFP(i * 3));
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 * 3));
ea += 12;
}
}
}
CYCLE(23 * 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 0x00 ... 0x07: // Dn
case 0x08 ... 0x0f: // An
case 0x18 ... 0x1f: // (An)+
case 0x3a: // d16(PC)
case 0x3b: // (PC,IX)
case 0x3c: // #imm
case 0x3d ... 0x3f:
fpu_op_illg2();
return;
case 0x20 ... 0x27: // -(An)
anpd = true;
break;
}
// ダイナミックモードなら reglist の %0nnn0000 にレジスタ番号が
// 入っているので、この Dn の下位8ビットを取り出す。レジスタ番号以外の
// ビットは無視、Dn の上位ビットも無視。
// ついでにここでベースクロックを記録
if (dynamic) {
reglist = reg.D[(reglist >> 4) & 7];
CYCLE2(51, 54);
} else {
CYCLE2(37, 40);
}
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) {
CYCLE(6);
uint n = eanum(ir);
save_reg_pd(n);
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 * 3));
}
}
} 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 * 3));
}
}
}
} else {
// -(An) 以外は EA を最初に一度計算しておく方法が使える
ea = cea_fpu(12 * count);
if (predec) {
// predec ならビット7から FP7..FP0 で、bit7 側から順に転送。
for (i = 7; reglist; reglist <<= 1, i--) {
if ((int8)reglist < 0) {
write_12(ea, &RegFP(i * 3));
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 * 3));
ea += 12;
}
}
}
}
CYCLE(25 * 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;
// 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
CYCLE2(18, 21);
reg.D[rr] &= 0xffffff00;
reg.D[rr] |= data;
return;
case 3: // (An)+
case 4: // -(An)
// ここだけ他のメモリ系 EA とはクロックが違うし、
// 条件の成立可否でも違う。
if (data == 0x00) {
CYCLE2(21, 24);
} else {
CYCLE2(22, 25);
}
break;
case 7:
if (mr == 0x38 || mr == 0x39) { // Abs.W, Abs.L
} else {
fpu_op_illg2();
return;
}
FALLTHROUGH;
default:
CYCLE2(20, 23);
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()
{
uint rr = ir & 7;
uint cond = ir2 & 0x3f;
if (cond > 0x1f) {
fpu_op_illg2();
return;
}
if (test_cc(&fe, cond) == -1) {
// 真なら何もしない。disp を読み飛ばして次へ
CYCLE2(20, 24);
fetch_2();
} else {
// 偽の場合
uint16 data = reg.D[rr] - 1;
reg.D[rr] = (reg.D[rr] & 0xffff0000) | data;
if (data == 0xffff) {
// ループ終了。disp を読み飛ばす
CYCLE2(24, 32);
fetch_2();
} else {
// 計算に使う PC は FDBcc 命令+2 のところらしい
CYCLE2(20, 26);
uint32 origin = ppc + 2;
int32 disp = (int32)(int16)fetch_2();
Jump(origin + disp);
}
}
}
// FTRAPcc.W #<data>
void
MPU680x0Device::fpu_op_ftrapcc_w()
{
uint cond = ir2 & 0x3f;
if (cond > 0x1f) {
fpu_op_illg2();
return;
}
// 即値はいずれにしても読み捨てる必要がある。
fetch_2();
if (test_cc(&fe, cond) == -1) {
CYCLE2(41, 45);
Exception(M68K::EXCEP_TRAPV);
} else {
CYCLE2(20, 23);
}
}
// FTRAPcc.L #<data>
void
MPU680x0Device::fpu_op_ftrapcc_l()
{
uint cond = ir2 & 0x3f;
if (cond > 0x1f) {
fpu_op_illg2();
return;
}
// 即値はいずれにしても読み捨てる必要がある。
fetch_4();
if (test_cc(&fe, cond) == -1) {
CYCLE2(43, 52);
Exception(M68K::EXCEP_TRAPV);
} else {
CYCLE2(22, 27);
}
}
// FTRAPcc
void
MPU680x0Device::fpu_op_ftrapcc()
{
uint cond = ir2 & 0x3f;
if (cond > 0x1f) {
fpu_op_illg2();
return;
}
// 即値なし。
if (test_cc(&fe, cond) == -1) {
CYCLE2(39, 47);
Exception(M68K::EXCEP_TRAPV);
} else {
CYCLE2(18, 22);
}
}
// 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()
{
uint cond = ir & 0x3f;
// TODO: Illegal condition の戻り値を処理すること。他の test_cc() も同様。
if (test_cc(&fe, cond) == -1) {
CYCLE2(20, 23);
uint32 origin = reg.pc;
int32 disp = (int32)(int16)fetch_2();
Jump(origin + disp);
} else {
// disp を読み飛ばす
CYCLE2(18, 19);
fetch_2();
}
}
// FBcc.L <label>
// 1111 ccc 01S CCCCCC | <32bit disp>
// S=1: 32bit disp
void
MPU680x0Device::fpu_op_fbcc_l()
{
uint cond = ir & 0x3f;
if (test_cc(&fe, cond) == -1) {
CYCLE2(20, 23);
uint32 origin = reg.pc;
int32 disp = (int32)fetch_4();
Jump(origin + disp);
} else {
// disp を読み飛ばす
CYCLE2(18, 21);
fetch_4();
}
}
// FSAVE の NULL フレーム (全機種共通で4バイト)
/*static*/ const uint32
MPU680x0Device::fsave_frame_null[] = {
0,
};
// FSAVE のアイドルフレーム (68881 は 0x1c = 28 バイト、 7ロングワード)
/*static*/ const uint32
MPU680x0Device::fsave_frame_idle[] = {
0x1f180000,
0,
0,
0,
0,
0,
0x70000000,
};
// FSAVE
// スーパーバイザモードは判定済み
void
MPU680x0Device::fpu_op_fsave()
{
uint mm = eamode(ir);
uint rr = eanum(ir);
uint32 ea;
const uint32 *frame;
int framelen;
// XXX EA が -(An) の時 An が逐次減算て書いてあるソースきぼんぬ
// フレームを選択
if (fpu_dirty == false) {
// NULL フレーム
CYCLE2(16, 18);
frame = fsave_frame_null;
framelen = countof(fsave_frame_null);
} else {
// IDLE フレーム
CYCLE2(52, 54); // XXX これは最小値らしいけど
frame = fsave_frame_idle;
framelen = countof(fsave_frame_idle);
}
// EA は独自体系なのでここでべたに書く
switch (mm) {
case 2: // (An)
ea = cea_anin(rr);
break;
case 4: // -(An)
// これだけ処理が違うので、ここでやる
save_reg_pd(rr);
for (int i = framelen - 1; i >= 0; i--) {
reg.A[rr] -= 4;
write_4(reg.A[rr], frame[i]);
}
return;
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;
default:
fpu_op_illg();
return;
}
break;
default:
fpu_op_illg();
return;
}
// -(An) 以外の EA の場合
for (int i = 0; i < framelen; i++) {
write_4(ea, frame[i]);
ea += 4;
}
}
// FRESTORE
// スーパーバイザモードは判定済み
void
MPU680x0Device::fpu_op_frestore()
{
uint mm = eamode(ir);
uint rr = eanum(ir);
uint32 ea;
uint32 data;
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;
}
// 1ロングワード読んでみる
data = read_4(ea);
ea += 4;
if (data == 0) {
// NULL フレームなら、これ以上フレームはない。
CYCLE2(21, 22);
// NULL フレームのリストアは FPU の状態をリセットする
fpu_reset();
} else {
// それ以外のフレームは先頭ロングワードから長さを取得。
// 先頭1ロングワードは $VVLL0000 で VV がバージョン(8bit)、
// LL が(自身を含まないこれ以降の)バイト数 (8bit) を示している。
// 現在の実装ではセーブ/リストアは何も実装していないので、
// 全部読み捨てる。
// 実際にはフレーム種別によってサイクル数は変わるけど、ここでは
// FSAVE は NULL フレーム以外には IDLE フレームしか吐かないので
// IDLE フレームのサイクルだけでいい。
CYCLE2(57, 58);
uint len = (data >> 16) & 0xff;
len /= 4;
for (int i = 0; i < len; i++) {
read_4(ea);
ea += 4;
}
fpu_dirty = true;
}
// (An)+ なら書き戻し
if (postinc) {
reg.A[rr] = ea;
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.