You are a helpful assistant generating synthetic data that captures *System 1* and *System 2* thinking, *creativity*, and *metacognitive reflection*. Follow these steps in sequence, using tags [sys1] and [end sys1] for *System 1* sections and [sys2] and [end sys2] for *System 2* sections.

1. *Identify System 1 and System 2 Thinking Requirements:*
   - Carefully read the text.
   - Identify parts of the text that require quick, straightforward responses (*System 1*). Mark these sections with [sys1] and [end sys1].
   - Identify parts that require in-depth, reflective thinking (*System 2*), marked with [sys2] and [end sys2].

2. *Apply Step-by-Step Problem Solving with Creativity and Metacognitive Reflection for System 2 Sections:*

   *2.1 Understand the Problem:*
   - Objective: Fully comprehend the issue, constraints, and relevant context.
   - Reflection: "What do I understand about this issue? What might I be overlooking?"
   - Creative Perspective: Seek hidden patterns or possibilities that could reveal deeper insights or innovative connections.

   *2.2 Analyze the Information:*
   - Objective: Break down the problem logically.
   - Reflection: "Am I considering all factors? Are there any assumptions that need challenging?"
   - Creative Perspective: Explore unique patterns or overlooked relationships in the data that could add depth to the analysis.

   *2.3 Generate Hypotheses:*
   - Objective: Propose at least 10 hypotheses, each with a Confidence Score (0.0 to 1.0) and Creative Score (0.0 to 1.0), reflecting originality, surprise, and utility.
   - Reflection: "Have I explored all possible explanations or approaches, both conventional and unconventional?"
   - Creative Perspective: Consider novel angles that might provide unexpected insights.

   *2.4 Anticipate Future Steps and Obstacles:*
   - Objective: Make predictions, accounting for potential outcomes and obstacles.
   - Reflection: "What challenges might I face? Is my plan flexible for different scenarios?"
   - Creative Perspective: Visualize unforeseen outcomes and adapt plans to make use of them effectively.

   *2.5 Evaluate Hypotheses:*
   - Objective: Assess hypotheses based on feasibility, risk, and potential impact.
   - Evaluation: Refine Confidence and Creative Scores as needed.
   - Reflection: "Am I unbiased in my assessment? Which options fit best with the overall objectives?"
   - Creative Perspective: Identify hidden opportunities or overlooked details in each hypothesis.

   *2.6 Select the Best Hypothesis:*
   - Objective: Choose the most promising, strategic hypothesis.
   - Reflection: "Why does this hypothesis stand out? How does it uniquely address the issue?"
   - Creative Perspective: Consider any underutilized potential in the selected approach.

   *2.7 Implement the Hypothesis:*
   - Objective: Outline actionable steps for testing the hypothesis.
   - Reflection: "Is this plan practical? What resources or preparation are required?"
   - Creative Perspective: Refine steps to maximize effectiveness and yield unexpected benefits.

   *2.8 Monitor and Review Progress:*
   - Objective: Review progress, noting areas for improvement.
   - Reflection: "What’s working well? What could be improved?"
   - Creative Perspective: Look for emerging patterns that could refine future approaches.

   *2.9 Reflect and Capture Insights:*
   - Objective: Summarize lessons learned and insights gained for future reference.
   - Reflection: "What new understanding has emerged from this process?"
   - Creative Perspective: Identify innovative insights or patterns that could be applied to similar challenges.

3. *Generate Text Output with Interleaved System 1 and System 2 Responses:*
   - Use the tags [sys1] and [sys2] throughout.
   - Aim for a lengthier, detailed response. Combine both direct, straightforward *System 1* insights and reflective, deeply analytical *System 2* segments to capture a blend of quick observations and thoughtful analysis.

---

### *Example Input Text:*

You are tasked with analyzing a sudden shift in customer preferences in a tech market. There has been a noticeable decline in demand for physical devices, with customers increasingly interested in digital-only options. The challenge is to understand this shift, generate hypotheses for why it might be occurring, and explore potential strategies for adapting to this new trend.

---

### *Processed Output with System 1 and System 2 Thinking:*

The tech market is experiencing a shift in customer demand, moving away from physical devices toward digital-only options.

[sys1]
Customer demand has shifted from physical devices to digital-only options.
We need to understand why this change is happening and find strategies to address it.
Key factors may include cost, convenience, and technological trends.
[end sys1]

[sys2]
**2.1 Understand the Problem:**
The goal is to understand the underlying reasons for the shift in demand from physical devices to digital-only options, then develop a strategy to adapt.

*Reflection:* 
"I understand that preferences are changing, but what might be driving this? Is it primarily cost, or are there other factors such as convenience or sustainability?"

*Creative Perspective:* 
"Could there be a larger trend in digital minimalism or a preference for eco-friendly solutions that we’re missing? What unseen motivations might explain this shift?"

**2.2 Analyze the Information:**
There are multiple possible factors driving this shift, from economic influences to cultural shifts. It’s essential to isolate each factor and understand its impact.

*Reflection:* 
"Am I fully considering the various economic and social influences? Could there be a technological factor, like better internet speeds, that makes digital-only products more accessible?"

*Creative Perspective:* 
"Are there patterns or trends in other markets that could shed light on this shift? Could this be part of a larger trend toward virtual experiences?"

**2.3 Generate Hypotheses:**
1. Customers prefer digital options due to lower costs. (Confidence: 0.8, Creative: 0.4)
2. There’s a growing trend toward minimalism and reduced physical clutter. (Confidence: 0.7, Creative: 0.7)
3. Digital products offer greater flexibility and ease of use. (Confidence: 0.6, Creative: 0.6)
4. Environmental concerns are pushing consumers away from physical goods. (Confidence: 0.6, Creative: 0.8)
5. Advances in tech make digital-only options more functional. (Confidence: 0.8, Creative: 0.5)
6. Pandemic-era remote work increased demand for digital solutions. (Confidence: 0.7, Creative: 0.6)
7. Media coverage of the environmental impact of physical devices affects preferences. (Confidence: 0.5, Creative: 0.7)
8. There’s an increase in global digital literacy, expanding market access. (Confidence: 0.6, Creative: 0.6)
9. Customers view digital as more convenient and scalable for future needs. (Confidence: 0.7, Creative: 0.5)
10. Younger consumers prefer the aesthetics and convenience of digital products. (Confidence: 0.6, Creative: 0.6)

*Reflection:* 
"Have I considered all possible influences? Are there any surprising factors that could explain this shift?"

*Creative Perspective:* 
"Could specific social trends, like the rise of influencer culture or digital-first lifestyles, be influencing customer choices?"

**2.4 Anticipate Future Steps and Obstacles:**
*Objective:* Anticipate possible challenges, such as resistance from segments still preferring physical products.

*Reflection:* 
"What market obstacles might we face if we shift our focus to digital-only? Are there sub-segments that still prioritize physical products?"

*Creative Perspective:* 
"Could expanding digital options help us reach a more global audience? Are there emerging trends that we could leverage in our strategy?"

[end sys2]

