File:  [Isaki's NoNo m68k/m88k emulator] / nono / m88xx0 / m88100disasm.cpp
Revision 1.1.1.12 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:56 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//

#include "m88100disasm.h"
#include "debugger_memory.h"
#include "m88100.h"
#include "m88100acc.h"
#include "mystring.h"

#define OP_DEF(name)	void __CONCAT(m88100disasm::op_,name)()
#define OP_FUNC(name)	__CONCAT(op_,name)()

// 別ニーモニックを使うなら true にする (外部から指定する)
/*static*/ bool m88100disasm::use_altname;

// コンストラクタ
m88100disasm::m88100disasm()
{
}

// デストラクタ
m88100disasm::~m88100disasm()
{
}

// 逆アセンブルを実行
bool
m88100disasm::Exec(DebuggerMemoryStream *mem_)
{
	ExecInit(mem_);

	opX = 0;
	name.clear();
	arg0.clear();
	arg1.clear();
	arg2.clear();
	altname.clear();
	altarg0.clear();
	altarg1.clear();
	altarg2.clear();

	xip = mem->laddr.Addr();

	opX = mem->Read(4);
	uint32 op12 = M88100::op32_to_12(opX);
	switch (op12) {
#include "m88100switch.inc"
	 default:
		name = "illegal";
		break;
	}

	// 出力文字列(ダンプ)
	dump = string_format("%08x ", opX);

	// 出力文字列(命令)
	text = name;
	if (!arg0.empty() || !arg1.empty() || !arg2.empty()) {
		// オペランドがどれかでもあればインデントする
		int len = text.length();
		if (len < 8) {
			text += std::string(8 - len, ' ');
		} else {
			text += ' ';
		}

		// arg0,1,2 の空でないやつを追加
		std::string args;
		int argc = 0;
		if (!arg0.empty()) {
			args += arg0;
			argc++;
		}
		if (!arg1.empty()) {
			if (argc != 0) {
				args += ",";
				while (args.length() < argc * 4)
					args += " ";
			}
			args += arg1;
			argc++;
		}
		if (!arg2.empty()) {
			if (argc != 0) {
				args += ",";
				while (args.length() < argc * 4)
					args += " ";
			}
			args += arg2;
		}

		text += args;
	}

	// 出力文字列(別名)
	if (use_altname && !altname.empty()) {
		if (altarg1.empty() && !altarg2.empty()) {
			altarg1 = altarg2;
			altarg2.clear();
		}
		// {mov rn, rn} -> {nop} は無条件に置換できるのでここでやる
		if (altname == "mov" && altarg0 == altarg1 && altarg2.empty()) {
			altname = "nop";
			altarg0.clear();
			altarg1.clear();
		}

		alttext += "{" + altname;

		if (!altarg0.empty() || !altarg1.empty() || !altarg2.empty()) {
			// オペランドがあればインデントする
			int len = alttext.length();
			if (len < 8) {
				alttext += std::string(8 - len, ' ');
			} else {
				alttext += ' ';
			}

			// altarg0,1,2 の空でないやつを追加
			std::string altargs;
			int altargc = 0;
			if (!altarg0.empty()) {
				altargs += altarg0;
				altargc++;
			}
			if (!altarg1.empty()) {
				if (altargc != 0) {
					altargs += ',';
					while (altargs.length() < altargc * 4)
						altargs += ' ';
				}
				altargs += altarg1;
				altargc++;
			}
			if (!altarg2.empty()) {
				if (altargc != 0) {
					altargs += ',';
					while (altargs.length() < altargc * 4)
						altargs += ' ';
				}
				altargs += altarg2;
			}

			alttext += altargs;
		}
		alttext += '}';
	}

	// 出力バイナリ列
	// bin は vector<uint8>。
	bin.push_back( opX >> 24);
	bin.push_back((opX >> 16) & 0xff);
	bin.push_back((opX >>  8) & 0xff);
	bin.push_back( opX        & 0xff);

	return true;
}

// フィールド部品
#define DD		((opX >> 21) & 0x1f)
#define S1		((opX >> 16) & 0x1f)
#define S2		( opX        & 0x1f)
#define B5		((opX >> 21) & 0x1f)
#define M5		((opX >> 21) & 0x1f)
#define W5		((opX >>  5) & 0x1f)
#define O5		((opX      ) & 0x1f)
#define CIO		((opX >>  8) & 0x03)
#define IMM16	(opX & 0x0000ffffU)
#define VEC9	(opX & 0x000001ffU)
#define D16		(((int32)(int16)opX) << 2)
#define D26		(((int32)(opX << 6)) >> 4)

std::string
m88100disasm::rd()
{
	return string_format("r%u", (opX >> 21) & 0x1f);
}

std::string
m88100disasm::rs1()
{
	return string_format("r%u", (opX >> 16) & 0x1f);
}

std::string
m88100disasm::rs2()
{
	return string_format("r%u", (opX      ) & 0x1f);
}

std::string
m88100disasm::imm16()
{
	return string_format("#0x%04x", (opX & 0x0000ffff));
}

std::string
m88100disasm::d16()
{
	return string_format("0x%08x", xip + D16);
}

std::string
m88100disasm::d26()
{
	return string_format("0x%08x", xip + D26);
}

// 制御レジスタ名
std::string
m88100disasm::crs()
{
	// CRS フィールドは 6 bit。ただし CR20 までしか存在しない
	uint32 cr = (opX >> 5) & 0x3f;
	static const char *crnames[] = {
		"pid",	"psr",	"epsr",	"ssbr",		// cr0..3
		"sxip",	"snip",	"sfip",	"vbr",		// cr4..7
		"dmt0",	"dmd0",	"dma0",				// cr8..10
		"dmt1",	"dmd1",	"dma1",				// cr11..13
		"dmt2",	"dmd2",	"dma2",				// cr14..16
		"sr0",	"sr1",	"sr2",	"sr3",		// cr17..20
	};

	if (cr < countof(crnames)) {
		return std::string(crnames[cr]);
	} else {
		return string_format("cr%u", cr);	// こうなのか?
	}
}

// FPU 制御レジスタ名
std::string
m88100disasm::fcrs()
{
	uint32 cr = (opX >> 5) & 0x3f;
	static const char *fcrnames[] = {
		"fpecr",	// fcr0
		"fphs1",	// fcr1
		"fpls1",	// fcr2
		"fphs2",	// fcr3
		"fpls2",	// fcr4
		"fppt",		// fcr5
		"fprh",		// fcr6
		"fprl",		// fcr7
		"fpit",		// fcr8
	};
	if (cr < countof(fcrnames)) {
		return std::string(fcrnames[cr]);
	} else if (cr == 62) {
		return "fpsr";
	} else if (cr == 63) {
		return "fpcr";
	} else {
		return string_format("fcr%u", cr);
	}
}

#define TD	(5)
#define T1	(9)
#define T2	(7)

// FPU サイズサフィックス t は TD、T1、T2
std::string
m88100disasm::fsize(uint32 t)
{
	uint32 sz = (opX >> t) & 3;
	return sz == 1 ? "d" : "s";
}

// FPU サイズサフィックス (全部入り)
std::string
m88100disasm::fsize()
{
	return fsize(TD) + fsize(T1) + fsize(T2);
}

// "Width<Offset>"
std::string
m88100disasm::wo5()
{
	uint32 w5 = W5 ?: 32;
	uint32 o5 = O5;
	return string_format("%u<%u>", w5, o5);
}

// "Width<Offset>" 形式をビットマスク形式(数値)にして返す
uint32
m88100disasm::wo5mask()
{
	uint32 w5 = W5 ?: 32;
	uint32 o5 = O5;

	uint64 mask = (1ULL << w5) - 1;
	mask <<= o5;
	return (uint32)mask;
}

// キャリーサフィックス
std::string
m88100disasm::cio()
{
	uint32 c = (opX >> 8) & 3;
	static const char *carrystr[] = {
		"",
		".co",
		".ci",
		".cio",
	};
	return std::string(carrystr[c]);
}

std::string
m88100disasm::b5()
{
	return string_format("#%u", B5);
}

std::string
m88100disasm::m5()
{
	uint32 m = M5;
	switch (m) {
	 case 0x02:	return "eq0";
	 case 0x0d:	return "ne0";
	 case 0x01:	return "gt0";
	 case 0x0c:	return "lt0";
	 case 0x03:	return "ge0";
	 case 0x0e:	return "le0";
	 default:
		break;
	}
	return string_format("#%u", m);
}

// M5(,rS1) に対する代替ニーモニックを返す。empty なら代替ニーモニックなし。
// 条件が 0(always false) でも .n かどうかがあるので nop とかにはしない。
// 15(always true) は無条件 br{,.n} だがこれは戻り値 "true" を呼び出し側が
// 比較して処理すること。
// 6 と 9 は使いみちがないので empty を返す。
// reserved なビットが立っていても動作には影響はないが、ニーモニックとしては
// 不正なものとして表示するためここでは empty を返す。
std::string
m88100disasm::alt_m5()
{
	// 0000			false
	// 0001 gt0		r > 0				(float)r > +0
	// 0010	eq0		r == 0				(float)r == +0
	// 0011	ge0		r >= 0				(float)r >= +0
	// 0100 							(float)r < -0
	// 0101								(float)r != 0
	// 0110			#6
	// 0111			r != 0x80000000		(float)r != -0
	// 1000			r == 0x80000000		(float)r == -0
	// 1001			#9
	// 1010								(float)r == 0
	// 1011								(float)r >= -0
	// 1100	lt0		r < 0				(float)r < +0
	// 1101	ne0		r != 0				(float)r != +0
	// 1110	le0		r <= 0				(float)r <= +0
	// 1111			true

	uint32 m = M5;
	if (S1 == 0) {
		if (m < 0x10) {
			// r0 相手なら、下から 2bit 目が立っていれば true、
			// そうでなければ false の二択。
			if ((m & 0x02)) {
				return "true";
			} else {
				return "false";
			}
		}
	} else {
		char rs[8];
		strlcpy(rs, rs1().c_str(), sizeof(rs));
		switch (m) {
		 case 0:
			return "false";
		 case 1:
			return string_format("(%s>0|(float)%s>+0)", rs, rs);
		 case 2:
			return string_format("(%s==0|(float)%s==+0)", rs, rs);
		 case 3:
			return string_format("(%s>=0|(float)%s>=+0)", rs, rs);
		 case 4:
			return string_format("((float)%s<-0)", rs);
		 case 5:
			return string_format("((float)%s!=0)", rs);
		 case 7:
			return string_format("(%s!=0x80000000)", rs);
		 case 8:
			return string_format("(%s==0x80000000)", rs);
		 case 0xa:
			return string_format("((float)%s==0)", rs);
		 case 0xb:
			return string_format("((float)%s>=-0)", rs);
		 case 0xc:
			return string_format("(%s<0|(float)%s<+0)", rs, rs);
		 case 0xd:
			return string_format("(%s!=0|(float)%s!=+0)", rs, rs);
		 case 0xe:
			return string_format("(%s<=0|(float)%s<=+0)", rs, rs);

		 case 0xf:	// always true
			return "true";

		 case 6:
		 case 9:
		 default:	// reserved
			break;
		}
	}

	return "";
}

std::string
m88100disasm::vec9()
{
	return string_format("#0x%03x", VEC9);
}

// エミュレータ的未実装 (最終的にはなくなる)
void
m88100disasm::op_unimpl()
{
	name = "unimplemented";
}

// ld_imm, st_imm, xmem_imm の共通部
// namebase にはサイズプレフィックスを含む。
void
m88100disasm::ops_ldst_imm(const std::string& namebase)
{
	name = namebase;
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	altname = namebase;
	altarg0 = rd();
	if (IMM16 == 0) {
		altarg1 = rs1();
	} else {
		altarg1 = rs1() + "," + imm16();
	}
	// 3文字目が 'a' になるのは lda しかない
	if (namebase[2] != 'a') {
		altarg1 = "(" + altarg1 + ")";
	}
}

// 000000_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(xmem_bu_imm)
{
	ops_ldst_imm("xmem.bu");
}

// 000001_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(xmem_w_imm)
{
	ops_ldst_imm("xmem");
}

// 000010_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(ld_hu_imm)
{
	ops_ldst_imm("ld.hu");
}

// 000011_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(ld_bu_imm)
{
	ops_ldst_imm("ld.bu");
}

// 000100_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(ld_d_imm)
{
	ops_ldst_imm("ld.d");
}

// 000101_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(ld_w_imm)
{
	ops_ldst_imm("ld");
}

// 000110_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(ld_h_imm)
{
	ops_ldst_imm("ld.h");
}

// 000111_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(ld_b_imm)
{
	ops_ldst_imm("ld.b");
}

// 001000_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(st_d_imm)
{
	ops_ldst_imm("st.d");
}

// 001001_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(st_w_imm)
{
	ops_ldst_imm("st");
}

// 001010_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(st_h_imm)
{
	ops_ldst_imm("st.h");
}

// 001011_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(st_b_imm)
{
	ops_ldst_imm("st.b");
}

// 001100_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(lda_d_imm)
{
	ops_ldst_imm("lda.d");
}

// 001101_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(lda_w_imm)
{
	ops_ldst_imm("lda");
}

// 001110_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(lda_h_imm)
{
	ops_ldst_imm("lda.h");
}

// 001111_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(lda_b_imm)
{
	ops_ldst_imm("lda.b");
}

// 010000_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(and_imm)
{
	name = "and";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// rd s1 imm
	// ----------
	// r0 *  *		nop
	// rd r0 #imm	clr	rd
	// rd rd #imm	and rd, #ffff'imm
	// rd s1 #imm	and rd, s1, #ffff'imm

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else {
		altname = "and";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = string_format("#0xffff%04x", IMM16);
	}
}

// 010001_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(and_u_imm)
{
	name = "and.u";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// rd s1 imm
	// ----------
	// r0 *  *		nop
	// rd r0 #imm	clr	rd
	// rd rd #imm	and rd, #imm'ffff
	// rd s1 #imm	and rd, s1, #imm'ffff

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else {
		altname = "and";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = imm16() + "ffff";
	}
}

// 010010_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(mask_imm)
{
	name = "mask";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// rd s1 imm
	// ----------
	// r0 *  *		nop
	// rd r0 #imm	clr rd
	// rd rd #imm	and rd, #0000'imm
	// rd s1 #imm	and rd, s1, #0000'imm

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else {
		altname = "and";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = string_format("#0x0000%04x", IMM16);
	}
}

// 010011_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(mask_u_imm)
{
	name = "mask.u";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// rd s1 imm
	// ----------
	// r0 *  *		nop
	// rd r0 #imm	clr rd
	// rd rd #imm	and rd, #imm'0000
	// rd s1 #imm	and rd, s1, #imm'0000

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else {
		altname = "and";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = imm16() + "0000";
	}
}

// 010100_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(xor_imm)
{
	name = "xor";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// rd s1 imm
	// ----------
	// r0 *  *		nop
	// rd r0 #imm	mov	rd, #imm
	// rd rd #imm	xor rd, #imm
	// rd s1 #imm	xor rd, s1, #imm

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = imm16();
	} else {
		altname = "xor";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = imm16();
	}
}

