package miggy.cpu.genpoet;

import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;

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

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

public class CorePLAGenerator {

	private static final int EA_AIS = 0x0001;
	private static final int EA_AIPS = 0x0002;
	private static final int EA_PAIS = 0x0004;
	private static final int EA_DAS = 0x0008;
	private static final int EA_DAIS = 0x0010;
	private static final int EA_ADR16 = 0x0020;
	private static final int EA_ADR32 = 0x0040;
	private static final int EA_DPC = 0x0080;
	private static final int EA_DPCI = 0x0100;
	private static final int EA_IMM = 0x0200; /* always fetched, no address */
	private static final int EA_FETCH = 0x8000;

	private static final int EA_ALL = EA_AIS | EA_AIPS | EA_PAIS | EA_DAS | EA_DAIS | EA_ADR16 | EA_ADR32 | EA_DPC
			| EA_DPCI | EA_IMM;
	private static final int EA_MEM = EA_ALL & (~EA_IMM);
	private static final int EA_CTRL = EA_MEM & (~(EA_AIPS | EA_PAIS));
	private static final int EA_MALT = EA_MEM & (~(EA_DPC | EA_DPCI));

	private static String move(CoreGenerator gen, String size, String src, String dst) {
		return gen.gen_move(size, src, dst);
	}

	private static String dyadic(CoreGenerator gen, String op, String size, String src, String dst) {
		return gen.gen_dyadic(op, size, src, dst);
	}

//	private static String dyadic(CoreGenerator gen, String name, String op, String size, String src, String dst, boolean update) {
//		return gen.gen_dyadic(name, op, size, src, dst, update);
//	}
//
//	private static String dyadic(CoreGenerator gen, String op, String size, String src, String dst, boolean update) {
//		return gen.gen_dyadic(op, op, size, src, dst, update);
//	}

	private static String monadic(CoreGenerator gen, String op, String size, String dst) {
		return gen.gen_monadic(op, size, dst);
	}