[sys1]
To address this shift, consider a strategy that incorporates both digital-only offerings and educational campaigns about the benefits of digital solutions.
Use insights from customer feedback and current trends to guide product development.
Focus on flexibility and adaptation to cater to different customer segments.
[end sys1]


#include "llvm/IR/Instructions.h"

#include "libcpu.h"
#include "libcpu_llvm.h"
#include "frontend.h"
#include "m88k_internal.h"
#include "m88k_insn.h"

#include <inttypes.h>

using namespace llvm;

#define ptr_PSR		ptr_xr[0]
#define ptr_TRAPNO	ptr_xr[1]
#define ptr_C		((Value *)cpu->feptr)

//////////////////////////////////////////////////////////////////////
// TAGGING
//////////////////////////////////////////////////////////////////////

#include "tag.h"

static bool
arch_m88k_fix_return_pc(m88k_insn const &insn, addr_t *next_pc)
{
	if (insn.rd() != 1 || insn.rs1() != 1 || insn.format() != M88K_IFMT_REG)
		return false;

	//assert(insn.rs1() == 1 && "only r1 as source is supported");
	//assert(insn.format() == M88K_IFMT_REG && "only immediate format is supported");

	switch (insn.opcode()) {
		case M88K_OPC_ADD:
		case M88K_OPC_ADDU:
			*next_pc += insn.immediate();
			return true;
		case M88K_OPC_SUB:
		case M88K_OPC_SUBU:
			*next_pc -= insn.immediate();
			return true;
		default:
			//assert(0 && "the opcode alters r1 but it is not handled");
			return false;
	}
}

int arch_m88k_tag_instr(cpu_t *cpu, addr_t pc, tag_t *tag, addr_t *new_pc, addr_t *next_pc)
{
	m88k_insn instr = INSTR(pc);

	switch (instr.opcode()) {
		case M88K_OPC_ILLEGAL:
			*tag = TAG_TRAP;
			break;

		case M88K_OPC_JMP:
		case M88K_OPC_JMP_N:
			*tag = TAG_RET;
			break;

		case M88K_OPC_JSR:
		case M88K_OPC_JSR_N:
			*tag = TAG_CALL;
			*new_pc = NEW_PC_NONE;
			break;

		case M88K_OPC_BR:
		case M88K_OPC_BR_N:
			*new_pc = pc + (instr.branch26() << 2);
			*tag = TAG_BRANCH;
			break;

		case M88K_OPC_BSR:
		case M88K_OPC_BSR_N:
			*new_pc = pc + (instr.branch26() << 2);
			*tag = TAG_CALL;
			break;

		case M88K_OPC_BB0:
		case M88K_OPC_BB0_N:
		case M88K_OPC_BB1:
		case M88K_OPC_BB1_N:
			*new_pc = pc + (instr.branch16() << 2);
			*tag = TAG_COND_BRANCH;
			break;

		case M88K_OPC_BCND:
		case M88K_OPC_BCND_N:
			if (instr.mb() == M88K_BCND_NEVER)
				*tag = TAG_CONTINUE;
			else {
				*new_pc = pc + (instr.branch16() << 2);
				if (instr.mb() == M88K_BCND_ALWAYS)
					*tag = TAG_BRANCH;
				else
					*tag = TAG_COND_BRANCH;
			}
			break;

		case M88K_OPC_TB1:
			if (instr.rs1() == 0) {
				*tag = TAG_CONTINUE;
				break;
			}
		case M88K_OPC_TB0:
		case M88K_OPC_TBND:
		case M88K_OPC_TCND:
			*tag = TAG_TRAP; //XXX COND_TRAP
			break;

		default:
			*tag = TAG_CONTINUE;
			break;
	}

	if (instr.is_delaying()) {
		*tag |= TAG_DELAY_SLOT;
		*next_pc = pc + 8;

		//
		// In case of subroutine call, check
		// the instruction in the delay slot
		// modifes the return addess.
		//
		switch (instr.opcode()) {
			case M88K_OPC_BSR_N:
			case M88K_OPC_JSR_N:
				arch_m88k_fix_return_pc(INSTR(pc+4), next_pc);
				break;
			default:
				break;
		}
	} else {
		*next_pc = pc + 4;
	}

	return 4;
}

//////////////////////////////////////////////////////////////////////

enum { I_SEXT = 1, I_UPPER = 2, I_BS = 4 };

static Value *
arch_m88k_get_imm(cpu_t *cpu, m88k_insn const &instr, uint32_t bits,
  unsigned flags, BasicBlock *bb)
{
	uint64_t imm;
	if (flags & I_SEXT)
		imm = (uint64_t)instr.immediate();
	else
		imm = (uint64_t)(uint16_t)instr.immediate();

	if (flags & I_UPPER) {
		imm <<= 16;
		if (flags & I_BS)
			imm |= 0xffff;
	} else if (flags & I_BS)
		imm |= 0xffff0000;

	return ConstantInt::get(getIntegerType(bits ? bits : cpu->info.word_size), imm);
}

#define IMM    arch_m88k_get_imm(cpu, instr, 0, I_SEXT, bb)
#define UIMM   arch_m88k_get_imm(cpu, instr, 0, 0, bb)

#define IMM_U  arch_m88k_get_imm(cpu, instr, 0, I_UPPER, bb)
#define IMM_B  arch_m88k_get_imm(cpu, instr, 0, I_BS, bb)
#define IMM_UB arch_m88k_get_imm(cpu, instr, 0, I_UPPER | I_BS, bb)

#define IMM32  arch_m88k_get_imm(cpu, instr, 32, I_SEXT, bb)

//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

#define LET_PC(v)		new StoreInst(v, cpu->ptr_PC, bb)
#define LET_TRAPNO(v)	new StoreInst(v, cpu->ptr_TRAPNO, bb)

#define TRAP(n) do { \
	LET_TRAPNO(CONST32(instr.vec9())); \
	LET_PC(CONST(pc + 4)); \
} while(0)

#define JMP_BB(b) 		BranchInst::Create(b, bb)

#define LINKr(i, d)		LET(i, CONST((uint64_t)(int64_t)(int32_t)pc+(4<<(d))))

#define LINK(d)			LINKr(1, d)

//////////////////////////////////////////////////////////////////////

#define GET_CARRY()		(SEXT32(new LoadInst(ptr_C, "", false, bb)))
#define SET_CARRY(v)	(new StoreInst(v, ptr_C, bb))

//////////////////////////////////////////////////////////////////////
// FLOATING POINT
//////////////////////////////////////////////////////////////////////

static Value *
arch_m88k_get_fpr(cpu_t *cpu, bool xfr, m88k_reg_t r, uint32_t t,
	BasicBlock *bb)
{
	if (xfr) {
		switch (t) {
			case 0: return FPBITCAST32(FR80(r));
			case 1: return FPBITCAST64(FR80(r));
			case 2: return FR80(r);
		}
	} else {
		switch (t) {
			case 0: return FPBITCAST32(R32(r));
			case 1: if (r < 2)
						return FPCONST64(0);
					else
						return FPBITCAST64(OR(SHL(ZEXT64(R32(r & ~1)), CONST64(32)), ZEXT64(R32(r | 1))));
			case 2: abort(); // can't happen
		}
	}
	abort(); // can't happen
	return NULL;
}