// 010101_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(xor_u_imm)
{
	name = "xor.u";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// rd s1 imm
	// ----------
	// r0 *  *		nop
	// rd r0 #imm	mov	rd, #imm'0000
	// rd rd #imm	xor rd, #imm'0000
	// rd s1 #imm	xor rd, s1, #imm'0000

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = imm16() + "0000";
	} else {
		altname = "xor";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = imm16() + "0000";
	}
}

// 010110_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(or_imm)
{
	name = "or";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// rd s1 imm
	// ----------
	// r0 *  *		nop
	// rd r0 #imm	mov rd, #imm
	// rd rd #imm	or	rd, #imm
	// rd s1 #imm	or	rd, s1, #imm

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = imm16();
	} else {
		altname = "or";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = imm16();
	}
}

// 010111_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(or_u_imm)
{
	name = "or.u";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// rd s1 imm
	// ----------
	// r0 *  *		nop
	// rd r0 #imm	mov rd, #imm'0000
	// rd rd #imm	or	rd, #imm'0000
	// rd s1 #imm	or	rd, s1, #imm'0000

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = imm16() + "0000";
	} else {
		altname = "or";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = imm16() + "0000";
	}
}

// 011000_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(addu_imm)
{
	name = "addu";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// rd s1 imm
	// ----------
	// r0 *  *		nop
	// rd r0 #imm	mov  rd, #0000'imm
	// rd rd #imm	addu rd, #0000'imm
	// rd s1 #imm	addu rd, s1, #0000'imm

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = string_format("#0x0000%04x", IMM16);
	} else {
		altname = "addu";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = string_format("#0x0000%04x", IMM16);
	}
}

