|
|
nono 1.5.0
//
// nono
// Copyright (C) 2021 nono project
// Licensed under nono-license.txt
//
// instruction test for m88100
//
// optestm88k_gen.cpp (this file)
// |
// | compile and link on the host
// v
// optestm88k_gen
// |
// | (executing it will create following files)
// v
// +--> optestm88k_add.s ---+
// +--> optestm88k_bitfield.s ---+
// +--> optestm88k_fcmp.s ---+
// +--> optestm88k_flt.s ---+
// +--> optestm88k_lda.s ---+
// +--> optestm88k_logical.s ---+
// +--> optestm88k_muldiv.s ---+
// +--> optestm88k_sub.s ---+
// +--> optestm88k_table.c ---+
// +--> optestm88k_table.h ---+
// |
// optestm88k_main.[ch] ---+
// optestm88k_code.s ---+
// | compile and link on m88k
// v
// optestm88k
// XXX mul/div/divu 命令の FPU disabled のケースは未検査
#include "nono.h"
#include "mystring.h"
#include <assert.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <algorithm>
#include <cfenv>
#include <cmath>
#include <tuple>
#include <vector>
struct fpi
{
union {
double d {};
uint64 q;
// float は h の位置に置く
#if BYTE_ORDER == LITTLE_ENDIAN
struct { uint32 l, h; };
struct { float dummy_f, f; };
struct { uint32 dummy_i, i; };
#else
struct { uint32 h, l; };
struct { float f, dummy_f; };
struct { uint32 i, dummy_i; };
#endif
};
enum {
NONE,
FLOAT,
DOUBLE,
INT,
} type {};
bool IsFloat() const { return (type == FLOAT); }
bool IsDouble() const { return (type == DOUBLE); }
bool IsInt() const { return (type == INT); }
// コンストラクタ
fpi() {
}
fpi(float f_) {
Set(f_);
}
fpi(double d_) {
Set(d_);
}
fpi(uint32 i_) {
SetInt(i_);
}
// どの値を保持しているか覚えておく
void Set(float f_) {
f = f_;
type = FLOAT;
}
void Set(double d_) {
d = d_;
type = DOUBLE;
}
void Set(uint32 i_) {
i = i_;
type = FLOAT;
}
void Set(uint64 q_) {
q = q_;
type = DOUBLE;
}
void SetInt(uint32 i_) {
i = i_;
type = INT;
}
// どっちであっても double 値を返す (printf 用)
double AsDouble() const {
if (IsFloat()) {
return (double)f;
} else {
return d;
}
}
std::string ToString() const {
switch (type) {
case FLOAT:
return string_format("%9.8g", f);
case DOUBLE:
return string_format("%19.18g", d);
case INT:
return string_format("$%08x", i);
default:
return "NONE";
}
}
};
// テスト情報 (グローバル)
static const char *testname;
static int testnum;
// 出力先 (グローバル)
static FILE *fp;
#define IMM (0xff)
// 理解しやすさのため 1, 2, 3 で表現しているが実際は r8, r10, r12 を使う
static constexpr uint32 ntor(uint32 n) {
assert(n < 4);
if (n == 0) return 0;
if (n == 1) return 8;
if (n == 2) return 10;
if (n == 3) return 12;
__unreachable();
}
static constexpr uint32 R3(uint32 d, uint32 s1, uint32 s2) {
d = ntor(d);
s1 = ntor(s1);
s2 = ntor(s2);
return (d << 16) | (s1 << 8) | s2;
}
// 3オペランドの場合のレジスタの組み合わせ
static std::vector<uint32> reglist3 = {
R3(0, 0, 0),
R3(0, 0, 1),
R3(0, 1, 0),
R3(0, 1, 1),
R3(0, 1, 2),
R3(1, 0, 0),
R3(1, 0, 1),
R3(1, 1, 0),
R3(1, 1, 1),
R3(1, 0, 2),
R3(1, 2, 0),
R3(1, 2, 2),
R3(1, 1, 2),
R3(1, 2, 1),
R3(1, 2, 3),
};
// 2オペランド(+IMM)の場合のレジスタの組み合わせ
#define R2(d, s1) R3(d, s1, 0)
static std::vector<uint32> reglist2 = {
R2(0, 0),
R2(0, 1),
R2(1, 0),
R2(1, 1),
R2(1, 2),
};
// r0 用の値リスト
static std::vector<uint32> v0 = {
0,
};
// 数値演算用の入力値リスト
static std::vector<uint32> vlist_arith = {
0,
1,
2,
0x7f,
0x80,
0xff,
0x7fff,
0x8000,
0xffff,
0x7fffffff,
0x80000000,
0xffffffff,
};
// ビットフィールド用の入力値リスト
// XXX どのパターンで試せばいいか
static std::vector<uint32> vlist_bf = {
0x00000000,
0x87654321,
0xffffffff,
};
// ビットフィールド用の入力 W,O リスト
// XXX どのパターンで試せばいいか
#define WO(w, o) (((w) << 5) | (o))
static std::vector<uint32> vlist_wo = {
WO( 0, 0),
WO( 0, 1),
WO( 0, 2),
WO( 0, 31),
WO( 1, 0),
WO( 1, 1),
WO( 1, 31),
WO(16, 0),
WO(16, 15),
WO(16, 16),
WO(16, 17),
WO(16, 31),
WO(31, 0),
WO(31, 1),
WO(31, 31),
};
// ff0/ff1 用の入力値リスト
static std::vector<uint32> vlist_ff = {
0x00000000,
0x00000001,
0x00000020,
0x00000480,
0x00009000,
0x00010000,
0x00200000,
0x06000000,
0xf0000000,
};
// r0 用
static std::vector<double> vf0 = {
0.0,
};
// 浮動小数点数の入力値リスト
static std::vector<double> vlist_double = {
0.0,
1.0,
2.0,
16777215.0, // float → int で正確に表現できる最大値?
16777215.5,
16777217.0, // 16777216 は 1.0*2^24 で表現できるため省略
1000000000.0, // 2^29 以上 2^30 以下
2147483646.0, // INT32_MAX と区別可能な最大値
2147483647.0, // INT32_MAX
2147483647.5,
2147483649.0,
4294967295.0, // double → uint32 で正確に表現できる最大値?
4294967295.5,
4294967297.0,
-0.0,
-1.0,
-2.0,
-16777215.0,
-16777215.5,
-16777217.0,
-1000000000.0,
-2000000000.0,
-2147483648.0,
-2147483648.5,
-2147483649.0,
std::numeric_limits<double>::infinity(),
-std::numeric_limits<double>::infinity(),
std::nan(""),
};
#define RM_NEAR (0)
#define RM_ZERO (1)
#define RM_MINUS (2)
#define RM_PLUS (3)
static const char *rmstr[] = {
"RN",
"RZ",
"RM",
"RP",
};
template <class T>
using Tuple2_1 = std::tuple<uint32, uint32, T>;
template <class T>
using Tuple2_2 = std::tuple<uint32, uint32, T, T>;
template <class T>
using Tuple3_2 = std::tuple<uint32, uint32, uint32, T, T>;
static std::vector<Tuple3_2<uint32>> param_arith_reg;
static std::vector<Tuple2_2<uint32>> param_arith_imm;
static std::vector<Tuple3_2<uint32>> param_bitfield_reg;
static std::vector<Tuple2_2<uint32>> param_bitfield_imm;
static std::vector<Tuple2_1<uint32>> param_ff01;
static std::vector<Tuple2_1<uint32>> param_flt;
static std::vector<Tuple2_1<double>> param_trnc;
static std::vector<Tuple3_2<double>> param_fcmp;
// 2オペランドテストの要素を変数に取り出す部分 (副作用のあるマクロ)
#define VAR_DIADIC(t, rd, rs2, vs2) \
uint32 rd = std::get<0>(t); \
uint32 rs2 = std::get<1>(t); \
auto vs2 = std::get<2>(t)
// 3オペランドテストの要素を変数に取り出す部分 (副作用のあるマクロ)
#define VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2) \
uint32 rd = std::get<0>(t); \
uint32 rs1 = std::get<1>(t); \
uint32 rs2 = std::get<2>(t); \
auto vs1 = std::get<3>(t); \
auto vs2 = std::get<4>(t)
// 3オペランド(IMM)テストの要素を変数に取り出す部分 (副作用のあるマクロ)
#define VAR_TRI_IMM(t, rd, rs1, vs1, imm) \
uint32 rd = std::get<0>(t); \
uint32 rs1 = std::get<1>(t); \
auto vs1 = std::get<2>(t); \
auto imm = std::get<3>(t)
// 自前の Round Mode 定数でシステムの丸めモードを設定する。
static void
setround(uint32 rm)
{
int mode;
switch (rm) {
case RM_NEAR:
mode = FE_TONEAREST;
break;
case RM_ZERO:
mode = FE_TOWARDZERO;
break;
case RM_MINUS:
mode = FE_DOWNWARD;
break;
case RM_PLUS:
mode = FE_UPWARD;
break;
default:
PANIC("corrupted rm=%d", rm);
}
std::fesetround(mode);
}
// d が float でも同じ値として表現可能なら true を返す
static bool
can_float(double d)
{
float f = (float)d;
// NAN 同士の == 比較は false になるため
if (std::isnan(d) && std::isnan(f))
return true;
if (d == f)
return true;
return false;
}
// fp に出力
static void out(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(fp, fmt, ap);
va_end(ap);
}
// レジスタ regno に即値 imm をセットする
static void
MOV_imm(int regno, uint32 imm)
{
if (regno == 0) {
// nop
} else if (imm == 0) {
out(" xor %%r%d, %%r%d, %%r%d\n", regno, regno, regno);
} else if (imm < 0x10000) {
out(" or %%r%d, %%r0, 0x%04x\n", regno, imm);
} else if (imm == 0xffffffff) {
out(" or.c %%r%d, %%r0, %%r0 | MOV %%r%d, 0xffffffff\n",
regno, regno);
} else {
out(" or.u %%r%d, %%r0, 0x%04x | MOV %%r%d, 0x%08x\n",
regno, (imm >> 16), regno, imm);
if ((imm & 0xffff) != 0) {
out(" or %%r%d, %%r%d, 0x%04x\n",
regno, regno, imm & 0xffff);
}
}
}
//
// ここからテストコード
//
// r2: 引数1: グローバル変数の先頭
// r2[0] = &exception_occurred
// r2[1] = &exception_expected
// r3: 引数2: 今回のテストのパラメータの先頭。内容は code ごとに異なる。
//
// r5: r1 のバックアップ
// r6(,r7): 期待 rD
// r8(,r9): rD (テストで使う)、結果の rD をここに入れて返す
// r10(,r11): rS1 (テストで使う)
// r12(,r13): rS2 (テストで使う)
//
// 規約では r10-13 がサブルーチンが破壊してよいレジスタで、
// r2-r9 は呼び出し側が引数を置くレジスタだがここもサブルーチンが破壊してよい
// ので使ってしまう。
#define Rexp (6) // 期待値を格納するレジスタ
#define Rres (8) // 結果を格納するレジスタ
// 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;
}
// 汎用演算命令共通のテストパターン出力部分
static void
outtest_generic(const char *mnemonic,
uint32 rd, uint32 rs1, uint32 rs2, uint32 vs1, uint32 vs2, uint32 exp_rd)
{
// rs2 が IMM なら #imm
bool is_imm = (rs2 == IMM);
uint32& imm = vs2;
out("test_%s_%d:\n", testname, testnum);
if (is_imm) {
out(" /* %s r%d, r%d, #0x%04x (S1=$%08x) */\n",
mnemonic, rd, rs1, imm, vs1);
} else {
out(" /* %s r%d, r%d, r%d (S1=$%08x, S2=$%08x) */\n",
mnemonic, rd, rs1, rs2, vs1, vs2);
}
out(" .word 0x%08x | exp_rd\n", exp_rd);
// ここからコード
MOV_imm(rs1, vs1);
if (!is_imm && rs1 != rs2) {
MOV_imm(rs2, vs2);
}
if (is_imm) {
out(" %s %%r%d, %%r%d, 0x%04x | test\n", mnemonic, rd, rs1, imm);
} else {
out(" %s %%r%d, %%r%d, %%r%d | test\n", mnemonic, rd, rs1, rs2);
}
if (rd != Rres) {
out(" or %%r%d, %%r0, %%r%d\n", Rres, rd);
}
out(" jmp %%r1\n");
out("\n");
testnum++;
}
// 2項ビットフィールド命令共通のテストパターン出力部分
// (outtest_generic と似てるけど微妙に違う)
static void
outtest_bitfield(const char *mnemonic,
uint32 rd, uint32 rs1, uint32 vs1, uint32 wo, uint32 exp_rd)
{
uint32 w = (wo >> 5) & 0x1f; // XXX 表記上は 0 なのか 32 なのか
uint32 o = wo & 0x1f;
out("test_%s_%d:\n", testname, testnum);
out(" /* %s r%d, r%d, %d<%d> (S1=$%08x) */\n",
mnemonic, rd, rs1, w, o, vs1);
out(" .word 0x%08x | exp_rd\n", exp_rd);
// ここからコード
MOV_imm(rs1, vs1);
out(" %s %%r%d, %%r%d, %d<%d> | test\n", mnemonic, rd, rs1, w, o);
if (rd != Rres) {
out(" or %%r%d, %%r0, %%r%d\n", Rres, rd);
}
out(" jmp %%r1\n");
out("\n");
testnum++;
}
// ff0/ff1 命令共通のテストパターン出力部分
// (outtest_generic と似てるけど微妙に違う)
static void
outtest_ff01(const char *mnemonic,
uint32 rd, uint32 rs2, uint32 vs2, uint32 exp_rd)
{
out("test_%s_%d:\n", testname, testnum);
out(" /* %s r%d, r%d (S2=$%08x) */\n",
mnemonic, rd, rs2, vs2);
out(" .word 0x%08x | exp_rd\n", exp_rd);
// ここからコード
MOV_imm(rs2, vs2);
out(" %s %%r%d, %%r%d | test\n", mnemonic, rd, rs2);
if (rd != Rres) {
out(" or %%r%d, %%r0, %%r%d\n", Rres, rd);
}
out(" jmp %%r1\n");
out("\n");
testnum++;
}
// add/sub 命令共通のテストパターン出力部分
// rs2 == IMM なら即値指定。
// scale == true なら lda rD, rS1[rS2] 形式。
static void
outtest_addsub(const char *mnemonic,
uint32 rd, uint32 rs1, uint32 rs2, uint32 vs1, uint32 vs2, int cin,
uint32 exp_rd, uint32 exp_cy, bool exception = false, bool is_scale = false)
{
// rs2 が IMM なら #imm
bool is_imm = (rs2 == IMM);
uint32& imm = vs2;
out("test_%s_%d:\n", testname, testnum);
if (is_imm) {
out(" /* %s r%d, r%d, #0x%04x (S1=$%08x, Cin=%d) */\n",
mnemonic, rd, rs1, imm, vs1, cin);
} else if (is_scale) {
out(" /* %s r%d, r%d[r%d] (S1=$%08x, S2=$%08x) */\n",
mnemonic, rd, rs1, rs2, vs1, vs2);
} else {
out(" /* %s r%d, r%d, r%d (S1=$%08x, S2=$%08x, Cin=%d) */\n",
mnemonic, rd, rs1, rs2, vs1, vs2, cin);
}
out(" .word 0x%08x | exp_rd\n", exp_rd);
out(" .word 0x%08x | Cin|ExpCy|Exception|0\n",
(cin << 24) | (exp_cy << 16) | (exception << 8));
// ここからテストコード
// ソースレジスタをセット、実行、結果を保存
MOV_imm(rs1, vs1);
if (!is_imm && rs1 != rs2) {
MOV_imm(rs2, vs2);
}
if (exception) {
// 例外期待なら、rD (と Cy)の期待値は命令実行前の値 (p.6-19, 6.6)
out(" or %%r%d, %%r0, %%r%d\n", Rexp, rd);
} else {
MOV_imm(Rexp, exp_rd);
}
if (exception) {
out(" | exception is expected\n");
}
if (is_imm) {
out(" %s %%r%d, %%r%d, 0x%04x | test\n", mnemonic, rd, rs1, imm);
} else if (is_scale) {
out(" %s %%r%d, %%r%d[%%r%d] | test\n", mnemonic, rd, rs1, rs2);
} else {
out(" %s %%r%d, %%r%d, %%r%d | test\n", mnemonic, rd, rs1, rs2);
}
if (rd != Rres) {
out(" or %%r%d, %%r0, %%r%d\n", Rres, rd);
}
out(" jmp %%r1\n");
out("\n");
testnum++;
}
// div 命令共通のテストパターン出力部分
static void
outtest_div(const char *mnemonic,
uint32 rd, uint32 rs1, uint32 rs2, uint32 vs1, uint32 vs2,
uint32 exp_rd, bool exception = false)
{
// rs2 が IMM なら #imm
bool is_imm = (rs2 == IMM);
uint32& imm = vs2;
out("test_%s_%d:\n", testname, testnum);
if (is_imm) {
out(" /* %s r%d, r%d, #0x%04x (S1=$%08x) */\n",
mnemonic, rd, rs1, imm, vs1);
} else {
out(" /* %s r%d, r%d, r%d (S1=$%08x, S2=$%08x) */\n",
mnemonic, rd, rs1, rs2, vs1, vs2);
}
out(" .word 0x%08x | exp_ex\n", exception ? 2 : 0);
// ここからテストコード
// ソースレジスタをセット、実行、結果を保存
MOV_imm(rs1, vs1);
if (!is_imm && rs1 != rs2) {
MOV_imm(rs2, vs2);
}
if (exception) {
// 例外期待なら、rD の期待値は命令実行前の値 (p.6-40, 6.8.3)
out(" or %%r%d, %%r0, %%r%d\n", Rexp, rd);
} else {
MOV_imm(Rexp, exp_rd);
}
if (exception) {
out(" | exception is expected\n");
}
if (is_imm) {
out(" %s %%r%d, %%r%d, 0x%04x | test\n", mnemonic, rd, rs1, imm);
} else {
out(" %s %%r%d, %%r%d, %%r%d | test\n", mnemonic, rd, rs1, rs2);
}
if (rd != Rres) {
out(" or %%r%d, %%r0, %%r%d\n", Rres, rd);
}
out(" jmp %%r1\n");
out("\n");
testnum++;
}
// FP 命令共通のテストパターン出力部分。
// ソースレジスタが1つの場合 (rs1 を使わない場合) は fsz1 = '\0'、rs1 = -1 に
// すること(vs1 は不定でよい)。
static void
outtest_fop(const char *mnemonic, char fsz0, char fsz1, char fsz2,
uint32 rd, uint32 rs1, uint32 rs2, fpi vs1, fpi vs2,
uint32 rm, fpi expected, uint32 exception)
{
bool r4_backup = false;
out("test_%s_%d:\n", testname, testnum);
if (fsz1 == '\0') {
out(" /* %s.%c%c r%d, r%d (S2=%s; %s) */\n",
mnemonic, fsz0, fsz2, rd, rs2,
vs2.ToString().c_str(), rmstr[rm]);
} else {
out(" /* %s.%c%c%c r%d, r%d, r%d (S1=%s, S2=%s; %s) */\n",
mnemonic, fsz0, fsz1, fsz2, rd, rs1, rs2,
vs1.ToString().c_str(), vs2.ToString().c_str(), rmstr[rm]);
}
out(" .word 0x%08x | RndMode | exp_ex\n",
exception | (rm << (16 + 14)));
// ここからテストコード
if (fsz1 != '\0') {
MOV_imm(rs1, vs1.h);
if (fsz1 == 'd') {
if (rs1 == 0) {
// ソースが r0,r1 になるので r1 を(さらに)退避
out(" or %%r4, %%r0, %%r1\n");
r4_backup = true;
}
MOV_imm(rs1 + 1, vs1.l);
}
}
MOV_imm(rs2, vs2.h);
if (fsz2 == 'd') {
if (r4_backup == false && rs2 == 0) {
// ソースが r0,r1 になるので r1 を(さらに)退避
out(" or %%r4, %%r0, %%r1\n");
r4_backup = true;
}
MOV_imm(rs2 + 1, vs2.l);
}
if (exception != 0) {
// 例外期待なら rD の期待値は命令実行前の値
out(" or %%r%d, %%r0, %%r%d\n", Rexp, rd);
if (fsz0 == 'd') {
out(" or %%r%d, %%r0, %%r%d\n", Rexp + 1, rd + 1);
}
} else {
// 例外が起きないことを期待する
MOV_imm(Rexp, expected.h);
if (fsz0 == 'd') {
MOV_imm(Rexp + 1, expected.l);
}
}
if (fsz1 == '\0') {
out(" %s.%c%c %%r%d, %%r%d | test\n",
mnemonic, fsz0, fsz2, rd, rs2);
} else {
out(" %s.%c%c%c %%r%d, %%r%d, %%r%d | test\n",
mnemonic, fsz0, fsz1, fsz2, rd, rs1, rs2);
}
if (r4_backup) {
out(" or %%r1, %%r0, %%r4\n");
}
if (rd != Rres) {
out(" or %%r%d, %%r0, %%r%d\n", Rres, rd);
if (fsz0 == 'd') {
out(" or %%r%d, %%r0, %%r%d\n", Rres + 1, rd + 1);
}
}
out(" jmp %%r1\n");
out("\n");
testnum++;
}
// FP 命令共通 (ソースレジスタが1つの形式)
static void
outtest_fop(const char *mnemonic, char fsz0, char fsz2,
uint32 rd, uint32 rs2, fpi vs2,
uint32 rm, fpi expected, uint32 exception)
{
fpi dummy_vs1;
outtest_fop(mnemonic, fsz0, '\0', fsz2,
rd, (uint32)-1, rs2, dummy_vs1, vs2,
rm, expected, exception);
}
// int, nint, trnc 命令共通のテストパターン出力部分。
// rm はテスト環境内の FPCR に設定する丸めモード (入力パラメータ)。
static void
outtest_toint(const char *mnemonic, uint32 rd, uint32 rs2, fpi vs2,
uint32 rm, uint64 exp)
{
uint32 exception = exp >> 32;
uint32 expected = exp;
outtest_fop(mnemonic, 's', (vs2.IsFloat() ? 's' : 'd'),
rd, rs2, vs2, rm, expected, exception);
}
//
// ここからテストの出力
//
static void
gen_add()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + vs2;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = cin;
if (rd == 0) {
exp_rd = 0;
}
bool exp_ex = isovf_add(vs1, vs2, result);
if (exp_ex) {
exp_cy = cin;
}
outtest_addsub("add",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex);
}
}
}
static void
gen_add_ci()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + vs2 + cin;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = cin;
if (rd == 0) {
exp_rd = 0;
}
bool exp_ex = isovf_add(vs1, vs2, result);
if (exp_ex) {
exp_cy = cin;
}
outtest_addsub("add.ci",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex);
}
}
}
static void
gen_add_co()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + vs2;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = result >> 32;
if (rd == 0) {
exp_rd = 0;
}
bool exp_ex = isovf_add(vs1, vs2, result);
if (exp_ex) {
exp_cy = cin;
}
outtest_addsub("add.co",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex);
}
}
}
static void
gen_add_cio()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + vs2 + cin;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = result >> 32;
if (rd == 0) {
exp_rd = 0;
}
bool exp_ex = isovf_add(vs1, vs2, result);
if (exp_ex) {
exp_cy = cin;
}
outtest_addsub("add.cio",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex);
}
}
}
static void
gen_add_imm()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + imm;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = cin;
if (rd == 0) {
exp_rd = 0;
}
bool exp_ex = isovf_add(vs1, imm, result);
if (exp_ex) {
exp_cy = cin;
}
outtest_addsub("add",
rd, rs1, IMM, vs1, imm, cin, exp_rd, exp_cy, exp_ex);
}
}
}
// addu rD, rS1, rS2 と lda rD, rS1, rS2 は名前が違うだけで同じ…
static void
gen_addu_lda(const char *mnemonic)
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + vs2;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = cin;
if (rd == 0) {
exp_rd = 0;
}
outtest_addsub(mnemonic,
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy);
}
}
}
static void
gen_addu()
{
gen_addu_lda("addu");
}
static void
gen_addu_ci()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + vs2 + cin;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = cin;
if (rd == 0) {
exp_rd = 0;
}
outtest_addsub("addu.ci",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy);
}
}
}
static void
gen_addu_co()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + vs2;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = result >> 32;
if (rd == 0) {
exp_rd = 0;
}
outtest_addsub("addu.co",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy);
}
}
}
static void
gen_addu_cio()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + vs2 + cin;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = result >> 32;
if (rd == 0) {
exp_rd = 0;
}
outtest_addsub("addu.cio",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy);
}
}
}
// addu rD, rS1, #imm と lda rD, rS1, #imm は名前が違うだけで同じ…
static void
gen_addu_lda_imm(const char *mnemonic)
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + imm;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = cin;
if (rd == 0) {
exp_rd = 0;
}
outtest_addsub(mnemonic,
rd, rs1, IMM, vs1, imm, cin, exp_rd, exp_cy);
}
}
}
static void
gen_addu_imm()
{
gen_addu_lda_imm("addu");
}
static void
gen_and()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd = vs1 & vs2;
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("and", rd, rs1, rs2, vs1, vs2, exp_rd);
}
}
static void
gen_and_c()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd = vs1 & ~vs2;
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("and.c", rd, rs1, rs2, vs1, vs2, exp_rd);
}
}
static void
gen_and_imm()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = vs1 & (imm | 0xffff0000);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("and", rd, rs1, IMM, vs1, imm, exp_rd);
}
}
static void
gen_and_u()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = vs1 & ((imm << 16) | 0xffff);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("and.u", rd, rs1, IMM, vs1, imm, exp_rd);
}
}
static uint32
calc_clr(uint32 src, uint32 wo)
{
uint32 w = ((wo >> 5) & 0x1f) ?: 32;
uint32 o = wo & 0x1f;
uint32 mask = (uint32)(((1ULL << w) - 1) << o);
return src & ~mask;
}
static void
gen_clr_imm()
{
for (const auto& t : param_bitfield_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = calc_clr(vs1, imm);
if (rd == 0) {
exp_rd = 0;
}
outtest_bitfield("clr", rd, rs1, vs1, imm, exp_rd);
}
}
static void
gen_clr_reg()
{
for (const auto& t : param_bitfield_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd = calc_clr(vs1, vs2);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("clr", rd, rs1, rs2, vs1, vs2, exp_rd);
}
}
static uint32
calc_cmp(uint32 vs1, uint32 vs2)
{
// 期待値
uint32 exp_rd = 0;
exp_rd |= (vs1 >= vs2) ? 0x0800 : 0;
exp_rd |= (vs1 < vs2) ? 0x0400 : 0;
exp_rd |= (vs1 <= vs2) ? 0x0200 : 0;
exp_rd |= (vs1 > vs2) ? 0x0100 : 0;
exp_rd |= ((int32)vs1 >= (int32)vs2) ? 0x0080 : 0;
exp_rd |= ((int32)vs1 < (int32)vs2) ? 0x0040 : 0;
exp_rd |= ((int32)vs1 <= (int32)vs2) ? 0x0020 : 0;
exp_rd |= ((int32)vs1 > (int32)vs2) ? 0x0010 : 0;
exp_rd |= (vs1 != vs2) ? 0x0008 : 0;
exp_rd |= (vs1 == vs2) ? 0x0004 : 0;
return exp_rd;
}
static void
gen_cmp()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd = calc_cmp(vs1, vs2);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("cmp", rd, rs1, rs2, vs1, vs2, exp_rd);
}
}
static void
gen_cmp_imm()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = calc_cmp(vs1, imm);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("cmp", rd, rs1, IMM, vs1, imm, exp_rd);
}
}
// 符号付きで x / y の期待値を計算する。
// 計算可能な場合は *exp_rd に結果を書き戻し *exp_ex に false を書き戻す。
// 例外を期待する場合は *exp_ex に true を書き戻す。
static void
calc_div(uint32 *exp_rd, bool *exp_ex, int32 x, int32 y)
{
// どちらかのオペランドが負でも例外らしい。符号付きとは一体…。
if (x < 0 || y <= 0) {
*exp_ex = true;
*exp_rd = 0; // なくていいはずだけどコンパイラに怒られるので
} else {
*exp_ex = false;
*exp_rd = (uint32)(x / y);
}
}
static void
gen_div()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd;
bool exp_ex;
calc_div(&exp_rd, &exp_ex, vs1, vs2);
if (rd == 0) {
exp_rd = 0;
}
outtest_div("div", rd, rs1, rs2, vs1, vs2, exp_rd, exp_ex);
}
}
static void
gen_div_imm()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd;
bool exp_ex;
calc_div(&exp_rd, &exp_ex, vs1, imm);
if (rd == 0) {
exp_rd = 0;
}
outtest_div("div", rd, rs1, IMM, vs1, imm, exp_rd, exp_ex);
}
}
// 符号無しで x / y の期待値を計算する。
// 計算可能な場合は *exp_rd に結果を書き戻し *exp_ex に false を書き戻す。
// DivByZero を期待する場合は *exp_ex に true を書き戻す。
static void
calc_divu(uint32 *exp_rd, bool *exp_ex, uint32 x, uint32 y)
{
if (y == 0) {
*exp_ex = true;
*exp_rd = 0; // なくていいはずだけどコンパイラに怒られるので
} else {
*exp_ex = false;
*exp_rd = x / y;
}
}
static void
gen_divu()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd;
bool exp_ex;
calc_divu(&exp_rd, &exp_ex, vs1, vs2);
if (rd == 0) {
exp_rd = 0;
}
outtest_div("divu", rd, rs1, rs2, vs1, vs2, exp_rd, exp_ex);
}
}
static void
gen_divu_imm()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd;
bool exp_ex;
calc_divu(&exp_rd, &exp_ex, vs1, imm);
if (rd == 0) {
exp_rd = 0;
}
outtest_div("divu", rd, rs1, IMM, vs1, imm, exp_rd, exp_ex);
}
}
static uint32
calc_ext(uint32 src, uint32 wo)
{
uint32 w = ((wo >> 5) & 0x1f) ?: 32;
uint32 o = wo & 0x1f;
uint32 mask = (uint32)(((1ULL << w) - 1) << o);
src &= mask;
int shift = 0;
if (o + w < 32) {
shift = 32 - (o + w);
src <<= shift;
}
src = (int32)src >> (o + shift);
return src;
}
static void
gen_ext_imm()
{
for (const auto& t : param_bitfield_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = calc_ext(vs1, imm);
if (rd == 0) {
exp_rd = 0;
}
outtest_bitfield("ext", rd, rs1, vs1, imm, exp_rd);
}
}
static void
gen_ext_reg()
{
for (const auto& t : param_bitfield_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd = calc_ext(vs1, vs2);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("ext", rd, rs1, rs2, vs1, vs2, exp_rd);
}
}
static uint32
calc_extu(uint32 src, uint32 wo)
{
uint32 w = ((wo >> 5) & 0x1f) ?: 32;
uint32 o = wo & 0x1f;
uint32 mask = (uint32)(((1ULL << w) - 1) << o);
return (src & mask) >> o;
}
static void
gen_extu_imm()
{
for (const auto& t : param_bitfield_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = calc_extu(vs1, imm);
if (rd == 0) {
exp_rd = 0;
}
outtest_bitfield("extu", rd, rs1, vs1, imm, exp_rd);
}
}
static void
gen_extu_reg()
{
for (const auto& t : param_bitfield_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd = calc_extu(vs1, vs2);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("extu", rd, rs1, rs2, vs1, vs2, exp_rd);
}
}
// fcmp.sXX の計算部分のみ
static uint32
calc_fcmp(double vs1, double vs2)
{
uint32 res = 0;
if (vs2 >= 0) {
res |= (vs1 <= 0 || vs1 >= vs2) ? 0x0800 : 0; // ob
res |= (0 < vs1 && vs1 < vs2) ? 0x0400 : 0; // in
res |= (0 <= vs1 && vs1 <= vs2) ? 0x0200 : 0; // ib
res |= (vs1 < 0 || vs1 > vs2) ? 0x0100 : 0; // ou
}
res |= (vs1 >= vs2) ? 0x0080 : 0; // ge
res |= (vs1 < vs2) ? 0x0040 : 0; // lt
res |= (vs1 <= vs2) ? 0x0020 : 0; // le
res |= (vs1 > vs2) ? 0x0010 : 0; // gt
res |= (vs1 != vs2) ? 0x0008 : 0; // ne
res |= (vs1 == vs2) ? 0x0004 : 0; // eq
// cp; if and only if the two operands are comparable
if (!std::isnan(vs1) && !std::isnan(vs2)) {
res |= 0x0002;
}
// nc; if, and only if, the two operands are not comparable
if (std::isnan(vs1) || std::isnan(vs2)) {
res |= 0x0001;
}
return res;
}
// fcmp.sXX の共通部分
static void
gen_fcmp(char fsz1, const char fsz2)
{
for (uint32 rm = 0; rm < 4; rm++) {
for (const auto& t : param_fcmp) {
VAR_TRI_REG(t, rd, rs1, rs2, v1, v2);
uint32 exception = 0;
uint32 exp_rd = 0;
fpi fpi1;
fpi fpi2;
// rS1, rS2 が同じで幅が違うのは(ゼロ以外は)意味がないので飛ばす
if (rs1 == rs2 && fsz1 != fsz2 && (v1 != 0 && v2 != 0)) {
continue;
}
// double で r0 を指すのは実質意味がないので飛ばす
// (ゼロの場合は飛ばさなくてもいいけど面倒なので飛ばす)
if (fsz1 == 'd' && rs1 == 0)
continue;
if (fsz2 == 'd' && rs2 == 0)
continue;
// ソースサイズが s なら float で表現できない値は飛ばす
if (fsz1 == 's' && can_float(v1) == false)
continue;
if (fsz2 == 's' && can_float(v2) == false)
continue;
// r0 なら値も 0 としておかないといけない
if (rs1 == 0) {
v1 = 0;
}
if (rs2 == 0) {
v2 = 0;
}
// rd ==0 の場合もソースに出力するコメントのために必要
if (fsz1 == 's') {
fpi1.Set((float)v1);
} else {
fpi1.Set(v1);
}
if (fsz2 == 's') {
fpi2.Set((float)v2);
} else {
fpi2.Set(v2);
}
if (rd == 0) {
exception = 0x1000;
} else {
exp_rd = calc_fcmp(fpi1.AsDouble(), fpi2.AsDouble());
}
outtest_fop("fcmp", 's', fsz1, fsz2,
rd, rs1, rs2, fpi1, fpi2, rm, exp_rd, exception);
}
}
}
static void gen_fcmp_sss() { gen_fcmp('s', 's'); }
static void gen_fcmp_ssd() { gen_fcmp('s', 'd'); }
static void gen_fcmp_sds() { gen_fcmp('d', 's'); }
static void gen_fcmp_sdd() { gen_fcmp('d', 'd'); }
static void
gen_ff0()
{
for (const auto& t : param_ff01) {
VAR_DIADIC(t, rd, rs2, vs2);
// 最も MSB 側にある %0 のビット位置(LSBを0とする)を返す
// %0 がなければ 32 を返す
uint32 exp_rd = 32;
if (vs2 != 0xffffffff) {
exp_rd = 31 - __builtin_clz(~vs2);
}
if (rd == 0) {
exp_rd = 0;
}
outtest_ff01("ff0", rd, rs2, vs2, exp_rd);
}
}
static void
gen_ff1()
{
for (const auto& t : param_ff01) {
VAR_DIADIC(t, rd, rs2, vs2);
// 最も MSB 側にある %1 のビット位置(LSBを0とする)を返す
// %0 がなければ 32 を返す
uint32 exp_rd = 32;
if (vs2 != 0x00000000) {
exp_rd = 31 - __builtin_clz(vs2);
}
if (rd == 0) {
exp_rd = 0;
}
outtest_ff01("ff1", rd, rs2, vs2, exp_rd);
}
}
static void
gen_flt_ss()
{
for (uint32 rm = 0; rm < 4; rm++) {
setround(rm);
for (const auto& t : param_flt) {
VAR_DIADIC(t, rd, rs2, vs2);
fpi fpi((float)(int32)vs2);
outtest_fop("flt", 's', 's',
rd, rs2, vs2, rm, fpi, (rd == 0 ? 0x1000 : 0));
}
}
}
static void
gen_flt_ds()
{
for (uint32 rm = 0; rm < 4; rm++) {
setround(rm);
for (const auto& t : param_flt) {
VAR_DIADIC(t, rd, rs2, vs2);
fpi fpi((double)(int32)vs2);
outtest_fop("flt", 'd', 's',
rd, rs2, vs2, rm, fpi, (rd == 0 ? 0x1000 : 0));
}
}
}
// int, nint, trnc 命令共通の期待値計算。
// 期待値を計算するための丸めモードはあらかじめ setround() で設定しておくこと。
// 戻り値は上位 32bit が例外、下位 32bit が期待値。
// 例外が出ることを期待する時は、上位が例外コード(非0)、下位は無視。
// 例外が出ないことを期待する場合は、上位が 0、下位が期待値。
static uint64
calc_toint(uint32 rd, fpi vs2)
{
uint32 expected = 0;
uint32 exception = 0;
if (rd == 0) {
exception = 0x1000;
} else {
double src = vs2.AsDouble();
double result = std::nearbyint(src);
int exponent = 0;
std::frexp(result, &exponent);
// 実際には trnc 命令は 30ビット以上で例外を出すが、
// そこから先は (OpenBSD の) ソフトウェアハンドラが処理して
// 実際に収まらなかった時は最大値を返す。
if (std::isinf(result) || std::isnan(result) || exponent >= 32) {
if (std::signbit(result)) {
expected = 0x80000000;
} else {
expected = 0x7fffffff;
}
} else {
expected = (int32)result;
}
}
return ((uint64)exception << 32) | expected;
}
static void
gen_int_ss()
{
for (uint32 rm = 0; rm < 4; rm++) {
setround(rm);
for (const auto& t : param_trnc) {
VAR_DIADIC(t, rd, rs2, vs2);
// float で表現できない値は飛ばす
if (can_float(vs2))
continue;
fpi fpi((float)vs2);
uint64 exp = calc_toint(rd, fpi);
outtest_toint("int", rd, rs2, fpi, rm, exp);
}
}
}
static void
gen_int_sd()
{
for (uint32 rm = 0; rm < 4; rm++) {
setround(rm);
for (const auto& t : param_trnc) {
VAR_DIADIC(t, rd, rs2, vs2);
fpi fpi(vs2);
uint64 exp = calc_toint(rd, fpi);
outtest_toint("int", rd, rs2, fpi, rm, exp);
}
}
}
// lda.* rD, rS1, #imm は addu rD, rS1, #imm と同じ
static void
gen_lda_imm()
{
gen_addu_lda_imm("lda");
}
static void
gen_lda_b_imm()
{
gen_addu_lda_imm("lda.b");
}
static void
gen_lda_h_imm()
{
gen_addu_lda_imm("lda.h");
}
static void
gen_lda_d_imm()
{
gen_addu_lda_imm("lda.d");
}
// lda.* rD, rS1, rS2 は addu rD, rS1, rS2 と同じ
static void
gen_lda_reg()
{
gen_addu_lda("lda");
}
static void
gen_lda_b_reg()
{
gen_addu_lda("lda.b");
}
static void
gen_lda_h_reg()
{
gen_addu_lda("lda.h");
}
static void
gen_lda_d_reg()
{
gen_addu_lda("lda.d");
}
static void
gen_lda_scale(const char *mnemonic, uint32 scale)
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + vs2 * scale;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = cin;
if (rd == 0) {
exp_rd = 0;
}
bool exception = false;
bool is_scale = true;
outtest_addsub(mnemonic,
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy,
exception, is_scale);
}
}
}
static void
gen_lda_scale()
{
gen_lda_scale("lda", 4);
}
static void
gen_lda_b_scale()
{
gen_addu_lda("lda.b");
}
static void
gen_lda_h_scale()
{
gen_lda_scale("lda.h", 2);
}
static void
gen_lda_d_scale()
{
gen_lda_scale("lda.d", 8);
}
static uint32
calc_mak(uint32 src, uint32 wo)
{
uint32 w = ((wo >> 5) & 0x1f) ?: 32;
uint32 o = wo & 0x1f;
uint32 mask = (uint32)((1ULL << w) - 1);
return (src & mask) << o;
}
static void
gen_mak_imm()
{
for (const auto& t : param_bitfield_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = calc_mak(vs1, imm);
if (rd == 0) {
exp_rd = 0;
}
outtest_bitfield("mak", rd, rs1, vs1, imm, exp_rd);
}
}
static void
gen_mak_reg()
{
for (const auto& t : param_bitfield_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd = calc_mak(vs1, vs2);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("mak", rd, rs1, rs2, vs1, vs2, exp_rd);
}
}
static void
gen_mask_imm()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = vs1 & imm;
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("mask", rd, rs1, IMM, vs1, imm, exp_rd);
}
}
static void
gen_mask_u()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = vs1 & (imm << 16);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("mask.u", rd, rs1, IMM, vs1, imm, exp_rd);
}
}
static void
gen_mul()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd = (uint32)((uint64)vs1 * vs2);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("mul", rd, rs1, rs2, vs1, vs2, exp_rd);
}
}
static void
gen_mul_imm()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = (uint32)((uint64)vs1 * imm);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("mul", rd, rs1, IMM, vs1, imm, exp_rd);
}
}
static void
gen_nint_ss()
{
// 期待値は NEAR で計算
setround(RM_NEAR);
for (uint32 rm = 0; rm < 4; rm++) {
for (const auto& t : param_trnc) {
VAR_DIADIC(t, rd, rs2, vs2);
// float で表現できない値は飛ばす
if (can_float(vs2))
continue;
fpi fpi((float)vs2);
uint64 exp = calc_toint(rd, fpi);
outtest_toint("nint", rd, rs2, fpi, rm, exp);
}
}
}
static void
gen_nint_sd()
{
// 期待値は NEAR で計算
setround(RM_NEAR);
for (uint32 rm = 0; rm < 4; rm++) {
for (const auto& t : param_trnc) {
VAR_DIADIC(t, rd, rs2, vs2);
fpi fpi(vs2);
uint64 exp = calc_toint(rd, fpi);
outtest_toint("nint", rd, rs2, fpi, rm, exp);
}
}
}
static void
gen_or()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd = vs1 | vs2;
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("or", rd, rs1, rs2, vs1, vs2, exp_rd);
}
}
static void
gen_or_c()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd = vs1 | ~vs2;
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("or.c", rd, rs1, rs2, vs1, vs2, exp_rd);
}
}
static void
gen_or_imm()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = vs1 | imm;
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("or", rd, rs1, IMM, vs1, imm, exp_rd);
}
}
static void
gen_or_u()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = vs1 | (imm << 16);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("or.u", rd, rs1, IMM, vs1, imm, exp_rd);
}
}
static uint32
calc_rot(uint32 src, uint32 wo)
{
uint32 o = wo & 0x1f;
return (src >> o) | (src << (32 - o));
}
static void
gen_rot_imm()
{
// 手抜きで W は無視するのでパターンが重複する
for (const auto& t : param_bitfield_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = calc_rot(vs1, imm);
if (rd == 0) {
exp_rd = 0;
}
outtest_bitfield("rot", rd, rs1, vs1, imm, exp_rd);
}
}
static void
gen_rot_reg()
{
// 手抜きで W は無視するのでパターンが重複する
for (const auto& t : param_bitfield_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd = calc_rot(vs1, vs2);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("rot", rd, rs1, rs2, vs1, vs2, exp_rd);
}
}
static uint32
calc_set(uint32 src, uint32 wo)
{
uint32 w = ((wo >> 5) & 0x1f) ?: 32;
uint32 o = wo & 0x1f;
uint32 mask = (uint32)(((1ULL << w) - 1) << o);
return src | mask;
}
static void
gen_set_imm()
{
for (const auto& t : param_bitfield_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = calc_set(vs1, imm);
if (rd == 0) {
exp_rd = 0;
}
outtest_bitfield("set", rd, rs1, vs1, imm, exp_rd);
}
}
static void
gen_set_reg()
{
for (const auto& t : param_bitfield_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd = calc_set(vs1, vs2);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("set", rd, rs1, rs2, vs1, vs2, exp_rd);
}
}
static void
gen_sub()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + (~vs2) + 1;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = cin;
if (rd == 0) {
exp_rd = 0;
}
bool exp_ex = isovf_sub(vs1, vs2, result);
if (exp_ex) {
exp_cy = cin;
}
outtest_addsub("sub",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex);
}
}
}
static void
gen_sub_ci()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + (~vs2) + cin;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = cin;
if (rd == 0) {
exp_rd = 0;
}
bool exp_ex = isovf_sub(vs1, vs2, result);
if (exp_ex) {
exp_cy = cin;
}
outtest_addsub("sub.ci",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex);
}
}
}
static void
gen_sub_co()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + (~vs2) + 1;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = result >> 32;
if (rd == 0) {
exp_rd = 0;
}
bool exp_ex = isovf_sub(vs1, vs2, result);
if (exp_ex) {
exp_cy = cin;
}
outtest_addsub("sub.co",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex);
}
}
}
static void
gen_sub_cio()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + (~vs2) + cin;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = result >> 32;
if (rd == 0) {
exp_rd = 0;
}
bool exp_ex = isovf_sub(vs1, vs2, result);
if (exp_ex) {
exp_cy = cin;
}
outtest_addsub("sub.cio",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex);
}
}
}
static void
gen_sub_imm()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + (~imm) + 1;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = cin;
if (rd == 0) {
exp_rd = 0;
}
bool exp_ex = isovf_sub(vs1, imm, result);
if (exp_ex) {
exp_cy = cin;
}
outtest_addsub("sub",
rd, rs1, IMM, vs1, imm, cin, exp_rd, exp_cy, exp_ex);
}
}
}
static void
gen_subu()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + (~vs2) + 1;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = cin;
if (rd == 0) {
exp_rd = 0;
}
outtest_addsub("subu",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy);
}
}
}
static void
gen_subu_ci()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + (~vs2) + cin;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = cin;
if (rd == 0) {
exp_rd = 0;
}
outtest_addsub("subu.ci",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy);
}
}
}
static void
gen_subu_co()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + (~vs2) + 1;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = result >> 32;
if (rd == 0) {
exp_rd = 0;
}
outtest_addsub("subu.co",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy);
}
}
}
static void
gen_subu_cio()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + (~vs2) + cin;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = result >> 32;
if (rd == 0) {
exp_rd = 0;
}
outtest_addsub("subu.cio",
rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy);
}
}
}
static void
gen_subu_imm()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
for (int cin = 0; cin <= 1; cin++) {
// 期待値
uint64 result = (uint64)vs1 + (~imm) + 1;
uint32 exp_rd = (uint32)result;
uint32 exp_cy = cin;
if (rd == 0) {
exp_rd = 0;
}
outtest_addsub("subu",
rd, rs1, IMM, vs1, imm, cin, exp_rd, exp_cy);
}
}
}
static void
gen_trnc_ss()
{
// 期待値は ZERO で計算
setround(RM_ZERO);
for (uint32 rm = 0; rm < 4; rm++) {
for (const auto& t : param_trnc) {
VAR_DIADIC(t, rd, rs2, vs2);
// float で表現できない値は飛ばす
if (can_float(vs2))
continue;
fpi fpi((float)vs2);
uint64 exp = calc_toint(rd, fpi);
outtest_toint("trnc", rd, rs2, fpi, rm, exp);
}
}
}
static void
gen_trnc_sd()
{
// 期待値は ZERO で計算
setround(RM_ZERO);
for (uint32 rm = 0; rm < 4; rm++) {
for (const auto& t : param_trnc) {
VAR_DIADIC(t, rd, rs2, vs2);
fpi fpi(vs2);
uint64 exp = calc_toint(rd, fpi);
outtest_toint("trnc", rd, rs2, fpi, rm, exp);
}
}
}
static void
gen_xor()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd = vs1 ^ vs2;
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("xor", rd, rs1, rs2, vs1, vs2, exp_rd);
}
}
static void
gen_xor_c()
{
for (const auto& t : param_arith_reg) {
VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2);
uint32 exp_rd = vs1 ^ ~vs2;
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("xor.c", rd, rs1, rs2, vs1, vs2, exp_rd);
}
}
static void
gen_xor_imm()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = vs1 ^ imm;
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("xor", rd, rs1, IMM, vs1, imm, exp_rd);
}
}
static void
gen_xor_u()
{
for (const auto& t : param_arith_imm) {
VAR_TRI_IMM(t, rd, rs1, vs1, imm);
uint32 exp_rd = vs1 ^ (imm << 16);
if (rd == 0) {
exp_rd = 0;
}
outtest_generic("xor.u", rd, rs1, IMM, vs1, imm, exp_rd);
}
}
//
// ここから出力用の処理
//
enum {
G_ADD = 1,
G_SUB,
G_LOGICAL,
G_BITFIELD,
G_LDA,
G_MULDIV,
G_FLT,
G_FCMP,
};
// テスト一覧 (編集しやすいように実体は一番下に置いてある)
struct testentry {
int group;
const char *code;
void (*genfunc)();
int quad; // 結果が 64bit なら 1
const char *name;
};
extern std::vector<testentry> test_table;
// list の中に val がなければ追加する
template <class T>
static void append(std::vector<T>& list, const T& val)
{
if (std::find(list.begin(), list.end(), val) == list.end()) {
list.emplace_back(val);
}
}
// ファイルオープンとクローズ (副作用のあるマクロ)
#define FOPEN(filename_) do { \
filename = filename_; \
tmpname = filename + ".tmp"; \
fp = fopen(tmpname.c_str(), "w"); \
if (fp == NULL) { \
err(1, "%s", tmpname.c_str()); \
} \
out("/* This file was created by %s */\n", getprogname()); \
out("\n"); \
} while (0)
#define FCLOSE() do { \
fclose(fp); \
compare_and_move(tmpname, filename); \
} while (0)
// src, dst のファイルが異なっていれば src を dst に rename する
static void
compare_and_move(const std::string& src, const std::string& dst)
{
struct stat st;
int r;
bool update;
update = false;
r = stat(dst.c_str(), &st);
if (r < 0) {
// dst not found
update = true;
} else {
// dst exists
std::string cmd = "cmp -s " + src + " " + dst;
r = system(cmd.c_str());
if (r < 0) {
err(1, "system");
}
r = WEXITSTATUS(r);
if (r > 1) {
errx(1, "cmp failed?");
}
if (r == 0) {
// src and dst are the same
} else {
update = true;
}
}
if (update) {
// 違っていれば、src を dst に移動
r = rename(src.c_str(), dst.c_str());
if (r < 0) {
err(1, "rename: %s %s", src.c_str(), dst.c_str());
}
printf("Updated: %s\n", dst.c_str());
} else {
// 同じなら、今作った src のほうを消す
r = remove(src.c_str());
if (r < 0) {
err(1, "remove: %s", src.c_str());
}
}
}
// テストアセンブリソースの出力共通部分
static void
print_test(const testentry& t)
{
// グローバル変数にセット
testname = t.name;
testnum = 0;
// テストの実体を出力
t.genfunc();
// テーブル
out(".globl list_%s\n", testname);
out("list_%s:\n", testname);
for (int i = 0; i < testnum; i++) {
out("\t.word test_%s_%d\n", testname, i);
}
out("\t.word 0\n");
out("\n");
}
// ソースファイルを作成
static void
create_file(const std::string& filename_, int group)
{
std::string filename;
std::string tmpname;
FOPEN(filename_);
for (const auto& t : test_table) {
if (t.group == group) {
print_test(t);
}
}
FCLOSE();
}
static void compare_and_move(const std::string&, const std::string&);
int
main(int ac, char *av[])
{
std::string filename;
std::string tmpname;
// テストパターンを用意
for (const auto& rlist : reglist3) {
auto rd = (rlist >> 16) & 0xff;
auto rs1 = (rlist >> 8) & 0xff;
auto rs2 = rlist & 0xff;
std::vector<uint32> *v_arith_s1 = (rs1 == 0) ? &v0 : &vlist_arith;
std::vector<uint32> *v_arith_s2 = (rs2 == 0) ? &v0 : &vlist_arith;
if (rs1 == rs2) {
for (const auto& vs1 : *v_arith_s1) {
Tuple3_2<uint32> val(rd, rs1, rs2, vs1, vs1);
append(param_arith_reg, val);
}
} else {
for (const auto& vs1 : *v_arith_s1) {
for (const auto& vs2 : *v_arith_s2) {
Tuple3_2<uint32> val(rd, rs1, rs2, vs1, vs2);
append(param_arith_reg, val);
}
}
}
}
for (const auto& rlist : reglist2) {
auto rd = (rlist >> 16) & 0xff;
auto rs1 = (rlist >> 8) & 0xff;
std::vector<uint32> *v_arith_s1 = (rs1 == 0) ? &v0 : &vlist_arith;
for (const auto& vs1 : *v_arith_s1) {
for (const auto& imm : vlist_arith) {
if (imm <= 0xffff) {
Tuple2_2<uint32> val(rd, rs1, vs1, imm);
append(param_arith_imm, val);
}
}
}
}
for (const auto& rlist : reglist3) {
auto rd = (rlist >> 16) & 0xff;
auto rs1 = (rlist >> 8) & 0xff;
auto rs2 = rlist & 0xff;
std::vector<uint32> *v_bf = (rs1 == 0) ? &v0 : &vlist_bf;
std::vector<uint32> *v_wo = (rs2 == 0) ? &v0 : &vlist_wo;
if (rs1 == rs2) {
for (const auto& vs1 : *v_bf) {
Tuple3_2<uint32> val(rd, rs1, rs2, vs1, vs1);
append(param_bitfield_reg, val);
}
for (const auto& vs2 : *v_wo) {
Tuple3_2<uint32> val(rd, rs1, rs2, vs2, vs2);
append(param_bitfield_reg, val);
}
} else {
for (const auto& vs1 : *v_bf) {
for (const auto& vs2 : *v_wo) {
Tuple3_2<uint32> val(rd, rs1, rs2, vs1, vs2);
append(param_bitfield_reg, val);
}
}
}
}
for (const auto& rlist : reglist2) {
auto rd = (rlist >> 16) & 0xff;
auto rs1 = (rlist >> 8) & 0xff;
std::vector<uint32> *v_bf = (rs1 == 0) ? &v0 : &vlist_bf;
for (const auto& vs1 : *v_bf) {
for (const auto& wo : vlist_wo) {
Tuple2_2<uint32> val(rd, rs1, vs1, wo);
append(param_bitfield_imm, val);
}
}
}
for (const auto& rlist : reglist2) {
auto rd = (rlist >> 16) & 0xff;
auto rs2 = (rlist >> 8) & 0xff;
std::vector<uint32> v_ff;
if (rs2 == 0) {
v_ff = v0;
} else {
// vlist_arith とそれをビット反転したもの
for (const auto v : vlist_arith) {
v_ff.emplace_back(v);
v_ff.emplace_back(~v);
}
}
for (const auto& vs2 : v_ff) {
Tuple2_1<uint32> val(rd, rs2, vs2);
append(param_ff01, val);
}
}
for (const auto& rlist : reglist2) {
auto rd = (rlist >> 16) & 0xff;
auto rs2 = (rlist >> 8) & 0xff;
std::vector<uint32> *v_arith_s2 = (rs2 == 0) ? &v0 : &vlist_arith;
for (const auto& vs2 : *v_arith_s2) {
Tuple2_1<uint32> val(rd, rs2, vs2);
append(param_flt, val);
}
}
for (const auto& rlist : reglist2) {
auto rd = (rlist >> 16) & 0xff;
auto rs2 = (rlist >> 8) & 0xff;
if (rs2 == 0) {
Tuple2_1<double> val(rd, rs2, 0.0);
append(param_trnc, val);
} else {
for (const auto& vs2 : vlist_double) {
Tuple2_1<double> val(rd, rs2, vs2);
append(param_trnc, val);
}
}
}
for (const auto& rlist : reglist3) {
auto rd = (rlist >> 16) & 0xff;
auto rs1 = (rlist >> 8) & 0xff;
auto rs2 = rlist & 0xff;
std::vector<double> *vlist1 = (rs1 == 0) ? &vf0 : &vlist_double;
std::vector<double> *vlist2 = (rs2 == 0) ? &vf0 : &vlist_double;
if (rs1 == rs2) {
for (const auto& vs1 : *vlist1) {
Tuple3_2<double> val(rd, rs1, rs2, vs1, vs1);
append(param_fcmp, val);
}
} else {
for (const auto& vs1 : *vlist1) {
for (const auto& vs2 : *vlist2) {
Tuple3_2<double> val(rd, rs1, rs2, vs1, vs2);
append(param_fcmp, val);
}
}
}
}
// ソースファイルを出力
create_file("optestm88k_add.s", G_ADD);
create_file("optestm88k_sub.s", G_SUB);
create_file("optestm88k_logical.s", G_LOGICAL);
create_file("optestm88k_bitfield.s",G_BITFIELD);
create_file("optestm88k_lda.s", G_LDA);
create_file("optestm88k_muldiv.s", G_MULDIV);
create_file("optestm88k_flt.s", G_FLT);
create_file("optestm88k_fcmp.s", G_FCMP);
//
// table.h を出力
//
FOPEN("optestm88k_table.h");
out("#ifndef optestm88k_table_h\n");
out("#define optestm88k_table_h\n");
out("\n");
// testcode は table_*.name を uniq したもの
std::vector<std::string> test_code_list;
for (const auto& t : test_table) {
append(test_code_list, std::string(t.code));
}
for (const auto& name : test_code_list) {
out("extern int test_%s(int *, int *);\n", name.c_str());
}
out("\n");
for (const auto& t : test_table) {
out("extern int *list_%s[];\n", t.name);
}
out("\n");
out("#endif /* !optestm88k_table_h */\n");
FCLOSE();
//
// table.c を出力
//
FOPEN("optestm88k_table.c");
out("#include \"optestm88k_main.h\"\n");
out("#include \"optestm88k_table.h\"\n");
out("\n");
out("struct testentry test_table[] = {\n");
for (const auto& t : test_table) {
out(" { test_%s,\tlist_%s,\t%d, \"%s\" },\n",
t.code, t.name, t.quad, t.name);
}
out(" { NULL },\n");
out("};\n");
FCLOSE();
return 0;
}
//
// テスト一覧
//
#define T(group, code, quad, name) { group, code, gen_##name, quad, #name }
std::vector<testentry> test_table = {
T(G_ADD, "addsub", 0, add),
T(G_ADD, "addsub", 0, add_ci),
T(G_ADD, "addsub", 0, add_co),
T(G_ADD, "addsub", 0, add_cio),
T(G_ADD, "addsub", 0, add_imm),
T(G_ADD, "addsub", 0, addu),
T(G_ADD, "addsub", 0, addu_ci),
T(G_ADD, "addsub", 0, addu_co),
T(G_ADD, "addsub", 0, addu_cio),
T(G_ADD, "addsub", 0, addu_imm),
T(G_SUB, "addsub", 0, sub),
T(G_SUB, "addsub", 0, sub_ci),
T(G_SUB, "addsub", 0, sub_co),
T(G_SUB, "addsub", 0, sub_cio),
T(G_SUB, "addsub", 0, sub_imm),
T(G_SUB, "addsub", 0, subu),
T(G_SUB, "addsub", 0, subu_ci),
T(G_SUB, "addsub", 0, subu_co),
T(G_SUB, "addsub", 0, subu_cio),
T(G_SUB, "addsub", 0, subu_imm),
T(G_SUB, "generic", 0, cmp),
T(G_SUB, "generic", 0, cmp_imm),
T(G_LOGICAL, "generic", 0, and),
T(G_LOGICAL, "generic", 0, and_c),
T(G_LOGICAL, "generic", 0, and_imm),
T(G_LOGICAL, "generic", 0, and_u),
T(G_LOGICAL, "generic", 0, mask_imm),
T(G_LOGICAL, "generic", 0, mask_u),
T(G_LOGICAL, "generic", 0, or),
T(G_LOGICAL, "generic", 0, or_c),
T(G_LOGICAL, "generic", 0, or_imm),
T(G_LOGICAL, "generic", 0, or_u),
T(G_LOGICAL, "generic", 0, xor),
T(G_LOGICAL, "generic", 0, xor_c),
T(G_LOGICAL, "generic", 0, xor_imm),
T(G_LOGICAL, "generic", 0, xor_u),
T(G_BITFIELD, "generic", 0, clr_imm),
T(G_BITFIELD, "generic", 0, clr_reg),
T(G_BITFIELD, "generic", 0, ext_imm),
T(G_BITFIELD, "generic", 0, ext_reg),
T(G_BITFIELD, "generic", 0, extu_imm),
T(G_BITFIELD, "generic", 0, extu_reg),
T(G_BITFIELD, "generic", 0, ff0),
T(G_BITFIELD, "generic", 0, ff1),
T(G_BITFIELD, "generic", 0, mak_imm),
T(G_BITFIELD, "generic", 0, mak_reg),
T(G_BITFIELD, "generic", 0, rot_imm),
T(G_BITFIELD, "generic", 0, rot_reg),
T(G_BITFIELD, "generic", 0, set_imm),
T(G_BITFIELD, "generic", 0, set_reg),
T(G_LDA, "addsub", 0, lda_imm),
T(G_LDA, "addsub", 0, lda_b_imm),
T(G_LDA, "addsub", 0, lda_h_imm),
T(G_LDA, "addsub", 0, lda_d_imm),
T(G_LDA, "addsub", 0, lda_reg),
T(G_LDA, "addsub", 0, lda_b_reg),
T(G_LDA, "addsub", 0, lda_h_reg),
T(G_LDA, "addsub", 0, lda_d_reg),
T(G_LDA, "addsub", 0, lda_scale),
T(G_LDA, "addsub", 0, lda_b_scale),
T(G_LDA, "addsub", 0, lda_h_scale),
T(G_LDA, "addsub", 0, lda_d_scale),
T(G_MULDIV, "generic", 0, mul),
T(G_MULDIV, "generic", 0, mul_imm),
T(G_MULDIV, "div", 0, div),
T(G_MULDIV, "div", 0, div_imm),
T(G_MULDIV, "div", 0, divu),
T(G_MULDIV, "div", 0, divu_imm),
T(G_FLT, "fop_s", 0, flt_ss),
T(G_FLT, "fop_d", 1, flt_ds),
T(G_FLT, "fop_s", 0, trnc_ss),
T(G_FLT, "fop_s", 0, trnc_sd),
T(G_FLT, "fop_s", 0, nint_ss),
T(G_FLT, "fop_s", 0, nint_sd),
T(G_FLT, "fop_s", 0, int_ss),
T(G_FLT, "fop_s", 0, int_sd),
T(G_FCMP, "fop_s", 0, fcmp_sss),
T(G_FCMP, "fop_s", 0, fcmp_ssd),
T(G_FCMP, "fop_s", 0, fcmp_sds),
T(G_FCMP, "fop_s", 0, fcmp_sdd),
};
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.