package miggy.cpu.genpoet;

import static javax.lang.model.element.Modifier.ABSTRACT;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PROTECTED;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
import static miggy.cpupoet.CoreALU.EAX_BS;
import static miggy.cpupoet.CoreALU.EAX_EXT;
import static miggy.cpupoet.CoreALU.EAX_IS;
import static miggy.cpupoet.CoreALU.EAX_ISL;
import static miggy.cpupoet.CoreALU.EAX_SCL;
import static miggy.cpupoet.CoreALU.EAX_SIZ;
import static miggy.cpupoet.CoreALU.FL_C;
import static miggy.cpupoet.CoreALU.FL_N;
import static miggy.cpupoet.CoreALU.FL_V;
import static miggy.cpupoet.CoreALU.FL_X;
import static miggy.cpupoet.CoreALU.FL_Z;
import static miggy.cpupoet.CoreALU.SR_M;
import static miggy.cpupoet.CoreALU.SR_S;
import static miggy.cpupoet.CoreALU.SR_T0;
import static miggy.cpupoet.CoreALU.SR_T1;
import static miggy.cpupoet.CoreALU.SSWI_AVEC;
import static miggy.cpupoet.CoreALU.SSWI_DERR;
import static miggy.cpupoet.CoreALU.SSWI_EAFH;
import static miggy.cpupoet.CoreALU.SSWI_FMT2;
import static miggy.cpupoet.CoreALU.SSWI_DNGR;
import static miggy.cpupoet.CoreALU.SSWI_SNGL;
import static miggy.cpupoet.CoreALU.SSW_32;
import static miggy.cpupoet.CoreALU.SSW_BR;
import static miggy.cpupoet.CoreALU.SSW_BY;
import static miggy.cpupoet.CoreALU.SSW_D;
import static miggy.cpupoet.CoreALU.SSW_DF;
import static miggy.cpupoet.CoreALU.SSW_HB;
import static miggy.cpupoet.CoreALU.SSW_IF;
import static miggy.cpupoet.CoreALU.SSW_P;
import static miggy.cpupoet.CoreALU.SSW_RM;
import static miggy.cpupoet.CoreALU.SSW_RR;
import static miggy.cpupoet.CoreALU.SSW_RW;
import static miggy.cpupoet.CoreALU.SSW_S;
import static miggy.cpupoet.CoreALU.SSW_WD;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import miggy.cpu.genpoet.CorePLAGenerator.MacroEntry;
import miggy.cpu.genpoet.CorePLAGenerator.OPEntry;
import miggy.cpupoet.CoreALU;

public class CoreGenerator {
	List<MicroBlock> blocks = new ArrayList<>();

	private static final int SSW_AC = 1 << 18; // transfer not critical (but address error is critical)
	private static final int SSW_AL = 1 << 17; // transfer aligned (generation time)
	private static final int SSW_CR = 1 << 16; // transfer critical (generation time)

	private static final FieldInt16Constant SR_I = new FieldInt16Constant("SR_I", CoreALU.SR_I);
	private static final FieldInt16Constant SSW_RS = new FieldInt16Constant("SSW_RS", CoreALU.SSW_RS);
	private static final FieldInt16Constant SSW_HL = new FieldInt16Constant("SSW_HL", CoreALU.SSW_HL);
	private static final FieldInt16Constant SSWI_PSLC = new FieldInt16Constant("SSWI_PSLC", CoreALU.SSWI_PSLC);

	public static final int GEN_BYTE = 0x0001;
	public static final int GEN_WORD = 0x0002;
	public static final int GEN_LONG = 0x0004;
	public static final int GEN_DDDS = 0x0008;
	public static final int GEN_ASDD = 0x0010;
	public static final int GEN_EADD = 0x0020;
	public static final int GEN_IMM3 = 0x0040;
	public static final int GEN_NUPT = 0x0080;

	/**
	 * exit from emulation loop as soon as possible (may take trap depending on
	 * RPIR)
	 */
	private static final FieldInt32Constant BKPT_EXIT = new FieldInt32Constant("BKPT_EXIT", 1 << 16);
	/**
	 * replace IR by lower 16 bits handler return code and continue execution
	 */
	private static final FieldInt32Constant BKPT_RPIR = new FieldInt32Constant("BKPT_RPIR", 1 << 17);

	/**
	 * exit from emulation loop as soon as possible (may take trap if SKIP not set)
	 */
	private static final FieldInt32Constant LINEA_EXIT = new FieldInt32Constant("LINEA_EXIT", 1 << 16);
	/**
	 * skip linea opcode (do not take trap), lower 16 bits are sign extended and
	 * added to PC
	 */
	private static final FieldInt32Constant LINEA_SKIP = new FieldInt32Constant("LINEA_SKIP", 1 << 17);

	/**
	 * exit from emulation loop as soon as possible (may take trap if SKIP not set)
	 */
	private static final FieldInt32Constant ILL_EXIT = new FieldInt32Constant("ILL_EXIT", 1 << 16);
	/**
	 * skip illegal opcode (do not take trap), lower 16 bits are sign extended and
	 * added to PC
	 */
	private static final FieldInt32Constant ILL_SKIP = new FieldInt32Constant("ILL_SKIP", 1 << 17);

	/**
	 * exit from emulation loop as soon as possible (may take trap if IGNR not set)
	 */
	private static final FieldInt16Constant TRACE_EXIT = new FieldInt16Constant("TRACE_EXIT", 1 << 15);
	/**
	 * ignore trace trap
	 */
	private static final FieldInt16Constant TRACE_IGNR = new FieldInt16Constant("TRACE_IGNR", 1 << 14);

	/**
	 * exit from emulation loop as soon as possible (make take trap if IGNR not set)
	 */
	private static final FieldInt16Constant IRQ_EXIT = new FieldInt16Constant("IRQ_EXIT", 1 << 15);
	/**
	 * unable to determine vector
	 */
	private static final FieldInt16Constant IRQ_IERR = new FieldInt16Constant("IRQ_IERR", 1 << 14);
	/**
	 * use spurious if IERR, AVEC if globally set, or provided vector in other cases
	 */
	private static final FieldInt16Constant IRQ_DFLT = new FieldInt16Constant("IRQ_DFLT", 0 << 12);
	/**
	 * force auto-vector interrupt (regardless of global setting)
	 */
	private static final FieldInt16Constant IRQ_AVEC = new FieldInt16Constant("IRQ_AVEC", 1 << 12);
	/**
	 * force spurious interrupt vector
	 */
	private static final FieldInt16Constant IRQ_SPUR = new FieldInt16Constant("IRQ_SPUR", 2 << 12);
	/**
	 * ignore interrupt
	 */
	private static final FieldInt16Constant IRQ_IGNR = new FieldInt16Constant("IRQ_IGNR", 3 << 12);

	private static final FieldSpec pc = FieldSpec.builder(int.class, "pc", PROTECTED).build();
	private static final FieldSpec alub = FieldSpec.builder(int.class, "alub", PROTECTED).build();
	private static final FieldSpec irb = FieldSpec.builder(int.class, "irb", PROTECTED).build();
	private static final FieldSpec ir = FieldSpec.builder(int.class, "ir", PROTECTED).build();
	private static final FieldSpec mpc = FieldSpec.builder(int.class, "mpc", PROTECTED).initializer("$L", "reset")
			.build();
	private static final FieldSpec cip = FieldSpec.builder(int.class, "cip", PROTECTED).build();
	private static final FieldSpec au = FieldSpec.builder(int.class, "au", PROTECTED).build();
	private static final FieldSpec at = FieldSpec.builder(int.class, "at", PROTECTED).build();
	private static final FieldSpec dt = FieldSpec.builder(int.class, "dt", PROTECTED).build();

	private static final FieldSpec slice = FieldSpec.builder(int.class, "slice", PROTECTED).initializer("$L", "0")
			.build();
	private static final FieldSpec scan = FieldSpec.builder(int.class, "scan", PROTECTED).build();

	private static final FieldSpec sswi = FieldSpec.builder(int.class, "sswi", PROTECTED).build();

	public static void main(String[] args) throws IOException {
		JavaFile javaFile = JavaFile.builder("miggy.cpupoet", coreType()).build();

		javaFile.writeTo(System.out);
	}

	public CoreGenerator() {
		reset_microcode();
		berr_microcode();
		trap2000_microcode();
		trap0000_microcode();
		interrupt_microcode();
		writeea_microcode();
		imm16_microcode();
		imm32_microcode();
		ea_aid_microcode();
		ea_ais_microcode();
		ea_aipd_microcode();
		ea_aips_microcode();
		ea_paid_microcode();
		ea_pais_microcode();
		ea_dad_microcode();
		ea_das_microcode();
		ea_daid_microcode();
		ea_dais_microcode();
		ea_dpc_microcode();
		ea_dpci_microcode();
		ea_adr16d_microcode();
		ea_adr16s_microcode();
		ea_adr32d_microcode();
		ea_adr32s_microcode();
		ea_imm_microcode();
		op_movep_microcode();
		misc_microcode();
		op_movem_microcode();
		op_bcc_microcode();
		op_bkpt_microcode();
		op_illegal_microcode();
		op_jmp_microcode();
		op_linea_microcode();
		op_linef_microcode();
		op_nop_microcode();
		op_rte_microcode();
		op_rtr_microcode();
		op_rts_microcode();
		op_reset_microcode();
		op_trapv_microcode();
	}