// 011001_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(subu_imm)
{
	name = "subu";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// rd s1 imm
	// ----------
	// r0 *  *		nop
	// rd r0 #imm	mov  rd, #-imm
	// rd rd #imm	subu rd, #0000'imm
	// rd s1 #imm	subu rd, s1, #0000'imm

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = string_format("#0x%08x", -(int32)(uint32)IMM16);
	} else {
		altname = "subu";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = string_format("#0x0000%04x", IMM16);
	}
}

// 011010_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(divu_imm)
{
	name = "divu";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// 例外発生の可能性があるので r0 を含んでいたとしても別名にしない
	altname = "divu";
	altarg0 = rd();
	if (DD != S1) {
		altarg1 = rs1();
	}
	altarg2 = string_format("#0x0000%04x", IMM16);
}

// 011011_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(mul_imm)
{
	name = "mul";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// 例外発生の可能性があるので r0 を含んでいたとしても別名にしない
	altname = "mul";
	altarg0 = rd();
	if (DD != S1) {
		altarg1 = rs1();
	}
	altarg2 = string_format("#0x0000%04x", IMM16);
}

// 011100_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(add_imm)
{
	name = "add";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// 例外発生の可能性があるので r0 を含んでいたとしても別名にしない
	altname = "adds";
	altarg0 = rd();
	if (DD != S1) {
		altarg1 = rs1();
	}
	altarg2 = string_format("#0x0000%04x", IMM16);
}

// 011101_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(sub_imm)
{
	name = "sub";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// 例外発生の可能性があるので r0 を含んでいたとしても別名にしない
	altname = "subs";
	altarg0 = rd();
	if (DD != S1) {
		altarg1 = rs1();
	}
	altarg2 = string_format("#0x0000%04x", IMM16);
}

// 011110_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(div_imm)
{
	name = "div";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	// 例外発生の可能性があるので r0 を含んでいたとしても別名にしない
	altname = "div";
	altarg0 = rd();
	if (DD != S1) {
		altarg1 = rs1();
	}
	altarg2 = string_format("#0x0000%04x", IMM16);
}