	public static void fillops(CoreGenerator gen, List<MacroEntry> entries, MacroEntry[] array) {
		appendOP_eas(0x0000, 0xffc0, "ori_b_imm8", EA_FETCH | EA_MALT, "8", dyadic(gen, "or", "b", "imm8", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "or", "b", "imm8", "ea"));
		});
		appendOP_eas(0x0040, 0xffc0, "ori_w_imm16", EA_FETCH | EA_MALT, "16", dyadic(gen, "or", "w", "imm16", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "or", "w", "imm16", "ea"));
		});
		appendOP_eas(0x0080, 0xffc0, "ori_l_imm32", EA_FETCH | EA_MALT, "32", dyadic(gen, "or", "l", "imm32", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm32", n2, mode == 0 ? "dbrr" : dyadic(gen, "or", "l", "imm32", "ea"));
		});

		appendOP(gen, entries, array, 0x003c, 0xffff, "ori_imm8_ccr", "op_imm16", dyadic(gen, "or", "b", "imm8", "ccr"), "dbrr");
		appendOP(gen, entries, array, 0x007c, 0xffff, "ori_i16u_sr", "op_imm16", dyadic(gen, "or", "w", "imm16", "sr"), "dbrr");

		appendOP_eas(0x0100, 0xf1c0, "btst_dd", EA_FETCH | EA_ALL, "8", dyadic(gen, "btst", "l", "dd", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", mode == 0 ? "dbrr" : dyadic(gen, "btst", "b", "dd", "ea"));
		});

		appendOP(gen, entries, array, 0x0108, 0xf1f8, "movep_w_das_dd", "ea_das16", "dbrr", "op_movepw_das_dd");

		appendOP_eas(0x0140, 0xf1c0, "bchg_dd", EA_FETCH | EA_MALT, "8", dyadic(gen, "bchg", "l", "dd", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", mode == 0 ? "dbrr" : dyadic(gen, "bchg", "b", "dd", "ea"));
		});

		appendOP(gen, entries, array, 0x0148, 0xf1f8, "movep_l_das_dd", "ea_das32", "dbrr", "op_movepl_das_dd");

		appendOP_eas(0x0180, 0xf1c0, "bclr_dd", EA_FETCH | EA_MALT, "8", dyadic(gen, "bclr", "l", "dd", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", mode == 0 ? "dbrr" : dyadic(gen, "bclr", "b", "dd", "ea"));
		});

		appendOP(gen, entries, array, 0x0188, 0xf1f8, "movep_w_dd_das", "ea_das16", "dbrr", "op_movepw_dd_das");

		appendOP_eas(0x01c0, 0xf1c0, "bset_dd", EA_FETCH | EA_MALT, "8", dyadic(gen, "bset", "l", "dd", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", mode == 0 ? "dbrr" : dyadic(gen, "bset", "b", "dd", "ea"));
		});

		appendOP(gen, entries, array, 0x01c8, 0xf1f8, "movep_l_dd_das", "ea_das32", "dbrr", "op_movepl_dd_das");

		appendOP_eas(0x0200, 0xffc0, "andi_b_imm8", EA_FETCH | EA_MALT, "8", dyadic(gen, "and", "b", "imm8", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "and", "b", "imm8", "ea"));
		});
		appendOP_eas(0x0240, 0xffc0, "andi_w_imm16", EA_FETCH | EA_MALT, "16", dyadic(gen, "and", "w", "imm16", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "and", "w", "imm16", "ea"));
		});
		appendOP_eas(0x0280, 0xffc0, "andi_l_imm32", EA_FETCH | EA_MALT, "32", dyadic(gen, "and", "l", "imm32", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm32", n2, mode == 0 ? "dbrr" : dyadic(gen, "and", "l", "imm32", "ea"));
		});

		appendOP(gen, entries, array, 0x023c, 0xffff, "andi_imm8_ccr", "op_imm16", dyadic(gen, "and", "b", "imm8", "ccr"), "dbrr");
		appendOP(gen, entries, array, 0x027c, 0xffff, "andi_i16u_sr", "op_imm16", dyadic(gen, "and", "w", "imm16", "sr"), "dbrr");

		appendOP_eas(0x0400, 0xffc0, "subi_b_imm8", EA_FETCH | EA_MALT, "8", dyadic(gen, "sub", "b", "imm8", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "sub", "b", "imm8", "ea"));
		});
		appendOP_eas(0x0440, 0xffc0, "subi_w_imm16", EA_FETCH | EA_MALT, "16", dyadic(gen, "sub", "w", "imm16", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "sub", "w", "imm16", "ea"));
		});
		appendOP_eas(0x0480, 0xffc0, "subi_l_imm32", EA_FETCH | EA_MALT, "32", dyadic(gen, "sub", "l", "imm32", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm32", n2, mode == 0 ? "dbrr" : dyadic(gen, "sub", "l", "imm32", "ea"));
		});

		appendOP_eas(0x0600, 0xffc0, "addi_b_imm8", EA_FETCH | EA_MALT, "8", dyadic(gen, "add", "b", "imm8", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "add", "b", "imm8", "ea"));
		});
		appendOP_eas(0x0640, 0xffc0, "addi_w_imm16", EA_FETCH | EA_MALT, "16", dyadic(gen, "add", "w", "imm16", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "add", "w", "imm16", "ea"));
		});
		appendOP_eas(0x0680, 0xffc0, "addi_l_imm32", EA_FETCH | EA_MALT, "32", dyadic(gen, "add", "l", "imm32", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm32", n2, mode == 0 ? "dbrr" : dyadic(gen, "add", "l", "imm32", "ea"));
		});

		appendOP_eas(0x0800, 0xffc0, "btst_imm8", EA_FETCH | EA_MEM, "8", dyadic(gen, "btst", "l", "imm8", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "btst", "b", "imm8", "ea"));
		});
		appendOP_eas(0x0840, 0xffc0, "bchg_imm8", EA_FETCH | EA_MALT, "8", dyadic(gen, "bchg", "l", "imm8", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "bchg", "b", "imm8", "ea"));
		});
		appendOP_eas(0x0880, 0xffc0, "bclr_imm8", EA_FETCH | EA_MALT, "8", dyadic(gen, "bclr", "l", "imm8", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "bclr", "b", "imm8", "ea"));
		});
		appendOP_eas(0x08c0, 0xffc0, "bset_imm8", EA_FETCH | EA_MALT, "8", dyadic(gen, "bset", "l", "imm8", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "bset", "b", "imm8", "ea"));
		});

		appendOP_eas(0x0a00, 0xffc0, "eori_b_imm8", EA_FETCH | EA_MALT, "8", dyadic(gen, "eor", "b", "imm8", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "eor", "b", "imm8", "ea"));
		});
		appendOP_eas(0x0a40, 0xffc0, "eori_w_imm16", EA_FETCH | EA_MALT, "16", dyadic(gen, "eor", "w", "imm16", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "eor", "w", "imm16", "ea"));
		});
		appendOP_eas(0x0a80, 0xffc0, "eori_l_imm32", EA_FETCH | EA_MALT, "32", dyadic(gen, "eor", "l", "imm32", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm32", n2, mode == 0 ? "dbrr" : dyadic(gen, "eor", "l", "imm32", "ea"));
		});

		appendOP(gen, entries, array, 0x0a3c, 0xffff, "eori_imm8_ccr", "op_imm16", dyadic(gen, "eor", "b", "imm8", "ccr"), "dbrr");
		appendOP(gen, entries, array, 0x0a7c, 0xffff, "eori_i16u_sr", "op_imm16", dyadic(gen, "eor", "w", "imm8", "sr"), "dbrr");

		appendOP_eas(0x0c00, 0xffc0, "cmpi_b_imm8", EA_FETCH | EA_MALT, "8", dyadic(gen, "cmp", "b", "imm8", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "cmp", "b", "imm8", "ea"));
		});
		appendOP_eas(0x0c40, 0xffc0, "cmpi_w_imm16", EA_FETCH | EA_MALT, "16", dyadic(gen, "cmp", "w", "imm16", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm16", n2, mode == 0 ? "dbrr" : dyadic(gen, "cmp", "w", "imm16", "ea"));
		});
		appendOP_eas(0x0c80, 0xffc0, "cmpi_l_imm32", EA_FETCH | EA_MALT, "32", dyadic(gen, "cmp", "l", "imm32", "ds"), null, (opcode, opmask, opname, n2, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, "op_imm32", n2, mode == 0 ? "dbrr" : dyadic(gen, "cmp", "l", "imm32", "ea"));
		});

		appendOP_eas(0x1000, 0xf000, "move_b", EA_FETCH | EA_ALL, "8", move(gen , "b", "ds", "ea"), null, (scode, smask, sname, n1, smode) -> {
			appendOP_ead(scode, smask, sname, EA_MALT, "8", move(gen , "b", "ea", "dd"), null, (opcode, opmask, opname, n3, dmode) -> {
				if ((smode == 0) && (dmode == 0)) {
					appendOP(gen, entries, array, opcode, opmask, opname, move(gen , "b", "ds", "dd"), "dbrr", "dbrr");
				} else if (smode == 0) {
					appendOP(gen, entries, array, opcode, opmask, opname, n3, n1, "dbrr");
				} else if (dmode == 0) {
					appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", n3);
				} else {
					appendOP(gen, entries, array, opcode, opmask, opname, n1, move(gen , "b", "ea", "ea"), n3);
				}
			});
		});

		appendOP_eas(0x2000, 0xf000, "move_l", EA_FETCH | EA_ALL, "32", move(gen , "l", "ds", "ea"), move(gen , "l", "as", "ea"), (scode, smask, sname, n1, smode) -> {
			appendOP_ead(scode, smask, sname, EA_MALT, "32", move(gen , "l", "ea", "dd"), null, (opcode, opmask, opname, n3, dmode) -> {
				if ((smode == 0) && (dmode == 0)) {
					appendOP(gen, entries, array, opcode, opmask, opname, move(gen , "l", (opcode & 0x38) == 8 ? "as" : "ds", "dd"), "dbrr", "dbrr");
				} else if (smode == 0) {
					appendOP(gen, entries, array, opcode, opmask, opname, n3, n1, "dbrr");
				} else if (dmode == 0) {
					appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", n3);
				} else {
					appendOP(gen, entries, array, opcode, opmask, opname, n1, move(gen , "l", "ea", "ea"), n3);
				}
			});
		});

		appendOP_eas(0x2040, 0xf1c0, "movea_l", EA_FETCH | EA_ALL, "32", move(gen , "l", "ds", "ad"), move(gen , "l", "as", "ad"), (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_ad", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", move(gen , "l", "ea", "ad"));
			}
		});

		appendOP_eas(0x3000, 0xf000, "move_w", EA_FETCH | EA_ALL, "16", move(gen , "w", "ds", "ea"), move(gen , "w", "as", "ea"), (scode, smask, sname, n1, smode) -> {
			appendOP_ead(scode, smask, sname, EA_MALT, "16", move(gen , "w", "ea", "dd"), null, (opcode, opmask, opname, n3, dmode) -> {
				if ((smode == 0) && (dmode == 0)) {
					appendOP(gen, entries, array, opcode, opmask, opname, move(gen , "w", (opcode & 0x38) == 8 ? "as" : "ds", "dd"), "dbrr", "dbrr");
				} else if (smode == 0) {
					appendOP(gen, entries, array, opcode, opmask, opname, n3, n1, "dbrr");
				} else if (dmode == 0) {
					appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", n3);
				} else {
					appendOP(gen, entries, array, opcode, opmask, opname, n1, move(gen , "w", "ea", "ea"), n3);
				}
			});
		});

		appendOP_eas(0x3040, 0xf1c0, "movea_w", EA_FETCH | EA_ALL, "16", move(gen , "w", "ds", "ad"), move(gen , "w", "as", "ad"), (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_ad", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", move(gen , "w", "ea", "ad"));
			}
		});

		appendOP_eas(0x4000, 0xffc0, "negx_b", EA_FETCH | EA_MALT, "8", monadic(gen, "negx", "b", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "negx", "b", "ea"));
			}
		});
		appendOP_eas(0x4040, 0xffc0, "negx_w", EA_FETCH | EA_MALT, "16", monadic(gen, "negx", "w", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "negx", "w", "ea"));
			}
		});
		appendOP_eas(0x4080, 0xffc0, "negx_l", EA_FETCH | EA_MALT, "32", monadic(gen, "negx", "l", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "negx", "l", "ea"));
			}
		});

		appendOP_eas(0x40c0, 0xffc0, "move_sr", EA_MALT, "16", move(gen , "w", "sr", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", move(gen , "w", "sr", "ea"));
			}
		});
		
		appendOP_eas(0x4180, 0xf1c0, "chk_w", EA_FETCH | EA_ALL, "16", "op_chk_w_ds", null, (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);
			
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", mode == 0 ? "dbrr" : "op_chk_w_ea");
		});

		appendOP_eas(0x42c0, 0xffc0, "move_ccr", EA_MALT, "16", move(gen , "w", "ccr", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", move(gen , "w", "ccr", "ea"));
			}
		});

		appendOP_eas(0x44c0, 0xffc0, "move", EA_FETCH | EA_ALL, "16", move(gen , "w", "ds", "ccr"), null, (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_ccr", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", move(gen , "w", "ea", "ccr"));
			}
		});

		appendOP_eas(0x46c0, 0xffc0, "move", EA_FETCH | EA_ALL, "16", move(gen , "w", "ds", "sr"), null, (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_sr", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", move(gen , "w", "ea", "sr"));
			}
		});

		appendOP_eas(0x41d0, 0xf1c0, "lea", EA_CTRL, "16", (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_ad", opname);

			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_lea_ea_ad");
		});

		appendOP_eas(0x4200, 0xffc0, "clr_b", EA_MALT, "8", "op_clrb_ds", null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_clrb_ea");
			}
		});
		appendOP_eas(0x4240, 0xffc0, "clr_w", EA_MALT, "16", "op_clrw_ds", null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_clrw_ea");
			}
		});
		appendOP_eas(0x4280, 0xffc0, "clr_l", EA_MALT, "32", "op_clrl_ds", null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_clrl_ea");
			}
		});

		appendOP_eas(0x4400, 0xffc0, "neg_b", EA_FETCH | EA_MALT, "8", monadic(gen, "neg", "b", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "neg", "b", "ea"));
			}
		});
		appendOP_eas(0x4440, 0xffc0, "neg_w", EA_FETCH | EA_MALT, "16", monadic(gen, "neg", "w", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "neg", "l", "ea"));
			}
		});
		appendOP_eas(0x4480, 0xffc0, "neg_l", EA_FETCH | EA_MALT, "32", monadic(gen, "neg", "l", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "neg", "l", "ea"));
			}
		});

		appendOP_eas(0x4600, 0xffc0, "not_b", EA_FETCH | EA_MALT, "8", monadic(gen, "not", "b", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "not", "b", "ea"));
			}
		});
		appendOP_eas(0x4640, 0xffc0, "not_w", EA_FETCH | EA_MALT, "16", monadic(gen, "not", "w", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "not", "w", "ea"));
			}
		});
		appendOP_eas(0x4680, 0xffc0, "not_l", EA_FETCH | EA_MALT, "32", monadic(gen, "not", "l", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "not", "l", "ea"));
			}
		});

		appendOP_eas(0x4800, 0xffc0, "nbcd_b", EA_FETCH | EA_MALT, "8", monadic(gen, "nbcd", "b", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "nbcd", "b", "ea"));
			}
		});

		appendOP(gen, entries, array, 0x4840, 0xfff8, "swap_ds", "op_swap_ds", "dbrr", "dbrr");
		
		appendOP_eas(0x4850, 0xffc0, "pea", EA_CTRL, "16", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_pea");
		});

		appendOP(gen, entries, array, 0x4880, 0xfff8, "ext_w_ds", "op_extw_ds", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x48c0, 0xfff8, "ext_l_ds", "op_extl_ds", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x49c0, 0xfff8, "extb_l_ds", "op_extbl_ds", "dbrr", "dbrr");
		
		appendOP_eas(0x4a00, 0xffc0, "tst_b", EA_FETCH | EA_MALT, "8", monadic(gen, "tst", "b", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "tst", "b", "ea"));
			}
		});
		appendOP_eas(0x4a40, 0xffc0, "tst_w", EA_FETCH | EA_MALT, "16", monadic(gen, "tst", "w", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "tst", "w", "ea"));
			}
		});
		appendOP_eas(0x4a80, 0xffc0, "tst_l", EA_FETCH | EA_MALT, "32", monadic(gen, "tst", "l", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "tst", "l", "ea"));
			}
		});

		appendOP(gen, entries, array, 0x4848, 0xfff8, "bkpt", "op_bkpt", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x4afc, 0xffff, "illegal", "op_illegal", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x4e40, 0xfff0, "trap_imm4", "op_trap", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x4e50, 0xfff8, "link_as_imm16", "op_imm16", "op_link_as_imm16", "dbrr");
		appendOP(gen, entries, array, 0x4808, 0xfff8, "link_as_imm32", "op_imm32", "op_link_as_imm32", "dbrr");
		appendOP(gen, entries, array, 0x4e58, 0xfff8, "unlk_as", "op_unlk_as", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x4e60, 0xfff8, "move_as_usp", "op_move_as_usp", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x4e68, 0xfff8, "move_usp_as", "op_move_usp_as", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x4e70, 0xffff, "reset", "op_reset", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x4e71, 0xffff, "nop", "op_nop", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x4e72, 0xffff, "stop_i16u", "op_imm16", "op_stop", "dbrr");
		appendOP(gen, entries, array, 0x4e73, 0xffff, "rte", "op_rte", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x4e75, 0xffff, "rts", "op_rts", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x4e76, 0xffff, "trapv", "op_trapv", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x4e77, 0xffff, "rtr", "op_rtr", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x4e7a, 0xffff, "movec_cr_rz", "op_movec_cr_rz", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x4e7b, 0xffff, "movec_rz_cr", "op_movec_rz_cr", "dbrr", "dbrr");

		appendOP_eas(0x4890, 0xffc0, "movem_w_list", EA_CTRL, "16", (opcode, opmask, opname, n1, mode) -> {
			//opname = String.format("%s", opname);

			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_movemw_rr_ea");
		});

		appendOP_eas(0x4890, 0xffc0, "movem_w_listp", EA_PAIS, "16", (opcode, opmask, opname, n1, mode) -> {
			//opname = String.format("%s", opname);

			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_movemw_rr_pais");
		});

		appendOP_eas(0x48d0, 0xffc0, "movem_l_list", EA_CTRL, "32", (opcode, opmask, opname, n1, mode) -> {
			//opname = String.format("%s", opname);

			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_moveml_rr_ea");
		});

		appendOP_eas(0x48d0, 0xffc0, "movem_l_listp", EA_PAIS, "32", (opcode, opmask, opname, n1, mode) -> {
			//opname = String.format("%s", opname);

			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_moveml_rr_pais");
		});

		appendOP_eas(0x4c90, 0xffc0, "movem_w", EA_CTRL, "16", (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_list", opname);

			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_movemw_ea_rr");
		});

		appendOP_eas(0x4c90, 0xffc0, "movem_w", EA_AIPS, "16", (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_list", opname);

			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_movemw_aips_rr");
		});

		appendOP_eas(0x4cd0, 0xffc0, "movem_l", EA_CTRL, "32", (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_list", opname);

			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_moveml_ea_rr");
		});

		appendOP_eas(0x4cd0, 0xffc0, "movem_l", EA_AIPS, "32", (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_list", opname);

			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_moveml_aips_rr");
		});

		appendOP_eas(0x4e90, 0xffc0, "jsr", EA_CTRL, "16", (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s", opname);

			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_jsr");
		});

		appendOP_eas(0x4ec0, 0xffc0, "jmp", EA_CTRL, "16", (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s", opname);

			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_jmp");
		});

		appendOP_eas(0x5000, 0xf1c0, "addq_b_imm3", EA_FETCH | EA_MALT, "8", dyadic(gen, "add", "b", "imm3", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "add", "b", "imm3", "ea"));
			}
		});
		appendOP_eas(0x5040, 0xf1c0, "addq_w_imm3", EA_FETCH | EA_MALT, "16", dyadic(gen, "add", "w", "imm3", "ds"), dyadic(gen, "add", "w", "imm3", "as"), (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "add", "w", "imm3", "ea"));
			}
		});
		appendOP_eas(0x5080, 0xf1c0, "addq_l_imm3", EA_FETCH | EA_MALT, "32", dyadic(gen, "add", "l", "imm3", "ds"), dyadic(gen, "add", "l", "imm3", "as"), (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "add", "l", "imm3", "ea"));
			}
		});

		appendOP(gen, entries, array, 0x50c8, 0xf0f8, "dbcc", "op_dbcc", "dbrr", "dbrr");

		appendOP_eas(0x5100, 0xf1c0, "subq_b_imm3", EA_FETCH | EA_MALT, "8", dyadic(gen, "sub", "b", "imm3", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "sub", "b", "imm3", "ea"));
			}
		});
		appendOP_eas(0x5140, 0xf1c0, "subq_w_imm3", EA_FETCH | EA_MALT, "16", dyadic(gen, "sub", "w", "imm3", "ds"), dyadic(gen, "sub", "w", "imm3", "as"), (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "sub", "w", "imm3", "ea"));
			}
		});
		appendOP_eas(0x5180, 0xf1c0, "subq_l_imm3", EA_FETCH | EA_MALT, "32", dyadic(gen, "sub", "l", "imm3", "ds"), dyadic(gen, "sub", "l", "imm3", "as"), (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "sub", "l", "imm3", "ea"));
			}
		});

		appendOP_eas(0x50c0, 0xf0c0, "scc_b", EA_MALT, "8", "op_scc_b_ds", null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "op_scc_b_ea");
			}
		});

		appendOP(gen, entries, array, 0x50fa, 0xf0ff, "trapcc16", "op_trapcc16", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x50fb, 0xf0ff, "trapcc32", "op_trapcc32", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x50fc, 0xf0ff, "trapcc", "op_trapcc", "dbrr", "dbrr");
		
		appendOP(gen, entries, array, 0x6000, 0xff00, "bra_rel8", "op_bra8", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x6000, 0xffff, "bra_rel16", "op_imm16", "op_bra16", "dbrr");
		appendOP(gen, entries, array, 0x60ff, 0xffff, "bra_rel32", "op_imm32", "op_bra32", "dbrr");
		appendOP(gen, entries, array, 0x6100, 0xff00, "bsr_rel8", "op_bsr8", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x6100, 0xffff, "bsr_rel16", "op_imm16", "op_bsr16", "dbrr");
		appendOP(gen, entries, array, 0x61ff, 0xffff, "bsr_rel32", "op_imm32", "op_bsr32", "dbrr");
		appendOP(gen, entries, array, 0x6000, 0xf000, "bcc_rel8", "op_bcc8", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x6000, 0xf0ff, "bcc_rel16", "op_imm16", "op_bcc16", "dbrr");
		appendOP(gen, entries, array, 0x60ff, 0xf0ff, "bcc_rel32", "op_imm32", "op_bcc32", "dbrr");

		appendOP(gen, entries, array, 0x7000, 0xf100, "moveq_imm8o_dd", move(gen, "l", "imm8o", "dd"), "dbrr", "dbrr");

		appendOP_eas(0x8000, 0xf1c0, "or_b", EA_FETCH | EA_ALL, "8", dyadic(gen, "or", "b", "ds", "dd"), null, (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "or", "b", "ea", "dd"));
			}
		});
		appendOP_eas(0x8040, 0xf1c0, "or_w", EA_FETCH | EA_ALL, "16", dyadic(gen, "or", "w", "ds", "dd"), null, (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "or", "w", "ea", "dd"));
			}
		});
		appendOP_eas(0x8080, 0xf1c0, "or_l", EA_FETCH | EA_ALL, "32", dyadic(gen, "or", "l", "ds", "dd"), null, (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "or", "l", "ea", "dd"));
			}
		});

		appendOP(gen, entries, array, 0x8100, 0xf1f8, "sbcd_ds_dd", dyadic(gen, "sbcd", "b", "ds", "dd"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x8108, 0xf1f8, "sbcd_pais_paid", "ea_pais8_read", dyadic(gen, "sbcd", "b", "ea", "ea"), "ea_paid8_read");

		appendOP_eas(0x8100, 0xf1c0, "or_b_dd", EA_FETCH | EA_MALT, "8", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "or", "b", "dd", "ea"));
		});
		appendOP_eas(0x8140, 0xf1c0, "or_w_dd", EA_FETCH | EA_MALT, "16", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "or", "w", "dd", "ea"));
		});
		appendOP_eas(0x8180, 0xf1c0, "or_l_dd", EA_FETCH | EA_MALT, "32", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "or", "l", "dd", "ea"));
		});

		appendOP_eas(0x9000, 0xf1c0, "sub_b", EA_FETCH | EA_ALL, "8", dyadic(gen, "sub", "b", "ds", "dd"), null, (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "sub", "b", "ea", "dd"));
			}
		});
		appendOP_eas(0x9040, 0xf1c0, "sub_w", EA_FETCH | EA_ALL, "16", dyadic(gen, "sub", "w", "ds", "dd"), dyadic(gen, "sub", "w", "as", "dd"), (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "sub", "w", "ea", "dd"));
			}
		});
		appendOP_eas(0x9080, 0xf1c0, "sub_l", EA_FETCH | EA_ALL, "32", dyadic(gen, "sub", "l", "ds", "dd"), dyadic(gen, "sub", "l", "as", "dd"), (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "sub", "l", "ea", "dd"));
			}
		});
		appendOP_eas(0x9100, 0xf1c0, "sub_b_dd", EA_FETCH | EA_MALT, "8", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "sub", "b", "dd", "ea"));
		});
		appendOP_eas(0x9150, 0xf1c0, "sub_w_dd", EA_FETCH | EA_MALT, "16", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "sub", "w", "dd", "ea"));
		});
		appendOP_eas(0x9190, 0xf1c0, "sub_l_dd", EA_FETCH | EA_MALT, "32", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "sub", "l", "dd", "ea"));
		});

		appendOP(gen, entries, array, 0x9100, 0xf1f8, "subx_b_ds_dd", dyadic(gen, "subx", "b", "ds", "dd"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x9108, 0xf1f8, "subx_b_pais_paid", "ea_pais8_read", dyadic(gen, "subx", "b", "ea", "ea"),
				"ea_paid8_read");
		appendOP(gen, entries, array, 0x9140, 0xf1f8, "subx_w_ds_dd", dyadic(gen, "subx", "w", "ds", "dd"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x9148, 0xf1f8, "subx_w_pais_paid", "ea_pais16_read", dyadic(gen, "subx", "w", "ea", "ea"),
				"ea_paid16_read");
		appendOP(gen, entries, array, 0x9180, 0xf1f8, "subx_l_ds_dd", dyadic(gen, "subx", "l", "ds", "dd"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0x9188, 0xf1f8, "subx_l_pais_paid", "ea_pais32_read", dyadic(gen, "subx", "l", "ea", "ea"), "ea_paid32_read");

		appendOP_eas(0x90c0, 0xf1c0, "suba_w", EA_FETCH | EA_ALL, "16", dyadic(gen, "sub", "w", "ds", "ad"), dyadic(gen, "sub", "w", "as", "ad"), (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_ad", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "sub", "w", "ea", "ad"));
			}
		});
		appendOP_eas(0x91c0, 0xf1c0, "suba_l", EA_FETCH | EA_ALL, "32", dyadic(gen, "sub", "l", "ds", "ad"), dyadic(gen, "sub", "l", "as", "ad"), (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_ad", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "sub", "l", "ea", "ad"));
			}
		});

		appendOP(gen, entries, array, 0xa000, 0xf000, "linea_imm12", "op_linea", "dbrr", "dbrr");

		appendOP_eas(0xb000, 0xf1c0, "cmp_b", EA_FETCH | EA_ALL, "8", dyadic(gen, "cmp", "b", "ds", "dd"), null, (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "cmp", "b", "ea", "dd"));
			}
		});
		appendOP_eas(0xb040, 0xf1c0, "cmp_w", EA_FETCH | EA_ALL, "16", dyadic(gen, "cmp", "w", "ds", "dd"), dyadic(gen, "cmp", "w", "as", "dd"), (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "cmp", "w", "ea", "dd"));
			}
		});
		appendOP_eas(0xb080, 0xf1c0, "cmp_l", EA_FETCH | EA_ALL, "32", dyadic(gen, "cmp", "l", "ds", "dd"), dyadic(gen, "cmp", "l", "as", "dd"), (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "cmp", "l", "ea", "dd"));
			}
		});

		appendOP_eas(0xb0c0, 0xf1c0, "cmpa_w", EA_FETCH | EA_ALL, "16", dyadic(gen, "cmp", "w", "ds", "ad"), dyadic(gen, "cmp", "w", "as", "ad"), (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_ad", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "cmp", "w", "ea", "ad"));
			}
		});
		appendOP_eas(0xb1c0, 0xf1c0, "cmpa_l", EA_FETCH | EA_ALL, "32", dyadic(gen, "cmp", "l", "ds", "ad"), dyadic(gen, "cmp", "l", "as", "ad"), (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_ad", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "cmp", "l", "ea", "ad"));
			}
		});

		appendOP(gen, entries, array, 0xb108, 0xf1f8, "cmpm_b_aips_aipd", "ea_aips8_read", dyadic(gen, "cmpm", "b", "ea", "ea"), "ea_aipd8_read");
		appendOP(gen, entries, array, 0xb148, 0xf1f8, "cmpm_w_aips_aipd", "ea_aips16_read", dyadic(gen, "cmpm", "w", "ea", "ea"), "ea_aipd16_read");
		appendOP(gen, entries, array, 0xb188, 0xf1f8, "cmpm_l_aips_aipd", "ea_aips32_read", dyadic(gen, "cmpm", "l", "ea", "ea"), "ea_aipd32_read");

		appendOP_eas(0xb100, 0xf1c0, "eor_b_dd", EA_FETCH | EA_MALT, "8", dyadic(gen, "eor", "b", "dd", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "eor", "b", "dd", "ea"));
			}
		});
		appendOP_eas(0xb140, 0xf1c0, "eor_w_dd", EA_FETCH | EA_MALT, "16", dyadic(gen, "eor", "w", "dd", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "eor", "w", "dd", "ea"));
			}
		});
		appendOP_eas(0xb180, 0xf1c0, "eor_l_dd", EA_FETCH | EA_MALT, "32", dyadic(gen, "eor", "l", "dd", "ds"), null, (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "eor", "l", "dd", "ea"));
			}
		});

		appendOP_eas(0xc000, 0xf1c0, "and_b", EA_FETCH | EA_ALL, "8", dyadic(gen, "and", "b", "ds", "dd"), null, (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "and", "b", "ea", "dd"));
			}
		});
		appendOP_eas(0xc040, 0xf1c0, "and_w", EA_FETCH | EA_ALL, "16", dyadic(gen, "and", "w", "ds", "dd"), null, (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "and", "w", "ea", "dd"));
			}
		});
		appendOP_eas(0xc080, 0xf1c0, "and_l", EA_FETCH | EA_ALL, "32", dyadic(gen, "and", "l", "ds", "dd"), null, (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "and", "l", "ea", "dd"));
			}
		});

		appendOP(gen, entries, array, 0xc140, 0xf1f8, "exg_dd_ds", "op_exg_dd_ds", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xc148, 0xf1f8, "exg_ad_as", "op_exg_ad_as", "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xc188, 0xf1f8, "exg_dd_as", "op_exg_dd_as", "dbrr", "dbrr");
		
		appendOP_eas(0xc100, 0xf1c0, "and_b_dd", EA_FETCH | EA_MALT, "8", (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "and", "b", "dd", "ea"));
			}
		});
		appendOP_eas(0xc140, 0xf1c0, "and_w_dd", EA_FETCH | EA_MALT, "16", (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "and", "w", "dd", "ea"));
			}
		});
		appendOP_eas(0xc180, 0xf1c0, "and_l_dd", EA_FETCH | EA_MALT, "32", (opcode, opmask, opname, n1, mode) -> {
			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "and", "l", "dd", "ea"));
			}
		});

		appendOP(gen, entries, array, 0xc100, 0xf1f8, "abcd_ds_dd", dyadic(gen, "abcd", "b", "ds", "dd"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xc108, 0xf1f8, "abcd_pais_paid", "ea_pais8_read", dyadic(gen, "abcd", "b", "ea", "ea"), "ea_paid8_read");

		appendOP_eas(0xd000, 0xf1c0, "add_b", EA_FETCH | EA_ALL, "8", dyadic(gen, "add", "b", "ds", "dd"), null, (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "add", "b", "ea", "dd"));
			}
		});
		appendOP_eas(0xd040, 0xf1c0, "add_w", EA_FETCH | EA_ALL, "16", dyadic(gen, "add", "w", "ds", "dd"), dyadic(gen, "add", "w", "as", "dd"), (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "add", "w", "ea", "dd"));
			}
		});
		appendOP_eas(0xd080, 0xf1c0, "add_l", EA_FETCH | EA_ALL, "32", dyadic(gen, "add", "l", "ds", "dd"), dyadic(gen, "add", "l", "as", "dd"), (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_dd", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "add", "l", "ea", "dd"));
			}
		});
		appendOP_eas(0xd100, 0xf1c0, "add_b_dd", EA_FETCH | EA_MALT, "8", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "add", "b", "dd", "ea"));
		});
		appendOP_eas(0xd150, 0xf1c0, "add_w_dd", EA_FETCH | EA_MALT, "16", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "add", "w", "dd", "ea"));
		});
		appendOP_eas(0xd190, 0xf1c0, "add_l_dd", EA_FETCH | EA_MALT, "32", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "add", "l", "dd", "ea"));
		});

		appendOP(gen, entries, array, 0xd100, 0xf1f8, "addx_b_ds_dd", dyadic(gen, "addx", "b", "ds", "dd"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xd108, 0xf1f8, "addx_b_pais_paid", "ea_pais8_read", dyadic(gen, "addx", "b", "ea", "ea"),
				"ea_paid8_read");
		appendOP(gen, entries, array, 0xd140, 0xf1f8, "addx_w_ds_dd", dyadic(gen, "addx", "w", "ds", "dd"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xd148, 0xf1f8, "addx_w_pais_paid", "ea_pais16_read", dyadic(gen, "addx", "w", "ea", "ea"),
				"ea_paid16_read");
		appendOP(gen, entries, array, 0xd180, 0xf1f8, "addx_l_ds_dd", dyadic(gen, "addx", "l", "ds", "dd"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xd188, 0xf1f8, "addx_l_pais_paid", "ea_pais32_read", dyadic(gen, "addx", "l", "ea", "ea"), "ea_paid32_read");

		appendOP_eas(0xd0c0, 0xf1c0, "adda_w", EA_FETCH | EA_ALL, "16", dyadic(gen, "add", "w", "ds", "ad"), dyadic(gen, "add", "w", "as", "ad"), (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_ad", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "add", "w", "ea", "ad"));
			}
		});
		appendOP_eas(0xd1c0, 0xf1c0, "adda_l", EA_FETCH | EA_ALL, "32", dyadic(gen, "add", "l", "ds", "ad"), dyadic(gen, "add", "l", "as", "ad"), (opcode, opmask, opname, n1, mode) -> {
			opname = String.format("%s_ad", opname);

			if (mode == 0) {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", "dbrr");
			} else {
				appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", dyadic(gen, "add", "l", "ea", "ad"));
			}
		});
		
		appendOP(gen, entries, array, 0xe000, 0xf1f8, "asr_b_imm3_ds", dyadic(gen, "asr", "b", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe020, 0xf1f8, "asr_b_dd_ds", dyadic(gen, "asr", "b", "dd", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe040, 0xf1f8, "asr_w_imm3_ds", dyadic(gen, "asr", "w", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe060, 0xf1f8, "asr_w_dd_ds", dyadic(gen, "asr", "w", "dd", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe080, 0xf1f8, "asr_l_imm3_ds", dyadic(gen, "asr", "l", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe0a0, 0xf1f8, "asr_l_dd_ds", dyadic(gen, "asr", "l", "dd", "ds"), "dbrr", "dbrr");
		appendOP_eas(0xe0d0, 0xffc0, "asr", EA_FETCH | EA_MALT, "16", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "asr", "w", "ea"));
		});

		appendOP(gen, entries, array, 0xe100, 0xf1f8, "asl_b_imm3_ds", dyadic(gen, "asl", "b", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe120, 0xf1f8, "asl_b_dd_ds", dyadic(gen, "asl", "b", "dd", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe140, 0xf1f8, "asl_w_imm3_ds", dyadic(gen, "asl", "w", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe160, 0xf1f8, "asl_w_dd_ds", dyadic(gen, "asl", "w", "dd", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe180, 0xf1f8, "asl_l_imm3_ds", dyadic(gen, "asl", "l", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe1a0, 0xf1f8, "asl_l_dd_ds", dyadic(gen, "asl", "l", "dd", "ds"), "dbrr", "dbrr");
		appendOP_eas(0xe1d0, 0xffc0, "asl", EA_FETCH | EA_MALT, "16", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "asl", "w", "ea"));
		});
		
		appendOP(gen, entries, array, 0xe008, 0xf1f8, "lsr_b_imm3_ds", dyadic(gen, "lsr", "b", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe028, 0xf1f8, "lsr_b_dd_ds", dyadic(gen, "lsr", "b", "dd", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe048, 0xf1f8, "lsr_w_imm3_ds", dyadic(gen, "lsr", "w", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe068, 0xf1f8, "lsr_w_dd_ds", dyadic(gen, "lsr", "w", "dd", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe088, 0xf1f8, "lsr_l_imm3_ds", dyadic(gen, "lsr", "l", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe0a8, 0xf1f8, "lsr_l_dd_ds", dyadic(gen, "lsr", "l", "dd", "ds"), "dbrr", "dbrr");
		appendOP_eas(0xe2d0, 0xffc0, "lsr", EA_FETCH | EA_MALT, "16", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "lsr", "w", "ea"));
		});

		appendOP(gen, entries, array, 0xe108, 0xf1f8, "lsl_b_imm3_ds", dyadic(gen, "lsl", "b", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe128, 0xf1f8, "lsl_b_dd_ds", dyadic(gen, "lsl", "b", "dd", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe148, 0xf1f8, "lsl_w_imm3_ds", dyadic(gen, "lsl", "w", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe168, 0xf1f8, "lsl_w_dd_ds", dyadic(gen, "lsl", "w", "dd", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe188, 0xf1f8, "lsl_l_imm3_ds", dyadic(gen, "lsl", "l", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe1a8, 0xf1f8, "lsl_l_dd_ds", dyadic(gen, "lsl", "l", "dd", "ds"), "dbrr", "dbrr");
		appendOP_eas(0xe3d8, 0xffc0, "lsl", EA_FETCH | EA_MALT, "16", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "lsl", "w", "ea"));
		});
		
		appendOP(gen, entries, array, 0xe018, 0xf1f8, "ror_b_imm3_ds", dyadic(gen, "ror", "b", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe038, 0xf1f8, "ror_b_dd_ds", dyadic(gen, "ror", "b", "dd", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe058, 0xf1f8, "ror_w_imm3_ds", dyadic(gen, "ror", "w", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe078, 0xf1f8, "ror_w_dd_ds", dyadic(gen, "ror", "w", "dd", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe098, 0xf1f8, "ror_l_imm3_ds", dyadic(gen, "ror", "l", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe0b8, 0xf1f8, "ror_l_dd_ds", dyadic(gen, "ror", "l", "dd", "ds"), "dbrr", "dbrr");
		appendOP_eas(0xe6d0, 0xffc0, "ror", EA_FETCH | EA_MALT, "16", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "ror", "w", "ea"));
		});

		appendOP(gen, entries, array, 0xe118, 0xf1f8, "rol_b_imm3_ds", dyadic(gen, "rol", "b", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe138, 0xf1f8, "rol_b_dd_ds", dyadic(gen, "rol", "b", "dd", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe158, 0xf1f8, "rol_w_imm3_ds", dyadic(gen, "rol", "w", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe178, 0xf1f8, "rol_w_dd_ds", dyadic(gen, "rol", "w", "dd", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe198, 0xf1f8, "rol_l_imm3_ds", dyadic(gen, "rol", "l", "imm3", "ds"), "dbrr", "dbrr");
		appendOP(gen, entries, array, 0xe1b8, 0xf1f8, "rol_l_dd_ds", dyadic(gen, "rol", "l", "dd", "ds"), "dbrr", "dbrr");
		appendOP_eas(0xe7d0, 0xffc0, "rol", EA_FETCH | EA_MALT, "16", (opcode, opmask, opname, n1, mode) -> {
			appendOP(gen, entries, array, opcode, opmask, opname, n1, "dbrr", monadic(gen, "rol", "w", "ea"));
		});

		appendOP(gen, entries, array, 0xf000, 0xf000, "linef_imm12", "op_linef", "dbrr", "dbrr");
	}

	public static void main(String[] args) throws IOException {
		TreeMap<OPEntry, MacroEntry> macros = new TreeMap<OPEntry, MacroEntry>();
		TreeSet<String> imports = new TreeSet<String>();

		CoreGenerator gen = new CoreGenerator();

		fillops(gen, macros);

		JavaFile.Builder builder = JavaFile.builder("miggy.cpupoet", corePLAType(gen, macros, imports));

		for (String item : imports) {
			builder.addStaticImport(ClassName.get("miggy.cpupoet", "Core"), item);
		}

		JavaFile javaFile = builder.build();

		javaFile.writeTo(System.out);
	}

	protected static void fillops(CoreGenerator gen, Map<OPEntry, MacroEntry> macros) {
		List<MacroEntry> entries = new ArrayList<MacroEntry>();
		MacroEntry[] array = new MacroEntry[0xffff + 1];

		fillops(gen, entries, array);

		for (MacroEntry entry : entries) {
			for (OPEntry op : entry.ops) {
				macros.put(op, entry);
			}
		}
	}

	protected static TypeSpec corePLAType(CoreGenerator gen, Map<OPEntry, MacroEntry> macros, Set<String> imports) {
		TypeSpec.Builder builder = TypeSpec.enumBuilder("MacroPLA").addModifiers(PUBLIC);
		ClassName platype = ClassName.get("miggy.cpupoet", "MacroPLA");
		ClassName plaarray = ClassName.get("miggy.cpupoet", "MacroPLA[]");

		FieldSpec opcodes = FieldSpec.builder(plaarray, "DECODER", PRIVATE, STATIC, FINAL)
				.initializer("new $T[0xffff + 1]", platype).build();

		builder.addField(FieldSpec.builder(int.class, "opcode", PUBLIC, FINAL).build());
		builder.addField(FieldSpec.builder(int.class, "opmask", PUBLIC, FINAL).build());
		builder.addField(FieldSpec.builder(int.class, "a1", PUBLIC, FINAL).build());
		builder.addField(FieldSpec.builder(int.class, "a2", PUBLIC, FINAL).build());
		builder.addField(FieldSpec.builder(int.class, "a3", PUBLIC, FINAL).build());

		for (Entry<OPEntry, MacroEntry> entry : macros.entrySet()) {
			OPEntry op = entry.getKey();
			MacroEntry micro = entry.getValue();

			String a1 = gen.getStateName(micro.a1);
			String a2 = gen.getStateName(micro.a2);
			String a3 = gen.getStateName(micro.a3);

			if (a1 != null) {
				imports.add(a1);
			}

			if (a2 != null) {
				imports.add(a2);
			}

			if (a3 != null) {
				imports.add(a3);
			}

			builder.addEnumConstant(op.name, TypeSpec.anonymousClassBuilder("$L, $L, $L, $L, $L",
					opcodeLiteral(op.opcode), opcodeLiteral(op.mask), a1, a2, a3).build());
		}

		MethodSpec.Builder constructor = MethodSpec.constructorBuilder();

		constructor.addModifiers(PRIVATE);
		constructor.addParameter(int.class, "opcode");
		constructor.addParameter(int.class, "opmask");
		constructor.addParameter(int.class, "a1");
		constructor.addParameter(int.class, "a2");
		constructor.addParameter(int.class, "a3");

		constructor.addStatement("this.opcode = opcode");
		constructor.addStatement("this.opmask = opmask");
		constructor.addStatement("this.a1 = a1");
		constructor.addStatement("this.a2 = a2");
		constructor.addStatement("this.a3 = a3");

		builder.addMethod(constructor.build());
		builder.addField(opcodes);

		CodeBlock.Builder initializer = CodeBlock.builder();

		initializer.beginControlFlow("for (MacroPLA insn : MacroPLA.values())");
		initializer.beginControlFlow(
				"for (int ir = insn.opcode & insn.opmask; ir < DECODER.length; ir = maskedIncrement(ir, insn.opmask))");
		initializer.beginControlFlow("if ((ir & insn.opmask) == insn.opcode)");
		initializer.addStatement("int bits = 0");
		initializer.beginControlFlow("if (DECODER[ir] != null)");
		initializer.addStatement("bits = Integer.bitCount(DECODER[ir].opmask)");
		initializer.endControlFlow();
		initializer.beginControlFlow("if ((bits == 0) || (Integer.bitCount(insn.opmask) > bits))");
		initializer.addStatement("DECODER[ir] = insn");
		initializer.endControlFlow();
		initializer.endControlFlow();
		initializer.endControlFlow();
		initializer.endControlFlow();
		initializer.beginControlFlow("for (int ir = 0; ir < DECODER.length; ir++)");
		initializer.beginControlFlow("if (DECODER[ir] == null)");
		initializer.addStatement("DECODER[ir] = illegal");
		initializer.endControlFlow();
		initializer.endControlFlow();

		builder.addStaticBlock(initializer.build());

		MethodSpec.Builder increment = MethodSpec.methodBuilder("maskedIncrement");

		increment.addModifiers(PRIVATE, STATIC, FINAL);
		increment.returns(int.class).addParameter(int.class, "value").addParameter(int.class, "mask");

		increment.addStatement("int variableBits = ~mask");
		increment.addStatement("int base = value & mask");
		increment.addStatement("int next = mask + (value & variableBits) + 1");
		increment.addStatement("return base + (next & variableBits)");

		builder.addMethod(increment.build());

		MethodSpec.Builder decoder = MethodSpec.methodBuilder("decode");
		decoder.addModifiers(PUBLIC, STATIC, FINAL);
		decoder.addParameter(int.class, "ir");
		decoder.returns(platype);
		decoder.addStatement("return $N[ir & 0xffff]", opcodes);

		builder.addMethod(decoder.build());

		return builder.build();
	}

	private static String opcodeLiteral(int opcode) {
		return String.format("0x%04x", opcode);
	}

	interface EAConsumer {
		void append(int opcode, int mask, String opname, String ea, int mode);
	}

	public static void appendOP_ead(int opcode, int mask, String opname, int ea, String size, String ds, String as, EAConsumer consumer) {
		if (ds != null) {
			consumer.append((opcode & 0xfe3f) | 0x0000, (mask & 0xfe3f) | 0x01c0, String.format("%s_dd", opname), ds, 0x0000);
		}

		if (as != null) {
			consumer.append((opcode & 0xfe3f) | 0x0040, (mask & 0xfe3f) | 0x01c0, String.format("%s_ad", opname), as, 0x0000);
		}

		appendOP_ead(opcode, mask, opname, ea, size, consumer);
	}

	public static void appendOP_ead(int opcode, int mask, String opname, int ea, String size, EAConsumer consumer) {
		if ((ea & EA_AIS) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_aid%s_read" : "ea_aid%s", size);

			consumer.append((opcode & 0xfe3f) | 0x0080, (mask & 0xfe3f) | 0x01c0, String.format("%s_aid", opname), n1, EA_AIS);
		}
		if ((ea & EA_AIPS) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_aipd%s_read" : "ea_aipd%s", size);

			consumer.append((opcode & 0xfe3f) | 0x00c0, (mask & 0xfe3f) | 0x01c0, String.format("%s_aipd", opname), n1, EA_AIPS);
		}
		if ((ea & EA_PAIS) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_paid%s_read" : "ea_paid%s", size);

			consumer.append((opcode & 0xfe3f) | 0x0100, (mask & 0xfe3f) | 0x01c0, String.format("%s_paid", opname), n1, EA_PAIS);
		}
		if ((ea & EA_DAS) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_dad%s_read" : "ea_dad%s", size);

			consumer.append((opcode & 0xfe3f) | 0x0140, (mask & 0xfe3f) | 0x01c0, String.format("%s_dad", opname), n1, EA_DAS);
		}
		if ((ea & EA_DAIS) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_daid%s_read" : "ea_daid%s", size);

			consumer.append((opcode & 0xfe3f) | 0x0180, (mask & 0xfe3f) | 0x01c0, String.format("%s_daid", opname), n1, EA_DAIS);
		}
		if ((ea & EA_ADR16) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_adr16d%s_read" : "ea_adr16d%s", size);

			consumer.append((opcode & 0xfe3f) | 0x01c0, (mask & 0xfe3f) | 0x0fc0, String.format("%s_adr16", opname), n1, EA_ADR16);
		}
		if ((ea & EA_ADR32) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_adr32d%s_read" : "ea_adr32d%s", size);

			consumer.append((opcode & 0xfe3f) | 0x03c0, (mask & 0xfe3f) | 0x0fc0, String.format("%s_adr32", opname), n1, EA_ADR32);
		}
	}

	public static void appendOP_eas(int opcode, int mask, String opname, int ea, String size, String ds, String as, EAConsumer consumer) {
		if (ds != null) {
			consumer.append((opcode & 0xffc0) | 0x0000, (mask & 0xffc0) | 0x0038, String.format("%s_ds", opname), ds, 0x0000);
		}

		if (as != null) {
			consumer.append((opcode & 0xffc0) | 0x0008, (mask & 0xffc0) | 0x0038, String.format("%s_as", opname), as, 0x0000);
		}

		appendOP_eas(opcode, mask, opname, ea, size, consumer);
	}

	public static void appendOP_eas(int opcode, int mask, String opname, int ea, String size, EAConsumer consumer) {
		if ((ea & EA_AIS) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_ais%s_read" : "ea_ais%s", size);

			consumer.append((opcode & 0xffc0) | 0x0010, (mask & 0xffc0) | 0x0038, String.format("%s_ais", opname), n1, EA_AIS);
		}
		if ((ea & EA_AIPS) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_aips%s_read" : "ea_aips%s", size);

			consumer.append((opcode & 0xffc0) | 0x0018, (mask & 0xffc0) | 0x0038, String.format("%s_aips", opname), n1, EA_AIPS);
		}
		if ((ea & EA_PAIS) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_pais%s_read" : "ea_pais%s", size);

			consumer.append((opcode & 0xffc0) | 0x0020, (mask & 0xffc0) | 0x0038, String.format("%s_pais", opname), n1, EA_PAIS);
		}
		if ((ea & EA_DAS) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_das%s_read" : "ea_das%s", size);

			consumer.append((opcode & 0xffc0) | 0x0028, (mask & 0xffc0) | 0x0038, String.format("%s_das", opname), n1, EA_DAS);
		}
		if ((ea & EA_DAIS) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_dais%s_read" : "ea_dais%s", size);

			consumer.append((opcode & 0xffc0) | 0x0030, (mask & 0xffc0) | 0x0038, String.format("%s_dais", opname), n1, EA_DAIS);
		}
		if ((ea & EA_DPC) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_dpc%s_read" : "ea_dpc%s", size);

			consumer.append((opcode & 0xffc0) | 0x003a, (mask & 0xffc0) | 0x003f, String.format("%s_dpc", opname), n1, EA_DPC);
		}
		if ((ea & EA_DPCI) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_dpci%s_read" : "ea_dpci%s", size);

			consumer.append((opcode & 0xffc0) | 0x003b, (mask & 0xffc0) | 0x003f, String.format("%s_dpci", opname), n1, EA_DPCI);
		}
		if ((ea & EA_ADR16) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_adr16s%s_read" : "ea_adr16s%s", size);

			consumer.append((opcode & 0xffc0) | 0x0038, (mask & 0xffc0) | 0x003f, String.format("%s_adr16", opname), n1, EA_ADR16);
		}
		if ((ea & EA_ADR32) != 0) {
			String n1 = String.format((ea & EA_FETCH) != 0 ? "ea_adr32s%s_read" : "ea_adr32s%s", size);

			consumer.append((opcode & 0xffc0) | 0x0039, (mask & 0xffc0) | 0x003f, String.format("%s_adr32", opname), n1, EA_ADR32);
		}
		if ((ea & EA_IMM) != 0) {
			String n1 = String.format("ea_imm%s_read", size);

			consumer.append((opcode & 0xffc0) | 0x003c, (mask & 0xffc0) | 0x003f, String.format("%s_imm%s", opname, size), n1, EA_IMM);
		}
	}

	private static final int maskedIncrement(int value, int mask) {
		int variableBits = ~mask;
		int base = value & mask;
		int next = mask + (value & variableBits) + 1;
		return base + (next & variableBits);
	}

	public static void appendOP(CoreGenerator gen, List<MacroEntry> entries, MacroEntry[] array, int opcode, int mask,
			String opname, String n1, String n2, String n3) {
		OPEntry op = new OPEntry(opname, opcode, mask);
		int a1 = gen.getStateAddress(n1);
		int a2 = gen.getStateAddress(n2);
		int a3 = gen.getStateAddress(n3);

		if ((a1 < 0) || (a2 < 0) || (a3 < 0)) {
			throw new IllegalStateException();
		}

		for (int ir = opcode & mask; ir <= 0xffff; ir = maskedIncrement(ir, mask)) {
			if ((ir & mask) == opcode) {
				MacroEntry entry = new MacroEntry(a1, a2, a3);
				MacroEntry found = null;

				for (int i = 0; i < entries.size(); i++) {
					if (entries.get(i).equals(entry)) {
						found = entry = entries.get(i);
					}
				}

				if (found == null) {
					entries.add(entry);
				}

				array[ir] = entry;

				if (!array[ir].ops.contains(op)) {
					array[ir].ops.add(op);
				}
			}
		}
	}

	protected static class OPEntry implements Comparable<OPEntry> {
		private String name;
		private int opcode;
		private int mask;

		public OPEntry(String name, int opcode, int mask) {
			this.name = name;
			this.opcode = opcode & mask;
			this.mask = mask;
		}

		@Override
		public int hashCode() {
			return Objects.hash(mask, opcode);
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			OPEntry other = (OPEntry) obj;
			return (opcode & mask) == (other.opcode & other.mask) && mask == other.mask;
		}

		@Override
		public int compareTo(OPEntry o) {
			int res = Integer.compare(opcode, o.opcode);

			if (res == 0) {
				res = Integer.compare(Integer.bitCount(o.mask & 0xffff), Integer.bitCount(mask & 0xffff));
			}

			return res;
		}
	}

	protected static class MacroEntry {
		private List<OPEntry> ops = new ArrayList<OPEntry>();

		protected final int a1;
		protected final int a2;
		protected final int a3;

		public MacroEntry(int a1, int a2, int a3) {
			this.a1 = a1;
			this.a2 = a2;
			this.a3 = a3;
		}

		@Override
		public int hashCode() {
			return Objects.hash(a1, a2, a3);
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			MacroEntry other = (MacroEntry) obj;
			return a1 == other.a1 && a2 == other.a2 && a3 == other.a3;
		}
	}

}