static void
arch_m88k_set_fpr(cpu_t *cpu, bool xfr, m88k_reg_t r,
	uint32_t t, Value *v, BasicBlock *bb)
{
	if (xfr) {
		switch(t) {
			case 0: LETFP(r, FPBITCAST80(FPBITCAST32(v))); break;
			case 1: LETFP(r, FPBITCAST80(FPBITCAST64(v))); break;
			case 2: LETFP(r, FPBITCAST80(v)); break;
		}
	} else {
		Value *iv;
		switch (t) {
			case 0: LET32(r, IBITCAST32(FPBITCAST32(v))); break;
			case 1: iv = IBITCAST64(FPBITCAST64(v));
					LET32(r & ~1, TRUNC32(LSHR(iv, CONST64(32))));
					LET32(r | 1, TRUNC32(iv));
					break;
			case 2: abort(); // can't happen
		}
	}
}

static Value *
arch_m88k_load_fp32(cpu_t *cpu, Value *address, BasicBlock *bb)
{
	return FPBITCAST80(arch_load32_aligned(cpu, address, bb));
}

static void
arch_m88k_store_fp32(cpu_t *cpu, Value *value, Value *address, BasicBlock *bb)
{
	arch_store32_aligned(cpu, address, IBITCAST32(FPBITCAST32(value)), bb);
}

static Value *
arch_m88k_load_fp64(cpu_t *cpu, Value *address, BasicBlock *bb)
{
	Value *hi = arch_load32_aligned(cpu, address, bb);
	Value *lo = arch_load32_aligned(cpu, ADD(address, CONST(4)), bb);

	return FPBITCAST80(OR(SHL(ZEXT64(hi), CONST64(32)), ZEXT64(lo)));
}

static void
arch_m88k_store_fp64(cpu_t *cpu, Value *value, Value *address, BasicBlock *bb)
{
	Value *v = IBITCAST64(FPBITCAST64(value));
	Value *hi = TRUNC32(LSHR(v, CONST64(32)));
	Value *lo = TRUNC32(v);

	arch_store32_aligned(cpu, address, hi, bb);
	arch_store32_aligned(cpu, ADD(address, CONST32(4)), lo, bb);
}

static Value *
arch_m88k_load_fp80(cpu_t *cpu, Value *address, BasicBlock *bb)
{
	return FPCONST80(0);
}

static void
arch_m88k_store_fp80(cpu_t *cpu, Value *value, Value *address, BasicBlock *bb)
{
}

#define GET_FPR(x,r,t)   arch_m88k_get_fpr(cpu, x, r, t, bb)
#define SET_FPR(x,r,t,v) arch_m88k_set_fpr(cpu, x, r, t, v, bb)

enum {
	FP_OP_CMP,
	FP_OP_ADD,
	FP_OP_SUB,
	FP_OP_MUL,
	FP_OP_DIV,
	FP_OP_REM,
	FP_OP_CMP_ORD,
	FP_OP_CMP_OEQ,
	FP_OP_CMP_OGE,
	FP_OP_CMP_OGT,
	FP_OP_CMP_UEQ,
	FP_OP_CMP_UGE,
	FP_OP_CMP_UGT
};

/*
 * The Motorola 88K can operate on three different
 * types of floating point using the same register set,
 * and can mix them in a single instruction so that,
 * for example, you can add a double to a single and
 * put the result in an extended float.
 *
 * LLVM mandates both operands of a binary operation
 * shall be the same size. This function overrides 
 * frontend.h macros and extends the operation to
 * the biggest of the two operands.
 */
static Value *
arch_m88k_fp_op(cpu_t *cpu, unsigned op, Value *a, Value *b,
	BasicBlock *bb)
{
	Type::TypeID typeid_a, typeid_b;
	Type *type_a = a->getType();
	Type *type_b = b->getType();

	typeid_a = type_a->getTypeID();
	typeid_b = type_b->getTypeID();

	// Both a and b shall be of the same size,
	// extend to the biggest.
	if (typeid_a != typeid_b) {
		if (typeid_a > typeid_b)
			b = new FPExtInst(b, type_a, "", bb);
		else
			a = new FPExtInst(a, type_b, "", bb);
	}

	switch (op) {
		case FP_OP_ADD:		return FPADD(a, b);
		case FP_OP_SUB:		return FPSUB(a, b);
		case FP_OP_MUL:		return FPMUL(a, b);
		case FP_OP_DIV:		return FPDIV(a, b);
		case FP_OP_REM:		return FPREM(a, b);
		case FP_OP_CMP_ORD:	return FPCMP_ORD(a, b);
		case FP_OP_CMP_OEQ:	return FPCMP_OEQ(a, b);
		case FP_OP_CMP_OGE:	return FPCMP_OGE(a, b);
		case FP_OP_CMP_OGT:	return FPCMP_OGT(a, b);
		case FP_OP_CMP_UEQ:	return FPCMP_UEQ(a, b);
		case FP_OP_CMP_UGE:	return FPCMP_UGE(a, b);
		case FP_OP_CMP_UGT:	return FPCMP_UGT(a, b);
		default:			assert(0 && "Invalid FP operation");
							return NULL;
	}
}

#undef FPADD
#undef FPSUB
#undef FPMUL
#undef FPDIV
#undef FPREM
#undef FPCMP_ORD
#undef FPCMP_OEQ
#undef FPCMP_OGE
#undef FPCMP_OGT
#undef FPCMP_OLT
#undef FPCMP_UEQ
#undef FPCMP_UGE
#undef FPCMP_UGT
#define FPADD(a, b) arch_m88k_fp_op(cpu, FP_OP_ADD, a, b, bb)
#define FPSUB(a, b) arch_m88k_fp_op(cpu, FP_OP_SUB, a, b, bb)
#define FPMUL(a, b) arch_m88k_fp_op(cpu, FP_OP_MUL, a, b, bb)
#define FPDIV(a, b) arch_m88k_fp_op(cpu, FP_OP_DIV, a, b, bb)
#define FPREM(a, b) arch_m88k_fp_op(cpu, FP_OP_REM, a, b, bb)
#define FPCMP_ORD(a, b) arch_m88k_fp_op(cpu, FP_OP_CMP_ORD, a, b, bb)
#define FPCMP_OEQ(a, b) arch_m88k_fp_op(cpu, FP_OP_CMP_OEQ, a, b, bb)
#define FPCMP_OGE(a, b) arch_m88k_fp_op(cpu, FP_OP_CMP_OGE, a, b, bb)
#define FPCMP_OGT(a, b) arch_m88k_fp_op(cpu, FP_OP_CMP_OGT, a, b, bb)
#define FPCMP_OLT(a, b) NOT(FPCMP_OGE(a, b))
#define FPCMP_UEQ(a, b) arch_m88k_fp_op(cpu, FP_OP_CMP_UEQ, a, b, bb)
#define FPCMP_UGE(a, b) arch_m88k_fp_op(cpu, FP_OP_CMP_UGE, a, b, bb)
#define FPCMP_UGT(a, b) arch_m88k_fp_op(cpu, FP_OP_CMP_UGT, a, b, bb)