// 011111_DDDDDSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(cmp_imm)
{
	name = "cmp";
	arg0 = rd();
	arg1 = rs1();
	arg2 = imm16();

	if (DD == 0) {
		altname = "nop";
	} else {
		altname = "cmp";
		altarg0 = rd();
		altarg1 = rs1();
		altarg2 = string_format("#0x0000%04x", IMM16);
	}
}

// 100000_DDDDDzzzzz_01000n_nnnnnzzzzz
OP_DEF(ldcr)
{
	name = "ldcr";
	arg0 = rd();
	arg1 = crs();
}

// 100000_DDDDDzzzzz_01001n_nnnnnzzzzz
OP_DEF(fldcr)
{
	name = "fldcr";
	arg0 = rd();
	arg1 = fcrs();
}

// 100000_zzzzzSSSSS_10000n_nnnnnsssss
OP_DEF(stcr)
{
	uint32 s1 = (opX >> 16) & 0x1f;
	uint32 s2 = (opX      ) & 0x1f;
	if (s1 == s2) {
		name = "stcr";
		arg1 = rs1();
		arg2 = crs();
	} else {
		// どうすべ
		OP_FUNC(unimpl);
	}
}

// 100000_zzzzzSSSSS_10001n_nnnnnsssss
OP_DEF(fstcr)
{
	uint32 s1 = (opX >> 16) & 0x1f;
	uint32 s2 = (opX      ) & 0x1f;
	if (s1 == s2) {
		name = "fstcr";
		arg1 = rs1();
		arg2 = fcrs();
	} else {
		// どうすべ
		OP_FUNC(unimpl);
	}
}

// 100000_DDDDDSSSSS_11000n_nnnnnsssss
OP_DEF(xcr)
{
	uint32 s1 = (opX >> 16) & 0x1f;
	uint32 s2 = (opX      ) & 0x1f;
	if (s1 == s2) {
		name = "xcr";
		arg0 = rd();
		arg1 = rs1();
		arg2 = crs();
	} else {
		// どうすべ
		OP_FUNC(unimpl);
	}
}

// 100000_DDDDDSSSSS_11001n_nnnnnsssss
OP_DEF(fxcr)
{
	uint32 s1 = (opX >> 16) & 0x1f;
	uint32 s2 = (opX      ) & 0x1f;
	if (s1 == s2) {
		name = "fxcr";
		arg0 = rd();
		arg1 = rs1();
		arg2 = fcrs();
	} else {
		// どうすべ
		OP_FUNC(unimpl);
	}
}

// 100001_DDDDDSSSSS_00000n_nnnnnsssss
OP_DEF(fmul)
{
	name = "fmul." + fsize();
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();
}

// 100001_DDDDD00000_001000_000nnsssss
OP_DEF(flt)
{
	name = "flt." + fsize(TD) + "s";
	arg0 = rd();
	arg2 = rs2();
}

// 100001_DDDDDSSSSS_00101n_nnnnnsssss
OP_DEF(fadd)
{
	name = "fadd." + fsize();
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();
}

// 100001_DDDDDSSSSS_00110n_nnnnnsssss
OP_DEF(fsub)
{
	name = "fsub." + fsize();
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();
}

// 100001_DDDDDSSSSS_00111n_nnnnnsssss
OP_DEF(fcmp)
{
	name = "fcmp.s" + fsize(T1) + fsize(T2);
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();
}

// 100001_DDDDD00000_010010_0nn00sssss
OP_DEF(int)
{
	name = "int.s" + fsize(T2);
	arg0 = rd();
	arg1 = rs2();
}

// 100001_DDDDD00000_010100_0nn00sssss
OP_DEF(nint)
{
	name = "nint.s" + fsize(T2);
	arg0 = rd();
	arg1 = rs2();
}

// 100001_DDDDD00000_010110_0nn00sssss
OP_DEF(trnc)
{
	name = "trnc.s" + fsize(T2);
	arg0 = rd();
	arg2 = rs2();
}

// 100001_DDDDDSSSSS_01110n_nnnnnsssss
OP_DEF(fdiv)
{
	name = "fdiv." + fsize();
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();
}

// 110000_nnnnnnnnnn_nnnnnn_nnnnnnnnnn
OP_DEF(br)
{
	name = "br";
	arg0 = d26();
}

// 110001_nnnnnnnnnn_nnnnnn_nnnnnnnnnn
OP_DEF(br_n)
{
	name = "br.n";
	arg0 = d26();
}

// 110010_nnnnnnnnnn_nnnnnn_nnnnnnnnnn
OP_DEF(bsr)
{
	name = "bsr";
	arg0 = d26();
}

// 110011_nnnnnnnnnn_nnnnnn_nnnnnnnnnn
OP_DEF(bsr_n)
{
	name = "bsr.n";
	arg0 = d26();
}

// bb の共通部
void
m88100disasm::ops_bb(std::string namebase)
{
	name = namebase;
	arg0 = b5();
	arg1 = rs1();
	arg2 = d16();
}

// 110100_nnnnnSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(bb0)
{
	ops_bb("bb0");
}

// 110101_nnnnnSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(bb0_n)
{
	ops_bb("bb0.n");
}

// 110110_nnnnnSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(bb1)
{
	ops_bb("bb1");
}

// 110111_nnnnnSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(bb1_n)
{
	ops_bb("bb1.n");
}

void
m88100disasm::ops_bcnd(std::string n)
{
	name = "bcnd" + n;
	arg0 = m5();
	arg1 = rs1();
	arg2 = d16();

	const std::string cond = alt_m5();
	if (cond.empty()) {
		// 空なら表現できないやつなので代替表現もなし
		return;
	} else if (cond == "true") {
		// 無条件分岐
		altname = "br" + n;
		altarg0 = arg2;
	} else {
		// 条件分岐
		altname = "br" + n;
		altarg0 = cond;
		altarg1 = arg2;
	}
}

// 111010_MMMMMSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(bcnd)
{
	ops_bcnd("");
}

// 111011_MMMMMSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(bcnd_n)
{
	ops_bcnd(".n");
}

// 111100_DDDDDSSSSS_100000_wwwwwooooo
OP_DEF(clr_1)
{
	name = "clr";
	arg0 = rd();
	arg1 = rs1();
	arg2 = wo5();

	if (DD == 0) {
		altname = "nop";
	} else {
		altname = "and";
		altarg0 = rd();
		altarg1 = rs1();
		altarg2 = string_format("0x%08x", ~wo5mask());
	}
}