	private void reset_microcode() {
		addState("reset");
		consume(40, 6, 0);
		// clear trace, disable interrupts and set supervisor mode
		setclr16("sr", SR_S | SR_I.value, SR_T1 | SR_T0 | SR_M);
		addFormattedMicroInsn("sp = spi(sr)"); // update stack pointer index
		setclr16("ssw", 0, SSW_RS.value | SSW_HL.value); // clear pending reset and halt
		// clear pending trace, ea fetch and double bus fault
		setclr16("sswi", 0, SR_T1 | SR_T0 | SSWI_EAFH | SSWI_DERR);
		setclr32("vbr", 0, ~0); // clear vbr register
		fetch32("dar[sp]", SSW_CR | SSW_S, null, 0x00000000); // critical supervisor program fetch of ssp
		fetch32("pc", SSW_CR | SSW_S, null, 0x00000004); // critical supervisor program fetch of pc
		addFormattedMicroInsn("mpc = resume_berr"); // resume with critical pc fetch
		addFormattedMicroInsn("continue");

		/* double bus fault entry point */
		addState("dbrr");
		setclr("ssw", SSW_HL.value, 0); // halt cpu
		setclr("sswi", SSWI_DERR, 0); // and set double bus fault
		addFormattedMicroInsn("nmpc = halt"); // never exit !

		/* halt entry point */
		addState("halt");
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) != 0)", SSW_RS.value);
		// handle pending reset
		addFormattedMicroInsn("mpc = reset");
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((ssw & 0x%04x) != 0)", SSW_HL.value);
		// handle halt
		addFormattedMicroInsn("elapsed += Math.max(0, slice - elapsed)");
		addEndControlFlow();
		addFormattedMicroInsn("break");
	}

	private void berr_microcode() {
		// entry point for non critical 32 bits fetch bus events
		addState("bevtf32");
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) != 0)", SSW_BR);
		setclr16("ssw", 0, SSW_WD);
		addFormattedMicroInsn("mpc = berr"); // handle bus error
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((ssw & 0x%04x) == 0)", SSW_WD);
		addFormattedMicroInsn("dib <<= 16"); // shift dib to keep dibh
		// indicate bus error handler that dibh needs to be saved
		setclr16("ssw", SSW_WD, SSW_32);
		addFormattedMicroInsn("dib |= fetch16(aob += 2) & 0xffff");
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0)", SSW_BR | SSW_RS.value);
		addFormattedMicroInsn("mpc = nmpc"); // dib is dirty, jump to next pc (without exiting)
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addEndControlFlow();
		addFormattedMicroInsn("mpc = bevt"); // handle bus error
		addFormattedMicroInsn("continue");

		// entry point for non critical 32 bits read bus events
		addState("bevtr32");
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) != 0)", SSW_BR);
		setclr16("ssw", 0, SSW_WD);
		addFormattedMicroInsn("mpc = berr"); // handle bus error
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((ssw & 0x%04x) == 0)", SSW_WD);
		addFormattedMicroInsn("dib <<= 16"); // shift dib to keep dibh
		// indicate bus error handler that dibh needs to be saved
		setclr16("ssw", SSW_WD, SSW_32);
		addFormattedMicroInsn("dib |= read16(aob += 2) & 0xffff");
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0)", SSW_BR | SSW_RS.value);
		addFormattedMicroInsn("mpc = nmpc"); // dib is dirty, jump to next pc (without exiting)
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addEndControlFlow();

		// entry point for non critical 32 bits write bus events
		addState("bevtw32");
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) != 0)", SSW_BR);
		setclr16("ssw", 0, SSW_WD);
		addFormattedMicroInsn("mpc = berr"); // handle bus error
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((ssw & 0x%04x) == 0)", SSW_WD);
		setclr16("ssw", 0, SSW_32);
		addFormattedMicroInsn("write16(aob += 2, dob)");
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0)", SSW_BR | SSW_RS.value);
		addClearSSW();
		addFormattedMicroInsn("break"); // write succeeded, can exit if needed
		addEndControlFlow();
		addEndControlFlow();
		addFormattedMicroInsn("mpc = bevt"); // handle bus error
		addFormattedMicroInsn("continue");

		/* entry point for non critical 8/16 bits bus events */
		addState("bevt");
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0)", SSW_BR);
		addFormattedMicroInsn("mpc = halt"); // reset will be handled in halt microcode
		addFormattedMicroInsn("continue");
		addEndControlFlow();

		/* bus error entry point */
		addState("berr");
		consume(126, 4, 26);

		/* vdt1 contains sswi pending bits, tvn and ssw (with RR flipped) */
		addFormattedMicroInsn("vdt1 = ((sswi & 0x%04x) << 16) | ((tvn & 0x3fc) << 14) | ((ssw ^ ~0x%04x) & 0x%04x)",
				SR_T1 | SR_T0 | SSWI_EAFH, SSW_RR, (~SSW_BR) & 0xffff);

		settvn(2); // set tvn AFTER previous tvn has been saved (in vdt1)
		addFormattedMicroInsn("mpc = trap8000");
		addFormattedMicroInsn("continue");

		/* address error entry point */
		addState("aerr");
		consume(126, 4, 26);

		/* vdt1 contains sswi pending bits, tvn and ssw (with RR flipped) */
		addFormattedMicroInsn("vdt1 = ((sswi & 0x%04x) << 16) | ((tvn & 0x3fc) << 14) | ((ssw ^ ~0x%04x) & 0x%04x)",
				SR_T1 | SR_T0 | SSWI_EAFH, SSW_RR, (~SSW_BR) & 0xffff);

		settvn(3); // set tvn AFTER previous tvn has been saved (in vdt1)

		addState("trap8000");

		addBeginFormattedControlFlow("if ((vdt1 & 0x%04x) == 0)", SSW_BY);
		/*
		 * for byte access, ensure aob is even and set HB according to requested
		 * address. Also adjust dob according to HB
		 */
		addBeginFormattedControlFlow("if ((aob & 0x000000001) != 0)");
		addFormattedMicroInsn("aob -= 1");
		setclr16("vdt1", 0, SSW_HB);
		addNextFormattedControlFlow("else");
		setclr16("vdt1", SSW_HB, 0);
		addBeginFormattedControlFlow("if ((vdt1 & 0x%04x) == 0)", SSW_RW);
		addFormattedMicroInsn("dob <<= 8");
		addEndControlFlow();
		addEndControlFlow();
		addEndControlFlow();

		addFormattedMicroInsn("vat1 = at");
		addFormattedMicroInsn("vat2 = au");
		addFormattedMicroInsn("vat3 = aob");
		addFormattedMicroInsn("vdt2 = dob");
		addFormattedMicroInsn("vdt3 = dib"); // not available in original 68010, added to save dib high bits
		addFormattedMicroInsn("sp = spi(sr | 0x%04x)", SR_S);
		addFormattedMicroInsn("au = dar[sp] - %d", 29 << 1); // allocate 29 words on supervisor stack
		addFormattedMicroInsn("dar[sp] = au"); // update supervisor stack pointer

		write16(SSW_CR | SSW_S | SSW_D, "au", "sr", 0 << 1, false); // write sr to stack frame
		write32(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "pc", 1 << 1, false); // write pc to stack frame
		write32(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "vat3", 5 << 1, false); // write saved aob to stack frame

		addBeginFormattedControlFlow("if ((vdt1 & 0x%04x) == 0)", SSW_RW);
		addBeginFormattedControlFlow("if ((vdt1 & 0x%04x) == 0)", SSW_32);
		write32(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "vdt2", 8 << 1, false); // write dob to stack frame (32 bits)
		addNextFormattedControlFlow("else");
		write16(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "vdt2", 8 << 1, false); // write dob to stack frame (16 bits
		// only)
		addEndControlFlow();
		addNextFormattedControlFlow("else if ((vdt1 & 0x%04x) == 0x%04x)", SSW_32 | SSW_WD, SSW_WD);
		write16(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "vdt3 >> 16", 9 << 1, false);
		addEndControlFlow();

		write16(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "irb", 12 << 1, false); // write prefetch to stack frame
		write16(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "nmpc", 13 << 1, false); // write next mpc to stack frame
		write16(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "scan", 14 << 1, false); // write scan value
		write32(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "alub", 15 << 1, false); // write alub to stack frame
		write32(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "vat1", 17 << 1, false); // write saved at to stack frame
		write32(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "vat2", 19 << 1, false); // write saved au to stack frame
		write16(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "vdt1 >> 16", 23 << 1, false); // write sswi and tvn to stack
		// frame
		write16(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "ir", 24 << 1, false); // write ir to stack frame
		write32(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "dt", 25 << 1, false); // write dt to stack frame
		write32(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "cip", 27 << 1, false); // write previous pc value

		addState("resume_trap8000");

		writevob(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 8, 3 << 1); // always update vob
		write16(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "vdt1", 4 << 1, false); // always update ssw

		setclr16("sr", SR_S, SR_T1 | SR_T0); // clear trace and set supervisor mode
		setclr("sswi", 0, SR_T1 | SR_T0 | SSWI_EAFH); // clear pending trace and ea fetch

		read32("pc", SSW_CR | SSW_S, "tvn + vbr", 0); // fetch pc from tvn

		/*
		 * specific entry for reset and bus error frames where prefetch error is fatal
		 */
		addState("resume_berr");
		addFormattedMicroInsn("scan = 0"); // clear scan value
		addFormattedMicroInsn("tvn = 0"); // clear tvn
		addFormattedMicroInsn("nmpc = resume");
		prefetchir(SSW_CR | SSW_S, true);
		addClearSSW();
		addFormattedMicroInsn("break");
	}

	private void trap2000_microcode() {
		addState("trap2000");
		/* consume is set by caller */

		addFormattedMicroInsn("sp = spi(sr | 0x%04x)", SR_S); // select supervisor stack
		addFormattedMicroInsn("au = dar[sp] - %d", 6 << 1); // allocate 6 words on supervisor stack
		addFormattedMicroInsn("dar[sp] = au"); // update supervisor stack pointer

		write16(SSW_CR | SSW_S | SSW_D, "au", "sr", 0 << 1, false); // write sr to stack frame
		write32(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "pc + scan", 1 << 1, false); // write next pc to stack frame
		writevob(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 2, 3 << 1); // write frame format and vector offset
		write32(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "cip", 4 << 1, false); // write previous pc to stack frame

		addFormattedMicroInsn("mpc = enter_trap0000"); // set next entry
		addFormattedMicroInsn("continue");
	}

	private void trap0000_microcode() {
		addState("trapill");
		addFormattedMicroInsn("pc = cip"); // make pc point to current instruction
		addFormattedMicroInsn("scan = 0"); // reset scan value

		addState("trap0000");
		/* consume is set by caller */

		addFormattedMicroInsn("sp = spi(sr | 0x%04x)", SR_S); // select supervisor stack
		addFormattedMicroInsn("au = dar[sp] - %d", 4 << 1); // allocate 4 words on supervisor stack
		addFormattedMicroInsn("dar[sp] = au"); // update supervisor stack pointer

		write16(SSW_CR | SSW_S | SSW_D, "au", "sr", 0 << 1, false); // write sr to stack frame
		write32(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "pc + scan", 1 << 1, false); // write pc to stack frame
		writevob(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 0, 3 << 1); // write frame format and vector offset

		addState("enter_trap0000");
		setclr16("sr", SR_S, SR_T1 | SR_T0); // clear trace and set supervisor mode
		setclr("sswi", 0, SR_T1 | SR_T0 | SSWI_EAFH); // clear pending trace and ea fetch

		read32("pc", SSW_AC | SSW_S, "tvn + vbr", 0); // fetch pc from tvn

		addFormattedMicroInsn("mpc = execute_trap"); // set next entry
		addFormattedMicroInsn("continue");
	}

	private void interrupt_microcode() {
		addState("uninitialized");
		settvn(15);
		read32("pc", SSW_AC | SSW_S, "tvn + vbr", 0); // fetch pc from new tvn
		addFormattedMicroInsn("mpc = enter_irq");
		addFormattedMicroInsn("continue");

		addState("interrupt");
		consume(46, 5, 4);
		addFormattedMicroInsn("hdlr = handle_interrupt((sswi & 0x%04x) >> 8)", SR_I.value);
		addFormattedMicroInsn("exit |= (hdlr & 0x%04x) != 0", IRQ_EXIT.value);
		addBeginFormattedControlFlow("if ((hdlr & 0x%04x) != 0x%04x)", IRQ_IGNR.value, IRQ_IGNR.value);
		setclr16("sswi", 0, SR_I.value);
		addFormattedMicroInsn("sp = spi(sr)"); // update stack pointer index
		addFormattedMicroInsn("mpc = resume"); // set next entry
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((hdlr & 0x%04x) == 0x%04x)", IRQ_IGNR.value, IRQ_SPUR.value);
		settvn(24);
		addNextFormattedControlFlow("else if ((hdlr & 0x%04x) == 0x%04x)", IRQ_IGNR.value, IRQ_AVEC.value);
		addFormattedMicroInsn("tvn = %d + (sswi & 0x%04x) >> 6", 24 << 2, SR_I.value);
		addNextFormattedControlFlow("else if ((hdlr & 0x%04x) != 0)", IRQ_IERR.value);
		settvn(24);
		addNextFormattedControlFlow("else if ((sswi & 0x%04x) != 0)", SSWI_AVEC);
		addFormattedMicroInsn("tvn = %d + (sswi & 0x%04x) >> 6", 24 << 2, SR_I.value);
		addNextFormattedControlFlow("else");
		addFormattedMicroInsn("tvn = (hdlr & 0xff) << 2");
		addEndControlFlow();

		addFormattedMicroInsn("sp = spi(sr | 0x%04x)", SR_S); // select msp (or isp)
		addFormattedMicroInsn("au = dar[sp] - %d", 4 << 1); // allocate 4 words on supervisor stack
		addFormattedMicroInsn("dar[sp] = au"); // update supervisor stack pointer
		write16(SSW_CR | SSW_S | SSW_D, "au", "sr", 0 << 1, false); // write sr to stack frame
		write32(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", "pc", 1 << 1, false); // write pc to stack frame

		setclr16("sr", SR_S, 0); // set supervisor mode

		/* check for 'master' ssp mode */
		addBeginFormattedControlFlow("if ((sr & 0x%04x) != 0)", SR_M);
		addFormattedMicroInsn("sp = spi(sr & ~0x%04x)", SR_M); // select isp
		addFormattedMicroInsn("at = dar[sp] - %d", 4 << 1); // allocate 4 words on interrupt stack
		addFormattedMicroInsn("dar[sp] = at"); // update interrupt stack pointer
		write16(SSW_CR | SSW_S | SSW_D, "at", "sr", 0 << 1, false); // write sr to interrupt stack frame
		write32(SSW_CR | SSW_S | SSW_D | SSW_AL, "at", "pc", 1 << 1, false); // write pc to interrupt stack frame
		addEndControlFlow();

		// clear trace, update interrupt level and set supervisor mode (in sr)
		addFormattedMicroInsn("sr = (sswi & 0x%04x) | (sr & ~0x%04x)", SR_I.value, SR_T1 | SR_T0 | SR_M | SR_I.value);
		// clear pending trace, ea fetch and pending interrupt
		setclr16("sswi", 0, SR_T1 | SR_T0 | SSWI_EAFH | SR_I.value);

		read32("pc", SSW_AC | SSW_S, "tvn + vbr", 0); // fetch pc from saved tvn

		addBeginFormattedControlFlow("if (pc == 0x00000000)");
		addFormattedMicroInsn("mpc = uninitialized");
		addFormattedMicroInsn("continue");
		addEndControlFlow();

		addState("enter_irq");
		// write frame format and vector offset (on master stack)
		writevob(SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 0, 3 << 1);

		addBeginFormattedControlFlow("if ((sr & 0x%04x) != 0)", SR_M);
		// write frame format and vector offset (on interrupt stack)
		writevob(SSW_CR | SSW_S | SSW_D | SSW_AL, "at", 1, 3 << 1);
		addFormattedMicroInsn("sr &= ~0x%04x", SR_M); // clear master flag
		addEndControlFlow();

		addState("execute_trap");
		addFormattedMicroInsn("scan = 0"); // clear scan value
		addFormattedMicroInsn("tvn = 0"); // clear tvn
		addState("resume_prefetch");
		addFormattedMicroInsn("nmpc = resume");
		prefetchir(0, true);
		addClearSSW();

		addState("resume");
		// check for pending halt
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) != 0)", SSW_RS.value | SSW_HL.value);
		addFormattedMicroInsn("nmpc = resume");
		addFormattedMicroInsn("mpc = halt");
		addFormattedMicroInsn("continue");
		// check if single step mode
		addNextFormattedControlFlow("else if ((sswi & 0x%04x) != 0)", SSWI_SNGL);
		addFormattedMicroInsn("exit |= sngl");
		addFormattedMicroInsn("sngl |= true");
		addBeginFormattedControlFlow("if (exit)");
		addFormattedMicroInsn("nmpc = resume");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		// check for pending trace
		addNextFormattedControlFlow("else if ((sswi & 0x%04x) != 0)", SR_T1 | SR_T0);
		consume(38, 4, 4);
		addFormattedMicroInsn("hdlr = handle_trace(cip)");
		addFormattedMicroInsn("exit |= (hdlr & 0x%04x) != 0", TRACE_EXIT.value);
		addBeginFormattedControlFlow("if ((hdlr & 0x%04x) == 0)", TRACE_IGNR.value);
		settvn(9);
		addFormattedMicroInsn("mpc = (sswi & 0x%04x) != 0 ? trap2000 : trap0000", SSWI_FMT2);
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		setclr16("sswi", 0, SR_T1 | SR_T0);
		addEndControlFlow();

		addState("resume_notrace");
		addFormattedMicroInsn("ipend = sswi & 0x%04x", SR_I.value);
		// lastly, check for pending interrupt (and nmi)
		addBeginFormattedControlFlow("if ((ipend == 0x%04x) || ((sr & 0x%04x) < ipend))", SR_I.value, SR_I.value);
		addFormattedMicroInsn("mpc = interrupt");
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addFormattedMicroInsn("cip = pc + scan");
		addFormattedMicroInsn("pc = cip + 2");
		addFormattedMicroInsn("scan = 0");
		addFormattedMicroInsn("ir = irb & 0xffff");
		addFormattedMicroInsn("decoded = MacroPLA.decode(ir)");
		addFormattedMicroInsn("nmpc = decoded.a1");
		addFormattedMicroInsn("sswi |= (sr & 0x%04x)", SR_T1);
		addFormattedMicroInsn("break");

		addState("op_stop");
		check_privileged();
		// update SR register from immediate value
		addFormattedMicroInsn("sr = dt & 0x%04x", SR_T1 | SR_T0 | SR_S | SR_M | SR_I.value | FL_C | FL_V | FL_Z | FL_N | FL_X);
		// enter stop loop
		addFormattedMicroInsn("nmpc = op_stop_loop");
		
		addState("op_stop_loop");
		// check for pending reset or halt
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) != 0)", SSW_RS.value | SSW_HL.value);
		addFormattedMicroInsn("nmpc = op_stop_loop");
		addFormattedMicroInsn("mpc = halt");
		addFormattedMicroInsn("continue");
		// check for pending trace
		addNextFormattedControlFlow("else if ((sswi & 0x%04x) != 0)", SR_T1 | SR_T0);
		consume(38, 4, 4);
		addFormattedMicroInsn("hdlr = handle_trace(cip)");
		addFormattedMicroInsn("exit |= (hdlr & 0x%04x) != 0", TRACE_EXIT.value);
		addBeginFormattedControlFlow("if ((hdlr & 0x%04x) == 0)", TRACE_IGNR.value);
		settvn(9);
		addFormattedMicroInsn("mpc = (sswi & 0x%04x) != 0 ? trap2000 : trap0000", SSWI_FMT2);
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		setclr16("sswi", 0, SR_T1 | SR_T0);
		addFormattedMicroInsn("mpc = resume_notrace");
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addFormattedMicroInsn("ipend = sswi & 0x%04x", SR_I.value);
		// lastly, check for pending interrupt (and nmi)
		addBeginFormattedControlFlow("if ((ipend == 0x%04x) || ((sr & 0x%04x) < ipend))", SR_I.value, SR_I.value);
		addFormattedMicroInsn("mpc = interrupt");
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		// no trace / reset / halt / interrupt, eat all remaining cycles and exit
		addFormattedMicroInsn("elapsed += Math.max(0, slice - elapsed)");
		addFormattedMicroInsn("break");

	}

	private void writeea_microcode() {
		addState("ea_resume_write8");
		prefetchir(0, false);
		write8(SSW_D, "at", "dt", 0, false);
		addClearSSW();
		addFormattedMicroInsn("mpc = resume");
		addFormattedMicroInsn("continue");

		addState("ea_resume_write16");
		prefetchir(0, false);
		write16(SSW_D, "at", "dt", 0, false);
		addClearSSW();
		addFormattedMicroInsn("mpc = resume");
		addFormattedMicroInsn("continue");

		addState("ea_resume_write32");
		prefetchir(0, false);
		write32(SSW_D, "at", "dt", 0, false);
		addClearSSW();
		addFormattedMicroInsn("mpc = resume");
		addFormattedMicroInsn("continue");
	}

	private void imm16_microcode() {
		addState("op_imm16");
		fetchimm16("dt", 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addClearSSW();
		addFormattedMicroInsn("break");
	}

	private void imm32_microcode() {
		addState("op_imm32");
		fetchimm32("dt", 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addClearSSW();
		addFormattedMicroInsn("break");
	}

	private void ea_aid_microcode() {
		addState("ea_aid8");
		decode_ax();
		addFormattedMicroInsn("at = dar[rx]"); // save address to temporary register
		consume(2, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");

		addState("ea_aid16");
		decode_ax();
		addFormattedMicroInsn("at = dar[rx]"); // save address to temporary register
		consume(2, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");

		addState("ea_aid32");
		decode_ax();
		addFormattedMicroInsn("at = dar[rx]"); // save address to temporary register
		consume(2, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");
	}

	private void ea_ais_microcode() {
		addState("ea_ais8_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_ais8");
		decode_ay();
		addFormattedMicroInsn("at = dar[ry]"); // save address to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(2, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(4, 1, 0);

		addState("ea_read8");
		setclr16("sswi", 0, SSWI_EAFH);
		addFormattedMicroInsn("alub = dt"); // save dt to alub
		read8("dt", 0, "at");
		addClearSSW();
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");

		addState("ea_ais16_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_ais16");
		decode_ay();
		addFormattedMicroInsn("at = dar[ry]"); // save address to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(2, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(4, 1, 0);

		addState("ea_read16");
		setclr16("sswi", 0, SSWI_EAFH);
		addFormattedMicroInsn("alub = dt"); // save dt to alub
		read16("dt", 0, "at");
		addClearSSW();
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");

		addState("ea_ais32_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_ais32");
		decode_ay();
		addFormattedMicroInsn("at = dar[ry]"); // save address to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(2, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(8, 2, 0);

		addState("ea_read32");
		setclr16("sswi", 0, SSWI_EAFH);
		addFormattedMicroInsn("alub = dt"); // save dt to alub
		read32("dt", 0, "at");
		addClearSSW();
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
	}

	private void ea_aipd_microcode() {
		addState("ea_aipd8_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_aipd8");
		addFormattedMicroInsn("rx = (ir >> 9) & 0x0007"); // retrieve address register index
		addBeginFormattedControlFlow("if (rx == 7)");
		addFormattedMicroInsn("at = dar[sp]"); // save address to temporary register
		addFormattedMicroInsn("dar[sp] += 2"); // increment address register
		addNextFormattedControlFlow("else");
		addFormattedMicroInsn("rx |= 8");
		addFormattedMicroInsn("at = dar[rx]"); // save address to temporary register
		addFormattedMicroInsn("dar[rx] += 1"); // increment address register
		addEndControlFlow();
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(4, 1, 0);
		addFormattedMicroInsn("mpc = ead_read8");
		addFormattedMicroInsn("continue");

		addState("ea_aipd16_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_aipd16");
		decode_ax();
		addFormattedMicroInsn("at = dar[rx]"); // save address to temporary register
		addFormattedMicroInsn("dar[rx] += 2"); // increment address register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(4, 1, 0);
		addFormattedMicroInsn("mpc = ead_read16");
		addFormattedMicroInsn("continue");

		addState("ea_aipd32_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_aipd32");
		decode_ax();
		addFormattedMicroInsn("at = dar[rx]"); // save address to temporary register
		addFormattedMicroInsn("dar[rx] += 4"); // increment address register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(4, 1, 0);
		addFormattedMicroInsn("mpc = ead_read32");
		addFormattedMicroInsn("continue");
	}

	private void ea_aips_microcode() {
		addState("ea_aips8_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_aips8");
		addFormattedMicroInsn("ry = ir & 0x0007"); // retrieve address register index
		addBeginFormattedControlFlow("if (ry == 7)");
		addFormattedMicroInsn("at = dar[sp]"); // save address to temporary register
		addFormattedMicroInsn("dar[sp] += 2"); // increment address register
		addNextFormattedControlFlow("else");
		addFormattedMicroInsn("ry |= 8");
		addFormattedMicroInsn("at = dar[ry]"); // save address to temporary register
		addFormattedMicroInsn("dar[ry] += 1"); // increment address register
		addEndControlFlow();
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(4, 1, 0);
		addFormattedMicroInsn("mpc = ea_read8");
		addFormattedMicroInsn("continue");

		addState("ea_aips16_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_aips16");
		decode_ay();
		addFormattedMicroInsn("at = dar[ry]"); // save address to temporary register
		addFormattedMicroInsn("dar[ry] += 2"); // increment address register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(4, 1, 0);
		addFormattedMicroInsn("mpc = ea_read16");
		addFormattedMicroInsn("continue");

		addState("ea_aips32_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_aips32");
		decode_ay();
		addFormattedMicroInsn("at = dar[ry]"); // save address to temporary register
		addFormattedMicroInsn("dar[ry] += 4"); // increment address register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(8, 2, 0);
		addFormattedMicroInsn("mpc = ea_read32");
		addFormattedMicroInsn("continue");
	}

	private void ea_paid_microcode() {
		addState("ea_paid8_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_paid8");
		addFormattedMicroInsn("rx = (ir >> 9) & 0x0007"); // retrieve address register index
		addBeginFormattedControlFlow("if (rx == 7)");
		addFormattedMicroInsn("dar[sp] -= 2"); // decrement address register
		addFormattedMicroInsn("at = dar[sp]"); // save address to temporary register
		addNextFormattedControlFlow("else");
		addFormattedMicroInsn("rx |= 8");
		addFormattedMicroInsn("dar[rx] -= 1"); // decrement address register
		addFormattedMicroInsn("at = dar[rx]"); // save address to temporary register
		addEndControlFlow();
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(6, 1, 0);
		addState("ead_read8");
		setclr16("sswi", 0, SSWI_EAFH);
		addFormattedMicroInsn("alub = dt"); // save dt to alub
		read8("dt", 0, "at");
		addClearSSW();
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");

		addState("ea_paid16_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_paid16");
		decode_ax();
		addFormattedMicroInsn("dar[rx] -= 2"); // decrement address register
		addFormattedMicroInsn("at = dar[rx]"); // save address to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(6, 1, 0);
		addState("ead_read16");
		setclr16("sswi", 0, SSWI_EAFH);
		addFormattedMicroInsn("alub = dt"); // save dt to alub
		read16("dt", 0, "at");
		addClearSSW();
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");

		addState("ea_paid32_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_paid32");
		decode_ax();
		addFormattedMicroInsn("dar[rx] -= 4"); // decrement address register
		addFormattedMicroInsn("at = dar[rx]"); // save address to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(10, 2, 0);
		addState("ead_read32");
		setclr16("sswi", 0, SSWI_EAFH);
		addFormattedMicroInsn("alub = dt"); // save dt to alub
		read32("dt", 0, "at");
		addClearSSW();
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");
	}

	private void ea_pais_microcode() {
		addState("ea_pais8_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_pais8");
		addFormattedMicroInsn("ry = ir & 0x0007"); // retrieve address register index
		addBeginFormattedControlFlow("if (ry == 7)");
		addFormattedMicroInsn("dar[sp] -= 2"); // decrement address register
		addFormattedMicroInsn("at = dar[sp]"); // save address to temporary register
		addNextFormattedControlFlow("else");
		addFormattedMicroInsn("ry |= 8");
		addFormattedMicroInsn("dar[ry] -= 1"); // decrement address register
		addFormattedMicroInsn("at = dar[ry]"); // save address to temporary register
		addEndControlFlow();
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(6, 1, 0);
		addFormattedMicroInsn("mpc = ea_read8");
		addFormattedMicroInsn("continue");

		addState("ea_pais16_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_pais16");
		decode_ay();
		addFormattedMicroInsn("dar[ry] -= 2"); // decrement address register
		addFormattedMicroInsn("at = dar[ry]"); // save address to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(6, 1, 0);
		addFormattedMicroInsn("mpc = ea_read16");
		addFormattedMicroInsn("continue");

		addState("ea_pais32_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_pais32");
		decode_ay();
		addFormattedMicroInsn("dar[ry] -= 4"); // decrement address register
		addFormattedMicroInsn("at = dar[ry]"); // save address to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(10, 2, 0);
		addFormattedMicroInsn("mpc = ea_read32");
		addFormattedMicroInsn("continue");
	}

	private void ea_dad_microcode() {
		addState("ea_dad8");
		fetchimm16("at", 0);
		addClearSSW();
		decode_ax();
		addFormattedMicroInsn("at = ((short) at) + dar[rx]"); // add address to temporary register
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");

		addState("ea_dad16");
		fetchimm16("at", 0);
		addClearSSW();
		decode_ax();
		addFormattedMicroInsn("at = ((short) at) + dar[rx]"); // add address to temporary register
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");

		addState("ea_dad32");
		fetchimm16("at", 0);
		addClearSSW();
		decode_ax();
		addFormattedMicroInsn("at = ((short) at) + dar[rx]"); // add address to temporary register
		consume(4, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");
	}

	private void ea_das_microcode() {
		addState("ea_das8_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_das8");
		fetchimm16("at", 0);
		addClearSSW();
		decode_ay();
		addFormattedMicroInsn("at = ((short) at) + dar[ry]"); // add address to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(8, 2, 0);
		addFormattedMicroInsn("mpc = ea_read8");
		addFormattedMicroInsn("continue");

		addState("ea_das16_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_das16");
		fetchimm16("at", 0);
		addClearSSW();
		decode_ay();
		addFormattedMicroInsn("at = ((short) at) + dar[ry]"); // add address to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(8, 2, 0);
		addFormattedMicroInsn("mpc = ea_read16");
		addFormattedMicroInsn("continue");

		addState("ea_das32_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_das32");
		fetchimm16("at", 0);
		addClearSSW();
		decode_ay();
		addFormattedMicroInsn("at = ((short) at) + dar[ry]"); // add address to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(12, 3, 0);
		addFormattedMicroInsn("mpc = ea_read32");
		addFormattedMicroInsn("continue");
	}

	private void ea_daix_fetch_helpers() {
		addState("ea_daix_imm32");
		anyread32(null, SSW_DF | SSW_P | SSW_AL, "pc + scan", 0, true, () -> {
			addFormattedMicroInsn("scan += 4");
		});
		addFormattedMicroInsn("break");
		addState("ea_dais_imm16");
		anyread16(null, SSW_DF | SSW_P | SSW_AL, "pc + scan", 0, true, () -> {
			addFormattedMicroInsn("scan += 2");
		});
		addFormattedMicroInsn("break");
	}

	private void ea_daix_calculate_microcode(String ea, String size, boolean pc, Runnable addEARegister) {
		addFormattedMicroInsn("au = pc + scan"); // save extension word address
		anyread16(null, SSW_IF | SSW_P, "au", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		}); // fetch extension word to irb
		addClearSSW();
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) != 0)", SSWI_DNGR);
		addFormattedMicroInsn("irb &= 0xf8ff");
		addEndControlFlow();
		addBeginFormattedControlFlow("if ((irb & 0x%04x) == 0)", EAX_EXT);
		addFormattedMicroInsn("at = (byte) irb"); // sign extend irb and set at

		if (pc) {
			addFormattedMicroInsn("at += au"); // add pc to at
		} else {
			addEARegister.run();
		}

		// retrieve register index (use ry) as scratch register
		addFormattedMicroInsn("rz = (irb >> 12) & 0x000f");
		addBeginFormattedControlFlow("if (rz == 0x000f)");
		addFormattedMicroInsn("rz = sp"); // if using a7, change to right stack pointer
		addEndControlFlow();
		// retrieve index register in au
		addFormattedMicroInsn("au = (irb & 0x%04x) == 0 ? (short) dar[rz] : dar[rz]", EAX_SIZ);
		// apply scale and add to at
		addFormattedMicroInsn("at += au << ((irb & 0x%04x) >> 9)", EAX_SCL);

		addFormattedMicroInsn("mpc = ea_%s%s_fetch", ea, size);
		addFormattedMicroInsn("continue");
		addEndControlFlow();

		addFormattedMicroInsn("at = 0"); // resets at

		// add base register to at
		addBeginFormattedControlFlow("if ((irb & 0x%04x) == 0)", EAX_BS);
		if (pc) {
			addFormattedMicroInsn("at += au"); // add pc to at
		} else {
			addEARegister.run();
		}
		addEndControlFlow();

		// compute index value (in au)
		addBeginFormattedControlFlow("if ((irb & 0x%04x) == 0)", EAX_IS);
		// retrieve register index (use ry) as scratch register
		addFormattedMicroInsn("rz = (irb >> 12) & 0x000f");
		addBeginFormattedControlFlow("if (rz == 0x000f)");
		addFormattedMicroInsn("rz = sp"); // if using a7, change to right stack pointer
		addEndControlFlow();
		// retrieve index register in au
		addFormattedMicroInsn("au = (irb & 0x%04x) == 0 ? (short) dar[rz] : dar[rz]", EAX_SIZ);
		// apply scale
		addFormattedMicroInsn("au <<= ((irb & 0x%04x) >> 9)", EAX_SCL);
		addEndControlFlow();

		// retrieve base displacement if any
		addFormattedMicroInsn("nmpc = ea_%s%s_base", ea, size);

		addBeginFormattedControlFlow("if ((irb & 0x%04x) == 0)", 1 << 5);
		addFormattedMicroInsn("mpc = ea_%s%s_no_base", ea, size);
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((irb & 0x%04x) == 0)", 1 << 4);
		addFormattedMicroInsn("mpc = ea_dais_imm16", ea, size);
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addFormattedMicroInsn("mpc = ea_daix_imm32", ea, size);
		addFormattedMicroInsn("continue");

		addState(String.format("ea_%s%s_base", ea, size));
		addFormattedMicroInsn("at += dib");
		addClearSSW();
		addState(String.format("ea_%s%s_no_base", ea, size));

		// memory indirect preindex, add au to at and reset au
		addBeginFormattedControlFlow("if ((irb & 0x%04x) == 0)", 1 << 2);
		addFormattedMicroInsn("at += au");
		addFormattedMicroInsn("au = 0");

		// no memory indirect operation, process to ea fetch
		addBeginFormattedControlFlow("if ((irb & 0x%04x) == 0)", EAX_ISL);
		addFormattedMicroInsn("mpc = ea_%s%s_fetch", ea, size);
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addEndControlFlow();

		// fetch memory operand and replace at
		read32("at", 0, "at");

		// add post index (if still here)
		addFormattedMicroInsn("at += au");
		addClearSSW();

		// retrieve outer displacement if any
		addFormattedMicroInsn("nmpc = ea_%s%s_outer", ea, size);

		addBeginFormattedControlFlow("if ((irb & 0x%04x) == 0)", 1 << 1);
		addFormattedMicroInsn("mpc = ea_%s%s_fetch", ea, size);
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((irb & 0x%04x) == 0)", 1 << 0);
		addFormattedMicroInsn("mpc = ea_dais_imm16", ea, size);
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addFormattedMicroInsn("mpc = ea_daix_imm32", ea, size);
		addFormattedMicroInsn("continue");

		addState(String.format("ea_%s%s_outer", ea, size));
		addFormattedMicroInsn("at += dib");
		addClearSSW();
	}

	private void ea_dais_calculate_microcode(String ea, String size, boolean pc, boolean forcefetch) {
		addState(String.format("ea_%s%s_read", ea, size));

		if (!forcefetch) {
			setclr16("sswi", SSWI_EAFH, 0);
			addState(String.format("ea_%s%s", ea, size));
		}

		ea_daix_calculate_microcode(ea, size, pc, () -> {
			decode_ay();
			addFormattedMicroInsn("at += dar[ry]"); // retrieve register value and add to at
		});
	}

	private void ea_daid_calculate_microcode(String ea, String size, boolean pc, boolean forcefetch) {
		addState(String.format("ea_%s%s", ea, size));
		ea_daix_calculate_microcode(ea, size, pc, () -> {
			decode_ax();
			addFormattedMicroInsn("at += dar[rx]"); // retrieve register value and add to at
		});
	}

	private void ea_daid_microcode() {
		ea_daid_calculate_microcode("daid", "8", false, false);
		addState("ea_daid8_fetch");
		consume(8, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");

		ea_daid_calculate_microcode("daid", "16", false, false);
		addState("ea_daid16_fetch");
		consume(8, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");

		ea_daid_calculate_microcode("daid", "32", false, false);
		addState("ea_daid32_fetch");
		consume(8, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");
	}

	private void ea_dais_microcode() {
		ea_daix_fetch_helpers();

		ea_dais_calculate_microcode("dais", "8", false, false);
		addState("ea_dais8_fetch");
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(8, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(10, 2, 0);
		addFormattedMicroInsn("mpc = ea_read8");
		addFormattedMicroInsn("continue");

		ea_dais_calculate_microcode("dais", "16", false, false);
		addState("ea_dais16_fetch");
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(8, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(10, 2, 0);
		addFormattedMicroInsn("mpc = ea_read16");
		addFormattedMicroInsn("continue");

		ea_dais_calculate_microcode("dais", "32", false, false);
		addState("ea_dais32_fetch");
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(8, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(14, 3, 0);
		addFormattedMicroInsn("mpc = ea_read32");
		addFormattedMicroInsn("continue");
	}

	private void ea_dpc_microcode() {
		addState("ea_dpc8_read");
		consume(8, 2, 0);
		addFormattedMicroInsn("au = pc + scan"); // save extension word address
		anyread16("at", SSW_DF | SSW_P, "au", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		});
		addFormattedMicroInsn("at = ((short) at) + au"); // add pc to temporary register
		addState("eapc_read8");
		setclr16("sswi", 0, SSWI_EAFH);
		addFormattedMicroInsn("alub = dt"); // save dt to alub
		anyread8(null, SSW_DF | SSW_P, "at", 0);
		addFormattedMicroInsn("dt = (byte) dib");
		addClearSSW();
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");

		addState("ea_dpc16_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_dpc16");
		addFormattedMicroInsn("au = pc + scan"); // save extension word address
		anyread16("at", SSW_DF | SSW_P, "au", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		});
		addFormattedMicroInsn("at = ((short) at) + au"); // add pc to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 0, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(8, 2, 0);
		addState("eapc_read16");
		setclr16("sswi", 0, SSWI_EAFH);
		addFormattedMicroInsn("alub = dt"); // save dt to alub
		anyread16(null, SSW_DF | SSW_P, "at", 0, false, null);
		addFormattedMicroInsn("dt = (short) dib");
		addClearSSW();
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");

		addState("ea_dpc32_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_dpc32");
		addFormattedMicroInsn("au = pc + scan"); // save extension word address
		anyread16("at", SSW_DF | SSW_P, "au", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		});
		addFormattedMicroInsn("at = ((short) at) + au"); // add pc to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(4, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(12, 3, 0);
		addState("eapc_read32");
		setclr16("sswi", 0, SSWI_EAFH);
		addFormattedMicroInsn("alub = dt"); // save dt to alub
		anyread32("dt", SSW_DF | SSW_P, "at", 0, false, null);
		addClearSSW();
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
	}

	private void ea_dpci_microcode() {
		ea_dais_calculate_microcode("dpci", "8", true, true);
		addState("ea_dpci8_fetch");
		consume(10, 2, 0);
		addFormattedMicroInsn("mpc = eapc_read8");
		addFormattedMicroInsn("continue");

		ea_dais_calculate_microcode("dpci", "16", true, false);
		addState("ea_dpci16_fetch");
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(8, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(10, 2, 0);
		addFormattedMicroInsn("mpc = eapc_read16");
		addFormattedMicroInsn("continue");

		ea_dais_calculate_microcode("dpci", "32", true, false);
		addState("ea_dpci32_fetch");
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		consume(8, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(14, 3, 0);
		addFormattedMicroInsn("mpc = eapc_read32");
		addFormattedMicroInsn("continue");
	}

	private void ea_adr16d_microcode() {
		addState("ea_adr16d8");
		fetchimm16(null, 0);
		addClearSSW();
		addFormattedMicroInsn("at = (short) dib"); // add address to temporary register
		consume(4, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");

		addState("ea_adr16d16");
		fetchimm16(null, 0);
		addClearSSW();
		addFormattedMicroInsn("at = (short) dib"); // add address to temporary register
		consume(4, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");

		addState("ea_adr16d32");
		fetchimm16(null, 0);
		addClearSSW();
		addFormattedMicroInsn("at = (short) dib"); // add address to temporary register
		consume(4, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");
	}

	private void ea_adr16s_microcode() {
		addState("ea_adr16s8_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_adr16s8");
		fetchimm16(null, 0);
		addFormattedMicroInsn("at = (short) dib"); // add address to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		addClearSSW();
		consume(4, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(8, 2, 0);
		addFormattedMicroInsn("mpc = ea_read8");
		addFormattedMicroInsn("continue");

		addState("ea_adr16s16_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_adr16s16");
		fetchimm16(null, 0);
		addFormattedMicroInsn("at = (short) dib"); // add address to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		addClearSSW();
		consume(4, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(8, 2, 0);
		addFormattedMicroInsn("mpc = ea_read16");
		addFormattedMicroInsn("continue");

		addState("ea_adr16s32_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_adr16s32");
		fetchimm16(null, 0);
		addFormattedMicroInsn("at = (short) dib"); // add address to temporary register
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		addClearSSW();
		consume(4, 1, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(12, 3, 0);
		addFormattedMicroInsn("mpc = ea_read32");
		addFormattedMicroInsn("continue");
	}

	private void ea_adr32d_microcode() {
		addState("ea_adr32d8");
		addState("ea_adr32d16");
		addState("ea_adr32d32");
		fetchimm32("at", 0);
		addClearSSW();
		consume(8, 2, 0);
		addFormattedMicroInsn("nmpc = decoded.a2");
		addFormattedMicroInsn("break");
	}

	private void ea_adr32s_microcode() {
		addState("ea_adr32s8_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_adr32s8");
		fetchimm32("at", 0);
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		addClearSSW();
		consume(8, 2, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(12, 3, 0);
		addFormattedMicroInsn("mpc = ea_read8");
		addFormattedMicroInsn("continue");

		addState("ea_adr32s16_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_adr32s16");
		fetchimm32("at", 0);
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		addClearSSW();
		consume(8, 2, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(12, 3, 0);
		addFormattedMicroInsn("mpc = ea_read16");
		addFormattedMicroInsn("continue");

		addState("ea_adr32s32_read");
		setclr16("sswi", SSWI_EAFH, 0);
		addState("ea_adr32s32");
		fetchimm32("at", 0);
		addBeginFormattedControlFlow("if ((sswi & 0x%04x) == 0)", SSWI_EAFH);
		addClearSSW();
		consume(8, 2, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
		addEndControlFlow();
		consume(16, 4, 0);
		addFormattedMicroInsn("mpc = ea_read32");
		addFormattedMicroInsn("continue");
	}

	private void ea_imm_microcode() {
		addState("ea_imm8_read");
		fetchimm16(null, 0);
		addClearSSW();
		consume(4, 1, 0);
		addFormattedMicroInsn("dt = (byte) dib");
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");

		addState("ea_imm16_read");
		fetchimm16(null, 0);
		addClearSSW();
		consume(4, 1, 0);
		addFormattedMicroInsn("dt = (short) dib");
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");

		addState("ea_imm32_read");
		fetchimm32("dt", 0);
		addClearSSW();
		consume(8, 2, 0);
		addFormattedMicroInsn("nmpc = decoded.a3");
		addFormattedMicroInsn("break");
	}

	private void misc_microcode() {
		addState("op_clrb_ds");
		decode_dy();
		addFormattedMicroInsn("dar[ry] &= ~0xff");
		microprefetch(true, null); // prefetch ir and resume execution

		addState("op_clrw_ds");
		decode_dy();
		addFormattedMicroInsn("dar[ry] &= ~0xffff");
		microprefetch(true, null); // prefetch ir and resume execution

		addState("op_clrl_ds");
		decode_dy();
		addFormattedMicroInsn("dar[ry] = 0");
		microprefetch(true, null); // prefetch ir and resume execution

		addState("op_clrb_ea");
		addFormattedMicroInsn("dt = 0");
		microprefetch(true, "8"); // prefetch ir and resume execution

		addState("op_clrw_ea");
		addFormattedMicroInsn("dt = 0");
		microprefetch(true, "16"); // prefetch ir and resume execution

		addState("op_clrl_ea");
		addFormattedMicroInsn("dt = 0");
		microprefetch(true, "32"); // prefetch ir and resume execution

		addState("op_lea_ea_ad");
		decode_ax();
		addFormattedMicroInsn("dar[rx] = at");
		microprefetch(true, null); // prefetch ir and resume execution

		addState("op_pea");
		addFormattedMicroInsn("au = dar[sp] - 4");
		addFormattedMicroInsn("dar[sp] = au");
		write32(SSW_D, "au", "at", 0, false);
		microprefetch(true, null); // prefetch ir and resume execution

		addState("op_swap_ds");
		decode_dy();
		addFormattedMicroInsn("dar[ry] = (dar[ry] << 16) | (dar[ry] >>> 16)");
		addFormattedMicroInsn("long_tst(dar[ry])");
		microprefetch(true, null); // prefetch ir and resume execution

		addState("op_extw_ds");
		decode_dy();
		addFormattedMicroInsn("dar[ry] = (dar[ry] & ~0xffff) | (((byte) dar[ry]) & 0xffff)");
		addFormattedMicroInsn("word_tst(dar[ry])");
		microprefetch(true, null); // prefetch ir and resume execution

		addState("op_extl_ds");
		decode_dy();
		addFormattedMicroInsn("dar[ry] = (short) dar[ry]");
		addFormattedMicroInsn("long_tst(dar[ry])");
		microprefetch(true, null); // prefetch ir and resume execution

		addState("op_extbl_ds");
		decode_dy();
		addFormattedMicroInsn("dar[ry] = (byte) dar[ry]");
		addFormattedMicroInsn("long_tst(dar[ry])");
		microprefetch(true, null); // prefetch ir and resume execution

		addState("op_exg_dd_ds");
		decode_dy();
		decode_dx();
		addFormattedMicroInsn("dt = dar[ry]");
		addFormattedMicroInsn("dar[ry] = dar[rx]");
		addFormattedMicroInsn("dar[rx] = dt");
		microprefetch(true, null); // prefetch ir and resume execution

		addState("op_exg_ad_as");
		decode_ay();
		decode_ax();
		addFormattedMicroInsn("dt = dar[ry]");
		addFormattedMicroInsn("dar[ry] = dar[rx]");
		addFormattedMicroInsn("dar[rx] = dt");
		microprefetch(true, null); // prefetch ir and resume execution

		addState("op_exg_dd_as");
		decode_ay();
		decode_dx();
		addFormattedMicroInsn("dt = dar[ry]");
		addFormattedMicroInsn("dar[ry] = dar[rx]");
		addFormattedMicroInsn("dar[rx] = dt");
		microprefetch(true, null); // prefetch ir and resume execution

		addState("op_scc_b_ds");
		decode_dy();
		addBeginFormattedControlFlow("if (testCC((ir & 0x0f00) >> 8))");
		addFormattedMicroInsn("dar[ry] |= 0xff");
		addNextFormattedControlFlow("else");
		addFormattedMicroInsn("dar[ry] &= ~0xff");
		addEndControlFlow();
		microprefetch(true, null); // prefetch ir from pc and resume execution

		addState("op_scc_b_ea");
		addFormattedMicroInsn("dt = testCC((ir & 0x0f00) >> 8) ? -1 : 0");
		microprefetch(true, "8"); // prefetch ir from pc and resume execution

		addState("op_chk_w_ds");
		decode_dy();
		decode_dx();
		addBeginFormattedControlFlow("if (word_chk(dar[ry], dar[rx]))");
		settvn(6);
		addFormattedMicroInsn("mpc = (sswi & 0x%04x) != 0 ? trap2000 : trap0000", SSWI_FMT2);
		addEndControlFlow();
		microprefetch(true, null); // prefetch ir from pc and resume execution

		addState("op_chk_w_ea");
		addFormattedMicroInsn("rx = (ir >> 9) & 0x0007"); // retrieve data register index
		addBeginFormattedControlFlow("if (word_chk(dt, dar[rx]))");
		settvn(6);
		addFormattedMicroInsn("mpc = (sswi & 0x%04x) != 0 ? trap2000 : trap0000", SSWI_FMT2);
		addEndControlFlow();
		microprefetch(true, null); // prefetch ir from pc and resume execution
		
		addState("op_link_as_imm16");
		addFormattedMicroInsn("au = dar[sp] - %d", 4); // allocate 2 words on stack
		decode_ay();
		write32(SSW_D, "au", "dar[ry]", 0, false);
		addFormattedMicroInsn("dar[ry] = au"); // update frame pointer
		addFormattedMicroInsn("dar[sp] = au + ((short) dt)"); // update stack pointer
		microprefetch(true, null); // prefetch ir from pc and resume execution
		
		addState("op_link_as_imm32");
		addFormattedMicroInsn("au = dar[sp] - %d", 4); // allocate 2 words on stack
		decode_ay();
		write32(SSW_D, "au", "dar[ry]", 0, false);
		addFormattedMicroInsn("dar[ry] = au"); // update frame pointer
		addFormattedMicroInsn("dar[sp] = au + dt"); // update stack pointer
		microprefetch(true, null); // prefetch ir from pc and resume execution
		
		addState("op_unlk_as");
		decode_ay();
		addFormattedMicroInsn("au = dar[ry]"); // retrieve linked stack pointer
		read32(null, 0, "au"); // restore address register value
		addFormattedMicroInsn("dar[sp] = au + %d", 4); // restore stack pointer
		addFormattedMicroInsn("dar[ry] = dib"); // restore link register
		microprefetch(true, null); // prefetch ir from pc and resume execution

		addState("op_move_usp_as");
		check_privileged();
		decode_ay();
		addFormattedMicroInsn("dar[ry] = dar[15]");
		microprefetch(true, null); // prefetch ir from pc and resume execution

		addState("op_move_as_usp");
		check_privileged();
		decode_ay();
		addFormattedMicroInsn("dar[15] = dar[ry]");
		microprefetch(true, null); // prefetch ir from pc and resume execution
		
		addState("op_movec_cr_rz");
		check_privileged();
		anyread16(null, SSW_IF | SSW_P, "pc + scan", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		}); // fetch extension word to irb
		addClearSSW();
		addFormattedMicroInsn("rz = (irb >> 12) & 0x000f");
		addBeginFormattedControlFlow("if (rz == 0x000f)");
		addFormattedMicroInsn("rz = sp"); // if using a7, change to right stack pointer
		addEndControlFlow();
		addFormattedMicroInsn("dt = irb & 0x0fff");
		addBeginFormattedControlFlow("if (dt == 0x000)");
		addFormattedMicroInsn("dar[rz] = sfc");
		addNextFormattedControlFlow("else if (dt == 0x001)");
		addFormattedMicroInsn("dar[rz] = dfc");
		addNextFormattedControlFlow("else if (dt == 0x800)");
		addFormattedMicroInsn("dar[rz] = dar[15]");
		addNextFormattedControlFlow("else if (dt == 0x801)");
		addFormattedMicroInsn("dar[rz] = vbr");
		addNextFormattedControlFlow("else if (dt == 0x803)");
		addFormattedMicroInsn("dar[rz] = dar[17]");
		addNextFormattedControlFlow("else if (dt == 0x804)");
		addFormattedMicroInsn("dar[rz] = dar[16]");
		addNextFormattedControlFlow("else");
		settvn(4); // set illegal exception trap
		addFormattedMicroInsn("mpc = trapill"); // set micro pc
		addFormattedMicroInsn("continue"); // branch to trap subroutine
		addEndControlFlow();
		microprefetch(true, null); // prefetch ir from pc and resume execution
		
		addState("op_movec_rz_cr");
		check_privileged();
		anyread16(null, SSW_IF | SSW_P, "pc + scan", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		}); // fetch extension word to irb
		addClearSSW();
		addFormattedMicroInsn("rz = (irb >> 12) & 0x000f");
		addBeginFormattedControlFlow("if (rz == 0x000f)");
		addFormattedMicroInsn("rz = sp"); // if using a7, change to right stack pointer
		addEndControlFlow();
		addFormattedMicroInsn("dt = irb & 0x0fff");
		addBeginFormattedControlFlow("if (dt == 0x000)");
		addFormattedMicroInsn("sfc = dar[rz] & 0x7");
		addNextFormattedControlFlow("else if (dt == 0x001)");
		addFormattedMicroInsn("dfc = dar[rz] & 0x7");
		addNextFormattedControlFlow("else if (dt == 0x800)");
		addFormattedMicroInsn("dar[15] = dar[rz]");
		addNextFormattedControlFlow("else if (dt == 0x801)");
		addFormattedMicroInsn("vbr = dar[rz]");
		addNextFormattedControlFlow("else if (dt == 0x803)");
		addFormattedMicroInsn("dar[17] = dar[rz]");
		addNextFormattedControlFlow("else if (dt == 0x804)");
		addFormattedMicroInsn("dar[16] = dar[rz]");
		addNextFormattedControlFlow("else");
		settvn(4); // set illegal exception trap
		addFormattedMicroInsn("mpc = trapill"); // set micro pc
		addFormattedMicroInsn("continue"); // branch to trap subroutine
		addEndControlFlow();
		microprefetch(true, null); // prefetch ir from pc and resume execution
	}
	
	private void op_movem_microcode() {		
		addState("op_movemw_rr_ea");
		// read extension word
		anyread16("dt", SSW_DF | SSW_P, "pc + scan", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		});
		prefetchir(0, false);
		addFormattedMicroInsn("alub = 0");
		int loop = blocks.size();

		addFormattedMicroInsn("nmpc = %d", loop);
		blocks.add(new MicroBlock());
		addBeginFormattedControlFlow("if (alub >= 16)");
		addClearSSW();
		addFormattedMicroInsn("mpc = resume");
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((dt & (1 << alub)) == 0)");
		addFormattedMicroInsn("alub += 1", loop);
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addFormattedMicroInsn("rz = alub == 15 ? sp : alub");
		write16(SSW_D, "at", "dar[rz]" , 0, true, () -> {
			addFormattedMicroInsn("alub += 1");
			addFormattedMicroInsn("at += 2");
		});
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");

		addState("op_movemw_rr_pais");
		// read extension word
		anyread16("dt", SSW_DF | SSW_P, "pc + scan", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		});
		prefetchir(0, false);
		addFormattedMicroInsn("alub = 0");
		loop = blocks.size();

		addFormattedMicroInsn("nmpc = %d", loop);
		blocks.add(new MicroBlock());
		addBeginFormattedControlFlow("if (alub >= 16)");
		addClearSSW();
		decode_ay();
		addFormattedMicroInsn("dar[ry] = at");
		addFormattedMicroInsn("mpc = resume");
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((dt & (1 << alub)) == 0)");
		addFormattedMicroInsn("alub += 1", loop);
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addFormattedMicroInsn("rz = alub == 0 ? sp : 15 - alub");
		addFormattedMicroInsn("at -= 2");
		write16(SSW_D, "at", "dar[rz]" , 0, true, () -> {
			addFormattedMicroInsn("alub += 1");
		});
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");

		
		addState("op_moveml_rr_ea");
		// read extension word
		anyread16("dt", SSW_DF | SSW_P, "pc + scan", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		});
		prefetchir(0, false);
		addFormattedMicroInsn("alub = 0");
		loop = blocks.size();

		addFormattedMicroInsn("nmpc = %d", loop);
		blocks.add(new MicroBlock());
		addBeginFormattedControlFlow("if (alub >= 16)");
		addClearSSW();
		addFormattedMicroInsn("mpc = resume");
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((dt & (1 << alub)) == 0)");
		addFormattedMicroInsn("alub += 1", loop);
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addFormattedMicroInsn("rz = alub == 15 ? sp : alub");
		write32(SSW_D, "at", "dar[rz]" , 0, true, () -> {
			addFormattedMicroInsn("alub += 1");
			addFormattedMicroInsn("at += 4");
		});
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");

		addState("op_moveml_rr_pais");
		// read extension word
		anyread16("dt", SSW_DF | SSW_P, "pc + scan", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		});
		prefetchir(0, false);
		addFormattedMicroInsn("alub = 0");
		loop = blocks.size();

		addFormattedMicroInsn("nmpc = %d", loop);
		blocks.add(new MicroBlock());
		addBeginFormattedControlFlow("if (alub >= 16)");
		addClearSSW();
		decode_ay();
		addFormattedMicroInsn("dar[ry] = at");
		addFormattedMicroInsn("mpc = resume");
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((dt & (1 << alub)) == 0)");
		addFormattedMicroInsn("alub += 1", loop);
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addFormattedMicroInsn("rz = alub == 0 ? sp : 15 - alub");
		addFormattedMicroInsn("at -= 4");
		write32(SSW_D, "at", "dar[rz]" , 0, true, () -> {
			addFormattedMicroInsn("alub += 1");
		});
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");

		// XXX should have another entry for dpc/dpci to have ssw program function code
		addState("op_movemw_ea_rr");
		// read extension word
		anyread16("dt", SSW_DF | SSW_P, "pc + scan", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		});
		prefetchir(0, false);
		addFormattedMicroInsn("alub = 0");
		loop = blocks.size();

		addFormattedMicroInsn("nmpc = %d", loop);
		blocks.add(new MicroBlock());
		addBeginFormattedControlFlow("if (alub >= 16)");
		addClearSSW();
		addFormattedMicroInsn("mpc = resume");
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((dt & (1 << alub)) == 0)");
		addFormattedMicroInsn("alub += 1", loop);
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addFormattedMicroInsn("rz = alub == 15 ? sp : alub");
		anyread16(null, SSW_DF, "at", 0, false, () -> {
			addFormattedMicroInsn("alub += 1");
			addFormattedMicroInsn("at += 2");
		});
		addFormattedMicroInsn("dar[rz] = (short) dib");
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");

		addState("op_movemw_aips_rr");
		// read extension word
		anyread16("dt", SSW_DF | SSW_P, "pc + scan", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		});
		prefetchir(0, false);
		addFormattedMicroInsn("alub = 0");
		loop = blocks.size();

		addFormattedMicroInsn("nmpc = %d", loop);
		blocks.add(new MicroBlock());
		addBeginFormattedControlFlow("if (alub >= 16)");
		addClearSSW();
		decode_ay();
		addFormattedMicroInsn("dar[ry] = at");
		addFormattedMicroInsn("mpc = resume");
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((dt & (1 << alub)) == 0)");
		addFormattedMicroInsn("alub += 1", loop);
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addFormattedMicroInsn("rz = alub == 15 ? sp : alub");
		anyread16(null, SSW_DF, "at", 0, false, () -> {
			addFormattedMicroInsn("alub += 1");
			addFormattedMicroInsn("at += 2");
		});
		addFormattedMicroInsn("dar[rz] = (short) dib");
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");

		// XXX should have another entry for dpc/dpci to have ssw program function code
		addState("op_moveml_ea_rr");
		// read extension word
		anyread16("dt", SSW_DF | SSW_P, "pc + scan", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		});
		prefetchir(0, false);
		addFormattedMicroInsn("alub = 0");
		loop = blocks.size();

		addFormattedMicroInsn("nmpc = %d", loop);
		blocks.add(new MicroBlock());
		addBeginFormattedControlFlow("if (alub >= 16)");
		addClearSSW();
		addFormattedMicroInsn("mpc = resume");
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((dt & (1 << alub)) == 0)");
		addFormattedMicroInsn("alub += 1", loop);
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addFormattedMicroInsn("rz = alub == 15 ? sp : alub");
		anyread32(null, SSW_DF, "at", 0, false, () -> {
			addFormattedMicroInsn("alub += 1");
			addFormattedMicroInsn("at += 4");
		});
		addFormattedMicroInsn("dar[rz] = dib");
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");

		addState("op_moveml_aips_rr");
		// read extension word
		anyread16("dt", SSW_DF | SSW_P, "pc + scan", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		});
		prefetchir(0, false);
		addFormattedMicroInsn("alub = 0");
		loop = blocks.size();

		addFormattedMicroInsn("nmpc = %d", loop);
		blocks.add(new MicroBlock());
		addBeginFormattedControlFlow("if (alub >= 16)");
		addClearSSW();
		decode_ay();
		addFormattedMicroInsn("dar[ry] = at");
		addFormattedMicroInsn("mpc = resume");
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((dt & (1 << alub)) == 0)");
		addFormattedMicroInsn("alub += 1", loop);
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addFormattedMicroInsn("rz = alub == 15 ? sp : alub");
		anyread32(null, SSW_DF, "at", 0, false, () -> {
			addFormattedMicroInsn("alub += 1");
			addFormattedMicroInsn("at += 4");
		});
		addFormattedMicroInsn("dar[rz] = dib");
		addFormattedMicroInsn("mpc = %d", loop);
		addFormattedMicroInsn("continue");
	}
	
	private void decode_dy() {
		addFormattedMicroInsn("ry = ir & 0x0007"); // retrieve data register index
	}

	private void decode_ay() {
		addFormattedMicroInsn("ry = ir & 0x0007"); // retrieve address register index
		addFormattedMicroInsn("ry = ry == 7 ? sp : ry | 8"); // convert to dar offset
	}
	
	private void decode_dx() {
		addFormattedMicroInsn("rx = (ir >> 9) & 0x0007"); // retrieve data register index
	}

	private void decode_ax() {
		addFormattedMicroInsn("rx = (ir >> 9) & 0x0007"); // retrieve data register index
		addFormattedMicroInsn("rx = rx == 7 ? sp : rx | 8"); // convert to dar offset
	}

	private void check_privileged() {
		// check for supervisor mode
		addBeginFormattedControlFlow("if ((sr & 0x%04x) == 0)", SR_S);
		consume(38, 4, 4);
		settvn(8); // set privilege exception trap
		addFormattedMicroInsn("mpc = trapill"); // set micro pc
		addFormattedMicroInsn("continue"); // branch to trap subroutine
		addEndControlFlow();
	}

	public String gen_move(String size, String src, String dst) {
		String msrc = src;

		if ("imm8o".equals(src)) {
			msrc = "im";
		} else if ("imm8".equals(src) || "imm16".equals(src) || "imm32".equals(src)) {
			msrc = "dt";
		} else if ("ea".equals(src)) {
			msrc = "dt";
		}

		String mnemonic = String.format("gen_move%s_%s_%s", size, msrc, dst);

		for(MicroBlock block : blocks) {
			if (mnemonic.equals(block.name)) {
				return mnemonic;
			}

		}

		addState(mnemonic);

		boolean hasrx = false;
		boolean hasry = false;
		boolean ccr = true;

		if ("sr".equals(src) || "sr".equals(dst)) {
			check_privileged();
		}

		String rsrc = null;

		if ("imm8o".equals(src)) {
			addFormattedMicroInsn("dt = (byte) ir");

			rsrc = "dt";
		} else if ("imm8".equals(src) || "imm16".equals(src) || "imm32".equals(src)) {
			rsrc = "dt";
		} else if ("dd".equals(src)) {
			if (!hasrx) {
				decode_dx();
				hasrx = true;
			}
			rsrc = "dar[rx]";
		} else if ("ds".equals(src)) {
			if (!hasry) {
				decode_dy();
				hasry = true;
			}

			rsrc = "dar[ry]";
		} else if ("ad".equals(src)) {
			if (!hasrx) {
				decode_ax();
				hasrx = true;
			}
			rsrc = "dar[rx]";
		} else if ("as".equals(src)) {
			if (!hasry) {
				decode_ay();
				hasry = true;
			}
			rsrc = "dar[ry]";
		} else if ("ea".equals(src)) {
			rsrc = "dt";
		} else if ("sr".equals(src) || "ccr".equals(src)) {
			rsrc = "sr";
			ccr &= false;
		} else {
			throw new IllegalStateException();
		}

		String rdst = null;

		if ("dd".equals(dst)) {
			if (!hasrx) {
				decode_dx();
				hasrx = true;
			}
			rdst = "dar[rx]";
		} else if ("ds".equals(dst)) {
			if (!hasry) {
				decode_dy();
				hasry = true;
			}

			rdst = "dar[ry]";
		} else if ("ad".equals(dst)) {
			if (!hasrx) {
				decode_ax();
				hasrx = true;
			}
			rdst = "dar[rx]";
			ccr &= false;
		} else if ("as".equals(dst)) {
			if (!hasry) {
				decode_ay();
				hasry = true;
			}
			rdst = "dar[ry]";
			ccr &= false;
		} else if ("ea".equals(dst)) {
			rdst = "dt";
		} else if ("sr".equals(dst) || "ccr".equals(dst)) {
			rdst = "sr";
			ccr &= false;
		} else {
			throw new IllegalStateException();
		}

		if (ccr) {
			if ("b".equals(size)) {
				addFormattedMicroInsn("byte_tst(%s)", rsrc); // perform byte operation
			} else if ("w".equals(size)) {
				addFormattedMicroInsn("word_tst(%s)", rsrc); // perform word operation
			} else if ("l".equals(size)) {
				addFormattedMicroInsn("long_tst(%s)", rsrc); // perform long word operation
			} else {
				throw new IllegalStateException();
			}
		}

		if ("ea".equals(dst)) {
			if ("sr".equals(src)) {
				addFormattedMicroInsn("dt = (%s & 0x%04x)", rsrc, SR_T1 | SR_T0 | SR_S | SR_M | SR_I.value | FL_C | FL_V | FL_Z | FL_N | FL_X);
			} else if ("ccr".equals(src)) {
				addFormattedMicroInsn("dt = (%s & 0x%04x)", rsrc, FL_C | FL_V | FL_Z | FL_N | FL_X);
			} else if (!"dt".equals(rsrc)) {
				addFormattedMicroInsn("dt = %s", rsrc); // copy source to data temporary
			}

			if ("b".equals(size)) {
				microprefetch(true, "8");
			} else if ("w".equals(size)) {
				microprefetch(true, "16");
			} else if ("l".equals(size)) {
				microprefetch(true, "32");
			} else {
				throw new IllegalStateException();
			}
		} else {
			if ("as".equals(dst) || "ad".equals(dst)) {
				if ("w".equals(size)) {
					addFormattedMicroInsn("%s = (short) %s", rdst, rsrc);
				} else if ("l".equals(size)) {
					addFormattedMicroInsn("%s = %s", rdst, rsrc);
				} else {
					throw new IllegalStateException();
				}

				microprefetch(true, null);
			} else {
				if ("sr".equals(dst)) {
					addFormattedMicroInsn("%s = %s & 0x%04x", rdst, rsrc, SR_T1 | SR_T0 | SR_S | SR_M | SR_I.value | FL_C | FL_V | FL_Z | FL_N | FL_X);
				} else if ("ccr".equals(dst)) {
					addFormattedMicroInsn("%s = (%s & ~0xff) | (%s & 0x%04x)", rdst, rdst, rsrc, FL_C | FL_V | FL_Z | FL_N | FL_X);
				} else if ("ccr".equals(src)) {
					addFormattedMicroInsn("%s = (%s & ~0xffff) | (%s & 0x%04x)", rdst, rdst, rsrc, FL_C | FL_V | FL_Z | FL_N | FL_X);
				} else if ("b".equals(size)) {
					addFormattedMicroInsn("%s = (%s & ~0xff) | (%s & 0xff)", rdst, rdst, rsrc);
				} else if ("w".equals(size)) {
					addFormattedMicroInsn("%s = (%s & ~0xffff) | (%s & 0xffff)", rdst, rdst, rsrc);
				} else if ("l".equals(size)) {
					addFormattedMicroInsn("%s = %s", rdst, rsrc);
				} else {
					throw new IllegalStateException();
				}

				microprefetch(true, null);
			}
		}

		return mnemonic;
	}

	public String gen_dyadic(String name, String size, String src, String dst) {
		boolean update = true;
		String msrc = src;
		String op = name;

		if ("btst".equals(name)) {
			update = false;
		} else if ("cmp".equals(name) || "cmpm".equals(name)) {
			update = false;
			op = "cmp";
		}

		if ("imm3".equals(src)) {
			msrc = "ir";
		} else if ("imm8".equals(src) || "imm16".equals(src) || "imm32".equals(src)) {
			if ("ea".equals(dst)) {
				msrc = "im";
			} else {
				msrc = "dt";
			}
		} else if ("ea".equals(src)) {
			if ("ea".equals(dst)) {
				msrc = "im";
			} else {
				msrc = "dt";
			}
		}

		String mnemonic = String.format("gen_%s%s_%s_%s", name, size, msrc, dst);

		for(MicroBlock block : blocks) {
			if (mnemonic.equals(block.name)) {
				return mnemonic;
			}
		}

		addState(mnemonic);

		boolean hasrx = false;
		boolean hasry = false;

		if ("sr".equals(src) || "sr".equals(dst)) {
			check_privileged();
		}

		String rsrc = null;

		if ("imm3".equals(src)) {
			addFormattedMicroInsn("alub = (ir >> 9) & 0x0007"); // retrieve immediate data
			addBeginFormattedControlFlow("if (alub == 0)");
			addFormattedMicroInsn("alub = 8"); // when immediate data if zero... use 8
			addEndControlFlow();

			rsrc = "alub";
		} else if ("imm8".equals(src) || "imm16".equals(src) || "imm32".equals(src)) {
			if ("ea".equals(dst)) {
				rsrc = "alub";
			} else {
				rsrc = "dt";
			}
		} else if ("dd".equals(src)) {
			if (!hasrx) {
				decode_dx();
				hasrx = true;
			}
			rsrc = "dar[rx]";
		} else if ("ds".equals(src)) {
			if (!hasry) {
				decode_dy();
				hasry = true;
			}

			rsrc = "dar[ry]";
		} else if ("ad".equals(src)) {
			if (!hasrx) {
				decode_ax();
				hasrx = true;
			}
			rsrc = "dar[rx]";
		} else if ("as".equals(src)) {
			if (!hasry) {
				decode_ay();
				hasry = true;
			}
			rsrc = "dar[ry]";
		} else if ("ea".equals(src)) {
			if ("ea".equals(dst)) {
				rsrc = "alub";
			} else {
				rsrc = "dt";
			}
		} else if ("sr".equals(src) || "ccr".equals(src)) {
			rsrc = "sr";
		}

		String rdst = null;
		String optype = null;
		boolean exsrc = false;

		if ("dd".equals(dst)) {
			if (!hasrx) {
				decode_dx();
				hasrx = true;
			}
			rdst = "dar[rx]";
		} else if ("ds".equals(dst)) {
			if (!hasry) {
				decode_dy();
				hasry = true;
			}

			rdst = "dar[ry]";
		} else if ("ad".equals(dst)) {
			if (!hasrx) {
				decode_ax();
				hasrx = true;
			}
			rdst = "dar[rx]";
			exsrc |= true;
		} else if ("as".equals(dst)) {
			if (!hasry) {
				decode_ay();
				hasry = true;
			}
			rdst = "dar[ry]";
		} else if ("ea".equals(dst)) {
			rdst = "dt";
		} else if ("sr".equals(dst) || "ccr".equals(dst)) {
			rdst = "sr";
		}

		if (("as".equals(dst) || "ad".equals(dst))) {
			if ("add".equals(op)) {
				optype = "+";
			} else if ("sub".equals(op) || "cmp".equals(op)) {
				optype = "-";
			} else {
				throw new IllegalStateException();
			}
		} else if ("sr".equals(dst) || "ccr".equals(dst)) {
			if ("or".equals(op)) {
				optype = "|";
			} else if ("and".equals(op)) {
				optype = "&";
			} else if ("eor".equals(op)) {
				optype = "^";
			} else {
				throw new IllegalStateException();
			}
		}

		if (update) {
			boolean writeback = true;

			if (("as".equals(dst) || "ad".equals(dst)) && (!"cmp".equals(name))) {
				writeback &= false;

				if ("l".equals(size)) {
					addFormattedMicroInsn("%s = %s %s %s", rdst, rdst, optype, rsrc); // perform long word operation
				} else if ("w".equals(size)) {
					addFormattedMicroInsn("%s = %s %s ((short) %s)", rdst, rdst, optype, rsrc); // perform word operation
				} else {
					throw new IllegalStateException();
				}
			} else if ("sr".equals(dst)) {
				writeback &= false;
				if ("and".equals(op)) {
					addFormattedMicroInsn("%s = %s %s %s", rdst, rdst, optype, rsrc);
				} else {
					addFormattedMicroInsn("%s = (%s %s %s) & 0x%04x", rdst, rdst, optype, rsrc, SR_T1 | SR_T0 | SR_S | SR_M | SR_I.value | FL_C | FL_V | FL_Z | FL_N | FL_X);
				}
			} else if ("ccr".equals(dst)) {
				writeback &= false;

				if ("and".equals(op)) {
					addFormattedMicroInsn("%s = (%s & ~0xff) | (%s %s %s & 0x%02x)", rdst, rdst, rdst, optype, rsrc, FL_C | FL_V | FL_Z | FL_N | FL_X);
				} else {
					addFormattedMicroInsn("%s = (%s & ~0xff) | ((%s %s %s) & 0x%02x)", rdst, rdst, rdst, optype, rsrc, FL_C | FL_V | FL_Z | FL_N | FL_X);
				}
			} else if ("b".equals(size)) {
				addFormattedMicroInsn("dt = byte_%s(%s, %s)", op, rsrc, rdst); // perform byte operation
			} else if ("l".equals(size)) {
				addFormattedMicroInsn("dt = long_%s(%s, %s)", op, rsrc, rdst); // perform long word operation
			} else if ("w".equals(size) && exsrc) {
				addFormattedMicroInsn("dt = long_%s((short) %s, %s)", op, rsrc, rdst); // perform long word operation
			} else if ("w".equals(size)) {
				addFormattedMicroInsn("dt = word_%s(%s, %s)", op, rsrc, rdst); // perform word operation
			} else {
				throw new IllegalStateException();
			}

			if (writeback) {
				if ("ea".equals(dst)) {
					if ("b".equals(size)) {
						microprefetch(true, "8");
					} else if ("w".equals(size)) {
						microprefetch(true, "16");
					} else if ("l".equals(size)) {
						microprefetch(true, "32");
					} else {
						throw new IllegalStateException();
					}
				} else {
					if ("dd".equals(dst)) {
						if ("b".equals(size)) {
							addFormattedMicroInsn("dar[rx] = (dar[rx] & ~0xff) | (dt & 0xff)"); // update register with result
						} else if ("w".equals(size)) {
							addFormattedMicroInsn("dar[rx] = (dar[rx] & ~0xffff) | (dt & 0xffff)"); // update register with result
						} else if ("l".equals(size)) {
							addFormattedMicroInsn("dar[rx] = dt"); // update register with result
						} else {
							throw new IllegalStateException();
						}
					} else if ("ds".equals(dst)) {
						if ("b".equals(size)) {
							addFormattedMicroInsn("dar[ry] = (dar[ry] & ~0xff) | (dt & 0xff)"); // update register with result
						} else if ("w".equals(size)) {
							addFormattedMicroInsn("dar[ry] = (dar[ry] & ~0xffff) | (dt & 0xffff)"); // update register with result
						} else if ("l".equals(size)) {
							addFormattedMicroInsn("dar[ry] = dt"); // update register with result
						} else {
							throw new IllegalStateException();
						}
					} else if ("as".equals(dst) | "ad".equals(dst)) {
						addFormattedMicroInsn("dar[rx] = dt"); // update register with result
					} else {
						throw new IllegalStateException();
					}

					microprefetch(true, null);
				}
			} else {
				microprefetch(true, null);
			}
		} else {
			if ("b".equals(size)) {
				addFormattedMicroInsn("byte_%s(%s, %s)", op, rsrc, rdst); // perform byte operation
			} else if ("l".equals(size)) {
				addFormattedMicroInsn("long_%s(%s, %s)", op, rsrc, rdst); // perform long word operation
			} else if ("w".equals(size) && exsrc) {
				addFormattedMicroInsn("long_%s((short) %s, %s)", op, rsrc, rdst); // perform long word operation (with src word sign extend)
			} else if ("w".equals(size)) {
				addFormattedMicroInsn("word_%s(%s, %s)", op, rsrc, rdst); // perform word operation
			} else {
				throw new IllegalStateException();
			}

			microprefetch(true, null);
		}

		return mnemonic;
	}

	public String gen_monadic(String name, String size, String dst) {
		String mnemonic = String.format("gen_%s%s_%s", name, size, dst);
		boolean update = !"tst".equals(name);

		for(MicroBlock block : blocks) {
			if (mnemonic.equals(block.name)) {
				return mnemonic;
			}

		}

		addState(mnemonic);

		boolean hasrx = false;
		boolean hasry = false;
		boolean shift = false;

		if ("asl".equals(name) || "asr".equals(name) || "lsl".equals(name) || "lsr".equals(name) || "rol".equals(name) || "ror".equals(name)) {
			shift = true;
		}

		String rdst = null;

		if ("dd".equals(dst)) {
			if (!hasrx) {
				decode_dx();
				hasrx = true;
			}
			rdst = "dar[rx]";
		} else if ("ds".equals(dst)) {
			if (!hasry) {
				decode_dy();
				hasry = true;
			}

			rdst = "dar[ry]";
		} else if ("ad".equals(dst)) {
			if (!hasrx) {
				decode_ax();
				hasrx = true;
			}
			rdst = "dar[rx]";
		} else if ("as".equals(dst)) {
			if (!hasry) {
				decode_ay();
				hasry = true;
			}
			rdst = "dar[ry]";
		} else if ("ea".equals(dst)) {
			rdst = "dt";
		}

		if (update) {
			if ("b".equals(size)) {
				addFormattedMicroInsn(shift ? "dt = byte_%s(1, %s)" : "dt = byte_%s(%s)", name, rdst); // perform byte operation
			} else if ("w".equals(size)) {
				addFormattedMicroInsn(shift ? "dt = word_%s(1, %s)" : "dt = word_%s(%s)", name, rdst); // perform word operation
			} else if ("l".equals(size)) {
				addFormattedMicroInsn(shift ? "dt = long_%s(1, %s)" : "dt = long_%s(%s)", name, rdst); // perform long word operation
			} else {
				throw new IllegalStateException();
			}

			if ("ea".equals(dst)) {
				if ("b".equals(size)) {
					microprefetch(true, "8");
				} else if ("w".equals(size)) {
					microprefetch(true, "16");
				} else if ("l".equals(size)) {
					microprefetch(true, "32");
				} else {
					throw new IllegalStateException();
				}
			} else {
				if ("dd".equals(dst)) {
					if ("b".equals(size)) {
						addFormattedMicroInsn("dar[rx] = (dar[rx] & ~0xff) | (dt & 0xff)"); // update register with result
					} else if ("w".equals(size)) {
						addFormattedMicroInsn("dar[rx] = (dar[rx] & ~0xffff) | (dt & 0xffff)"); // update register with result
					} else if ("l".equals(size)) {
						addFormattedMicroInsn("dar[rx] = dt"); // update register with result
					} else {
						throw new IllegalStateException();
					}
				} else if ("ds".equals(dst)) {
					if ("b".equals(size)) {
						addFormattedMicroInsn("dar[ry] = (dar[ry] & ~0xff) | (dt & 0xff)"); // update register with result
					} else if ("w".equals(size)) {
						addFormattedMicroInsn("dar[ry] = (dar[ry] & ~0xffff) | (dt & 0xffff)"); // update register with result
					} else if ("l".equals(size)) {
						addFormattedMicroInsn("dar[ry] = dt"); // update register with result
					} else {
						throw new IllegalStateException();
					}
				} else if ("ad".equals(dst)) {
					if ("w".equals(size)) {
						addFormattedMicroInsn("dar[rx] = (short) dt"); // update register with result
					} else if ("l".equals(size)) {
						addFormattedMicroInsn("dar[rx] = dt"); // update register with result
					} else {
						throw new IllegalStateException();
					}
				} else if ("as".equals(dst)) {
					if ("w".equals(size)) {
						addFormattedMicroInsn("dar[ry] = (short) dt"); // update register with result
					} else if ("l".equals(size)) {
						addFormattedMicroInsn("dar[ry] = dt"); // update register with result
					} else {
						throw new IllegalStateException();
					}
				} else {
					throw new IllegalStateException();
				}

				microprefetch(true, null);
			}
		} else {
			if ("b".equals(size)) {
				addFormattedMicroInsn(shift ? "byte_%s(1, %s)" : "byte_%s(%s)", name, rdst); // perform byte operation
			} else if ("w".equals(size)) {
				addFormattedMicroInsn(shift ? "word_%s(1, %s)" : "word_%s(%s)", name, rdst); // perform word operation
			} else if ("l".equals(size)) {
				addFormattedMicroInsn(shift ? "long_%s(1, %s)" : "long_%s(%s)", name, rdst); // perform long word operation
			} else {
				throw new IllegalStateException();
			}

			microprefetch(true, null);
		}

		return mnemonic;
	}

	private void op_bcc_microcode() {
		addState("op_bcc8");
		addBeginFormattedControlFlow("if (!testCC((ir & 0x0f00) >> 8))");
		consume(6, 1, 0);
		microprefetch(true, null); // prefetch ir from pc and resume execution
		addEndControlFlow();

		addState("op_bra8");
		consume(10, 2, 0);
		addFormattedMicroInsn("scan = (byte) ir");
		addFormattedMicroInsn("sswi |= (sr & 0x%04x)", SR_T0); // trigger trace flow
		microprefetch(true, null); // prefetch ir from pc and resume execution

		addState("op_bcc16");
		addBeginFormattedControlFlow("if (!testCC((ir & 0x0f00) >> 8))");
		consume(10, 2, 0);
		microprefetch(true, null); // prefetch ir from pc and resume execution
		addEndControlFlow();

		addState("op_bra16");
		consume(10, 2, 0);
		addFormattedMicroInsn("scan = (short) dt");
		addFormattedMicroInsn("sswi |= (sr & 0x%04x)", SR_T0); // trigger trace flow
		microprefetch(true, null); // prefetch ir from pc and resume execution

		addState("op_bcc32");
		addBeginFormattedControlFlow("if (!testCC((ir & 0x0f00) >> 8))");
		consume(14, 3, 0);
		microprefetch(true, null); // prefetch ir from pc and resume execution
		addEndControlFlow();

		addState("op_bra32");
		consume(14, 3, 0);
		addFormattedMicroInsn("scan = dt");
		addFormattedMicroInsn("sswi |= (sr & 0x%04x)", SR_T0); // trigger trace flow
		microprefetch(true, null); // prefetch ir from pc and resume execution

		addState("op_bsr8");
		consume(18, 2, 2);
		addFormattedMicroInsn("au = dar[sp] - 4");
		addFormattedMicroInsn("dar[sp] = au");
		write32(SSW_D, "au", "pc + scan", 0, false);
		addFormattedMicroInsn("scan = (byte) ir");
		addFormattedMicroInsn("sswi |= (sr & 0x%04x)", SR_T0); // trigger trace flow
		microprefetch(true, null); // prefetch ir from pc and resume execution

		addState("op_bsr16");
		consume(18, 2, 2);
		addFormattedMicroInsn("au = dar[sp] - 4");
		addFormattedMicroInsn("dar[sp] = au");
		write32(SSW_D, "au", "pc + scan", 0, false);
		addFormattedMicroInsn("scan = (short) dt");
		addFormattedMicroInsn("sswi |= (sr & 0x%04x)", SR_T0); // trigger trace flow
		microprefetch(true, null); // prefetch ir from pc and resume execution

		addState("op_bsr32");
		consume(22, 3, 2);
		addFormattedMicroInsn("au = dar[sp] - 4");
		addFormattedMicroInsn("dar[sp] = au");
		write32(SSW_D, "au", "pc + scan", 0, false);
		addFormattedMicroInsn("scan = dt");
		addFormattedMicroInsn("sswi |= (sr & 0x%04x)", SR_T0); // trigger trace flow
		microprefetch(true, null); // prefetch ir from pc and resume execution

		addState("op_dbcc");
		addBeginFormattedControlFlow("if (testCC((ir & 0x0f00) >> 8))");
		consume(10, 2, 0);
		addFormattedMicroInsn("scan += 2"); // skip displacement word
		microprefetch(true, null); // prefetch ir from pc and resume execution
		addEndControlFlow();
		decode_dy();
		// decrement and update data register
		addFormattedMicroInsn("dar[ry] = (dar[ry] & ~0xffff) | ((dt = ((short) dar[ry]) - 1) & 0xffff)");
		addBeginFormattedControlFlow("if (dt == -1)");
		consume(16, 3, 0);
		addFormattedMicroInsn("scan += 2"); // skip displacement word
		microprefetch(true, null); // prefetch ir from pc and resume execution
		addEndControlFlow();
		fetch16(null, 0, "pc + scan");
		addFormattedMicroInsn("scan += (short) dib");
		consume(10, 2, 0);
		microprefetch(true, null); // prefetch ir from pc and resume execution
	}

	private void op_bkpt_microcode() {
		addState("op_bkpt");
		consume(42, 5, 4);
		addFormattedMicroInsn("hdlr = handle_bkpt(cip, ir & 0x0007)");
		addFormattedMicroInsn("exit |= (hdlr & 0x%08x) != 0", BKPT_EXIT.value);
		addBeginFormattedControlFlow("if ((hdlr & 0x%08x) != 0)", BKPT_RPIR.value);
		addFormattedMicroInsn("ir = hdlr & 0xffff");
		addFormattedMicroInsn("sp = spi(sr)"); // update stack pointer index
		addFormattedMicroInsn("decoded = MacroPLA.decode(ir)");
		addFormattedMicroInsn("nmpc = decoded.a1");
		addFormattedMicroInsn("break");
		addEndControlFlow();

		settvn(4); // set illegal exception trap
		addFormattedMicroInsn("mpc = trapill"); // set micro pc
		addFormattedMicroInsn("continue"); // branch to trap subroutine
	}

	private void op_illegal_microcode() {
		addState("op_illegal");
		consume(38, 4, 4);
		addFormattedMicroInsn("hdlr = handle_illegal(cip, ir)");
		addFormattedMicroInsn("exit |= (hdlr & 0x%08x) != 0", ILL_EXIT.value);
		addBeginFormattedControlFlow("if ((hdlr & 0x%08x) != 0)", ILL_SKIP.value);
		addFormattedMicroInsn("scan += (short) hdlr");
		addFormattedMicroInsn("sp = spi(sr)"); // update stack pointer index
		microprefetch(true, null); // prefetch ir and resume execution
		addEndControlFlow();

		settvn(4); // set illegal exception trap
		addFormattedMicroInsn("mpc = trapill"); // set micro pc
		addFormattedMicroInsn("continue"); // branch to trap subroutine
	}

	private void op_jmp_microcode() {
		addState("op_jmp");
		addFormattedMicroInsn("pc = at"); // set pc from effective address
		addFormattedMicroInsn("scan = 0"); // clear scan value
		addFormattedMicroInsn("sswi |= (sr & 0x%04x)", SR_T0); // trigger trace flow
		microprefetch(true, null); // prefetch ir from pc and resume execution

		addState("op_jsr");
		addFormattedMicroInsn("au = dar[sp] - 4");
		addFormattedMicroInsn("dar[sp] = au");
		write32(SSW_D, "au", "pc + scan", 0, false);
		addFormattedMicroInsn("pc = at"); // set pc from effective address
		addFormattedMicroInsn("scan = 0"); // clear scan value
		addFormattedMicroInsn("sswi |= (sr & 0x%04x)", SR_T0); // trigger trace flow
		microprefetch(true, null); // prefetch ir from pc and resume execution
	}

	private void op_linea_microcode() {
		addState("op_linea");
		consume(38, 4, 4);
		addFormattedMicroInsn("hdlr = handle_linea(cip, ir & 0x0fff)");
		addFormattedMicroInsn("exit |= (hdlr & 0x%08x) != 0", LINEA_EXIT.value);
		addBeginFormattedControlFlow("if ((hdlr & 0x%08x) != 0)", LINEA_SKIP.value);
		addFormattedMicroInsn("scan += (short) hdlr");
		addFormattedMicroInsn("sp = spi(sr)"); // update stack pointer index
		microprefetch(true, null); // prefetch ir and resume execution
		addEndControlFlow();

		settvn(10); // set linea exception trap
		addFormattedMicroInsn("mpc = trapill"); // set micro pc
		addFormattedMicroInsn("continue"); // branch to trap subroutine
	}

	private void op_linef_microcode() {
		addState("op_linef");
		consume(38, 4, 4);
		settvn(11); // set linef exception trap
		addFormattedMicroInsn("mpc = trapill"); // set micro pc
		addFormattedMicroInsn("continue"); // branch to trap subroutine
	}

	private void op_movep_microcode() {
		addState("op_movepw_dd_das");
		decode_dx();
		addFormattedMicroInsn("dt = dar[rx]"); // retrieve data register value
		prefetchir(0, false);
		write8(SSW_D, "at", "dt >> 8", 0, false);
		write8(SSW_D | SSW_AL, "at", "dt", 2, false);
		addFormattedMicroInsn("mpc = resume");
		addFormattedMicroInsn("continue");

		addState("op_movepw_das_dd");
		decode_dx();
		read8("dt", 0, "at", 0);
		read8(null, SSW_AL, "at", 2);
		addFormattedMicroInsn("dt = (dt << 8) | (dib & 0xff)");
		addFormattedMicroInsn("dar[rx] = (dar[rx] & ~0xffff) | (dt & 0xffff)");
		microprefetch(true, null);

		addState("op_movepl_dd_das");
		decode_dx();
		addFormattedMicroInsn("dt = dar[rx]"); // retrieve data register value
		prefetchir(0, false);
		write8(SSW_D, "at", "dt >> 24", 0, false);
		write8(SSW_D | SSW_AL, "at", "dt >> 16", 2, false);
		write8(SSW_D | SSW_AL, "at", "dt >> 8", 4, false);
		write8(SSW_D | SSW_AL, "at", "dt", 6, false);
		addFormattedMicroInsn("mpc = resume");
		addFormattedMicroInsn("continue");

		addState("op_movepl_das_dd");
		decode_dx();
		read8("dt", 0, "at", 0);
		read8(null, SSW_AL, "at", 2);
		addFormattedMicroInsn("dt = (dt << 8) | (dib & 0xff)");
		read8(null, SSW_AL, "at", 4);
		addFormattedMicroInsn("dt = (dt << 8) | (dib & 0xff)");
		read8(null, SSW_AL, "at", 6);
		addFormattedMicroInsn("dt = (dt << 8) | (dib & 0xff)");
		addFormattedMicroInsn("dar[rx] = dt");
		microprefetch(true, null);

	}

	private void op_nop_microcode() {
		addState("op_nop");
		consume(4, 1, 0);
		microprefetch(true, null);
	}

	private void op_rte_microcode() {
		addState("op_rte");
		// check for supervisor mode
		addBeginFormattedControlFlow("if ((sr & 0x%04x) == 0)", SR_S);
		consume(38, 4, 4);
		settvn(8); // set privilege exception trap
		addFormattedMicroInsn("mpc = trapill"); // set micro pc
		addFormattedMicroInsn("continue"); // branch to trap subroutine
		addEndControlFlow();
		addFormattedMicroInsn("au = dar[sp]"); // retrieve supervisor stack pointer

		addState("check_vob");
		read16(null, SSW_CR | SSW_S, "au", 3 << 1); // read vob from stack frame
		// select rte implementation according to stack frame on master stack
		addFormattedMicroInsn("mpc = select_rte(dib)");
		addFormattedMicroInsn("continue");

		addState("rteill");
		consume(50, 7, 4);
		settvn(14);
		addFormattedMicroInsn("mpc = trapill"); // set next entry
		addFormattedMicroInsn("continue");

		addState("exit_trap");
		read32("pc", SSW_CR | SSW_S | SSW_AL, "au", 1 << 1); // read pc from stack frame
		read16(null, SSW_CR | SSW_S | SSW_AL, "au", 0); // read sr from stack frame
		addFormattedMicroInsn("sr = dib & 0x%04x",
				SR_T1 | SR_T0 | SR_S | SR_M | SR_I.value | FL_C | FL_V | FL_Z | FL_N | FL_X);
		addFormattedMicroInsn("sp = spi(sr)"); // update stack pointer index
		addFormattedMicroInsn("scan = 0"); // reset scan value
		microprefetch(true, null);

		// specific routine for restore state from a $0 format frame
		// au already contains sp, since vob has been read, assume au is word aligned
		addState("rte0000");
		consume(24, 6, 0);
		addFormattedMicroInsn("dar[sp] = au + %d", 4 << 1); // update supervisor stack pointer
		addFormattedMicroInsn("mpc = exit_trap");
		addFormattedMicroInsn("continue");

		// specific routine for restore state from a $1 format frame
		// au already contains sp, since vob has been read, assume au is word aligned
		addState("rte1000");
		// do not do anything with the throw away stack frame. drop it and fallback to
		// master frame
		addFormattedMicroInsn("dar[sp] = au + %d", 4 << 1); // update interrupt supervisor stack pointer
		addFormattedMicroInsn("au = dar[sp = spi(sr | 0x%04x)]", SR_M); // retrieve master supervisor stack pointer

		// select rte implementation again according to stack frame on master stack
		addFormattedMicroInsn("mpc = check_vob");
		addFormattedMicroInsn("continue");

		// specific routine for restore state from a $2 format frame
		// au already contains sp, since vob has been read, assume au is word aligned
		addState("rte2000");
		consume(24, 6, 0); // use timing of rte0000
		addFormattedMicroInsn("dar[sp] = au + %d", 6 << 1); // update supervisor stack pointer
		addFormattedMicroInsn("mpc = exit_trap");
		addFormattedMicroInsn("continue");

		addState("rte8000");
		consume(110, 26, 0);
		read32("cip", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 27 << 1); // read cip
		read32("dt", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 25 << 1); // read dt
		read16("ir", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 24 << 1); // read ir
		addFormattedMicroInsn("decoded = MacroPLA.decode(ir)"); // ensure ir is decoded (for RMW re-run)
		read16(null, SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 23 << 1); // read sswi
		addFormattedMicroInsn("vdt1 = dib << 16"); // save dibh in vdt1
		read32("vat2", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 19 << 1); // read au
		read32("vat1", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 17 << 1); // read at
		read32("alub", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 15 << 1); // read alub
		read16("scan", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 14 << 1); // read scan value
		read16("nmpc", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 13 << 1); // read next mpc
		read16("irb", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 12 << 1); // read prefetch buffer

		read32("vat3", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 5 << 1); // read saved aob
		read16(null, SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 4 << 1); // read ssw
		addFormattedMicroInsn("vdt1 |= dib & 0xffff");

		read32("pc", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 1 << 1); // read pc

		addBeginFormattedControlFlow("if ((vdt1 & 0x%04x) == 0)", SSW_RR);
		// processor re-run requested
		addFormattedMicroInsn("elapsed += 2"); // will take two more cycles to execute
		addBeginFormattedControlFlow("if ((vdt1 & 0x%04x) != 0)", SSW_RM);
		// read/modify/write case, jump to a3 to retry decoded instruction
		addFormattedMicroInsn("nmpc = decoded.a3");
		setclr16("vdt1", 0, SSW_MASK); // clear ssw before returning
		addNextFormattedControlFlow("else");

		// need to read dob (or dibh) for retry
		addBeginFormattedControlFlow("if ((vdt1 & 0x%04x) == 0)", SSW_RW);
		// choose between short or long dob
		addBeginFormattedControlFlow("if ((vdt1 & 0x%04x) != 0)", SSW_32);
		read32("vdt2", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 8 << 1); // read dobh/l
		addNextFormattedControlFlow("else");
		read16("vdt2", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 8 << 1); // read dobl
		addEndControlFlow();
		addNextFormattedControlFlow("else if ((vdt1 & 0x%04x) == 0x%04x)", SSW_32 | SSW_WD, SSW_WD);
		read16("vdt3", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 9 << 1); // read dibh only
		addFormattedMicroInsn("vdt3 <<= 16"); // adjust vdt3 for dibh
		addEndControlFlow();

		addFormattedMicroInsn("ssw = (ssw & 0x%04x) | ((vdt1 ^ ~0x%04x) & 0x%04x)", SSW_RS.value | SSW_HL.value, SSW_RR,
				SSW_MASK & ~SSW_BR);
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0x%04x)", SSW_32 | SSW_BY, SSW_BY);
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0)", SSW_HB);
		addFormattedMicroInsn("vat3 += 1"); // add 1 to aob to match low byte
		addNextFormattedControlFlow("else");
		setclr16("ssw", 0, SSW_HB); // ensure HB is cleared before access
		addFormattedMicroInsn("vdt2 >>= 8"); // set dob data to low byte
		addEndControlFlow();
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0x%04x)", SSW_DF | SSW_RW, SSW_DF | SSW_RW);
		addFormattedMicroInsn("vdt3 = read8(vat3)");
		addNextFormattedControlFlow("else if ((ssw & 0x%04x) == 0)", SSW_DF | SSW_RW);
		addFormattedMicroInsn("write8(vat3, vdt2)");
		addEndControlFlow();
		addNextFormattedControlFlow("else if ((vat3 & 0x000000001) != 0)");
		/* misaligned transfer, signal address error */
		consume(126, 4, 26);
		settvn(3);
		addFormattedMicroInsn("mpc = resume_trap8000");
		addFormattedMicroInsn("continue");
		addNextFormattedControlFlow("else if ((ssw & 0x%04x) == 0)", SSW_32);
		// 16 bits access (aligned)
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0x%04x)", SSW_IF | SSW_DF | SSW_RW, SSW_IF | SSW_RW);
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0x%04x)", SSW_P | SSW_D, SSW_P);
		addFormattedMicroInsn("irb = fetch16(vat3)");
		addNextFormattedControlFlow("else");
		addFormattedMicroInsn("irb = read16(vat3)");
		addEndControlFlow();
		addNextFormattedControlFlow("else if ((ssw & 0x%04x) == 0x%04x)", SSW_IF | SSW_DF | SSW_RW | SSW_WD,
				SSW_DF | SSW_RW | SSW_WD);
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0x%04x)", SSW_P | SSW_D, SSW_P);
		addFormattedMicroInsn("vdt3 = (vdt3 & ~0xffff) | fetch16(vat3) & 0xffff");
		addNextFormattedControlFlow("else");
		addFormattedMicroInsn("vdt3 = (vdt3 & ~0xffff) | read16(vat3) & 0xffff");
		addEndControlFlow();
		addNextFormattedControlFlow("else if ((ssw & 0x%04x) == 0x%04x)", SSW_IF | SSW_DF | SSW_RW, SSW_DF | SSW_RW);
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0x%04x)", SSW_P | SSW_D, SSW_P);
		addFormattedMicroInsn("vdt3 = fetch16(vat3)");
		addNextFormattedControlFlow("else");
		addFormattedMicroInsn("vdt3 = read16(vat3)");
		addEndControlFlow();
		addNextFormattedControlFlow("else if ((ssw & 0x%04x) == 0)", SSW_IF | SSW_DF | SSW_RW);
		addFormattedMicroInsn("write16(vat3, vdt2)");
		addEndControlFlow();
		addNextFormattedControlFlow("else");
		// 32 bits access (aligned on 16 bits boundary)
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0x%04x)", SSW_IF | SSW_DF | SSW_RW, SSW_IF | SSW_RW);
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0x%04x)", SSW_P | SSW_D, SSW_P);
		addFormattedMicroInsn("vdt3 = fetch32(vat3)");
		addNextFormattedControlFlow("else");
		addFormattedMicroInsn("vdt3 = read32(vat3)");
		addEndControlFlow();
		addNextFormattedControlFlow("else if ((ssw & 0x%04x) == 0)", SSW_IF | SSW_DF | SSW_RW);
		addFormattedMicroInsn("write32(vat3, vdt2)");
		addEndControlFlow();
		addEndControlFlow();

		// update vdt1 according to new ssw (may be used to resume berr/aerr trap
		addFormattedMicroInsn("vdt1 = (vdt1 & ~0xffff) | ((ssw ^ ~0x%04x) & 0x%04x)", SSW_RR, (~SSW_BR) & 0xffff);

		addBeginFormattedControlFlow("if ((ssw & 0x%04x) != 0)", SSW_BR);
		/* got bus error, retry with current frame */
		consume(126, 4, 26);
		settvn(2);
		addFormattedMicroInsn("mpc = resume_trap8000");
		addFormattedMicroInsn("continue");
		addEndControlFlow();
		addEndControlFlow();
		addNextFormattedControlFlow("else if ((vdt1 & 0x%04x) != 0)", SSW_RW);
		// software read retry requested, need to read dibh/l
		addBeginFormattedControlFlow("if ((vdt1 & 0x%04x) != 0)", SSW_WD); // check 'wide' bit for read
		read32("vdt3", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 9 << 1); // read dibh/l
		addNextFormattedControlFlow("else");
		read16("vdt3", SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 10 << 1); // read dibl only
		addEndControlFlow();
		addEndControlFlow();

		read16(null, SSW_CR | SSW_S | SSW_D | SSW_AL, "au", 0); // read sr
		addFormattedMicroInsn("sr = dib & 0x%04x",
				SR_T1 | SR_T0 | SR_S | SR_M | SR_I.value | FL_C | FL_V | FL_Z | FL_N | FL_X);
		addFormattedMicroInsn("dar[sp] = au + %d", 29 << 1); // update supervisor stack pointer
		addFormattedMicroInsn("sp = spi(sr)"); // update stack pointer index

		addFormattedMicroInsn("au = vat1"); // restore at
		addFormattedMicroInsn("at = vat2"); // restore au
		addFormattedMicroInsn("aob = vat3"); // restore aob
		addFormattedMicroInsn("dob = vdt2"); // restore dob
		addFormattedMicroInsn("dib = vdt3"); // restore dib
		addFormattedMicroInsn("tvn = (vdt1 >> 14) & 0x3fc"); // restore tvn
		addFormattedMicroInsn("sswi = (sswi & ~0x%04x) | ((vdt1 >> 16) & 0x%04x)", SR_T1 | SR_T0 | SSWI_EAFH,
				SR_T1 | SR_T0 | SSWI_EAFH);
		addFormattedMicroInsn("ssw = (ssw & 0x%04x) | ((vdt1 ^ ~0x%04x) & 0x%04x)", SSW_RS.value | SSW_HL.value, SSW_RR,
				SSW_MASK & ~SSW_BR);

		// if last 32 bits access has only 16 bits transfered
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0x%04x)", SSW_RR | SSW_32 | SSW_WD, SSW_RR | SSW_32);
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) != 0)", SSW_RW);
		addFormattedMicroInsn("mpc = (ssw & 0x%04x) == 0x%04x ? bevtf32 : bevtr32", SSW_D | SSW_P, SSW_P);
		addNextFormattedControlFlow("else");
		addFormattedMicroInsn("mpc = bevtw32");
		addEndControlFlow();
		addFormattedMicroInsn("continue");
		addEndControlFlow();

		addFormattedMicroInsn("break"); // resume execution to nmpc
	}

	private void op_rtr_microcode() {
		addState("op_rtr");
		consume(20, 5, 0);
		addFormattedMicroInsn("au = dar[sp]"); // clear scan value
		read32("pc", 0, "au", 1 << 1);
		read16(null, SSW_AL, "au", 0 << 1);
		addClearSSW();
		addFormattedMicroInsn("sr = (sr & ~0x%04x) | (dib & 0x%04x)", FL_C | FL_V | FL_Z | FL_N | FL_X,
				FL_C | FL_V | FL_Z | FL_N | FL_X);
		addFormattedMicroInsn("scan = 0"); // clear scan value
		addFormattedMicroInsn("dar[sp] = au + 6"); // update stack pointer
		addFormattedMicroInsn("sswi |= (sr & 0x%04x)", SR_T0); // trigger trace flow
		microprefetch(true, null); // prefetch ir from pc and resume execution
	}

	private void op_rts_microcode() {
		addState("op_rts");
		consume(16, 4, 0);
		addFormattedMicroInsn("au = dar[sp]"); // clear scan value
		read32("pc", 0, "au");
		addClearSSW();
		addFormattedMicroInsn("scan = 0"); // clear scan value
		addFormattedMicroInsn("dar[sp] = au + 4"); // update stack pointer
		addFormattedMicroInsn("sswi |= (sr & 0x%04x)", SR_T0); // trigger trace flow
		microprefetch(true, null); // prefetch ir from pc and resume execution
	}

	private void op_reset_microcode() {
		addState("op_reset");
		consume(130, 1, 0);
		microprefetch(true, null);
	}

	private void op_trapv_microcode() {
		addState("op_trap");
		consume(38, 4, 4);
		addFormattedMicroInsn("tvn = (32 + (ir & 0x000f)) << 2");
		addFormattedMicroInsn("mpc = trap0000"); // use format 2 (68020+ instruction)
		addFormattedMicroInsn("continue"); // branch to trap subroutine

		addState("op_trapv");
		consume(4, 1, 0);
		addBeginFormattedControlFlow("if ((sr & 0x%04x) != 0)", FL_V);
		consume(40, 5, 4);
		settvn(7); // set trapv exception trap
		addFormattedMicroInsn("mpc = (sswi & 0x%04x) != 0 ? trap2000 : trap0000", SSWI_FMT2);
		addFormattedMicroInsn("continue"); // branch to trap subroutine
		addEndControlFlow();
		microprefetch(true, null);

		addState("op_trapcc");
		addBeginFormattedControlFlow("if (!testCC((ir & 0x0f00) >> 8))");
		microprefetch(true, null); // prefetch ir from pc and resume execution
		addEndControlFlow();
		consume(40, 5, 4);
		settvn(7); // set trapcc exception trap
		addFormattedMicroInsn("mpc = trap2000"); // use format 2 (68020+ instruction)
		addFormattedMicroInsn("continue"); // branch to trap subroutine

		addState("op_trapcc16");
		addFormattedMicroInsn("scan += 2"); // skip next word
		addBeginFormattedControlFlow("if (!testCC((ir & 0x0f00) >> 8))");
		microprefetch(true, null); // prefetch ir from pc and resume execution
		addEndControlFlow();
		consume(40, 5, 4);
		settvn(7); // set trapcc exception trap
		addFormattedMicroInsn("mpc = trap2000"); // use format 2 (68020+ instruction)
		addFormattedMicroInsn("continue"); // branch to trap subroutine

		addState("op_trapcc32");
		addFormattedMicroInsn("scan += 4"); // skip next long word
		addBeginFormattedControlFlow("if (!testCC((ir & 0x0f00) >> 8))");
		microprefetch(true, null); // prefetch ir from pc and resume execution
		addEndControlFlow();

		consume(40, 5, 4);
		settvn(7); // set trapcc exception trap
		addFormattedMicroInsn("mpc = trap2000"); // use format 2 (68020+ instruction)
		addFormattedMicroInsn("continue"); // branch to trap subroutine
	}

	private void emitStates(TypeSpec.Builder builder, Set<String> imports) {
		int count = blocks.size();

		for (int i = 0; i < count; i++) {
			MicroBlock block = blocks.get(i);

			if (block.name != null) {
				FieldSpec field = null;

				if ((imports == null) || (imports.contains(block.name))) {
					field = FieldSpec.builder(int.class, block.name, PROTECTED, STATIC, FINAL).initializer("$L", i)
							.build();
				} else {
					field = FieldSpec.builder(int.class, block.name, PRIVATE, STATIC, FINAL).initializer("$L", i)
							.build();
				}

				builder.addField(field);
			}
		}
	}

	private void emitMicrocode(MethodSpec.Builder builder) {
		int count = blocks.size();

		for (int i = 0; i < count; i++) {
			blocks.get(i).emit(i, builder);
		}
	}

	public static TypeSpec coreType() {
		TypeSpec.Builder builder = TypeSpec.classBuilder("Core").addModifiers(PUBLIC, ABSTRACT);

		builder.superclass(CoreALU.class);

		TreeMap<OPEntry, MacroEntry> macros = new TreeMap<OPEntry, MacroEntry>();
		TreeSet<String> imports = new TreeSet<String>();
		CoreGenerator gen = new CoreGenerator();

		CorePLAGenerator.fillops(gen, macros);
		CorePLAGenerator.corePLAType(gen, macros, imports);

		gen.emitStates(builder, imports);

		builder.addField(BKPT_EXIT.field);
		builder.addField(BKPT_RPIR.field);
		builder.addField(LINEA_EXIT.field);
		builder.addField(LINEA_SKIP.field);
		builder.addField(ILL_EXIT.field);
		builder.addField(ILL_SKIP.field);
		builder.addField(TRACE_EXIT.field);
		builder.addField(TRACE_IGNR.field);
		builder.addField(IRQ_EXIT.field);
		builder.addField(IRQ_IERR.field);
		builder.addField(IRQ_DFLT.field);
		builder.addField(IRQ_AVEC.field);
		builder.addField(IRQ_SPUR.field);
		builder.addField(IRQ_IGNR.field);

		builder.addField(pc);
		builder.addField(scan);

		builder.addField(alub);
		builder.addField(irb);
		builder.addField(ir);
		builder.addField(mpc);
		builder.addField(cip);
		builder.addField(au);
		builder.addField(at);
		builder.addField(dt);
		// builder.addField(aob);
		// builder.addField(dob);
		// builder.addField(dib);

		builder.addField(slice);

		builder.addMethod(pulse_reset_method());
		builder.addMethod(set_pending_irq_method());
		builder.addMethod(select_rte_method());
		builder.addMethod(execute_method(gen));
		builder.addMethod(fetch16_method());
		builder.addMethod(fetch32_method());
		builder.addMethod(read8_method());
		builder.addMethod(read16_method());
		builder.addMethod(read32_method());
		builder.addMethod(write8_method());
		builder.addMethod(write16_method());
		builder.addMethod(write32_method());
		builder.addMethod(handle_bkpt_method());
		builder.addMethod(handle_illegal_method());
		builder.addMethod(handle_interrupt_method());
		builder.addMethod(handle_linea_method());
		builder.addMethod(handle_reset_method());
		builder.addMethod(handle_trace_method());

		return builder.build();
	}

	public static MethodSpec pulse_reset_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("pulse_reset");

		builder.addModifiers(PUBLIC, FINAL);
		builder.returns(void.class);

		builder.addStatement("ssw |= $N", SSW_RS.field);

		return builder.build();
	}

	public static MethodSpec set_pending_irq_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("set_pending_irq");

		builder.addModifiers(PUBLIC, FINAL);
		builder.returns(void.class).addParameter(int.class, "level");

		/*
		 * sswi contains pending interrupt level. This value will be set to 0 at next
		 * instruction boundary
		 */
		builder.addStatement("level = (level << 8) & $N", SR_I.field);
		builder.addStatement("$N = ($N & ~$N) | level", sswi, sswi, SR_I.field);

		return builder.build();
	}

	public static MethodSpec select_rte_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("select_rte");

		builder.addModifiers(PRIVATE, STATIC, FINAL);
		builder.returns(int.class).addParameter(int.class, "vob");

		builder.beginControlFlow("switch((vob & 0xf000) >> 12)");
		builder.addCode("case 0:\n");
		builder.addStatement("return rte0000");
		builder.addCode("case 1:\n");
		builder.addStatement("return rte1000");
		builder.addCode("case 2:\n");
		builder.addStatement("return rte2000");
		builder.addCode("case 8:\n");
		builder.addStatement("return rte8000");
		builder.addCode("default:\n");
		builder.addStatement("return rteill");
		builder.endControlFlow();

		return builder.build();
	}

	public static MethodSpec execute_method(CoreGenerator gen) {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("execute");

		builder.addModifiers(PUBLIC, FINAL);
		builder.returns(int.class).addParameter(int.class, "cycles");

		builder.addStatement("MacroPLA decoded = MacroPLA.decode(ir)");
		builder.addStatement("int slice = Math.max(0, cycles)");
		builder.addStatement("int consumed = 0");

		builder.beginControlFlow("if ((sswi & $N) != 0)", SSWI_PSLC.field);
		builder.addStatement("slice += this.slice");
		builder.endControlFlow();

		builder.beginControlFlow("if (slice > 0)");
		builder.addStatement("boolean sngl = false");
		builder.addStatement("boolean exit = false");
		builder.addStatement("int sp = spi(sr)");
		builder.addStatement("int elapsed = 0");
		builder.addStatement("int nmpc = 0");

		builder.addStatement("int hdlr = 0");
		builder.addStatement("int ipend = 0");
		builder.addStatement("int tvn = 0");
		builder.addStatement("int aob = 0");
		builder.addStatement("int dob = 0");
		builder.addStatement("int dib = 0");
		builder.addStatement("int vat1 = 0");
		builder.addStatement("int vat2 = 0");
		builder.addStatement("int vat3 = 0");
		builder.addStatement("int vdt1 = 0");
		builder.addStatement("int vdt2 = 0");
		builder.addStatement("int vdt3 = 0");
		builder.addStatement("int rx = 0");
		builder.addStatement("int ry = 0");
		builder.addStatement("int rz = 0");
		builder.addStatement("consumed = slice");

		builder.beginControlFlow("if ((ssw & ($N | $N)) != 0)", SSW_RS.field, SSW_HL.field);
		builder.addStatement("nmpc = mpc");
		builder.addStatement("mpc = halt");
		builder.endControlFlow();

		builder.beginControlFlow("while (true)");
		builder.beginControlFlow("switch(mpc)");

		gen.emitMicrocode(builder);

		builder.addCode("default:\n");
		builder.addStatement("mpc = dbrr");
		builder.addStatement("continue");

		builder.endControlFlow();

		builder.addStatement("mpc = nmpc");
		builder.addStatement("slice -= elapsed");
		builder.addStatement("elapsed = 0");

		builder.beginControlFlow("if (exit || (slice <= 0))");
		builder.addStatement("break");
		builder.endControlFlow();

		builder.endControlFlow();

		builder.addStatement("consumed -= slice");
		builder.endControlFlow();

		builder.beginControlFlow("if ((sswi & $N) != 0)", SSWI_PSLC.field);
		builder.addStatement("this.slice = slice");
		builder.endControlFlow();

		builder.addStatement("return consumed");

		return builder.build();
	}

	public static MethodSpec fetch16_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("fetch16");

		builder.addModifiers(PUBLIC, ABSTRACT);
		builder.returns(int.class).addParameter(int.class, "aob");

		return builder.build();
	}

	public static MethodSpec fetch32_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("fetch32");

		builder.addModifiers(PUBLIC, ABSTRACT);
		builder.returns(int.class).addParameter(int.class, "aob");

		return builder.build();
	}

	public static MethodSpec read8_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("read8");

		builder.addModifiers(PUBLIC, ABSTRACT);
		builder.returns(byte.class).addParameter(int.class, "aob");

		return builder.build();
	}

	public static MethodSpec read16_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("read16");

		builder.addModifiers(PUBLIC, ABSTRACT);
		builder.returns(short.class).addParameter(int.class, "aob");

		return builder.build();
	}

	public static MethodSpec read32_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("read32");

		builder.addModifiers(PUBLIC, ABSTRACT);
		builder.returns(int.class).addParameter(int.class, "aob");

		return builder.build();
	}

	public static MethodSpec write8_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("write8");

		builder.addModifiers(PUBLIC, ABSTRACT);
		builder.addParameter(int.class, "aob").addParameter(int.class, "dob");

		return builder.build();
	}

	public static MethodSpec write16_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("write16");

		builder.addModifiers(PUBLIC, ABSTRACT);
		builder.addParameter(int.class, "aob").addParameter(int.class, "dob");

		return builder.build();
	}

	public static MethodSpec write32_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("write32");

		builder.addModifiers(PUBLIC, ABSTRACT);
		builder.addParameter(int.class, "aob").addParameter(int.class, "dob");

		return builder.build();
	}

	public static MethodSpec handle_bkpt_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("handle_bkpt");

		builder.addModifiers(PUBLIC);
		builder.returns(int.class).addParameter(int.class, "pc").addParameter(int.class, "data");

		builder.addStatement("return 0");

		return builder.build();
	}

	public static MethodSpec handle_linea_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("handle_linea");

		builder.addModifiers(PUBLIC);
		builder.returns(int.class).addParameter(int.class, "pc").addParameter(int.class, "data");

		builder.addStatement("return 0");

		return builder.build();
	}

	public static MethodSpec handle_illegal_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("handle_illegal");

		builder.addModifiers(PUBLIC);
		builder.returns(int.class).addParameter(int.class, "pc").addParameter(int.class, "ir");

		builder.addStatement("return 0");

		return builder.build();
	}

	public static MethodSpec handle_interrupt_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("handle_interrupt");

		builder.addModifiers(PUBLIC);
		builder.returns(int.class).addParameter(int.class, "level");

		builder.addStatement("return $N", IRQ_DFLT.field);

		return builder.build();
	}

	public static MethodSpec handle_reset_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("handle_reset");

		builder.addModifiers(PUBLIC);

		return builder.build();
	}

	public static MethodSpec handle_trace_method() {
		MethodSpec.Builder builder = MethodSpec.methodBuilder("handle_trace");

		builder.addModifiers(PUBLIC);
		builder.returns(int.class).addParameter(int.class, "pc");

		builder.addStatement("return 0");

		return builder.build();
	}

	private static class FieldInt16Constant {
		public final FieldSpec field;
		public int value;

		public FieldInt16Constant(String name, int value) {
			this.field = FieldSpec.builder(int.class, name, PUBLIC, STATIC, FINAL).initializer("$L", hex16(value))
					.build();
			this.value = value;
		}
	}

	private static class FieldInt32Constant {
		public final FieldSpec field;
		public int value;

		public FieldInt32Constant(String name, int value) {
			this.field = FieldSpec.builder(int.class, name, PUBLIC, STATIC, FINAL).initializer("$L", hex32(value))
					.build();
			this.value = value;
		}
	}

	private static final String hex16(int value) {
		return String.format("0x%04x", value & 0xffff);
	}

	private static final String hex32(int value) {
		return String.format("0x%08x", value);
	}

	class MicroBlock {
		List<MicroInsn> instructions = new ArrayList<>();
		MicroInsn sswclr = null;
		String name = null;

		private MicroBlock() {
		}

		public void emit(int mpc, MethodSpec.Builder builder) {
			if (name == null) {
				builder.addCode(String.format("case %d:\n", mpc));
			} else {
				builder.addCode(String.format("case %d: /* %s */\n", mpc, name));
			}

			for (MicroInsn insn : instructions) {
				insn.emit(builder);
			}
		}
	}

	interface MicroInsn {
		public void emit(MethodSpec.Builder builder);
	}

	private void microprefetch(boolean resume, String eawritesize) {
		if (resume) {
			if (eawritesize != null) {
				addFormattedMicroInsn(String.format("mpc = ea_resume_write%s", eawritesize));
			} else {
				addFormattedMicroInsn("mpc = resume_prefetch");
			}
			addFormattedMicroInsn("continue");
		} else {
			if (eawritesize != null) {
				throw new IllegalStateException();
				// addFormattedMicroInsn(String.format("nmpc = ea_write%s", eawritesize));
				// prefetchir(0, true);
				// addFormattedMicroInsn("break");
			} else {
				prefetchir(0, false);
			}
		}
	}

	private void prefetchir(int fc, boolean has_nmpc) {
		anyread16(null, SSW_IF | SSW_P | fc, "pc + scan", 0, has_nmpc, null);
	}

	private void fetchimm16(String reg, int fc) {
		anyread16(reg, SSW_DF | SSW_P | fc, "pc + scan", 0, false, () -> {
			addFormattedMicroInsn("scan += 2");
		});
	}

	private void fetchimm32(String reg, int fc) {
		anyread32(reg, SSW_DF | SSW_P | fc, "pc + scan", 0, false, () -> {
			addFormattedMicroInsn("scan += 4");
		});
	}

	private void fetch16(String reg, int fc, String aob) {
		anyread16(reg, SSW_DF | SSW_P | fc, aob, 0, false, null);
	}

	private void fetch32(String reg, int fc, String aob) {
		fetch32(reg, fc, aob, 0);
	}

	private void fetch32(String reg, int fc, String aob, int off) {
		anyread32(reg, SSW_DF | SSW_P | fc, aob, off, false, null);
	}

	private void read8(String reg, int fc, String aob) {
		read8(reg, fc, aob, 0);
	}

	private void read8(String reg, int fc, String aob, int off) {
		anyread8(reg, SSW_DF | SSW_D | fc, aob, off);
	}

	private void read16(String reg, int fc, String aob) {
		read16(reg, fc, aob, 0);
	}

	private void read16(String reg, int fc, String aob, int off) {
		anyread16(reg, SSW_DF | SSW_D | fc, aob, off, false, null);
	}

	private void read32(String reg, int fc, String aob) {
		read32(reg, fc, aob, 0);
	}

	private void read32(String reg, int fc, String aob, int off) {
		anyread32(reg, SSW_DF | SSW_D | fc, aob, off, false, null);
	}

	private void writevob(int fc, String aob, int format, int off) {
		String vob = "tvn";
		if (format != 0) {
			vob = String.format("0x%04x | %s", format << 12, vob);
		}
		write16(fc, aob, vob, off, false);
	}

	private void write8(int fc, String aob, String value, int off, boolean has_nmpc) {
		fc |= SSW_RR | SSW_BY;

		boolean critical = (fc & SSW_CR) != 0;
		if ((!critical) && (!has_nmpc)) {
			int nstate8 = blocks.size();

			addFormattedMicroInsn("nmpc = %d", nstate8);
		}

		addFormattedMicroInsn("elapsed += 4");

		String address = setup_busaccess(fc, aob, off);
		addFormattedMicroInsn("write8(%s, dob = %s)", address, value);
		check_buserror(fc, "bevt");

		if ((!critical) && (!has_nmpc)) {
			blocks.add(new MicroBlock());
		}
	}

	private void write16(int fc, String aob, String value, int off, boolean has_nmpc) {
		write16(fc, aob, value, off, has_nmpc, null);
	}

	private void write16(int fc, String aob, String value, int off, boolean has_nmpc, Runnable post) {
		fc |= SSW_RR;

		boolean critical = (fc & SSW_CR) != 0;
		int nstate16 = blocks.size();

		if ((!critical) && (!has_nmpc)) {
			addFormattedMicroInsn("nmpc = %d", nstate16);
		}

		addFormattedMicroInsn("elapsed += 4");

		String address = setup_busaccess(fc, aob, off);
		addFormattedMicroInsn("write16(%s, dob = %s)", address, value);
		if (post != null) {
			post.run();
		}
		check_buserror(fc, "bevt");

		if ((!critical) && (!has_nmpc)) {
			blocks.add(new MicroBlock());
		}
	}

	private void write32(int fc, String aob, String value, int off, boolean has_nmpc) {
		write32(fc, aob, value, off, has_nmpc, null);
	}
	
	private void write32(int fc, String aob, String value, int off, boolean has_nmpc, Runnable post) {
		fc |= SSW_RR | SSW_32 | SSW_WD;

		boolean critical = (fc & SSW_CR) != 0;
		int nstate32 = blocks.size();

		if (critical) {
			addFormattedMicroInsn("elapsed += 8");
		} else {
			if (!has_nmpc) {
				addFormattedMicroInsn("nmpc = %d", nstate32);
			}
			addFormattedMicroInsn("elapsed += 4");
		}

		String address = setup_busaccess(fc, aob, off);
		addFormattedMicroInsn("write32(%s, dob = %s)", address, value);
		if (post != null) {
			post.run();
		}
		check_buserror(fc, "bevtw32");

		if ((!critical) && (!has_nmpc)) {
			blocks.add(new MicroBlock());
		}
	}

	private void anyread8(String reg, int fc, String aob, int off) {
		String dib = "dib";

		fc |= SSW_RR | SSW_RW | SSW_BY;

		boolean critical = (fc & SSW_CR) != 0;

		if (!critical) {
			int nstate8 = blocks.size();

			addFormattedMicroInsn("nmpc = %d", nstate8);
		}

		addFormattedMicroInsn("elapsed += 4");

		String address = setup_busaccess(fc, aob, off);
		addFormattedMicroInsn("%s = %s(%s)", critical && (reg != null) ? reg : dib, "read8", address);
		check_buserror(fc, "bevt");
		addBeginFormattedControlFlow("if ((ssw & 0x%04x) == 0x%04x)", SSW_BY | SSW_HB, SSW_BY | SSW_HB);
		addFormattedMicroInsn("%s >>= 8", critical && (reg != null) ? reg : dib);
		addEndControlFlow();

		if (!critical) {
			blocks.add(new MicroBlock());

			if (reg != null) {
				addFormattedMicroInsn("%s = %s", reg, dib);
			}
		}
	}

	private void anyread16(String reg, int fc, String aob, int off, boolean has_nmpc, Runnable post) {
		String dib = (fc & SSW_IF) != 0 ? "irb" : "dib";

		fc |= SSW_RR | SSW_RW;

		boolean critical = (fc & SSW_CR) != 0;
		String function = "read16";

		if ((fc & (SSW_P | SSW_D)) == SSW_P) {
			function = "fetch16";
		}

		if ((!critical) && (!has_nmpc)) {
			int nstate16 = blocks.size();

			addFormattedMicroInsn("nmpc = %d", nstate16);
		}

		addFormattedMicroInsn("elapsed += 4");

		String address = setup_busaccess(fc, aob, off);
		// addFormattedMicroInsn("%s = %s(%s)", dib, function, address);
		addFormattedMicroInsn("%s = %s(%s)", critical && (reg != null) ? reg : dib, function, address);
		if (post != null) {
			post.run();
		}
		check_buserror(fc, "bevt");

		if (!has_nmpc) {
			if (!critical) {
				blocks.add(new MicroBlock());

				if (reg != null) {
					addFormattedMicroInsn("%s = %s", reg, dib);
				}
			}
		}
	}

	// XXX
	private void anyread32(String reg, int fc, String aob, int off, boolean has_nmpc, Runnable post) {
		String dib = (fc & SSW_IF) != 0 ? "irb" : "dib";

		fc |= SSW_RR | SSW_32 | SSW_WD | SSW_RW;

		boolean critical = (fc & SSW_CR) != 0;
		String function32 = "read32";
		String berr = "bevtr32";

		if ((fc & (SSW_P | SSW_D)) == SSW_P) {
			function32 = "fetch32";
			berr = "bevtf32";
		}

		if (critical) {
			addFormattedMicroInsn("elapsed += 8");
		} else {
			if (!has_nmpc) {
				int nstate32 = blocks.size();

				addFormattedMicroInsn("nmpc = %d", nstate32);
			}

			addFormattedMicroInsn("elapsed += 4");
		}

		String address = setup_busaccess((fc & SSW_AC) != 0 ? fc | SSW_CR : fc, aob, off);
		addFormattedMicroInsn("%s = %s(%s)", critical && (reg != null) ? reg : dib, function32, address);
		if (post != null) {
			post.run();
		}
		check_buserror(fc, berr);

		if (!has_nmpc) {
			if (!critical) {
				blocks.add(new MicroBlock());

				if (reg != null) {
					addFormattedMicroInsn("%s = %s", reg, dib);
				}
			}
		}
	}

	private String setup_busaccess(int fc, String aob, int off) {
		boolean supervisor = (fc & SSW_S) != 0;
		boolean aligned = ((aob == null) && ((off & 0x0001) == 0)) || ((fc & (SSW_AL | SSW_BY)) != 0);

		String formatted = null;

		if (aob == null) {
			formatted = String.format("aob = 0x%08x", off);
		} else if (off == 0) {
			formatted = String.format("aob = %s", aob);
		} else if (off > 0) {
			formatted = String.format("aob = %s + 0x%04x", aob, off);
		} else {
			formatted = String.format("aob = %s - 0x%04x", aob, -off);
		}

		String expr = formatted;

		if (((fc & SSW_BY) == 0) && (!aligned)) {
			formatted = "aob";
		}

		if (supervisor) {
			addFormattedMicroInsn("ssw = (ssw & 0x%04x) | 0x%04x", SSW_RS.value | SSW_HL.value, (fc | SSW_S) & 0xffff);
		} else {
			addFormattedMicroInsn("ssw = (ssw & 0x%04x) | ((sr >> 11) & 0x%04x) | 0x%04x", SSW_RS.value | SSW_HL.value,
					SSW_S, fc & 0xffff);
		}

		if (((fc & SSW_BY) == 0) && (!aligned)) {
			addBeginFormattedControlFlow("if (((%s) & 0x000000001) != 0)", expr);
			if ((fc & SSW_CR) != 0) {
				/* misaligned critical transfer, signal double bus fault */
				addFormattedMicroInsn("mpc = dbrr");
				addFormattedMicroInsn("continue");
			} else {
				/* misaligned transfer, signal address error */
				addFormattedMicroInsn("mpc = aerr");
				addFormattedMicroInsn("continue");
			}
			addEndControlFlow();
		}

		return formatted;
	}

	// XXX
	private void check_buserror(int fc, String berr) {
		if ((fc & SSW_CR) != 0) {
			addBeginFormattedControlFlow("if ((ssw & 0x%04x) != 0)", SSW_BR | SSW_RS.value);
			addFormattedMicroInsn("mpc = dbrr");
			addFormattedMicroInsn("continue");
		} else {
			if (((fc & SSW_32) != 0) && ((fc & SSW_WD) != 0)) {
				addBeginFormattedControlFlow("if ((ssw & 0x%04x) != 0x%04x)", SSW_WD | SSW_BR | SSW_RS.value, SSW_WD);
			} else {
				addBeginFormattedControlFlow("if ((ssw & 0x%04x) != 0)", SSW_BR | SSW_RS.value);

				if (((fc & SSW_WD) != 0) && ((fc & SSW_RW) != 0)) {
					/*
					 * indicate bus handler that this transfer is part of a 32 bits read access
					 * (saves dibh in stackframe)
					 */
					addFormattedMicroInsn("ssw |= 0x%04x", SSW_WD);
				}
			}
			addFormattedMicroInsn("mpc = %s", berr);
			addFormattedMicroInsn("continue");
		}
		addEndControlFlow();
	}

	private void consume(int cycles, int read, int write) {
		int consumed = cycles - (read * 4) - (write * 4);

		if (consumed > 0) {
			addFormattedMicroInsn("elapsed += %d", consumed);
		} else if (cycles < 0) {
			addFormattedMicroInsn("elapsed -= %d", -consumed);
		}
	}

	private void setclr16(String reg, int set, int clr) {
		int eclr = clr & ~set & 0xffff;

		set &= 0xffff;

		if ((set != 0) && (eclr != 0)) {
			addFormattedMicroInsn("%s = (%s & ~0x%04x) | 0x%04x", reg, reg, eclr, set);
		} else if (set == 0) {
			if (eclr == 0xffff) {
				addFormattedMicroInsn("%s = 0x%04x", reg, 0);
			} else if (eclr != 0) {
				addFormattedMicroInsn("%s &= ~0x%04x", reg, eclr);
			}
		} else if (eclr == 0) {
			if ((set & 0xffff) == 0xffff) {
				addFormattedMicroInsn("%s = 0x%04x", reg, set);
			} else {
				addFormattedMicroInsn("%s |= 0x%04x", reg, set);
			}
		}
	}

	private void setclr32(String reg, int set, int clr) {
		int eclr = clr & ~set;

		if ((set != 0) && (eclr != 0)) {
			addFormattedMicroInsn("%s = (%s & ~0x%08x) | 0x%08x", reg, reg, eclr, set);
		} else if (set == 0) {
			if (~eclr == 0) {
				addFormattedMicroInsn("%s = 0x%08x", reg, 0);
			} else if (eclr != 0) {
				addFormattedMicroInsn("%s &= ~0x%08x", reg, eclr);
			}
		} else if (eclr == 0) {
			if (set == 0xffffffff) {
				addFormattedMicroInsn("%s = 0x%08x", reg, set);
			} else {
				addFormattedMicroInsn("%s |= 0x%08x", reg, set);
			}
		}
	}

	private void setclr(String reg, int set, int clr) {
		if (((set & 0xffff0000) == 0) && ((clr & 0xffff0000) == 0)) {
			setclr16(reg, set, clr);
		} else {
			setclr32(reg, set, clr);
		}
	}

	private void settvn(int tvn) {
		addFormattedMicroInsn("tvn = %d", tvn << 2);
	}

	private void addState(String name) {
		MicroBlock block = new MicroBlock();

		block.name = name;
		blocks.add(block);
	}

	private void addMicroInsn(MicroInsn insn, boolean clrssw) {
		if (blocks.isEmpty()) {
			blocks.add(new MicroBlock());
		}

		MicroBlock block = blocks.get(blocks.size() - 1);

		block.instructions.add(insn);

		if (clrssw) {
			block.instructions.remove(block.sswclr);
			block.sswclr = insn;
		}
	}

	private void addFormattedMicroInsn(String expression, Object... args) {
		addMicroInsn((builder) -> {
			builder.addStatement(String.format(expression, args));
		}, false);
	}

	private void addBeginFormattedControlFlow(String expression, Object... args) {
		addMicroInsn((builder) -> {
			builder.beginControlFlow(String.format(expression, args));
		}, false);
	}

	private void addNextFormattedControlFlow(String expression, Object... args) {
		addMicroInsn((builder) -> {
			builder.nextControlFlow(String.format(expression, args));
		}, false);
	}

	private void addEndControlFlow() {
		addMicroInsn((builder) -> {
			builder.endControlFlow();
		}, false);
	}

	/**
	 * This SSW bits must be cleared for each transfer
	 */
	private static final int SSW_MASK = SSW_RR | SSW_IF | SSW_DF | SSW_RM | SSW_HB | SSW_BY | SSW_RW | SSW_32 | SSW_WD
			| SSW_BR | SSW_S | SSW_P | SSW_D;

	private void addClearSSW() {
		addMicroInsn((builder) -> {
			builder.addStatement(String.format("ssw &= ~0x%04x", SSW_MASK));
		}, true);
	}

	private void unclearSSW() {
		if (!blocks.isEmpty()) {
			MicroBlock block = blocks.get(blocks.size() - 1);

			block.instructions.remove(block.sswclr);
			block.sswclr = null;
		}
	}

	public String getStateName(int address) {
		MicroBlock block = blocks.get(address);

		return block.name;
	}

	public int getStateAddress(String name) {
		int length = blocks.size();
		for (int address = 0; address < length; address++) {
			MicroBlock block = blocks.get(address);

			if (name.equals(block.name)) {
				return address;
			}
		}

		return -1;
	}

}