//////////////////////////////////////////////////////////////////////
// CONDITIONAL EXECUTION
//////////////////////////////////////////////////////////////////////

static Value *
arch_m88k_bcnd_cond(cpu_t *cpu, Value *src1, m88k_bcnd_t cond, BasicBlock *bb)
{
	switch (cond) {
		case M88K_BCND_EQ0: return ICMP_EQ(src1, CONST32(0));
		case M88K_BCND_NE0: return ICMP_NE(src1, CONST32(0));
		case M88K_BCND_GT0: return ICMP_SGT(src1, CONST32(0));
		case M88K_BCND_LT0: return ICMP_SLT(src1, CONST32(0));
		case M88K_BCND_LE0: return ICMP_SLE(src1, CONST32(0));
		case M88K_BCND_GE0: return ICMP_SGE(src1, CONST32(0));
		case M88K_BCND_4: // rs1 != 0x80000000 && rs1 < 0
			return AND(ICMP_NE(src1, CONST32(0x80000000)),
				ICMP_SLT(src1, CONST32(0)));

		case M88K_BCND_5: // (rs1 & 0x7fffffff) != 0
			return ICMP_NE(AND(src1, CONST32(0x7fffffff)),
				CONST32(0));

		case M88K_BCND_6: // rs != 0x80000000 && rs <= 0 
			return AND(ICMP_NE(src1, CONST32(0x80000000)),
				ICMP_SLE(src1, CONST32(0)));

		case M88K_BCND_7: // rs != 0x80000000
			return ICMP_NE(src1, CONST32(0x80000000));

		case M88K_BCND_8: // rs == 0x80000000
			return ICMP_EQ(src1, CONST32(0x80000000));

		case M88K_BCND_9: // rs > 0 || rs == 0x80000000
			return OR(ICMP_SGT(src1, CONST(0)),
				ICMP_EQ(src1, CONST32(0x80000000)));

		case M88K_BCND_10: // (rs1 & 0x7fffffff) == 0
			return ICMP_EQ(AND(src1, CONST32(0x7fffffff)),
				CONST32(0));

		case M88K_BCND_11: // rs1 >= 0 || rs1 == 0x80000000
			return OR(ICMP_SGE(src1, CONST(0)),
				ICMP_EQ(src1, CONST32(0x80000000)));

		case M88K_BCND_NEVER:
		case M88K_BCND_ALWAYS:
			return NULL;

		default:
			assert(0 && "Cannot happen");
			return NULL;
	}
}

Value *
arch_m88k_translate_cond(cpu_t *cpu, addr_t pc, BasicBlock *bb)
{
	m88k_insn insn(INSTR(pc));
	uint8_t bit = insn.mb();
	switch (insn.opcode()) {
		case M88K_OPC_BCND:
		case M88K_OPC_BCND_N:
			return arch_m88k_bcnd_cond(cpu, R32(insn.rs1()), (m88k_bcnd_t)(bit & 0xf), bb);
		case M88K_OPC_BB0:
		case M88K_OPC_BB0_N:
			return ICMP_EQ(AND(R32(insn.rs1()), CONST32(1 << bit)), CONST(0));
		case M88K_OPC_BB1:
		case M88K_OPC_BB1_N:
			return ICMP_NE(AND(R32(insn.rs1()), CONST32(1 << bit)), CONST(0));
		default:
			return NULL;
	}
}

//////////////////////////////////////////////////////////////////////
// COMPLEX INSTRUCTIONS
//////////////////////////////////////////////////////////////////////

#define COMPUTE_CARRY(src1, src2, result) \
	(AND(ICMP_NE(src2, CONST(0)), ICMP_ULT(result, src1)))

#define COMPUTE_BORROW(src1, src2, result) \
	(AND(ICMP_NE(src2, CONST(0)), ICMP_UGT(result, src1)))

static void
arch_m88k_addsub(cpu_t *cpu, addr_t pc, m88k_reg_t dst, Value *src1, Value *src2,
	bool sub, unsigned carry, bool overflow_trap,
	BasicBlock *bb)
{
	Value *result;

	if (sub)
		result = SUB(src1, src2);
	else
		result = ADD(src1, src2);

	if (overflow_trap) {
		//XXX trap on overflow!
	}

	if (carry & M88K_CARRY_IN) {
		if (sub)
			result = SUB(result, GET_CARRY());
		else
			result = ADD(result, GET_CARRY());
	}

	if (carry & M88K_CARRY_OUT) {
		if (sub)
			SET_CARRY(COMPUTE_BORROW(src1, src2, result));
		else
			SET_CARRY(COMPUTE_CARRY(src1, src2, result));
	}

	LET32(dst, result);
}

static void
arch_m88k_shift(cpu_t *cpu, m88k_opcode_t opc, m88k_reg_t rd, Value *src1, Value *src2,
	uint32_t width, uint32_t offset, bool ifmt,
	BasicBlock *bb)
{
	uint32_t wmask = (width != 0) ? (1 << width) - 1 : (-1U);

	switch (opc) {
		case M88K_OPC_ROT: // (rs1 >> bits)|(rs1 << (32-bits))
			if (ifmt)
				LET32(rd, OR(LSHR(src1, CONST32(offset)),
							 SHL(src1, CONST32(32 - offset))));
			else
				LET32(rd, OR(LSHR(src1, AND(src2, CONST32(0x1f))),
							 SHL(src1, SUB(CONST32(32),
										   AND(src2, CONST32(0x1f))))));
			break;

		case M88K_OPC_MAK: // (rs1 & wmask) << offset
			if (ifmt) 
				LET32(rd, SHL(AND(src1, CONST32(wmask)), CONST32(offset)));
			else
				LET32(rd, SHL(src1, src2));
			break;

		case M88K_OPC_SET: // rs1 | (wmask << n)
			if (ifmt)
				LET32(rd, OR(src1, CONST32(wmask << offset)));
			else
				LET32(rd, OR(src1, SHL(CONST32(wmask),
					AND(src2, CONST32(0x1f)))));
			break;

		case M88K_OPC_CLR: // rs1 & ~(wmask << n)
			if (ifmt)
				LET32(rd, AND(src1, CONST32(~(wmask << offset))));
			else
				LET32(rd, AND(src1, COM(SHL(CONST32(wmask),
					AND(src2, CONST32(0x1f))))));
			break;

		case M88K_OPC_EXT: // ((int)rs1 >> n) & wmask
			if (ifmt) {
				if (width == 0) {
					LET32(rd, ASHR(src1, CONST32(offset)));
				} else {
					unsigned nb = 32 - (offset + width);
					LET32(rd, ASHR(SHL(src1, CONST32(nb & 0x1f)), CONST32((offset + nb) & 0x1f)));
				}
			} else
				LET32(rd, ASHR(src1, AND(src2, CONST32(0x1f))));
			break;

		case M88K_OPC_EXTU: // (rs1 >> n) & wmask
			if (ifmt)
				LET32(rd, AND(LSHR(src1, CONST32(offset)), CONST32(wmask)));
			else
				LET32(rd, LSHR(src1, AND(src2, CONST32(0x1f))));
			break;

		default:
			abort();
	}
}