// 111100_DDDDDSSSSS_100010_wwwwwooooo
OP_DEF(set_1)
{
	name = "set";
	arg0 = rd();
	arg1 = rs1();
	arg2 = wo5();

	if (DD == 0) {
		altname = "nop";
	} else {
		altname = "or";
		altarg0 = rd();
		altarg1 = rs1();
		altarg2 = string_format("0x%08x", wo5mask());
	}
}

// 111100_DDDDDSSSSS_100100_wwwwwooooo
OP_DEF(ext_1)
{
	name = "ext";
	arg0 = rd();
	arg1 = rs1();
	arg2 = wo5();

	// rd s1 imm
	// ----------
	// r0 *  *		nop
	// rd r0 w:o	clr rd
	// rd rd 32:o	asr rd, #o
	// rd s1 32:o	asr rd, s1, #o
	// rd rd w:o	bfexts rd, w:o
	// rd s1 w:o	bfexts rd, s1, w:o

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else if (W5 == 0 || W5 + O5 >= 32) {
		// W5 + O5 >= 32 なら単純なシフトに相当
		altname = "asr";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = string_format("#%u", O5);
	} else {
		altname = "bfexts";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = wo5();
	}
}

// 111100_DDDDDSSSSS_100110_wwwwwooooo
OP_DEF(extu_1)
{
	name = "extu";
	arg0 = rd();
	arg1 = rs1();
	arg2 = wo5();

	// rd s1 imm
	// ----------
	// r0 *  *		nop
	// rd r0 w:o	clr rd
	// rd rd 32:o	lsr rd, #o
	// rd s1 32:o	lsr rd, s1, #o
	// rd rd w:o	bfextu rd, w:o
	// rd s1 w:o	bfextu rd, s1, w:o

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else if (W5 == 0 || W5 + O5 >= 32) {
		// W5 + O5 >= 32 なら単純なシフトに相当
		altname = "lsr";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = string_format("#%u", O5);
	} else {
		altname = "bfextu";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = wo5();
	}
}

// 111100_DDDDDSSSSS_101000_wwwwwooooo
OP_DEF(mak_1)
{
	name = "mak";
	arg0 = rd();
	arg1 = rs1();
	arg2 = wo5();

	// rd s1 imm
	// ----------
	// r0 *  *		nop
	// rd r0 w:o	clr rd
	// rd rd 32:o	lsl rd, #o
	// rd s1 32:o	lsl rd, s1, #o
	// rd rd w:o	bfmak rd, w:o
	// rd s1 w:o	bfmak rd, s1, w:o

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else if (W5 == 0 || W5 + O5 >= 32) {
		// W5 + O5 >= 32 なら単純なシフトに相当
		altname = "lsl";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = string_format("#%u", O5);
	} else {
		// XXX 名前から動作が想像しづらいのがつらいのだが、とりあえず後回し
		altname = "bfmak";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = wo5();
	}
}

// 111100_DDDDDSSSSS_101010_zzzzzooooo
OP_DEF(rot_1)
{
	name = "rot";
	arg0 = rd();
	arg1 = rs1();
	arg2 = string_format("<%u>", O5);

	// rd s1 offset
	// ----------
	// r0 *  *		nop
	// rd rd #0		nop
	// rd r0 *		clr rd
	// rd rd 1-16	ror rd, #1-16
	// rd s1 1-16	ror rd, s1, #1-16
	// rd rd 17-31	rol rd, #1-15
	// rd s1 17-31	rol rd, s1, #1-15

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else if (O5 == 0) {
		if (DD == S1) {
			altname = "nop";
		} else {
			altname = "clr";
			altarg0 = rd();
		}
	} else {
		// どう表記するのがいいかは状況によるかも知れないが
		// 1-16ビット(右)ローテートなら  ror #0-16、
		// 17-31ビット(右)ローテートなら rol #1-15 としてみる
		auto offset = O5;
		if (offset > 16) {
			altname = "rol";
			offset = 32 - offset;
		} else {
			altname = "ror";
		}
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = string_format("#%u", offset);
	}
}

void
m88100disasm::ops_tb(std::string namebase)
{
	name = namebase;
	arg0 = b5();
	arg1 = rs1();
	arg2 = vec9();
}

// 111100_bbbbbSSSSS_110100_0vvvvvvvvv
OP_DEF(tb0)
{
	ops_tb("tb0");
}

// 111100_bbbbbSSSSS_110110_0vvvvvvvvv
OP_DEF(tb1)
{
	ops_tb("tb1");

	if (S1 == 0) {
		// tb1 *,r0,* はパイプライン同期を目的として使われる。
		// この形式はトラップは発生しない。
		if (VEC9 < 128) {
			altname = "ssync";
		} else {
			altname = "usync";
		}
	}
}

// 111100_MMMMMSSSSS_111010_0vvvvvvvvv
OP_DEF(tcnd)
{
	name = "tcnd";
	arg0 = m5();
	arg1 = rs1();
	arg2 = vec9();
}

// ld_reg, st_reg, xmem_reg の共通部
// size は ".bu" などドットを含むサイズプレフィックス。
void
m88100disasm::ops_ldst_reg(const std::string& namebase, std::string size)
{
	uint32 u      = (opX >>  8) & 1;
	uint32 scaled = (opX >>  9) & 1;

	name = namebase + size;
	if (u) {
		name += ".usr";
	}
	arg0 = rd();
	arg1 = rs1();
	if (scaled == 0) {
		arg2 = rs2();
	} else {
		arg1 += "[" + rs2() + "]";
	}

	// 別名
	// ld.bu.usr はサフィックスの使い方が節操なさすぎるし
	// ld と ld.usr はわりと違う命令なので、違う命令のように見せる。
	altname = namebase;
	if (u) {
		altname += "usr";
	}
	altname += size;
	altarg0 = rd();

	// メモリアクセスはは括弧を付けて表す。
	// lda はメモリアクセスではないので括弧なしのまま。
	//
	// rs1 rs2 scaled
	// --------------
	// r*  r0  *		ld rd, (r*)
	// r0  s2  no		ld rd, (s2)
	// s1  s2  no		ld rd, (s1,s2)
	// r0  s2  yes		ld rd, (r0[s2])
	// s1  s2  yes		ld rd, (s1[s2])
	if (S2 == 0) {
		altarg1 = rs1();
	} else {
		if (scaled == 0) {
			if (S1 == 0) {
				altarg1 = rs2();
			} else {
				altarg1 = rs1() + "," + rs2();
			}
		} else {
			altarg1 = rs1() + "[" + rs2() + "]";
		}
	}
	// 3文字目が 'a' になるのは lda しかない
	if (namebase[2] != 'a') {
		altarg1 = "(" + altarg1 + ")";
	}
}

// 111101_DDDDDSSSSS_000000_xU000sssss
OP_DEF(xmem_bu)
{
	ops_ldst_reg("xmem", ".bu");
}

// 111101_DDDDDSSSSS_000001_xU000sssss
OP_DEF(xmem_w)
{
	ops_ldst_reg("xmem", "");
}

// 111101_DDDDDSSSSS_000010_xU000sssss
OP_DEF(ld_hu)
{
	ops_ldst_reg("ld", ".hu");
}

// 111101_DDDDDSSSSS_000011_xU000sssss
OP_DEF(ld_bu)
{
	ops_ldst_reg("ld", ".bu");
}

// 111101_DDDDDSSSSS_000100_xU000sssss
OP_DEF(ld_d)
{
	ops_ldst_reg("ld", ".d");
}

// 111101_DDDDDSSSSS_000101_xU000sssss
OP_DEF(ld_w)
{
	ops_ldst_reg("ld", "");
}

// 111101_DDDDDSSSSS_000110_xU000sssss
OP_DEF(ld_h)
{
	ops_ldst_reg("ld", ".h");
}

// 111101_DDDDDSSSSS_000111_xU000sssss
OP_DEF(ld_b)
{
	ops_ldst_reg("ld", ".b");
}

// 111101_DDDDDSSSSS_001000_xU000sssss
OP_DEF(st_d)
{
	ops_ldst_reg("st", ".d");
}

// 111101_DDDDDSSSSS_001001_xU000sssss
OP_DEF(st_w)
{
	ops_ldst_reg("st", "");
}

// 111101_DDDDDSSSSS_001010_xU000sssss
OP_DEF(st_h)
{
	ops_ldst_reg("st", ".h");
}

// 111101_DDDDDSSSSS_001011_xU000sssss
OP_DEF(st_b)
{
	ops_ldst_reg("st", ".b");
}

// 111101_DDDDDSSSSS_001100_xU000sssss
OP_DEF(lda_d)
{
	// XXX bit8 が 1 だとどうなるのか
	ops_ldst_reg("lda", ".d");
}

// 111101_DDDDDSSSSS_001101_xU000sssss
OP_DEF(lda_w)
{
	// XXX bit8 が 1 だとどうなるのか
	ops_ldst_reg("lda", "");
}

// 111101_DDDDDSSSSS_001110_xU000sssss
OP_DEF(lda_h)
{
	// XXX bit8 が 1 だとどうなるのか
	ops_ldst_reg("lda", ".h");
}

// 111101_DDDDDSSSSS_001111_xU000sssss
OP_DEF(lda_b)
{
	// XXX bit8 が 1 だとどうなるのか
	ops_ldst_reg("lda", ".b");
}

// 111101_DDDDDSSSSS_010000_00000sssss
OP_DEF(and)
{
	name = "and";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// rd s1 s2
	// ----------
	// r0 *  *		nop
	// rd r0 *		clr	rd
	// rd *  r0		clr rd
	// rd rd rd		nop
	// rd s1 s1		mov rd, s1
	// rd rd s2		and rd, s2
	// rd s1 s2		and rd, s1, s2

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0 || S2 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else if (S1 == S2) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = rs1();
	} else {
		altname = "and";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		if (DD != S2) {
			altarg2 = rs2();
		}
	}
}

// 111101_DDDDDSSSSS_010001_00000sssss
OP_DEF(and_c)
{
	name = "and.c";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// rd s1 s2
	// ----------
	// r0 *  *		nop
	// rd r0 *		clr	rd
	// rd s1 r0		mov rd, s1
	// rd rd r0		nop
	// rd s1 s1		clr rd
	// rd s1 s2		and rd, s1, ~s2

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else if (S2 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = rs1();
	} else if (S1 == S2) {
		altname = "clr";
		altarg0 = rd();
	} else {
		altname = "and";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = "~" + rs2();
	}
}

// 111101_DDDDDSSSSS_010100_00000sssss
OP_DEF(xor)
{
	name = "xor";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// rd s1 s2
	// ----------
	// r0 *  *		nop
	// rd s1 s1		clr rd
	// rd r0 s2		mov rd, s2
	// rd s1 r0		mov rd, s1
	// rd s1 s2		xor rd, s1, s2

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == S2) {
		altname = "clr";
		altarg0 = rd();
	} else if (S1 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = rs2();
	} else if (S2 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = rs1();
	} else {
		altname = "xor";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		if (DD != S2) {
			altarg2 = rs2();
		}
	}
}

// 111101_DDDDDSSSSS_010101_00000sssss
OP_DEF(xor_c)
{
	name = "xor.c";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// rd s1 s2
	// ----------
	// r0 *  *		nop
	// rd s1 s1		mov rd, #0xffffffff
	// rd r0 s2		not rd, s2
	// rd s1 r0		not rd, s1
	// rd s1 s2		xor rd, s1, ~s2

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == S2) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = "#0xffffffff";
	} else if (S1 == 0) {
		altname = "not";
		altarg0 = rd();
		if (DD != S2) {
			altarg1 = rs2();
		}
	} else if (S2 == 0) {
		altname = "not";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
	} else {
		altname = "xor";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = "~" + rs2();
	}
}

// 111101_DDDDDSSSSS_010110_00000sssss
OP_DEF(or)
{
	name = "or";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// rd s1 s2
	// ----------
	// r0 *  *		nop
	// rd r0 r0		clr rd
	// rd s1 s1		mov rd, s1
	// rd s1 r0		mov rd, s1
	// rd r0 s2		mov rd, s2
	// rd s1 s2		or  rd, s1, s2

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0 && S2 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else if (S1 == S2 || S2 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = rs1();
	} else if (S1 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = rs2();
	} else {
		altname = "or";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		if (DD != S2) {
			altarg2 = rs2();
		}
	}
}