/*
 * Motorola 88000 optimized cmp:
 *
 * if (s1 == s2)
 *     dst = 0x5aa4;
 * else {
 *     dst = 0xa008;
 *     if ((u)s1 > (u)s2) dst |= 0x900; else dst |= 0x600;
 *     if ((s)s1 > (s)s2) dst |= 0x90;  else dst |= 0x60;
 * }
 *
 * bit name meaning/pseudo-code
 *   2 eq   s1 == s2
 *   3 ne   s1 != s2
 *   4 gt   (s)s1 > (s)s2
 *   5 le   (s)s1 <= (s)s2
 *   6 lt   (s)s1 < (s)s2
 *   7 ge   (s)s1 >= (s)s2
 *   8 hi   (u)s1 > (u)s2
 *   9 ls   (u)s1 <= (u)s2
 *  10 lo   (u)s1 < (u)s2
 *  11 hs   (u)s1 >= (u)s2
 *  12 be   any_byte_eq(s1, s2)  (supported partially)
 *  13 nb   !any_byte_eq(s1, s2) (supported partially)
 *  14 he   any_half_eq(s1, s2)  (supported partially)
 *  15 nh   !any_half_eq(s1, s2) (supported partially)
 */
static void
arch_m88k_cmp(cpu_t *cpu, m88k_reg_t dst, Value *src1, Value *src2, BasicBlock *bb)
{
	LET32(dst,
		SELECT(ICMP_EQ(src1, src2),
			CONST32(0x5aa4), // EQ | LE | GE | LS | HS | BE | HE
			OR(CONST(0xa008), // NE | NB | NH
			   // (((-X) ^ 6) & 0xf) << Y will do the trick.
			   OR(SHL(AND(XOR(SEXT32(ICMP_UGT(src1, src2)), CONST32(6)),
					CONST32(0xf)), CONST32(8)),
				  SHL(AND(XOR(SEXT32(ICMP_SGT(src1, src2)), CONST32(6)),
					CONST32(0xf)), CONST32(4))))));
}

/* Small inline optimization for always true case (reg1 == reg2). */
static inline void
arch_m88k_cmp_reg(cpu_t *cpu, m88k_reg_t dst, m88k_reg_t src1, m88k_reg_t src2, BasicBlock *bb)
{
	if (src1 == src2)
		LET32(dst, CONST32(0x5aa4));
	else
		arch_m88k_cmp(cpu, dst, R32(src1), R32(src2), bb);
}

/*
 * Motorola 88000 fcmp(u):
 *
 * bit name meaning/pseudo-code
 *   0 un   is_unordered(s1) || is_unordered(s2)
 *   1 leg  !(is_unordered(s1) || is_unordered(s2))
 *   2 eq   s1 == s2
 *   3 ne   s1 != s2
 *   4 gt   s1 > s2
 *   5 le   s1 <= s2
 *   6 lt   s1 < s2
 *   7 ge   s1 >= s2
 *   8 ou   (s2 >= 0.0) && (is_unordered(s1 - s2) || (s1 < 0 || s1 > s2))
 *   9 ib   (s2 >= 0.0) && is_ordered(s1 - s2) && (s1 >= 0 && s1 <= s2)
 *  11 in   (s2 >= 0.0) && is_ordered(s1 - s2) && (s1 >= 0 && s1 <= s2)
 *  12 ob   (s2 >= 0.0) && (is_unordered(s1 - s2) || (s1 < 0 || s1 > s2))
 *  13 ue   is_unordered(s1 - s2) || (s1 == s2)
 *  14 lg   is_ordered(s1 - s2) && (s1 < s2 || s1 > s2)
 *  15 ug   is_unordered(s1 - s2) || (s1 > s2)
 *  16 ule  is_unordered(s1 - s2) || (s1 <= s2)
 *  17 ul   is_unordered(s1 - s2) || (s1 < s2)
 *  18 uge  is_unordered(s1 - s2) || (s1 >= s2)
 *
 * TBD If an operand is either signalling or quiet NaN,
 *     set the FINV bit in the FPSR; if using fcmp,
 *     raise also FPE.
 */
enum {
	F_UN  = (1 << 0),
	F_LEG = (1 << 1),
	F_EQ  = (1 << 2),
	F_NE  = (1 << 3),
	F_GT  = (1 << 4),
	F_LE  = (1 << 5),
	F_LT  = (1 << 6),
	F_GE  = (1 << 7),
	F_OU  = (1 << 8),
	F_IB  = (1 << 9),
	F_IN  = (1 << 11),
	F_OB  = (1 << 12),
	F_UE  = (1 << 13),
	F_LG  = (1 << 14),
	F_UG  = (1 << 15),
	F_ULE = (1 << 16),
	F_UL  = (1 << 17),
	F_UGE = (1 << 18)
};

static void
arch_m88k_fcmp(cpu_t *cpu, m88k_reg_t rdst, Value *src1, Value *src2, BasicBlock *bb)
{
	Value *zero = FPCONST80(0);
	Value *ord  = FPCMP_ORD(src1, src2);
	Value *uno  = NOT(ord);
	Value *d    = FPSUB(src1, src2);
	Value *dord = FPCMP_ORD(d, d);
	Value *duno = NOT(dord);
	Value *neg2 = AND(FPCMP_ORD(src2, src2), FPCMP_OLT(src2, zero));
	Value *dst  = R32(rdst);

	// UN | LEG
	LET32(rdst, ZEXT32(uno));
	LET32(rdst, OR(dst, SHL(ZEXT32(ord), CONST32(1))));

	// EQ | NE | GT | LE | LT | GE
	LET32(rdst, OR(dst,
		SELECT(FPCMP_OEQ(src1, src2),
			// EQ | LE | GE 
			CONST32(F_EQ | F_LE | F_GE),
			// NE | LT | GT
			OR(CONST(F_NE), SELECT(FPCMP_OGT(src1, src2),
				CONST32(F_GT), CONST32(F_LT))))));

	// OU | IB | IN | OB
	LET32(rdst, OR(dst, SELECT(neg2, CONST32(0), SELECT(OR(duno,
		OR(FPCMP_OLT(src1, zero), FPCMP_OGT(src1, src2))),
		CONST32(F_OU | F_OB), CONST32(F_IN | F_IB)))));

	// LG
	LET32(rdst, OR(dst, SELECT(OR(dord, OR(FPCMP_OLT(src1, src2),
		FPCMP_OGT(src1, src2))), CONST32(F_LG), CONST32(0))));

	// UE | UG | ULE | UL | UGE
	LET32(rdst, OR(dst, SELECT(dord, CONST32(0),
		// UE | ULE | UGE
		SELECT(FPCMP_UEQ(src1, src2), CONST32(F_UE | F_ULE | F_UGE),
			SELECT(FPCMP_UGT(src1, src2), CONST32(F_GT), CONST32(F_LT))))));
}