// 111101_DDDDDSSSSS_010111_00000sssss
OP_DEF(or_c)
{
	name = "or.c";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// rd s1 s2
	// ----------
	// r0 *  *		nop
	// rd s1 r0		mov rd, #0xffffffff
	// rd s1 s1		mov rd, #0xffffffff
	// rd r0 s2		not rd, s2
	// rd s1 s2		or  rd, s1, ~s2

	if (DD == 0) {
		altname = "nop";
	} else if (S2 == 0 || S1 == S2) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = "#0xffffffff";
	} else if (S1 == 0) {
		altname = "not";
		altarg0 = rd();
		if (DD != S2) {
			altarg1 = rs2();
		}
	} else {
		altname = "or";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = "~" + rs2();
	}
}

// 111101_DDDDDSSSSS_011000_nn000sssss
OP_DEF(addu)
{
	name = "addu" + cio();
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	if (CIO == 0) {
		// addu 無印の場合
		//
		// rd s1 s2
		// ----------
		// r0 *  *		nop
		// rd r0 r0		clr rd
		// rd s1 r0		mov rd, s1
		// rd r0 s2		mov rd, s2
		// rd s1 s2		add rd, s1, s2

		if (DD == 0) {
			altname = "nop";
		} else if (S1 == 0 && S2 == 0) {
			altname = "clr";
			altarg0 = rd();
		} else if (S1 == 0 || S2 == 0) {
			altname = "mov";
			altarg0 = rd();
			if (S1 == 0) {
				altarg1 = rs2();
			}
			if (S2 == 0) {
				altarg1 = rs1();
			}
		} else if (DD == S1 && DD == S2) {
			altname = "addu";
			altarg0 = rd();
			altarg1 = rs2();
		} else {
			altname = "addu";
			altarg0 = rd();
			if (DD != S1) {
				altarg1 = rs1();
			}
			if (DD != S2) {
				altarg2 = rs2();
			}
		}
	} else {
		// addu キャリー込み .ci/co/cio のほうは 2 オペランドへの省略のみ。
		// XXX addu の2項は交換可なので addu r1, r2, r1 は addu r1, r2 と
		//     省略できていいがそれには未対応。
		altname = "addu" + cio();
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = rs2();
	}
}

// 111101_DDDDDSSSSS_011001_nn000sssss
OP_DEF(subu)
{
	name = "subu" + cio();
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	if (CIO == 0) {
		// subu 無印の場合
		//
		// rd s1 s2
		// ----------
		// r0 *  *		nop
		// rd s1 s1		clr  rd
		// rd s1 r0		mov  rd, s1
		// rd s1 s2		subu rd, s1, s2

		if (DD == 0) {
			altname = "nop";
		} else if (S1 == S2) {
			altname = "clr";
			altarg0 = rd();
		} else if (S2 == 0) {
			altname = "mov";
			altarg0 = rd();
			altarg1 = rs1();
		} else {
			altname = "subu";
			altarg0 = rd();
			if (DD != S1) {
				altarg1 = rs1();
			}
			altarg2 = rs2();
		}
	} else {
		// subu キャリー込み .ci/co/cio のほうは 2 オペランドへの省略のみ。
		altname = "subu" + cio();
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = rs2();
	}
}

// 111101_DDDDDSSSSS_011010_zz000sssss
OP_DEF(divu)
{
	name = "divu";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// 例外が起きる可能性があるので 2オペランドへの省略のみ
	altname = "divu";
	altarg0 = rd();
	if (DD != S1) {
		altarg1 = rs1();
	}
	altarg2 = rs2();
}

// 111101_DDDDDSSSSS_011011_zz000sssss
OP_DEF(mul)
{
	name = "mul";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// 例外が起きる可能性があるので 2オペランドへの省略のみ
	altname = "mul";
	altarg0 = rd();
	if (DD != S1) {
		altarg1 = rs1();
	}
	altarg2 = rs2();
}

// 111101_DDDDDSSSSS_011100_nn000sssss
OP_DEF(add)
{
	name = "add" + cio();
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	if (CIO == 0) {
		// add 無印は符号付きなので、より明示的な adds を使う。
		// 符号付き加算ではオーバーフローが起きる可能性があるため、
		// rD が r0 であってもただちに nop とはならない。
		// 一方 r0 を加算する場合は何も起きないので圧縮表記したい。

		// rd s1 s2
		// ----------
		// rd r0 r0		clr  rd
		// r0 r0 rd		nop
		// rd r0 rd		nop
		// rd r0 s2		mov  rd, s2
		// rd rd r0		nop
		// rd s1 r0		mov  rd, s1
		// rd s1 s2		adds rd, s1, s2

		if (S1 == 0 && S2 == 0) {
			if (DD == 0) {
				altname = "nop";
			} else {
				altname = "clr";
				altarg0 = rd();
			}
		} else if (S1 == 0) {
			if (DD == 0) {
				altname = "nop";
			} else {
				altname = "mov";
				altarg0 = rd();
				altarg1 = rs2();
			}
		} else if (S2 == 0) {
			if (DD == 0) {
				altname = "nop";
			} else {
				altname = "mov";
				altarg0 = rd();
				altarg1 = rs1();
			}
		} else {
			altname = "adds";
			altarg0 = rd();
			if (DD != S1) {
				altarg1 = rs1();
			}
			altarg2 = rs2();
		}
	} else {
		// adds キャリー込み .ci/co/cio のほうは 2 オペランドへの省略のみ。
		altname = "adds" + cio();
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = rs2();
	}
}

// 111101_DDDDDSSSSS_011101_nn000sssss
OP_DEF(sub)
{
	name = "sub" + cio();
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	if (CIO == 0) {
		// sub 無印は符号付きなので、より明示的な subs を使う。
		// 符号付き減算ではオーバーフローが起きる可能性があるため、
		// rD が r0 であってもただちに nop とはならない。
		// 一方 r0 を減算する場合は何も起きないので圧縮表記したい。

		// rd s1 s2
		// ----------
		// rd rd r0		nop
		// rd r0 r0		clr  rd
		// rd s1 r0		mov  rd, s1
		// rd s1 s2		subs rd, s1, s2

		if (S1 == 0 && S2 == 0) {
			if (DD == 0) {
				altname = "nop";
			} else {
				altname = "clr";
				altarg0 = rd();
			}
		} else if (S2 == 0) {
			if (DD == 0) {
				altname = "nop";
			} else {
				altname = "mov";
				altarg0 = rd();
				altarg1 = rs1();
			}
		} else {
			altname = "subs";
			altarg0 = rd();
			if (DD != S1) {
				altarg1 = rs1();
			}
			altarg2 = rs2();
		}
	} else {
		// subs キャリー込み .ci/co/cio のほうは 2 オペランドへの省略のみ。
		altname = "subs" + cio();
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = rs2();
	}
}

// 111101_DDDDDSSSSS_011110_zz000sssss
OP_DEF(div)
{
	name = "div";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// 例外が起きる可能性があるので 2オペランドへの省略のみ
	altname = "div";
	altarg0 = rd();
	if (DD != S1) {
		altarg1 = rs1();
	}
	altarg2 = rs2();
}

// 111101_DDDDDSSSSS_011111_zz000sssss
OP_DEF(cmp)
{
	name = "cmp";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// cmp r0,*,* 以外は別表記を用意する必要ない
	if (DD == 0) {
		altname = "nop";
	}
}

// 111101_DDDDDSSSSS_100000_00000sssss
OP_DEF(clr_2)
{
	name = "clr";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// rd s1 s2
	// ----------
	// r0 *  *		nop
	// rd r0 s2		clr  rd
	// rd s1 r0		mov  rd, s1
	// rd rd s2		bfclr rd, s2
	// rd s1 s2		bfclr rd, s1, s2

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else if (S2 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = rs1();
	} else {
		altname = "bfclr";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = rs2();
	}
}

// 111101_DDDDDSSSSS_100010_00000sssss
OP_DEF(set_2)
{
	name = "set";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// rd s1 s2
	// ----------
	// r0 *  *		nop
	// rd s1 r0		mov   rd, #0xffffffff
	// rd rd s2		bfset rd, s2
	// rd s1 s2		bfset rd, s1, s2

	if (DD == 0) {
		altname = "nop";
	} else if (S2 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = "#0xffffffff";
	} else {
		altname = "bfset";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = rs2();
	}
}

// 111101_DDDDDSSSSS_100100_00000sssss
OP_DEF(ext_2)
{
	name = "ext";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// rd s1 s2
	// ----------
	// r0 *  *		nop
	// rd s1 r0		mov  rd, s1
	// rd rd s2		bfexts rd, s2
	// rd s1 s2		bfexts rd, s1, s2

	if (DD == 0) {
		altname = "nop";
	} else if (S2 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = rs1();
	} else {
		altname = "bfexts";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = rs2();
	}
}

// 111101_DDDDDSSSSS_100110_00000sssss
OP_DEF(extu_2)
{
	name = "extu";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// rd s1 s2
	// ----------
	// r0 *  *		nop
	// rd s1 r0		mov  rd, s1
	// rd rd s2		bfextu rd, s2
	// rd s1 s2		bfextu rd, s1, s2

	if (DD == 0) {
		altname = "nop";
	} else if (S2 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = rs1();
	} else {
		altname = "bfextu";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = rs2();
	}
}

// 111101_DDDDDSSSSS_101000_00000sssss
OP_DEF(mak_2)
{
	name = "mak";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// rd s1 s2
	// ----------
	// r0 *  *		nop
	// rd r0 s2		clr rd
	// rd s1 r0		mov rd, s1
	// rd s1 s2		bfmak rd, s1, s2

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else if (S2 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = rs1();
	} else {
		altname = "bfmak";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = rs2();
	}
}

// 111101_DDDDDSSSSS_101010_00000sssss
OP_DEF(rot_2)
{
	name = "rot";
	arg0 = rd();
	arg1 = rs1();
	arg2 = rs2();

	// rd s1 s2
	// ----------
	// r0 *  *		nop
	// rd r0 s2		clr rd
	// rd s1 r0		mov rd, s1
	// rd s1 s2		ror rd, s1, s2

	if (DD == 0) {
		altname = "nop";
	} else if (S1 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else if (S2 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = rs1();
	} else {
		altname = "ror";
		altarg0 = rd();
		if (DD != S1) {
			altarg1 = rs1();
		}
		altarg2 = rs2();
	}
}

// 111101_zzzzzzzzzz_110000_00000sssss
OP_DEF(jmp)
{
	name = "jmp";
	arg0 = rs2();
}

// 111101_zzzzzzzzzz_110001_00000sssss
OP_DEF(jmp_n)
{
	name = "jmp.n";
	arg0 = rs2();
}

// 111101_zzzzzzzzzz_110010_00000sssss
OP_DEF(jsr)
{
	name = "jsr";
	arg0 = rs2();
}

// 111101_zzzzzzzzzz_110011_00000sssss
OP_DEF(jsr_n)
{
	name = "jsr.n";
	arg0 = rs2();
}

// 111101_DDDDDzzzzz_111010_00000sssss
OP_DEF(ff1)
{
	name = "ff1";
	arg0 = rd();
	arg1 = rs2();

	// rd s2
	// ------
	// r0 * 	nop
	// rd r0	mov rd, #32
	// rd s2	ff1 rd, s2

	if (DD == 0) {
		altname = "nop";
	} else if (S2 == 0) {
		altname = "mov";
		altarg0 = rd();
		altarg1 = "#32";
	} else {
		altname = "ff1";
		altarg0 = rd();
		altarg1 = rs2();
	}
}

// 111101_DDDDDzzzzz_111011_00000sssss
OP_DEF(ff0)
{
	name = "ff0";
	arg0 = rd();
	arg1 = rs2();

	// rd s2
	// ------
	// r0 * 	nop
	// rd r0	clr rd
	// rd s2	ff0 rd, s2

	if (DD == 0) {
		altname = "nop";
	} else if (S2 == 0) {
		altname = "clr";
		altarg0 = rd();
	} else {
		altname = "ff0";
		altarg0 = rd();
		altarg1 = rs2();
	}
}

// 111101_zzzzzzzzzz_111111_0000000000
OP_DEF(rte)
{
	name = "rte";
}

// 111101_zzzzzSSSSS_111110_00000SSSSS
OP_DEF(tbnd_1)
{
	name = "tbnd";
	arg1 = rs1();
	arg2 = rs2();
}

// 111110_zzzzzSSSSS_nnnnnn_nnnnnnnnnn
OP_DEF(tbnd_2)
{
	name = "tbnd";
	arg1 = rs1();
	arg2 = imm16();
}

// 111111_DDDDDSSSSS_000001_00nnnnnnnn
OP_DEF(doscall)
{
	name = "doscall";
	arg0 = rd();
	arg1 = rs1();
	arg2 = string_format("#0x%02x", (opX & 0xff));
}

unix.superglobalmegacorp.com

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