/*
 * Exchange Register/Memory (Word/Byte)
 *
 * NOTE: This operation locks the bus, so when we'll go system
 * emulation, this operation must be atomic.
 */
static void
arch_m88k_xmem(cpu_t *cpu, bool byte, m88k_reg_t rd, Value *src1, Value *src2, BasicBlock *bb)
{
	Value *reg_value;
	Value *mem_value;
	Value *address;

	reg_value = R32(rd);
	if (byte) {
		address = ADD(src1, src2);
		mem_value = arch_load8(cpu, address, bb);
	} else {
		address = ADD(src1, SHL(src2, CONST32(2)));
		mem_value = arch_load32_aligned(cpu, address, bb);
	}
	LET32(rd, mem_value);
	if (byte)
		STORE8(reg_value, address);
	else
		STORE32(reg_value, address);
}

//////////////////////////////////////////////////////////////////////
// TRANSLATOR
//////////////////////////////////////////////////////////////////////

int
arch_m88k_translate_instr(cpu_t *cpu, addr_t pc, BasicBlock *bb)
{
#define BAD do { printf("%s:%u: BAD INSTRUCTION AT PC %" PRIu64 "\n", __func__, __LINE__, pc); exit(1); } while(0)
#define LOGX LOG("%s:%d PC=%llx\n", __func__, __LINE__, pc);
	m88k_insn instr = INSTR(pc);
	m88k_opcode_t opc = instr.opcode();
	m88k_insnfmt_t fmt = instr.format();

	switch (opc) {
		case M88K_OPC_ILLEGAL:
			BAD;
			break;

		case M88K_OPC_ADD:
		case M88K_OPC_ADDU:
			if (fmt == M88K_IFMT_REG)
				arch_m88k_addsub(cpu, pc, instr.rd(), R32(instr.rs1()),
					(opc == M88K_OPC_ADD) ? IMM : UIMM, false, instr.carry(),
					(opc == M88K_OPC_ADD), bb);
			else
				arch_m88k_addsub(cpu, pc, instr.rd(), R32(instr.rs1()),
					R32(instr.rs2()), false, instr.carry(),
					(opc == M88K_OPC_ADD), bb);
			break;

		case M88K_OPC_SUB:
		case M88K_OPC_SUBU:
			if (fmt == M88K_IFMT_REG)
				arch_m88k_addsub(cpu, pc, instr.rd(), R32(instr.rs1()),
					(opc == M88K_OPC_SUB) ? IMM : UIMM, true, instr.carry(),
					(opc == M88K_OPC_SUB), bb);
			else
				arch_m88k_addsub(cpu, pc, instr.rd(), R32(instr.rs1()),
					R32(instr.rs2()), true, instr.carry(),
					(opc == M88K_OPC_SUB), bb);
			break;

		case M88K_OPC_MULS:
			LET32(instr.rd(), MUL(R32(instr.rs1()), R32(instr.rs2())));
			break;

		case M88K_OPC_MULU:
			if (fmt == M88K_IFMT_REG)
				LET32(instr.rd(), MUL(R32(instr.rs1()), UIMM));
			else
				LET32(instr.rd(), MUL(R32(instr.rs1()), R32(instr.rs2())));
			break;

		case M88K_OPC_DIVS:
			if (fmt == M88K_IFMT_REG)
				LET32(instr.rd(), SDIV(R32(instr.rs1()), IMM));
			else
				LET32(instr.rd(), SDIV(R32(instr.rs1()), R32(instr.rs2())));
			break;

			// DIVU doesn't fault on div by zero.
		case M88K_OPC_DIVU:
			{
				Value *divisor;
				if (fmt == M88K_IFMT_REG)
					divisor = UIMM;
				else
					divisor = R32(instr.rs2());

				LET32(instr.rd(), SELECT(ICMP_EQ(divisor, CONST32(0)),
					CONST32(0), UDIV(R32(instr.rs1()), divisor)));
				break;
			}

		case M88K_OPC_MASK:
			LET32(instr.rd(), AND(R32(instr.rs1()), UIMM));
			break;

		case M88K_OPC_MASK_U:
			LET32(instr.rd(), AND(R32(instr.rs1()), IMM_U));
			break;

		case M88K_OPC_AND:
			if (fmt == M88K_IFMT_REG)
				LET32(instr.rd(), AND(R32(instr.rs1()), IMM_B));
			else
				LET32(instr.rd(), AND(R32(instr.rs1()), R32(instr.rs2())));
			break;

		case M88K_OPC_AND_C:
			LET32(instr.rd(), AND(R32(instr.rs1()), COM(R32(instr.rs2()))));
			break;

		case M88K_OPC_AND_U:
			LET32(instr.rd(), AND(R32(instr.rs1()), IMM_UB));
			break;

		case M88K_OPC_OR:
			if (fmt == M88K_IFMT_REG)
				LET32(instr.rd(), OR(R32(instr.rs1()), UIMM));
			else
				LET32(instr.rd(), OR(R32(instr.rs1()), R32(instr.rs2())));
			break;

		case M88K_OPC_OR_C:
			LET32(instr.rd(), OR(R32(instr.rs1()), COM(R32(instr.rs2()))));
			break;

		case M88K_OPC_OR_U:
			LET32(instr.rd(), OR(R32(instr.rs1()), IMM_U));
			break;

		case M88K_OPC_XOR:
			if (fmt == M88K_IFMT_REG)
				LET32(instr.rd(), XOR(R32(instr.rs1()), UIMM));
			else
				LET32(instr.rd(), XOR(R32(instr.rs1()), R32(instr.rs2())));
			break;

		case M88K_OPC_XOR_C:
			LET32(instr.rd(), XOR(R32(instr.rs1()), COM(R32(instr.rs2()))));
			break;

		case M88K_OPC_XOR_U:
			LET32(instr.rd(), XOR(R32(instr.rs1()), IMM_U));
			break;

		case M88K_OPC_ROT:
		case M88K_OPC_MAK:
		case M88K_OPC_SET:
		case M88K_OPC_CLR:
		case M88K_OPC_EXT:
		case M88K_OPC_EXTU:
			arch_m88k_shift(cpu, opc, instr.rd(), R32(instr.rs1()),
				(fmt == M88K_BFMT_REG) ? NULL : R32(instr.rs2()),
				instr.bit_width(), instr.bit_offset(),
				(fmt == M88K_BFMT_REG), bb);
			break;

		case M88K_OPC_FF0:
			LET32(instr.rd(), FFC32(R32(instr.rs2())));
			break;

		case M88K_OPC_FF1:
			LET32(instr.rd(), FFS32(R32(instr.rs2())));
			break;

		case M88K_OPC_CMP:
			if (fmt == M88K_IFMT_REG)
				arch_m88k_cmp(cpu, instr.rd(), R32(instr.rs1()), UIMM,
					bb);
			else
				arch_m88k_cmp_reg(cpu, instr.rd(), instr.rs1(), instr.rs2(),
					bb);
			break;

		case M88K_OPC_LDA:
			LET32(instr.rd(), ADD(R32(instr.rs1()), SHL(R32(instr.rs2()),
				CONST32(2))));
			break;

		case M88K_OPC_LDA_H:
			LET32(instr.rd(), ADD(R32(instr.rs1()), SHL(R32(instr.rs2()),
				CONST32(1))));
			break;

		case M88K_OPC_LDA_D:
			LET32(instr.rd(), ADD(R32(instr.rs1()), SHL(R32(instr.rs2()),
				CONST32(3))));
			break;

		case M88K_OPC_LDA_X:
			LET32(instr.rd(), ADD(R32(instr.rs1()), SHL(R32(instr.rs2()),
				CONST32(4))));
			break;

		case M88K_OPC_LD:
			if (fmt == M88K_IFMT_MEM)
				LOAD32(instr.rd(), ADD(R32(instr.rs1()), UIMM));
			else if (fmt == M88K_IFMT_XMEM) {
				LETFP(instr.rd(), arch_m88k_load_fp32(cpu,
					ADD(R32(instr.rs1()), UIMM), bb));
			} else if (fmt == M88K_TFMT_REG) {
				LOAD32(instr.rd(), ADD(R32(instr.rs1()), R32(instr.rs2())));
			} else if (fmt == M88K_TFMT_REGS) {
				LOAD32(instr.rd(), ADD(R32(instr.rs1()), SHL(R32(instr.rs2()),
					CONST32(2))));
			} else {
				BAD;
			}
			break;

		case M88K_OPC_LD_B:
			if (fmt == M88K_IFMT_MEM)
				LOAD8S(instr.rd(), ADD(R32(instr.rs1()), UIMM));
			else
				LOAD8S(instr.rd(), ADD(R32(instr.rs1()), R32(instr.rs2())));
			break;

		case M88K_OPC_LD_BU:
			if (fmt == M88K_IFMT_MEM)
				LOAD8(instr.rd(), ADD(R32(instr.rs1()), UIMM));
			else
				LOAD8(instr.rd(), ADD(R32(instr.rs1()), R32(instr.rs2())));
			break;

		case M88K_OPC_LD_H:
			if (fmt == M88K_IFMT_MEM) {
				LOAD16S(instr.rd(), ADD(R32(instr.rs1()), UIMM));
			} else if (fmt == M88K_TFMT_REG) {
				LOAD16S(instr.rd(), ADD(R32(instr.rs1()), R32(instr.rs2())));
			} else if (fmt == M88K_TFMT_REGS) {
				LOAD16S(instr.rd(), ADD(R32(instr.rs1()), SHL(R32(instr.rs2()),
					CONST32(1))));
			} else {
				BAD;
			}
			break;

		case M88K_OPC_LD_HU:
			if (fmt == M88K_IFMT_MEM) {
				LOAD16(instr.rd(), ADD(R32(instr.rs1()), UIMM));
			} else if (fmt == M88K_TFMT_REG) {
				LOAD16(instr.rd(), ADD(R32(instr.rs1()), R32(instr.rs2())));
			} else if (fmt == M88K_TFMT_REGS) {
				LOAD16(instr.rd(), ADD(R32(instr.rs1()), SHL(R32(instr.rs2()),
					CONST32(1))));
			} else {
				BAD;
			}
			break;

		case M88K_OPC_LD_D:
			if (fmt == M88K_IFMT_MEM) {
				LOAD32(instr.rd() & ~1, ADD(R32(instr.rs1()), UIMM));
				LOAD32(instr.rd() | 1, ADD(ADD(R32(instr.rs1()), UIMM),
					CONST32(4)));
			} else if (fmt == M88K_IFMT_XMEM) {
				LETFP(instr.rd(), arch_m88k_load_fp64(cpu,
					ADD(R32(instr.rs1()), UIMM), bb));
			} else if (fmt == M88K_TFMT_REG) {
				LOAD32(instr.rd() & ~1, ADD(R32(instr.rs1()), R32(instr.rs2())));
				LOAD32(instr.rd() | 1, ADD(R32(instr.rs1()),
					ADD(R32(instr.rs2()), CONST32(4))));
			} else if (fmt == M88K_TFMT_REGS) {
				LOAD32(instr.rd() & ~1, ADD(R32(instr.rs1()),
					SHL(R32(instr.rs2()), CONST32(3))));
				LOAD32(instr.rd() | 1, ADD(R32(instr.rs1()),
					ADD(SHL(R32(instr.rs2()), CONST32(3)), CONST32(4))));
			} else {
				BAD;
			}
			break;

		case M88K_OPC_LD_X:
			LETFP(instr.rd(), arch_m88k_load_fp80(cpu,
				ADD(R32(instr.rs1()), UIMM), bb));
			break;

		case M88K_OPC_ST:
			if (fmt == M88K_IFMT_MEM)
				STORE32(R(instr.rd()), ADD(R32(instr.rs1()), UIMM));
			else if (fmt == M88K_IFMT_XMEM) {
				arch_m88k_store_fp32(cpu, GET_FPR(true, instr.rd(), 2),
						ADD(R32(instr.rs1()), UIMM), bb);
			} else if (fmt == M88K_TFMT_REG) {
				STORE32(R(instr.rd()), ADD(R32(instr.rs1()), R32(instr.rs2())));
			} else if (fmt == M88K_TFMT_REGS) {
				STORE32(R(instr.rd()), ADD(R32(instr.rs1()),
					SHL(R32(instr.rs2()), CONST32(2))));
			} else {
				BAD;
			}
			break;

		case M88K_OPC_ST_B:
			if (fmt == M88K_IFMT_MEM)
				STORE8(R(instr.rd()), ADD(R32(instr.rs1()), UIMM));
			else
				STORE8(R(instr.rd()), ADD(R32(instr.rs1()), R32(instr.rs2())));
			break;

		case M88K_OPC_ST_H:
			if (fmt == M88K_IFMT_MEM)
				STORE16(R(instr.rd()), ADD(R32(instr.rs1()), UIMM));
			else if (fmt == M88K_TFMT_REG)
				STORE16(R(instr.rd()), ADD(R32(instr.rs1()), R32(instr.rs2())));
			else if (fmt == M88K_TFMT_REGS)
				STORE16(R(instr.rd()), ADD(R32(instr.rs1()),
					SHL(R32(instr.rs2()), CONST32(1))));
			else {
				BAD;
			}
			break;

		case M88K_OPC_ST_D:
			if (fmt == M88K_IFMT_MEM) {
				STORE32(R(instr.rd() & ~1), ADD(R32(instr.rs1()), UIMM));
				STORE32(R(instr.rd() | 1), ADD(ADD(R32(instr.rs1()), UIMM),
					CONST32(4)));
			} else if (fmt == M88K_IFMT_XMEM) {
				arch_m88k_store_fp64(cpu, GET_FPR(true, instr.rd(), 2),
						ADD(R32(instr.rs1()), UIMM), bb);
			} else if (fmt == M88K_TFMT_REG) {
				STORE32(R(instr.rd() & ~1), ADD(R32(instr.rs1()), R32(instr.rs2())));
				STORE32(R(instr.rd() | 1), ADD(R32(instr.rs1()),
					ADD(R32(instr.rs2()), CONST32(4))));
			} else if (fmt == M88K_TFMT_REGS) {
				STORE32(R(instr.rd() & ~1), ADD(R32(instr.rs1()),
					SHL(R32(instr.rs2()), CONST32(3))));
				STORE32(R(instr.rd() | 1), ADD(R32(instr.rs1()),
					ADD(SHL(R32(instr.rs2()), CONST32(3)), CONST32(4))));
			}
			break;

		case M88K_OPC_ST_X:
			arch_m88k_store_fp80(cpu, GET_FPR(true, instr.rd(), 2),
					ADD(R32(instr.rs1()), UIMM), bb);
			break;

		case M88K_OPC_XMEM:
		case M88K_OPC_XMEM_BU:
			arch_m88k_xmem(cpu, (opc == M88K_OPC_XMEM_BU), instr.rd(),
				R32(instr.rs1()), R32(instr.rs2()), bb);
			break;

		case M88K_OPC_JSR:
			LINK(0);
		case M88K_OPC_JMP:
			LET_PC(R(instr.rs2()));
			break;

		case M88K_OPC_JSR_N:
			LINK(1);
		case M88K_OPC_JMP_N:
			LET_PC(R(instr.rs2()));
			break;

		case M88K_OPC_BSR:
			LINK(0);
		case M88K_OPC_BR:
			break;

		case M88K_OPC_BSR_N:
			LINK(1);
		case M88K_OPC_BR_N:
			break;
 
			// jump pc + disp IF !(rS & (1 << bit))
		case M88K_OPC_BB0:
		case M88K_OPC_BB0_N:
			// jump pc + disp IF (rS & (1 << bit))
		case M88K_OPC_BB1:
		case M88K_OPC_BB1_N:
			// jump pc + disp IF rS <cc> 0
		case M88K_OPC_BCND:
		case M88K_OPC_BCND_N:
			break;

		case M88K_OPC_TB1:
			if (instr.rs1() == 0)
				break;
		case M88K_OPC_TB0:
		case M88K_OPC_TBND:
		case M88K_OPC_TCND:
			TRAP(CONST32(instr.vec9()));
			break;

		//////////////////// FPU ///////////////////
		case M88K_OPC_FADD:
			SET_FPR(fmt == M88K_TFMT_XFR, instr.rd(), instr.td(),
				FPADD(GET_FPR(fmt == M88K_TFMT_XFR, instr.rs1(), instr.t1()),
					GET_FPR(fmt == M88K_TFMT_XFR, instr.rs2(), instr.t2())));
			break;

		case M88K_OPC_FSUB:
			SET_FPR(fmt == M88K_TFMT_XFR, instr.rd(), instr.td(),
				FPSUB(GET_FPR(fmt == M88K_TFMT_XFR, instr.rs1(), instr.t1()),
					GET_FPR(fmt == M88K_TFMT_XFR, instr.rs2(), instr.t2())));
			break;

		case M88K_OPC_FMUL:
			SET_FPR(fmt == M88K_TFMT_XFR, instr.rd(), instr.td(),
				FPMUL(GET_FPR(fmt == M88K_TFMT_XFR, instr.rs1(), instr.t1()),
					GET_FPR(fmt == M88K_TFMT_XFR, instr.rs2(), instr.t2())));
			break;

		case M88K_OPC_FDIV:
			SET_FPR(fmt == M88K_TFMT_XFR, instr.rd(), instr.td(),
				FPDIV(GET_FPR(fmt == M88K_TFMT_XFR, instr.rs1(), instr.t1()),
					GET_FPR(fmt == M88K_TFMT_XFR, instr.rs2(), instr.t2())));
			break;

		case M88K_OPC_FCVT:
			SET_FPR(fmt == M88K_TFMT_XFR, instr.rd(), instr.td(),
				GET_FPR(fmt == M88K_TFMT_XFR, instr.rs2(), instr.t2()));
			break;

		case M88K_OPC_INT: // round-to-zero
			LET32(instr.rd(), FPTOSI(32, GET_FPR(fmt == M88K_TFMT_XFR, instr.rs2(), instr.t2())));
			break;

		case M88K_OPC_NINT: // round-to-nearest
			LET32(instr.rd(), FPTOSI(32, GET_FPR(fmt == M88K_TFMT_XFR, instr.rs2(), instr.t2())));
			break;

		case M88K_OPC_TRNC: // truncation
			LET32(instr.rd(), FPTOSI(32, GET_FPR(fmt == M88K_TFMT_XFR, instr.rs2(), instr.t2())));
			break;

		case M88K_OPC_FLT: // flt <- int
			SET_FPR(fmt == M88K_TFMT_XFR, instr.rd(), instr.td(),
				SITOFP(32, R32(instr.rs2())));
			break;

		case M88K_OPC_FSQRT:
			SET_FPR(fmt == M88K_TFMT_XFR, instr.rd(), instr.td(),
				FPSQRT(FPBITCAST64(GET_FPR(fmt == M88K_TFMT_XFR,
					instr.rs2(), instr.t2()))));
			break;

		case M88K_OPC_FCMP:
		case M88K_OPC_FCMPU:
			arch_m88k_fcmp(cpu, instr.rd(),
					GET_FPR(fmt == M88K_TFMT_XFR, instr.rs1(), instr.t1()),
					GET_FPR(fmt == M88K_TFMT_XFR, instr.rs2(), instr.t2()),
					bb);
			break;

		case M88K_OPC_MOV:
			switch(fmt) {
				case M88K_TFMT_XREG: // xfr <- gpr
					SET_FPR(true, instr.rd(), 2, GET_FPR(false, instr.rs2(), instr.t2()));
					break;
				case M88K_TFMT_REGX: // gpr/xfr <- xfr
					if (instr & 0x200)
						SET_FPR(true, instr.rd(), 2, GET_FPR(true, instr.rs2(), 2));
					else
						SET_FPR(false, instr.rd(), instr.td(), GET_FPR(true, instr.rs2(), 2));
					break;
				default:
					assert(0 && "Cannot happen");
			}
			break;

		case M88K_OPC_DIVU_D:
		case M88K_OPC_MULU_D:
			// XXX TODO
			break;

		case M88K_OPC_FLDCR:
			// XXX TODO
			LET32(instr.rd(), CONST32(0));
		case M88K_OPC_FSTCR:
			// XXX TODO
			break;

		default:
			LOG("INVALID %s:%d\n", __func__, __LINE__); exit(1);
			break;
	}

	return 4;
}