#define RAINE

/*

Raine version:

- fixed writel trashing ecx
- commented out read/writel_split checks
- commented out address offset subs, raine does them beforehand

*/

/*
** Starscream 680x0 emulation library
** Copyright 1997, 1998, 1999 Neill Corlett
**
** Refer to STARDOC.TXT for terms of use, API reference, and directions on
** how to compile.
*/

#ifndef RAINE
#define VERSION "0.26a"
#else
#define VERSION "0.26r2"
/* 0.26r2 : patch to use the fetch array instead of the memory read arrays
   to access memory in pc relative addressing modes (needed to decrypt cps2) */
#endif

/***************************************************************************/
/*
** NOTE
**
** All 68020-related variables and functions are currently experimental, and
** unsupported.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

/***************************************************************************/
/*
** Register Usage
** --------------
**
** This is fairly consistent throughout the file.  Occasionally, EAX or EDX
** will be used as a scratch register (in some cases the x86 instruction set
** demands this).
**
** EAX: Bit   0  : V flag
**      Bit  1-7 : MUST BE ZERO
**      Bit   8  : C flag
**      Bit  14  : Z flag
**      Bit  15  : N flag
**      Bit 16-31: undefined
** EBX: Lower 16 bits: Current instruction word or register number
**      Upper 16 bits: zero
** ECX: Primary data
** EDX: Primary address
** EBP: Current base offset of PC
** ESI: Current PC, including base offset
** EDI: Cycle counter
*/
/***************************************************************************/
/*
**  68010 Loop Mode Timing
**  ----------------------
**
**  Loop mode is implemented entirely in the DBRA instruction.  It will
**  detect when it's supposed to be in loop mode, and adjust its timing when
**  applicable.
**
**  The __loopmode variable controls when loop mode is active.  It is set to
**  1 after an eligible loop is completed.  It is set to 0 when the loop
**  terminates, or when an interrupt / exception occurs.
**
**  Loop info byte:
**
**  Bits 1-3:  Continuation cycles / 2
**  Bits 4-6:  Termination cycles / 2
**  Bits 7-0:  (Expiration cycles - continuation cycles) / 2
**  (bit 7 wraps around to bit 0)
**
**  With the loop info byte in AL:
**     To get the continuation cycles:
**          and eax,byte 0Eh
**     To get the termination cycles:
**          shr al,3
**          and eax,byte 0Eh
**     To get the continue/expire difference:
**          rol al,2
**          and eax,byte 06h
**
**  Default = DBh
**           (11011011)
**                101 : 101 = 5   2*5            = 10 continuation cycles
**             101    : 101 = 5   2*5            = 10 termination cycles
**            1      1:  11 = 3   2*3 = 6   10+6 = 16 expiration cycles
**
**  (10/10/16 corresponds to the normal DBRA timing behavior)
*/
/***************************************************************************/
/*
** Algorithm for trace checkpoint in s680x0exec:
**
** If the SR trace flag is set {
**   Set the trace trickybit.  This differentiates us from the out-of-time
**    case above.
**   Set cycles_leftover = cycles_needed.
**   Force a context switch.
** } otherwise {
**   Clear the trace trickybit.
** }
** Begin the fetch/decode/execute loop as usual.
**
**
** In selected ret_timing routines:
**
** Subtract usual number of cycles from edi
** If I'm out of time (edi is negative) {
**   Jump to execend with SF set, as usual.
** } otherwise (guaranteed at least one more instruction) {
**   Jump to the s680x0exec trace checkpoint.
** }
**
**
** Make sure that the group 1 exception handler clears the trace trickybit.
**
**
** Upon reaching execend:
**
** If the trace trickybit is set {
**   Set cycles_needed = cycles_leftover.
**   Add cycles_needed to edi.
**   Generate a trace exception (clearing the SR trace flag in the process).
**   Clear the trace trickybit.
**   If edi is positive, resume the fetch/decode/execute loop.
**    Otherwise, fall through to the usual epilogue code.
** }
*/
/***************************************************************************/
/*
** Rebasing notes
** --------------
**
** Cached rebase happens on:
**  * JMP, JSR, RTS, RTE, RTR, RTD
**  * Exceptions (except hardware interrupts)
**
** Uncached rebase happens on:
**  * Entry to s680x0exec()
**  * Hardware interrupts
**  * Supervisor flag change
**  * Cache disable/invalidate (68020+)
**
** I can't think of any good reason why the hardware interrupt case should be
** uncached, except it happens to be convenient.
*/

typedef unsigned char  byte;
typedef unsigned short word;
typedef unsigned int   dword;

static int use_stack   = -1;
static int hog         = -1;
static int addressbits = -1;
static int cputype     = -1;
static char *sourcename = NULL;

/* This counts the number of instruction handling routines.  There's not much
** point to it except for curiosity. */
static int routine_counter = 0;

/* Misc. constants */
static char *x86ax   [5] = {"?", "al"  , "ax"  , "?", "eax"  };
static char *x86bx   [5] = {"?", "bl"  , "bx"  , "?", "ebx"  };
static char *x86cx   [5] = {"?", "cl"  , "cx"  , "?", "ecx"  };
static char *x86dx   [5] = {"?", "dl"  , "dx"  , "?", "edx"  };
static char *sizename[5] = {"?", "byte", "word", "?", "dword"};
static int quickvalue[8] = {8, 1, 2, 3, 4, 5, 6, 7};
static char direction[2] = {'r','l'};

/* Output file where code will be emitted */
static FILE *codefile;

/* Line number - used to make temporary labels i.e. "ln1234" */
static int linenum;

/* Effective address modes */
enum eamode {
  dreg, /* Data Register Direct*/
  areg, /* Address Register Direct */
  aind, /* Address Register Indirect */
  ainc, /* Address Register Indirect with Post-Increment */
  adec, /* Address Register Indirect with Pre-Decrement */
  adsp, /* Address Register Indirect with Displacement */
  axdp, /* Address Register Indirect with Index */
  absw, /* Absolute Short */
  absl, /* Absolute Long  */
  pcdp, /* Program Counter with Displacement */
  pcxd, /* Program Counter with Index */
  immd /* Immediate */
};

/* Loop information (68010) */
static int loop_c_cycles;
static int loop_t_cycles;
static int loop_x_cycles;
static unsigned char loopinfo[0x10000];

/*
** Misc. global variables which are used while generating instruction
** handling routines.  Some of these may assume more than one role.
*/
static enum eamode main_eamode;   /* EA mode, usually source */
static enum eamode main_destmode; /* EA mode, destination (for MOVE) */
static int main_size;             /* Operand size (1, 2, or 4) */
static int sizedef;               /* Size field in instruction word */
static int main_reg;              /* Register number */
static int main_cc;               /* Condition code (0-F) */
static int main_dr;               /* Direction (right or left) */
static int main_ir;               /* Immediate or register (for shifts) */
static int main_qv;               /* Quick value */

/* Emit a line of code (format string with other junk) */
static void emit(const char *fmt, ...) {
	va_list a;
	va_start(a, fmt);
	if(codefile) {
		vfprintf(codefile, fmt, a);
	} else {
		fprintf(stderr, "Bad news: Tried to emit() to null file\n");
		exit(1);
	}
}

/* Dump all options.  This is delivered to stderr and to the code file. */
static void optiondump(FILE *o, char *prefix) {
	fprintf(o, "%sCPU type: %d (%d-bit addresses)\n", prefix,
		cputype, addressbits);
	fprintf(o, "%sIdentifiers begin with \"%s\"\n", prefix,
		sourcename);
	fprintf(o, "%s%s calling conventions\n", prefix,
		use_stack ? "Stack" : "Register");
	fprintf(o, "%sHog mode: %s\n", prefix,
		hog ? "On" : "Off");
}

static void gen_banner(void) {
	emit("; Generated by STARSCREAM version " VERSION "\n");
	emit("; For assembly by NASM only\n");
	emit(";\n");
	emit("; Options:\n");
	optiondump(codefile, "; *  ");
	emit(";\n");
	emit("bits 32\n");
}

static void align(int n) {
	emit("times ($$-$)&%d db 0\n", n - 1);
}

static void maskaddress(char *reg) {
	if(addressbits < 32) {
		emit("and %s,%d\n", reg, (1 << addressbits) - 1);
	}
}

static void begin_source_proc(char *fname) {
	emit("global _%s%s\n", sourcename, fname);
	emit("global %s%s\n", sourcename, fname);
	emit("global %s%s_\n", sourcename, fname);
	emit("_%s%s:\n", sourcename, fname);
	emit("%s%s:\n", sourcename, fname);
	emit("%s%s_:\n", sourcename, fname);
}

/* Generate variables */
static void gen_variables(void) {
	emit("section .data\n");
	emit("bits 32\n");
	emit("global _%scontext\n", sourcename);
	emit("global %scontext\n", sourcename);
	align(8);
	emit("_%scontext:\n", sourcename);
	emit("%scontext:\n", sourcename);
	emit("contextbegin:\n");
	/*
	** CONTEXTINFO_MEM16
	** CONTEXTINFO_MEM16FC
	**
	** 16-bit memory interface
	*/
	if(cputype <= 68010) {
		emit("__fetch                dd 0\n");
		emit("__readbyte             dd 0\n");
		emit("__readword             dd 0\n");
		emit("__writebyte            dd 0\n");
		emit("__writeword            dd 0\n");
		emit("__s_fetch              dd 0\n");
		emit("__s_readbyte           dd 0\n");
		emit("__s_readword           dd 0\n");
		emit("__s_writebyte          dd 0\n");
		emit("__s_writeword          dd 0\n");
		emit("__u_fetch              dd 0\n");
		emit("__u_readbyte           dd 0\n");
		emit("__u_readword           dd 0\n");
		emit("__u_writebyte          dd 0\n");
		emit("__u_writeword          dd 0\n");
		if(cputype == 68010) {
			emit("__f_readbyte           dd 0\n");
			emit("__f_readword           dd 0\n");
			emit("__f_writebyte          dd 0\n");
			emit("__f_writeword          dd 0\n");
		}
	/*
	** CONTEXTINFO_MEM32
	**
	** 32-bit memory interface
	*/
	} else {
		emit("__fetch                dd 0\n");
		emit("__readbus              dd 0\n");
		emit("__writebus             dd 0\n");
		emit("__s_fetch              dd 0\n");
		emit("__s_readbus            dd 0\n");
		emit("__s_writebus           dd 0\n");
		emit("__u_fetch              dd 0\n");
		emit("__u_readbus            dd 0\n");
		emit("__u_writebus           dd 0\n");
		emit("__f_readbus            dd 0\n");
		emit("__f_writebus           dd 0\n");
	}
	/*
	** CONTEXTINFO_COMMON
	**
	** Registers and other info common to all CPU types
	**
	** It should be noted that on a double fault, bit 0 of both __pc and
	** __interrupts will be set to 1.
	*/
	if(cputype >= 68000) {
		emit("__resethandler         dd 0\n");
		emit("__reg:\n");
		emit("__dreg                 dd 0,0,0,0,0,0,0,0\n");
		emit("__areg                 dd 0,0,0,0,0,0,0\n");
		emit("__a7                   dd 0\n");
		emit("__asp                  dd 0\n");
		emit("__pc                   dd 0\n");
		emit("__odometer             dd 0\n");
		/* Bit 0 of __interrupts = stopped state */
		emit("__interrupts           db 0,0,0,0,0,0,0,0\n");
		emit("__sr                   dw 0\n");
	}
	/*
	** CONTEXTINFO_68000SPECIFIC
	*/
	if(cputype == 68000) {
		emit("__contextfiller00      dw 0\n");
	}
	/*
	** CONTEXTINFO_68010
	**
	** Registers used on the 68010 and higher
	*/
	if(cputype >= 68010) {
		emit("__sfc                  db 0\n");
		emit("__dfc                  db 0\n");
		emit("__vbr                  dd 0\n");
		emit("__bkpthandler          dd 0\n");
	}
	/*
	** CONTEXTINFO_68010SPECIFIC
	**
	** Registers used only on the 68010
	*/
	if(cputype == 68010) {
		emit("__loopmode             db 0\n");
		emit("__contextfiller10      db 0,0,0\n");
	}
	/*
	** CONTEXTINFO_68020
	**
	** Registers used on the 68020 and higher
	*/
	if(cputype >= 68020) {
		/*
		** 68020 stack pointer rules (tentative)
		**
		** First of all, the 68000/68010 stack pointer behavior has
		** not changed:
		**
		** 1. In supervisor mode, __a7 contains the supervisor stack
		**    pointer and __asp contains the user stack pointer.
		** 2. In user mode, __a7 contains the user stack pointer and
		**    __asp contains the supervisor stack pointer.
		**
		** The only difference is that the "supervisor stack pointer"
		** can be either ISP or MSP.  __xsp contains whichever stack
		** pointer is _not_ the current "supervisor stack pointer".
		**
		** Here's a table summarizing the above rules:
		**
		**   S M | __a7 __asp __xsp
		**   ----+-----------------
		**   0 0 |  USP   ISP   MSP
		**   0 1 |  USP   MSP   ISP
		**   1 0 |  ISP   USP   MSP
		**   1 1 |  MSP   USP   ISP
		**
		** As usual, whenever SR changes, we have to play Stack
		** Pointer Switcheroo:
		**
		**  * If S changes: swap __asp and __a7 (as usual)
		**  * If M changes:
		**    - If S=0, swap __xsp and __asp
		**    - If S=1, swap __xsp and __a7
		*/
		emit("__xsp                  dd 0\n");
	}
/*	align(4);*/
	emit("contextend:\n");
	emit("__cycles_needed        dd 0\n");
	emit("__cycles_leftover      dd 0\n");
	emit("__fetch_region_start   dd 0\n");/* Fetch region cache */
	emit("__fetch_region_end     dd 0\n");
	emit("__xflag                db 0\n");
	/*
	**  Format of __execinfo:
	**  Bit 0:  s680x0exec currently running
	**  Bit 1:  PC out of bounds
	**  Bit 2:  Special I/O section
	**
	** "Special I/O section" is enabled during group 0 exception
	** processing, and it means a couple things:
	**   * Address and bus errors will not be tolerated (the CPU will
	**     just keel over and die).  Therefore, information such as the
	**     current PC is not relevant.
	**   * Registers are not necessarily live.  Since special I/O
	**     sections are guaranteed not to cause exceptions, this is not a
	**     problem.
	*/
	emit("__execinfo             db 0\n");
	emit("__trace_trickybit      db 0\n");/* Pending trace exception */
	emit("__filler               db 0\n");
	emit("__io_cycle_counter     dd -1\n");/*always -1 when idle*/
	emit("__io_fetchbase         dd 0\n");
	emit("__io_fetchbased_pc     dd 0\n");
	emit("__access_address       dd 0\n");
}

/* Prepare to leave into the cold, dark world of compiled C code */
static void airlock_exit(void) {
	emit("mov [__io_cycle_counter],edi\n");
	emit("mov [__io_fetchbase],ebp\n");
	emit("mov [__io_fetchbased_pc],esi\n");
	emit("push ebx\n");
	emit("push eax\n");
}

/* Prepare to return to the warm fuzzy world of assembly code
** (where everybody knows your name) */
static void airlock_enter(void) {
	emit("pop eax\n");
	emit("pop ebx\n");
	emit("mov edi,[__io_cycle_counter]\n");
	emit("mov ebp,[__io_fetchbase]\n");
	emit("mov esi,[__io_fetchbased_pc]\n");
}

enum { airlock_stacksize = 8 };

static void cache_ccr(void) {
	emit("mov al,[__sr]\n");    /* read CCR -> AL */                 /* ????????000XNZVC */
	emit("mov ah,al\n");        /* copy to AH */                     /* 000XNZVC000XNZVC */
	emit("and ax,0C10h\n");     /* isolate NZ...X */                 /* 0000NZ00000X0000 */
	emit("shl ah,3\n");       /* put NZ almost where we want it */   /* 0NZ00000000X0000 */
	emit("shr al,4\n");         /* shift X flag into bit 0 */        /* 0NZ000000000000X */
	emit("mov [__xflag],al\n"); /* store X flag */                   /* 0NZ000000000000X al -> xflag */
	emit("mov al,[__sr]\n");    /* read CCR -> AL again */           /* 0NZ00000000XNZVC */
	emit("and al,3\n");         /* isolate VC */                     /* 0NZ00000000000VC */
	emit("shr al,1\n");         /* just V */                         /* 0NZ000000000000V carry */
	emit("adc ah,ah\n");        /* append C to rest of flags */      /* NZ00000C0000000V */
}

static void writeback_ccr(void) {
	emit("shr ah,1\n");         /* C flag -> x86 carry */            /* 0NZ?????0000000V carry */
	emit("adc ax,ax\n");        /* append to V flag */               /* NZ?????0000000VC */
	emit("and ax,0C003h\n");    /* isolate NZ.......VC */            /* NZ000000000000VC */
	emit("or ah,[__xflag]\n");  /* load X flag */                    /* NZ00000X000000VC */
	emit("ror ah,4\n");         /* now we have XNZ....VC */          /* 000XNZ00000000VC */
	emit("or al,ah\n");         /* OR them together */               /* 000XNZ00000XNZVC */
	emit("mov [__sr],al\n");    /* store the result */               /* 000XNZ00000XNZVC al -> sr */
}

/*
** This will make edi _very_ negative... far enough negative that the
** leftover cycle incorporation at the end of s68000exec() shouldn't be
** enough to make it positive.
*/
static void force_context_switch(void) {
	emit("sub edi,[__cycles_needed]\n");
	emit("mov dword[__cycles_needed],0\n");
}

/*
** Put all the unused cycles in the leftover cycle bank, so we can call
** attention to the tricky bit processor.
*/
static void force_trickybit_process(void) {
	emit("inc edi\n");
	emit("add [__cycles_leftover],edi\n");
	emit("or edi,byte -1\n");/* smaller than a mov */
}

/* "newpc" has been renamed to this */
void perform_cached_rebase(void);

/* Copy either __s_* or __u_* memory map pointers */
static void copy_memory_map(char *map, char *reg) {
	emit("mov %s,[__%s_fetch]\n", reg, map);
	emit("mov [__fetch],%s\n", reg);
	if(cputype < 68020) {
		emit("mov %s,[__%s_readbyte]\n" , reg, map);
		emit("mov [__readbyte],%s\n" , reg);
		emit("mov %s,[__%s_readword]\n" , reg, map);
		emit("mov [__readword],%s\n" , reg);
		emit("mov %s,[__%s_writebyte]\n", reg, map);
		emit("mov [__writebyte],%s\n", reg);
		emit("mov %s,[__%s_writeword]\n", reg, map);
		emit("mov [__writeword],%s\n", reg);
	} else {
		emit("mov %s,[__%s_readbus]\n"  , reg, map);
		emit("mov [__readbus],%s\n"  , reg);
		emit("mov %s,[__%s_writebus]\n" , reg, map);
		emit("mov [__writebus],%s\n" , reg);
	}
}

/***************************************************************************/

static void gen_interface(void) {
	emit("section .text\n");
	emit("bits 32\n");
	emit("top:\n");

/***************************************************************************/
/*
**  s680x0init()
**
**  Entry: Nothing
**  Exit:  Zero
**
**  This must be called before anything else.  It decompresses the main jump
**  table (and loop info, in the case of the 68010).
*/
	begin_source_proc("init");

	emit("pushad\n");
	emit("mov edi,__jmptbl\n");
	emit("mov esi,__jmptblcomp\n");
	if(cputype == 68010) {
		emit("mov ebx,__looptbl\n");
	}
	emit(".decomp:\n");
	emit("lodsd\n");
	emit("mov ecx,eax\n");
	emit("and eax,0FFFFFFh\n");
	emit("shr ecx,24\n");
	emit("add eax,top\n");
	emit("inc ecx\n");
	if(cputype == 68010) {
		emit("mov ebp,ecx\n");
	}
	emit(".jloop:\n");
	emit("mov [edi],eax\n");
	emit("add edi,byte 4\n");
	emit("dec ecx\n");
	emit("jnz short .jloop\n");
	if(cputype == 68010) {
		emit("lodsb\n");
		emit(".lloop:\n");
		emit("mov [ebx],al\n");
		emit("inc ebx\n");
		emit("dec ebp\n");
		emit("jnz short .lloop\n");
	}
	emit("cmp edi,__jmptbl+262144\n");
	emit("jne short .decomp\n");

	emit("popad\n");
	emit("xor eax,eax\n");
	emit("ret\n");

/***************************************************************************/
/*
**  s680x0exec(cycles)
**
**  Entry: EAX = # cycles to execute
**  Exit:  80000000h:  success
**         80000001h:  PC out of range
**         80000002h:  unsupported stack frame
**         FFFFFFFFh:  CPU is dead because of a double fault
**       < 80000000h:  invalid instruction = address of invalid instr.
*/
	begin_source_proc("exec");

	if(use_stack) emit("mov eax,[esp+4]\n");

	/*
	** Check for stopped and double-faulted states.
	*/
	emit("test byte[__interrupts],1\n");
	emit("jz .notstopped\n");
	emit("test byte[__pc],1\n");
	emit("jz .notfaulted\n");
	emit("or eax,byte -1\n");
	emit("ret\n");
	emit(".notfaulted:\n");
	emit("add [__odometer],eax\n");
	emit("mov eax,80000000h\n");
	emit("ret\n");
	emit(".notstopped:\n");

	emit("push ebp\n");
	emit("push ebx\n");
	emit("push ecx\n");
	emit("push edx\n");
	emit("push esi\n");
	emit("push edi\n");

	emit("mov [__cycles_needed],eax\n");
	emit("mov edi,eax\n");/* store # of cycles to execute */
	emit("dec edi\n");

	emit("xor ebx,ebx\n");
	emit("mov esi,[__pc]\n");
	cache_ccr();
	emit("xor ebp,ebp\n");
	emit("mov byte[__execinfo],1\n");

	/*
	** Force an uncached re-base.
	** This fulfills the "Entry to s680x0exec()" case.
	*/
	emit("call basefunction\n");
	emit("add esi,ebp\n");
	emit("test byte[__execinfo],2\n"); /* Check for PC out of bounds */
	emit("jnz near exec_bounderror\n");

	emit("mov dword[__cycles_leftover],0\n");/* an extra precaution */

	/* PPL and Trace checkpoint */
	emit("exec_checkpoint:\n");
	emit("js short execquit\n");

	/* Check PPL */
	emit("mov cl,[__sr+1]\n");
	emit("and ecx,byte 7\n");
	emit("inc ecx\n");
	emit("mov ch,[__interrupts]\n");
	emit("or ch,ch\n");
	emit("js short .yesint\n");
	emit("shr ch,cl\n");
	emit("jz short .noint\n");
	emit(".yesint:\n");
	emit("call flush_interrupts\n");
	/* Force an uncached re-base */
	emit("call basefunction\n");
	emit("add esi,ebp\n");
	emit("test byte[__execinfo],2\n"); /* Check for PC out of bounds */
	emit("jnz near exec_bounderror\n");
	emit(".noint:\n");

	/*
	** If the SR Trace flag is set, generate a pending trace exception.
	*/
	emit("mov ch,[__sr+1]\n");
	emit("and ch,80h\n"); /* isolate trace flag */
	emit("mov [__trace_trickybit],ch\n");
	emit("jz short execloop\n");
	/*
	** Activate the tricky bit processor.
	**
	** Because edi isn't checked for negativity before entering the
	** fetch/decode/execute loop, we're guaranteed to execute at least
	** one more instruction before any trace exception.
	**
	** If another group 1 exception happens in the course of executing
	** this next instruction, then the group_1_exception routine will
	** clear the trace tricky bit and re-adjust the cycle counters, and
	** we'll pretend none of this ever happened.
	*/
	force_trickybit_process();

	emit("execloop:\n");
/*	emit("xor ebx,ebx\n");suffice to say, bits 16-31 should be zero... */
	emit("mov bx,[esi]\n");
	emit("add esi,byte 2\n");
	emit("jmp dword[__jmptbl+ebx*4]\n");
	/* Traditional loop - used when hog mode is off */
	if(!hog) {
		emit("execend:\n");
		emit("jns short execloop\n");
	}
	emit("execquit:\n");

	/*
	** Tricky Bit Processor
	*/
	/* Look for trace tricky bit */
	emit("cmp byte[__trace_trickybit],0\n");
	emit("je short execquit_notrace\n");
	/* Generate trace exception */
	emit("mov edx,24h\n");
	emit("call group_1_exception\n");
	perform_cached_rebase();
	/* Subtract time used by exception processing */
	emit("sub edi,byte %d\n", (cputype == 68010) ? 38 : 34);
	emit("execquit_notrace:\n");

	/*
	** Look for pending interrupts that exceed the current PPL.  These
	** are higher priority and are therefore processed last (the ISR will
	** end up getting control).
	*/
	emit("mov cl,[__sr+1]\n");
	emit("and ecx,byte 7\n");
	emit("inc ecx\n");
	emit("mov ch,[__interrupts]\n");
	emit("or ch,ch\n");
	emit("js short execquit_yesinterrupt\n");
	emit("shr ch,cl\n");
	emit("jz short execquit_nointerrupt\n");
	emit("execquit_yesinterrupt:\n");
	emit("call flush_interrupts\n");
	/*
	** Force an uncached re-base.
	** This fulfills the "Hardware interrupt" case.
	*/
	emit("call basefunction\n");
	emit("add esi,ebp\n");
	emit("test byte[__execinfo],2\n"); /* Check for PC out of bounds */
	emit("jnz short exec_bounderror\n");
	emit("execquit_nointerrupt:\n");

	/*
	** Incorporate leftover cycles (if any) and see if we should keep
	** running.
	*/
	emit("add edi,[__cycles_leftover]\n");
	emit("mov dword[__cycles_leftover],0\n");
	emit("jns short execloop\n");

	/* Leave s680x0exec with "Success" code. */
	emit("mov ecx,80000000h\n");

	/*
	** Exit the s680x0exec routine.  By this time the return code should
	** already be in ecx.
	*/
	emit("execexit:\n");
	emit("sub esi,ebp\n");
	writeback_ccr();
	emit("mov [__pc],esi\n");
	emit("inc edi\n");
	emit("mov edx,[__cycles_needed]\n");
	emit("sub edx,edi\n");
	emit("add [__odometer],edx\n");
	emit("mov byte[__execinfo],0\n");

	/*
	** Remember: __io_cycle_counter is always -1 when idle!
	**
	** This prevents us from having to check __execinfo during the
	** readOdometer / tripOdometer calls.
	*/
	emit("mov dword[__cycles_needed],0\n");
	emit("mov dword[__io_cycle_counter],-1\n");

	emit("mov eax,ecx\n");/* return code */
	emit("pop edi\n");
	emit("pop esi\n");
	emit("pop edx\n");
	emit("pop ecx\n");
	emit("pop ebx\n");
	emit("pop ebp\n");
	emit("ret\n");

	/*
	** Leave s680x0exec with "Out of bounds" code.
	*/
	emit("exec_bounderror:\n");
	emit("mov ecx,80000001h\n");
	emit("jmp short execexit\n");

	/*
	** Invalid instruction handler
	*/
	emit("invalidins:\n");
	emit("sub esi,byte 2\n");  /* back up one word */
	emit("mov ecx,esi\n");/* get address in ecx */
	emit("sub ecx,ebp\n");/* subtract base */
	maskaddress("ecx");
	if(addressbits == 32) {
		emit("and ecx,7FFFFFFFh\n");
	}
/*	emit("or byte[__stopped],2\n");*/
	emit("jmp short execexit\n");

/***************************************************************************/
/*
**  s680x0reset()
**
**  Entry: Nothing
**  Exit:  0 on success
**         1 on failure:
**           * if there's no Supervisor Program entry for address 0
**           * if s680x0exec() is active
**        -1 on double fault
*/
	begin_source_proc("reset");

	emit("mov eax,1\n");
	emit("test [__execinfo],al\n"); /* Ensure s680x0exec() inactive */
	emit("jnz near .return\n");
	emit("cmp dword[__s_fetch],0\n");
	emit("je near .return\n");
	emit("dec eax\n");
	emit("mov [__execinfo],al\n");
	emit("sub eax,byte 16\n");
	emit(".gp:\n");
	emit("mov dword[__reg+64+eax*4],0\n");
	emit("inc eax\n");
	emit("jnz short .gp\n");
	emit("mov [__asp],eax\n");
	if(cputype >= 68020) emit("mov [__xsp],eax\n");
	/* Set up SR for no tracing, supervisor mode, ISP, PPL 7 */
	emit("mov word[__sr],2700h\n");
	if(cputype >= 68010) {
		emit("mov [__vbr],eax\n");
		emit("mov [__sfc],al\n");
		emit("mov [__dfc],al\n");
	}
	if(cputype == 68010) {
		emit("mov [__loopmode],al\n");
 	}
	/* Copy supervisor address space information */
	copy_memory_map("s", "eax");
	/* Generate Supervisor Program Space reads to get the initial PC and
	** SSP/ISP */
	emit("mov eax,1\n"); /* assume failure */
	emit("mov [__pc],eax\n");
	emit("mov [__interrupts],al\n");
	emit("push esi\n");
	emit("push ebp\n");
	emit("xor esi,esi\n");
	emit("call basefunction\n");/* will preserve eax */
	emit("test byte[__execinfo],2\n");
	emit("jnz short .exit\n");
	emit("add esi,ebp\n");
	emit("mov eax,[esi]\n");
	emit("rol eax,16\n");
	emit("mov [__a7],eax\n");
	emit("mov eax,[esi+4]\n");
	emit("rol eax,16\n");
	emit("mov [__pc],eax\n");
	/* An address error here will cause a double fault */
	emit("and eax,byte 1\n");
	emit("mov [__interrupts],al\n");
	emit("neg eax\n"); /* -1 on double fault, 0 on success */
	emit(".exit:\n");
	emit("pop ebp\n");
	emit("pop esi\n");
	emit(".return:\n");
	emit("ret\n");

/***************************************************************************/
/*
**  s680x0interrupt(level, vector)
**
**  Entry: EAX = interrupt level
**         EDX = vector (-1 for auto, -2 for spurious)
**  Exit:  EAX = 0 on success
**               1 on failure, previous vector exists
**               2 on invalid input
*/
	begin_source_proc("interrupt");

	emit("push edx\n");
	if(use_stack) {
		emit("mov eax,[esp+8]\n");  /* eax = level  */
		emit("mov edx,[esp+12]\n"); /* edx = vector */
	}
	/*
	** Verify parameters.
	*/
	emit("cmp eax,byte 7\n");
	emit("ja short .badinput\n");
	emit("or eax,eax\n");
	emit("jz short .badinput\n");
	emit("cmp edx,255\n");
	emit("jg short .badinput\n");
	emit("cmp edx,byte -2\n");
	emit("jl short .badinput\n");
	/*
	** Calculate the vector number.
	*/
	emit("jne short .notspurious\n");
	emit("mov edx,18h\n");
	emit(".notspurious:\n");
	emit("or edx,edx\n");
	emit("jns short .notauto\n");
	emit("lea edx,[eax+18h]\n");
	emit(".notauto:\n");
	/*
	** Test to see if this interrupt level is already pending.
	** If it is, return with failure.
	*/
	emit("push ecx\n");
	emit("mov cl,al\n");
	emit("mov ah,1\n");
	emit("shl ah,cl\n");
	emit("pop ecx\n");
	emit("test [__interrupts],ah\n");
	emit("jnz .failure\n");
	/*
	** Commit the given interrupt and vector number.
	*/
	emit("or [__interrupts],ah\n");
	emit("mov ah,0\n");
	emit("mov [__interrupts+eax],dl\n");
	emit("and byte[__interrupts],0FEh\n");
	/*
	** Notify the tricky bit handler.  If we're doing this outside of
	** s680x0exec(), then the notification will have no effect, because
	** __io_cycle_counter is always -1 when idle.
	*/
	emit("mov edx,[__io_cycle_counter]\n");
	emit("inc edx\n");
	emit("add [__cycles_leftover],edx\n");
	emit("mov dword[__io_cycle_counter],-1\n");
	/*
	** Success (0)
	*/
	emit("pop edx\n");
	emit("xor eax,eax\n");
	emit("ret\n");
	/*
	** Failure (1)
	*/
	emit(".failure:\n");
	emit("pop edx\n");
	emit("mov eax,1\n");
	emit("ret\n");
	/*
	** Bad input (2)
	*/
	emit(".badinput:\n");
	emit("pop edx\n");
	emit("mov eax,2\n");
	emit("ret\n");

/***************************************************************************/
/*
**  s680x0flushInterrupts()
**
**  Flushes all pending interrupts.
**
**  Entry: Nothing
**  Exit:  Nothing
*/
	begin_source_proc("flushInterrupts");

	/*
	** If s680x0exec() is already running, then the interrupts are about
	** to get flushed anyway.  So ignore this call.
	*/
	emit("test byte[__execinfo],1\n");
	emit("jnz .noflush\n");
	/* Make registers "live" */
	emit("pushad\n");
	emit("mov esi,[__pc]\n");
	emit("xor ebp,ebp\n");
	cache_ccr();
	emit("xor edi,edi\n"); /* well, semi-live */
	emit("call flush_interrupts\n");
	emit("sub [__odometer],edi\n"); /* edi will be <= 0 here */
	emit("mov [__pc],esi\n"); /* PC guaranteed unbased */
	writeback_ccr();
	emit("popad\n");
	emit(".noflush:\n");
	emit("ret\n");

/***************************************************************************/
/*
**  s680x0GetContextSize()
**
**  Entry: Nothing
**  Exit:  Size of context array (in bytes)
*/
	begin_source_proc("GetContextSize");

	emit("mov eax,contextend-contextbegin\n");
	emit("ret\n");

/***************************************************************************/
/*
**  s680x0GetContext(context)
**
**  Entry: Address of context in EAX
**  Exit:  Nothing
*/
	begin_source_proc("GetContext");

	emit("push edx\n");
	emit("push edi\n");
	if(use_stack) emit("mov edi,[esp+12]\n");
	else          emit("mov edi,eax\n");
	emit("%%assign i 0\n");
	emit("%%rep ((contextend-contextbegin) / 8)\n");
	emit("  mov eax,[contextbegin+i+0]\n");
	emit("  mov edx,[contextbegin+i+4]\n");
	emit("  mov [edi+i+0],eax\n");
	emit("  mov [edi+i+4],edx\n");
	emit("%%assign i i+8\n");
	emit("%%endrep\n");
	emit("%%if ((contextend-contextbegin) %% 8)!=0\n");
	emit("  mov eax,[contextbegin+i+0]\n");
	emit("  mov [edi+i+0],eax\n");
	emit("%%endif\n");
	emit("pop edi\n");
	emit("pop edx\n");
	emit("xor eax,eax\n");
	emit("ret\n");

/***************************************************************************/
/*
**  s680x0SetContext(context)
**
**  Entry: Address of context in EAX
**  Exit:  Nothing
*/
	begin_source_proc("SetContext");

	emit("push edx\n");
	emit("push esi\n");
	if(use_stack) emit("mov esi,[esp+12]\n");
	else          emit("mov esi,eax\n");
	emit("%%assign i 0\n");
	emit("%%rep ((contextend-contextbegin) / 8)\n");
	emit("  mov eax,[esi+i+0]\n");
	emit("  mov edx,[esi+i+4]\n");
	emit("  mov [contextbegin+i+0],eax\n");
	emit("  mov [contextbegin+i+4],edx\n");
	emit("%%assign i i+8\n");
	emit("%%endrep\n");
	emit("%%if ((contextend-contextbegin) %% 8)!=0\n");
	emit("  mov eax,[esi+i+0]\n");
	emit("  mov [contextbegin+i+0],eax\n");
	emit("%%endif\n");
	emit("pop esi\n");
	emit("pop edx\n");
	emit("xor eax,eax\n");
	emit("ret\n");

/***************************************************************************/
/*
**  s680x0fetch(address)
**
**  (Will need to be rewritten to handle function codes)
**
**  Reads a word from memory using the _supervisor_ fetch map.
**  Entry: Address in EAX
**  Exit:  The fetched word (or -1 if the address was invalid)
*/
	begin_source_proc("fetch");

	if(use_stack) emit("mov eax,[esp+4]\n");
	emit("push ebx\n");
	emit("push esi\n");
	emit("push edi\n");/* can be destroyed by rebase */
	emit("push ebp\n");
	emit("push dword[__fetch]\n");
	emit("mov ebx,[__s_fetch]\n");
	emit("mov [__fetch],ebx\n");
	/* Must save the fetch region cache as well */
	emit("push dword[__fetch_region_start]\n");
	emit("push dword[__fetch_region_end]\n");
	/* and __execinfo */
	emit("mov bl,[__execinfo]\n");
	emit("push ebx\n");

	emit("xor ebp,ebp\n");
	emit("mov esi,eax\n");
	emit("and byte[__execinfo],0FDh\n");
	/* Force re-base */
	emit("call basefunction\n");
	emit("test byte[__execinfo],2\n");
	emit("mov eax,-1\n");
	emit("jnz short .badfetch\n");
	emit("add esi,ebp\n");
	emit("inc eax\n"); /* make eax zero */
	emit("mov ax,[esi]\n");
	emit(".badfetch:\n");
	emit("pop ebx\n");
	emit("mov [__execinfo],bl\n");
	emit("pop dword[__fetch_region_end]\n");
	emit("pop dword[__fetch_region_start]\n");
	emit("pop dword[__fetch]\n");
	emit("pop ebp\n");
	emit("pop edi\n");
	emit("pop esi\n");
	emit("pop ebx\n");
	emit("ret\n");

/***************************************************************************/
/*
**  s680x0readOdometer()
**
**  Reads the odometer (works from anywhere)
**  Entry: Nothing
**  Exit:  Odometer in EAX
*/
	begin_source_proc("readOdometer");

	emit("mov eax,[__cycles_needed]\n");
	emit("sub eax,[__io_cycle_counter]\n");
	emit("dec eax\n");               /* eax = elapsed cycles */
	emit("sub eax,[__cycles_leftover]\n");
	emit("add eax,[__odometer]\n");  /* add to old __odometer */
	emit("ret\n");

/***************************************************************************/
/*
**  s680x0tripOdometer()
**
**  Reads and then clears the odometer (works from anywhere)
**  Entry: Nothing
**  Exit:  Old odometer value in EAX
*/
	begin_source_proc("tripOdometer");
	/* Read */
	emit("mov eax,[__cycles_needed]\n");
	emit("sub eax,[__io_cycle_counter]\n");
	emit("dec eax\n");               /* eax = elapsed cycles */
	emit("sub eax,[__cycles_leftover]\n");
	emit("add [__odometer],eax\n");  /* add to old __odometer */
	/* Clear */
	emit("mov eax,[__io_cycle_counter]\n");
	emit("inc eax\n");
	emit("mov [__cycles_needed],eax\n");
	emit("mov eax,[__odometer]\n");
	emit("mov dword[__odometer],0\n");
	emit("ret\n");

/***************************************************************************/
/*
**  s680x0controlOdometer(n)
**
**  Reads the odometer, clears it only if n != 0
**  Entry: n in EAX
**  Exit:  Old odometer value in EAX
*/
	begin_source_proc("controlOdometer");
	if(use_stack) emit("mov eax,[esp+4]\n");
	emit("or eax,eax\n");
	emit("jnz short _%stripOdometer\n", sourcename);
	emit("jmp short _%sreadOdometer\n", sourcename);

/***************************************************************************/
/*
**  s680x0releaseTimeslice()
**
**  Ends the s680x0exec call prematurely.  The early exit is reflected in
**  __odometer.
**  Entry: Nothing
**  Exit:  Nothing
*/
	begin_source_proc("releaseTimeslice");

	emit("mov eax,[__cycles_needed]\n");
	emit("sub [__io_cycle_counter],eax\n");
	emit("xor eax,eax\n");
	emit("mov [__cycles_needed],eax\n");
	emit("ret\n");

/***************************************************************************/
/*
**  s680x0readPC()
**
**  Returns the current program counter.  Works anywhere, including I/O,
**  RESET, and BKPT handlers.
**
**  Note that the value returned won't necessarily coincide exactly with the
**  beginning of an instruction.
*/
	begin_source_proc("readPC");

	emit("test byte[__execinfo],1\n");
	emit("jnz short .live\n");
	emit("mov eax,[__pc]\n");
	emit("ret\n");
	emit(".live:\n");
	emit("mov eax,[__io_fetchbased_pc]\n");
	emit("sub eax,[__io_fetchbase]\n");
	emit("ret\n");

}

/***************************************************************************/

/*
** Routine that flushes pending interrupts (with correct priority).
** Assumes "live" registers, including EDI and EBP.
**
** Does not rebase the PC.  In fact, it will un-base the PC even if there
** aren't any pending interrupts.
**
** s680x0flushInterrupts() is actually a wrapper for this.
*/
static void gen_flush_interrupts(void) {
	int cycles = (cputype == 68010) ? 46 : 44;
	align(16);
	emit("flush_interrupts:\n");
	/* Unbase PC */
	emit("sub esi,ebp\n");
	emit("xor ebp,ebp\n");
	/* This loop is intentionally post-tested because interrupt level 7
	** is non-maskable. */
	emit("mov edx,7\n");
	emit("mov cl,80h\n");
	emit("mov ch,[__sr+1]\n"); /* current PPL */
	emit("and ch,7\n");
	emit(".loop:\n");
	emit("test [__interrupts],cl\n");
	emit("jz short .noint\n");
	emit("mov dl,[__interrupts+edx]\n");
	emit("not cl\n");
	emit("and [__interrupts],cl\n");
	emit("shl edx,2\n");
	emit("call group_1_exception\n");
	emit("sub edi,byte %d\n", cycles);
	emit("jmp short .intdone\n");
	emit(".noint:\n");
	emit("dec edx\n");
	emit("jz short .intdone\n");
	emit("shr cl,1\n");
	emit("cmp dl,ch\n");
	emit("jg short .loop\n");
	emit(".intdone:\n");
	emit("ret\n");
}

/***************************************************************************/

static void ret_timing(int n) {
	if(n) {
		emit("sub edi,%s%d\n", (n < 128) ? "byte " : "", n);
	} else {
		emit("or edi,edi\n");
	}
	/* If hog mode is off, jump back to the main loop */
	if(!hog) {
		emit("jmp execend\n");
	/* If hog mode is on, fetch and execute the next instruction */
	} else {
		emit("js near execquit\n");
		emit("mov bx,[esi]\n");
		emit("add esi,byte 2\n");
		emit("jmp dword[__jmptbl+ebx*4]\n");
	}
}

/*
** This version of ret_timing does a trace flag check.
**
** Note: this only needs to be used for instructions which can potentially
**  _set_ the trace flag.  Instructions which can't set the trace flag (even
**  if they can clear it) are OK to use ret_timing as usual.  Why?  Well, if
**  an instruction is run in trace mode, that instruction is doomed to be
**  traced, regardless if it clears the trace flag during its execution.
**  Furthermore, the trace exception (being a group 1 exception after all)
**  will clear the trace tricky bit as well as the trace flag.
*/
static void ret_timing_checkpoint(int n) {
	if(n) {
		emit("sub edi,%s%d\n", (n < 128) ? "byte " : "", n);
	} else {
		emit("or edi,edi\n");
	}
	emit("jmp exec_checkpoint\n");
}

/***************************************************************************/

/* This routine decodes an extension word into EDX. */
static void gen_decode_ext(void) {
	align(16);
	emit("decode_ext:\n");
	if(cputype <= 68010) {
		emit("push ecx\n");
		emit("movzx edx,word[esi]\n");
		emit("movsx ecx,dl\n");
		emit("add esi,byte 2\n");
		emit("shr edx,12\n");
		emit("mov edx,[__reg+edx*4]\n");
		emit("jc short .long\n");
		emit("movsx edx,dx\n");
		emit(".long:\n");
		emit("add edx,ecx\n");
		emit("pop ecx\n");
		emit("ret\n");
	} else {
		/* For future expansion... */
		/* need an extra jump table here */
	}
}

/***************************************************************************/

/*
** Perform a cached rebase
*/
void perform_cached_rebase(void) {
	int myline = linenum; linenum += 2;
	emit("cmp esi,[__fetch_region_start]\n");
	emit("jb short ln%d\n", myline);
	emit("cmp esi,[__fetch_region_end]\n");
	emit("jbe short ln%d\n", myline + 1);
	emit("ln%d:\n", myline);
	emit("call basefunction\n");
	emit("ln%d:\n", myline + 1);
	emit("add esi,ebp\n");
}

/***************************************************************************/

/*
** This is the function that generates a base for a given 68K PC.
**
**  Entry: 68K PC in ESI
**  Exit:  Newly calculated base in EBP
**
** Sounds like a simple lookup into the __fetch array, and in the case of
** 32-bit addresses, it is.  But for anything less, we'll need to compensate
** for potential garbage in the unused address bits, by subtracting the value
** of these unused bits from the base.  This way the full 32 bits of the PC
** are preserved, even if they're not used.
**
** The only registers which need to be "live" here are ESI and EBP.  The
** fetch region cache is updated, and bit 1 of __execinfo is set if the base
** couldn't be calculated.
*/
static void gen_basefunction(void) {
	align(16);
	emit("basefunction:\n");
	/*
	** Prepare ESI by masking off unused address bits (but save it
	** first).
	*/
	if(addressbits < 32) {
		emit("push esi\n");
		maskaddress("esi");
	}
	emit("mov ebp,[__fetch]\n");
	emit(".check:\n");
	emit("db 3Eh\n");
	emit("cmp esi,[ebp]\n");           /* Are we smaller? */
	emit("jb short .next\n");          /* Yes, go to next address */
	emit("db 3Eh\n");
	emit("cmp esi,[ebp+4]\n");         /* Are we bigger? */
	emit("jbe short .base\n");
	emit(".next:\n");
	emit("db 3Eh\n");
	emit("cmp dword [ebp],byte -1\n"); /* End of list? */
	emit("je short .outofrange\n");
	emit("add ebp,byte 12\n");         /* To the next structure */
	emit("jmp short .check\n");
	/* Bad news... we jumped out into the weeds. */
	emit(".outofrange:\n");
	if(addressbits < 32) emit("pop esi\n");
	emit("xor ebp,ebp\n");
	emit("mov dword[__fetch_region_start],-1\n");
	emit("mov dword[__fetch_region_end],ebp\n");
	force_context_switch();
	emit("or byte[__execinfo],2\n");
	emit("ret\n");

	emit(".base:\n");
	/*
	** Dealing with addressbits < 32 again... if the unused PC bits are
	** anything but zero, then we'll need to adjust the base to
	** compensate.
	*/
	if(addressbits < 32) {
		emit("mov esi,[esp]\n");
		emit("and esi,%d\n", 0xFFFFFFFF << addressbits);
	}
	emit("push edx\n");
	emit("mov edx,ebp\n");
	/*
	** Update the fetch region cache, adding in the garbage bits where
	** applicable.
	*/
	emit("mov ebp,[edx]\n");
	if(addressbits < 32) emit("or ebp,esi\n");
	emit("mov [__fetch_region_start],ebp\n");
	emit("mov ebp,[edx+4]\n");
	if(addressbits < 32) emit("or ebp,esi\n");
	emit("mov [__fetch_region_end],ebp\n");
	emit("mov ebp,[edx+8]\n");
	emit("pop edx\n");
	if(addressbits < 32) {
		/*
		** Subtract garbage bits from the base, and restore the
		** original 32-bit PC value.
		*/
		emit("sub ebp,esi\n");
		emit("pop esi\n");
	}
	emit("ret\n");
}

/***************************************************************************/

/* Read flags from CL into our CCR.  CX is unmodified. */
static void cl2ccr(void){
	emit("mov al,cl\n");        /* read CCR -> AL */                 /* ???????????XNZVC */
	emit("mov ah,al\n");        /* copy to AH */                     /* ???XNZVC???XNZVC */
	emit("and ax,0C10h\n");     /* isolate NZ...X */                 /* 0000NZ00000X0000 */
	emit("shl ah,3\n");       /* put NZ almost where we want it */   /* 0NZ00000000X0000 */
	emit("shr al,4\n");         /* shift X flag into bit 0 */        /* 0NZ000000000000X */
	emit("mov [__xflag],al\n"); /* store X flag */                   /* 0NZ000000000000X al -> xflag */
	emit("mov al,cl\n");        /* read CCR -> AL again */           /* 0NZ00000000XNZVC */
	emit("and al,3\n");         /* isolate VC */                     /* 0NZ00000000000VC */
	emit("shr al,1\n");         /* just V */                         /* 0NZ000000000000V carry */
	emit("adc ah,ah\n");        /* append C to rest of flags */      /* NZ00000C0000000V */
}

/*
** Read flags from CX into our SR, performing a mode switch where applicable.
** CX is unmodified.  Uses 4 bytes of stack.
**
** This does not do any of the trace flag mojo, so be sure to check for it
** explicitly where applicable (hint: ret_timing_checkpoint).
*/
static void cx2sr(void){
	int myline = linenum; linenum += 2;

	emit("push ecx\n");
	/* Step 1: switch supervisor mode */
	/* Is the new mode different from the last? */
	emit("mov cl,[__sr+1]\n");
	emit("and cx,2020h\n");
	emit("xor ch,cl\n");
	emit("jz near ln%d\n", myline);
	/* If so, swap stack pointers */
	emit("mov ecx,[__a7]\n");
	emit("xchg ecx,[__asp]\n");
	emit("mov [__a7],ecx\n");
	/* and copy the appropriate memory map */
	emit("test byte[esp+1],20h\n");
	emit("jz short ln%d\n", myline + 1);
	copy_memory_map("s", "ecx");
	emit("jmp short ln%d\n", myline);
	emit("ln%d:\n", myline + 1);
	copy_memory_map("u", "ecx");
	emit("ln%d:\n", myline);
	emit("pop ecx\n");

	/* Step 2: set new PPL / supervisor mode / trace flag */
	emit("mov [__sr+1],ch\n");
	emit("and byte[__sr+1],0A7h\n");

	/* Step 3: Store CL into CCR */
	cl2ccr();
}

/* Read flags from our CCR into CL.  CH is zeroed. */
static void ccr2cl(void){
	/* Read XNZ */
	emit("mov ch,[__xflag]\n");  /* 0000000X???????? */
	emit("mov cl,ah\n");         /* 0000000XNZ?????C */
	emit("shr cx,6\n");          /* 0000000000000XNZ */
	/* Read V */
	emit("add cl,cl\n");         /* 000000000000XNZ0 */
	emit("or cl,al\n");          /* 000000000000XNZV */
	/* Read C */
	emit("mov ch,ah\n");         /* NZ?????C0000XNZV */
	emit("shl ch,8\n");          /* 000000000000XNZV carry */
	emit("adc cl,cl\n");         /* 00000000000XNZVC */
}

/* Read flags from our SR into CX. */
static void sr2cx(void){
	/* Condition codes */
	ccr2cl();
	/* PPL / supervisor mode / trace flag */
	emit("mov ch,[__sr+1]\n");
}

/* Switch to supervisor mode.  Can potentially destroy ECX. */
static void supervisor(void){
	int myline=linenum;linenum++;
	emit("test byte[__sr+1],20h\n");
	emit("jnz short ln%d\n",myline);
	emit("mov ecx,[__a7]\n");
	emit("xchg ecx,[__asp]\n");
	emit("mov [__a7],ecx\n");
	copy_memory_map("s", "ecx");
	emit("or byte[__sr+1],20h\n");
	emit("ln%d:\n",myline);
}

/***************************************************************************/

static void gen_readbw(int size){
	char z='x';
	if(size==1)z='b';
	if(size==2)z='w';

	align(16);
	emit("readmemory%s:\n",sizename[size]);

	emit("mov [__access_address],edx\n");
	maskaddress("edx");
	emit("mov ecx,[__read%s]\n",sizename[size]);
	emit("read%c_check:\n",z);
	emit("cmp edx,[ecx]\n"); /* Are we smaller? */
	emit("jb short read%c_next\n",z); /* Yes, go to next address */
	emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */
	emit("jbe short read%c_call\n",z);
	emit("read%c_next:\n",z);
	emit("cmp dword[ecx],byte -1\n");/* End of list? */
	emit("je short read%c_outofrange\n",z);
	emit("add ecx,byte 16\n");   /* To the next structure */
	emit("jmp short read%c_check\n",z);

	emit("read%c_outofrange:\n",z);
	emit("or ecx,byte -1\n");
	emit("mov edx,[__access_address]\n");
	emit("ret\n");

	emit("read%c_call:\n",z);
	emit("cmp dword[ecx+8],byte 0\n");
	emit("jne short read%c_callio\n",z);
#ifndef RAINE
	emit("sub edx,[ecx]\n");
#endif
	emit("add edx,[ecx+12]\n");
	if(size==1){
		emit("xor edx,byte 1\n");
		emit("mov cl,[edx]\n");
	}else{
		emit("mov cx,[edx]\n");
	}
	emit("mov edx,[__access_address]\n");
	emit("ret\n");

	emit("read%c_callio:\n",z);
	airlock_exit();
	/*
	** What's this undocumented thingy?  Read/write handlers actually get
	** an extra parameter (the address of the record that caused the
	** call).
	*/
	emit("mov eax,edx\n"); /* address */
	emit("mov edx,ecx\n"); /* pointer to structure */
	if(use_stack){
		emit("push edx\n");
		emit("push eax\n");
	}
	emit("call dword[edx+8]\n");
	if(use_stack)emit("add esp,byte 8\n");
	emit("mov ecx,eax\n");
	airlock_enter();
	emit("mov edx,[__access_address]\n");
	emit("ret\n");
}

static void gen_readl(void){
	align(16);
	emit("readmemory%s:\n",sizename[4]);

	emit("mov [__access_address],edx\n");
	maskaddress("edx");
	emit("mov ecx,[__readword]\n");
	emit("readl_check:\n");
	emit("cmp edx,[ecx]\n"); /* Are we smaller? */
	emit("jb short readl_next\n"); /* Yes, go to next address */
	emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */
	emit("jbe short readl_call\n");
	emit("readl_next:\n");
	emit("cmp dword[ecx],byte -1\n");/* End of list? */
	emit("je short readl_outofrange\n");
	emit("add ecx,byte 16\n");   /* To the next structure */
	emit("jmp short readl_check\n");

	emit("readl_outofrange:\n");
#ifndef RAINE
	emit("add edx,byte 2\n");
	emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */
#endif
	emit("mov ecx,-1\n");
#ifndef RAINE
	emit("ja near readl_lower\n");
#endif
	emit("mov edx,[__access_address]\n");
	emit("ret\n");

	emit("readl_call:\n");
	emit("cmp dword[ecx+8],byte 0\n");
	emit("jne short readl_callio\n");

#ifndef RAINE
	emit("add edx,byte 2\n");
	emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */
	emit("ja short readl_split\n");
#endif
	/* Unsplit version */
#ifndef RAINE
	emit("sub edx,[ecx]\n");
#endif
	emit("add edx,[ecx+12]\n");
#ifndef RAINE
	emit("mov ecx,[edx-2]\n");
#else
	emit("mov ecx,[edx]\n");
#endif
	emit("rol ecx,16\n");
	emit("mov edx,[__access_address]\n");
	emit("ret\n");

	emit("readl_callio:\n");
#ifndef RAINE
	emit("add edx,byte 2\n");
	emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */
	emit("ja near readl_iosplit\n");
	emit("sub edx,byte 2\n");
#endif
	/* Unsplit version */
	airlock_exit();
	emit("mov eax,edx\n"); /* address */
	emit("mov edx,ecx\n"); /* pointer to structure */
	emit("push eax\n");
	emit("push edx\n");
	if(use_stack){
		emit("push edx\n");
		emit("push eax\n");
	}
	emit("call dword[edx+8]\n");
	if(use_stack)emit("add esp,byte 8\n");
	emit("pop edx\n");
	emit("xchg eax,[esp]\n");
	emit("add eax,byte 2\n");
/*	maskaddress("eax");*/ /* this case would force a split read anyway */
	if(use_stack){
		emit("push edx\n");
		emit("push eax\n");
	}
	emit("call dword[edx+8]\n");
	if(use_stack)emit("add esp,byte 8\n");
	emit("pop ecx\n");
	emit("shl ecx,16\n");
	emit("mov cx,ax\n");
	airlock_enter();
	emit("mov edx,[__access_address]\n");
	emit("ret\n");

#ifndef RAINE
	emit("readl_split:\n");
	emit("sub edx,[ecx]\n");
	emit("add edx,[ecx+12]\n");
	emit("mov cx,[edx-2]\n");

	emit("readl_lower:\n");
	emit("mov edx,[esp]\n");
	emit("add edx,byte 2\n");
	emit("shl ecx,16\n");
	emit("push ecx\n");
	emit("call readmemoryword\n");
	emit("and ecx,0FFFFh\n");
	emit("or ecx,[esp]\n");
	emit("add esp,byte 4\n");
	emit("mov edx,[__access_address]\n");
	emit("ret\n");

	emit("readl_iosplit:\n");
	emit("sub edx,byte 2\n");
	airlock_exit();
	emit("mov eax,edx\n"); /* address */
	emit("mov edx,ecx\n"); /* pointer to structure */
	if(use_stack){
		emit("push edx\n");
		emit("push eax\n");
	}
	emit("call dword[edx+8]\n");
	if(use_stack)emit("add esp,byte 8\n");
	emit("mov ecx,eax\n");
	airlock_enter();
	emit("jmp short readl_lower\n");
#endif
}

static void gen_writebw(int size){
	char z='x';
	if(size==1)z='b';
	if(size==2)z='w';

	align(16);
	emit("writememory%s:\n",sizename[size]);

	emit("mov [__access_address],edx\n");
	emit("push ecx\n");
	emit("write%c_top:\n",z);
	maskaddress("edx");
	emit("mov ecx,[__write%s]\n",sizename[size]);
	emit("write%c_check:\n",z);
	emit("cmp edx,[ecx]\n"); /* Are we smaller? */
	emit("jb short write%c_next\n",z); /* Yes, go to next address */
	emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */
	emit("jbe short write%c_call\n",z);
	emit("write%c_next:\n",z);
	emit("cmp dword[ecx],byte -1\n");/* End of list? */
	emit("je short write%c_end\n",z);
	emit("add ecx,byte 16\n");   /* To the next structure */
	emit("jmp short write%c_check\n",z);
	emit("write%c_call:\n",z);
	emit("cmp dword[ecx+8],byte 0\n");
	emit("jne short write%c_callio\n",z);
#ifndef RAINE
	emit("sub edx,[ecx]\n");
#endif
	emit("add edx,[ecx+12]\n");
	if(z=='b')emit("xor edx,byte 1\n");
	emit("pop ecx\n");
	emit("mov [edx],%s\n",x86cx[size]);
	emit("mov edx,[__access_address]\n");
	emit("ret\n");

	emit("write%c_callio:\n",z);
	/* --- we have:
	** index in ecx
	** address in edx
	** value in [esp]
	** --- we want:
	** index in anything (saved first)
	** address in eax
	** value in edx
	*/
	airlock_exit();
	emit("mov ebx,ecx\n");      /* pointer to structure */
	emit("mov eax,edx\n");      /* address */
	emit("xor edx,edx\n");      /* data */
	emit("mov %s,[esp+%d]\n",x86dx[size],airlock_stacksize);
	if(use_stack){
		emit("push ebx\n");
		emit("push edx\n");
		emit("push eax\n");
	}
	emit("call dword[ebx+8]\n");
	if(use_stack)emit("add esp,byte 12\n");
	airlock_enter();

	emit("write%c_end:\n",z);
	emit("pop ecx\n");
	emit("mov edx,[__access_address]\n");
	emit("ret\n");
}

static void gen_writel(void){
	align(16);
	emit("writememory%s:\n",sizename[4]);

	emit("mov [__access_address],edx\n");
	emit("push ecx\n");
	maskaddress("edx");
	emit("mov ecx,[__write%s]\n",sizename[2]);
	emit("writel_check:\n");
	emit("cmp edx,[ecx]\n"); /* Are we smaller? */
	emit("jb short writel_next\n"); /* Yes, go to next address */
	emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */
	emit("jbe short writel_call\n");
	emit("writel_next:\n");
	emit("cmp dword[ecx],byte -1\n");/* End of list? */
#ifndef RAINE
	emit("je short writel_outofrange\n");
#else
	emit("je short writel_end\n");
#endif
	emit("add ecx,byte 16\n");   /* To the next structure */
	emit("jmp short writel_check\n");

	/* The top word was out of range, but the bottom word might not be */
#ifndef RAINE
	emit("writel_outofrange:\n");
	emit("add edx,byte 2\n");
	emit("jmp writew_top\n");
#endif

	emit("writel_call:\n");
	emit("cmp dword[ecx+8],byte 0\n");
	emit("jne short writel_callio\n");

#ifndef RAINE
	emit("add edx,byte 2\n");
	emit("cmp edx,[ecx+4]\n");
	emit("ja short writel_split\n");
	emit("sub edx,[ecx]\n");
#endif
	emit("add edx,[ecx+12]\n");
#ifndef RAINE
	emit("pop ecx\n");
#else
	emit("mov ecx,[esp]\n");
#endif
	emit("rol ecx,16\n");
#ifndef RAINE
	emit("mov [edx-2],ecx\n");
#else
	emit("mov [edx],ecx\n");
	emit("pop ecx\n");
#endif
	emit("mov edx,[__access_address]\n");
	emit("ret\n");

	emit("writel_callio:\n");
#ifndef RAINE
	emit("add edx,byte 2\n");
	emit("cmp edx,[ecx+4]\n");
	emit("ja short writel_iosplit\n");
	emit("sub edx,byte 2\n");
#endif

	/* Unsplit version */
	/* --- we have:
	** index in ecx
	** address in edx
	** value in [esp]
	** --- we want:
	** index in anything (saved first)
	** address in eax
	** value in edx
	*/
	airlock_exit();
	emit("mov ebx,ecx\n");      /* pointer to structure */
	emit("mov eax,edx\n");      /* address */
	emit("xor edx,edx\n");      /* data */
	emit("mov dx,[esp+%d]\n",airlock_stacksize+2);
	emit("push eax\n");
	emit("push ebx\n");
	if(use_stack){
		emit("push ebx\n");
		emit("push edx\n");
		emit("push eax\n");
	}
	emit("call dword[ebx+8]\n");
	if(use_stack)emit("add esp,byte 12\n");
	emit("pop ebx\n");
	emit("pop eax\n");
	emit("add eax,byte 2\n");
/*	maskaddress("eax");*/ /*this case would force a split write anyway */
	emit("xor edx,edx\n");    /* data */
	emit("mov dx,[esp+%d]\n",airlock_stacksize);
	if(use_stack){
		emit("push ebx\n");
		emit("push edx\n");
		emit("push eax\n");
	}
	emit("call dword[ebx+8]\n");
	if(use_stack)emit("add esp,byte 12\n");
	airlock_enter();

	emit("writel_end:\n");
	emit("pop ecx\n");
	emit("mov edx,[__access_address]\n");
	emit("ret\n");

#ifndef RAINE
	emit("writel_split:\n");
	emit("sub edx,[ecx]\n");
	emit("add edx,[ecx+12]\n");
	emit("mov cx,[esp+2]\n");
	emit("mov [edx-2],cx\n");
	emit("mov edx,[esp+4]\n");
	emit("add edx,byte 2\n");
	emit("jmp writew_top\n");

	emit("writel_iosplit:\n");
	/* --- we have:
	** index in ecx
	** address in edx
	** value in [esp]
	** --- we want:
	** index in anything (saved first)
	** address in eax
	** value in edx
	*/
	airlock_exit();
	emit("mov ebx,ecx\n");      /* pointer to structure */
	emit("mov eax,edx\n");      /* address */
	emit("xor edx,edx\n");      /* data */
	emit("mov dx,[esp+%d]\n",airlock_stacksize+2);
	if(use_stack){
		emit("push ebx\n");
		emit("push edx\n");
		emit("push eax\n");
	}
	emit("call dword[ebx+8]\n");
	if(use_stack)emit("add esp,byte 12\n");
	airlock_enter();

	emit("mov edx,[esp+4]\n");
	emit("add edx,byte 2\n");
	emit("jmp writew_top\n");
#endif
}

/***************************************************************************/
/*
** Group 1 and 2 exceptions
** Exception address is passed in EDX
**
** Does not fix the new PC!
*/
static void gen_group_12_exception(void) {
	align(16);
	emit("group_1_exception:\n");
	emit("group_2_exception:\n");
	emit("and byte[__interrupts],0FEh\n"); /* first thing's first */
	if(cputype == 68010) {
		emit("mov byte[__loopmode],0\n");
	}
	if(cputype >= 68010) {
		emit("push edx\n");
		emit("add edx,[__vbr]\n");
	}
	emit("call readmemorydword\n");
	if(cputype >= 68010) {
		emit("pop edx\n");
	}
	emit("push ecx\n");/* dest. PC */
	sr2cx();
	emit("push ecx\n");/* old SR */
	supervisor();
	/*
	** Exception handlers do not like being traced, so clear the SR trace
	** flag as well as the trace tricky bit.
	**
	** Leave the cycle leftover count alone, in case we still need to
	** call attention to other unrelated tricky bits.
	*/
	emit("and byte[__sr+1],27h\n");
	emit("mov byte[__trace_trickybit],0\n");

	emit("mov ecx,esi\n");
	emit("sub ecx,ebp\n");
	if(cputype >= 68010) {
		emit("push ecx\n");/* old PC */
		emit("mov ecx,edx\n");
	}
	emit("mov edx,[__a7]\n");
	if(cputype >= 68010) {
		emit("and ecx,0FFCh\n");/* Format code */
		emit("sub edx,byte 2\n");
		emit("call writememoryword\n");
		emit("pop ecx\n");
	}
	emit("sub edx,byte 4\n");
	emit("call writememorydword\n");
	emit("pop ecx\n");/* old SR */
	emit("sub edx,byte 2\n");
	emit("call writememoryword\n");
	emit("mov [__a7],edx\n");
	emit("pop esi\n");/* dest. PC */
	emit("ret\n");
}

/***************************************************************************/

/* Privilege violation */
static void gen_privilege_violation(void){
	align(16);
	emit("privilege_violation:\n");
	emit("sub esi,byte 2\n");
	emit("mov edx,20h\n");
	emit("call group_1_exception\n");
	perform_cached_rebase();
	ret_timing((cputype==68010)?38:34);
}

/***************************************************************************/

static void usereg(void) {
	emit("and ebx,byte 7\n");
}

/* usereg only where applicable */
static void selective_usereg(void) {
	switch(main_eamode) {
	case dreg: case areg:
	case aind: case ainc: case adec:
	case adsp: case axdp:
		usereg();
	default:
		break;
	}
}

static void selftest(int size) {
	emit("test %s,%s\n", x86cx[size], x86cx[size]);
}

/***************************************************************************/

/* Get condition: Less Than (N^V)
** If true, the x86 sign flag will be set */
static void getcondition_l_s_ns(void) {
	emit("push eax\n");
	emit("neg al\n");
	emit("xor al,ah\n");
	emit("pop eax\n");
}

/* Get condition: Less Than or Equal ((N^V)|Z)
** If true, the x86 sign flag will be set */
static void getcondition_le_s_ns(void) {
	emit("push eax\n");
	emit("neg al\n");
	emit("xor al,ah\n");
	emit("add ah,ah\n");
	emit("or al,ah\n");
	emit("pop eax\n");
}

static char optcc[5];
static char optrc[5];

static void getcondition(int cc) {
	switch(cc) {
	case 0x0:
	case 0x1:
		break;
	case 0x2:/* a */
		emit("test ah,41h\n");
		sprintf(optcc, "z");
		sprintf(optrc, "nz");
		break;
	case 0x3:/* be */
		emit("test ah,41h\n");
		sprintf(optcc, "nz");
		sprintf(optrc, "z");
		break;
	case 0x4:/* nc */
		emit("test ah,1\n");
		sprintf(optcc, "z");
		sprintf(optrc, "nz");
		break;
	case 0x5:/* c */
		emit("test ah,1\n");
		sprintf(optcc, "nz");
		sprintf(optrc, "z");
		break;
	case 0x6:/* ne */
		emit("test ah,40h\n");
		sprintf(optcc, "z");
		sprintf(optrc, "nz");
		break;
	case 0x7:/* e */
		emit("test ah,40h\n");
		sprintf(optcc, "nz");
		sprintf(optrc, "z");
		break;
	case 0x8:/* no */
		emit("test al,1\n");
		sprintf(optcc, "z");
		sprintf(optrc, "nz");
		break;
	case 0x9:/* o */
		emit("test al,1\n");
		sprintf(optcc, "nz");
		sprintf(optrc, "z");
		break;
	case 0xA:/* ns */
		emit("or ah,ah\n");
		sprintf(optcc, "ns");
		sprintf(optrc, "s");
		break;
	case 0xB:/* s */
		emit("or ah,ah\n");
		sprintf(optcc, "s");
		sprintf(optrc, "ns");
		break;
	case 0xC:/* ge */
		getcondition_l_s_ns();
		sprintf(optcc, "ns");
		sprintf(optrc, "s");
		break;
	case 0xD:/* l */
		getcondition_l_s_ns();
		sprintf(optcc, "s");
		sprintf(optrc, "ns");
		break;
	case 0xE:/* g */
		getcondition_le_s_ns();
		sprintf(optcc, "ns");
		sprintf(optrc, "s");
		break;
	case 0xF:/* le */
		getcondition_le_s_ns();
		sprintf(optcc, "s");
		sprintf(optrc, "ns");
		break;
	default:break;
	}
}

static void flags(void) {
	emit("lahf\n");
	emit("seto al\n");
}

static void flags_v0(void) {
	emit("lahf\n");
	emit("mov al,0\n");
}

/* Put one of the x86 flags into the 68K zero flag. */
static void flag_to_z(char *f) {
	int myline = linenum; linenum += 2;
	emit("j%s short ln%d\n", f, myline);
	emit("and ah,0BFh\n");
	emit("jmp short ln%d\n", myline + 1);
	emit("ln%d:\n", myline);
	emit("or ah,40h\n");
	emit("ln%d:\n", myline + 1);
}

/* carry to X flag */
static void c2x(void) {
	emit("setc [__xflag]\n");
}

/* with previous flags in another register, adjust for non-changing zero */
static void adjzero(char *reg) {
	int myline = linenum; linenum++;
	emit("jnz short ln%d\n", myline);
	emit("or %s,0BFh\n", reg);
	emit("and ah,%s\n", reg);
	emit("ln%d:\n", myline);
}

/* Check for privilege violation */
static void privilegecheck(void) {
	emit("test byte[__sr+1],20h\n");
	emit("jz near privilege_violation\n");
}

/****************************************************************************
** EFFECTIVE ADDRESS GENERATION
****************************************************************************/

/*
** There are five types of EA activity:
**
** 1. Read:       precalc -> read -> postcalc
** 2. Write:      precalc -> write -> postcalc
** 3. R-M-W:      precalc -> read -> (modify) -> write -> postcalc
** 4. Move:       Read followed by Write
** 5. Control:    precalc
*/

/* Calculate address */
static void ea_step_precalc(int size, enum eamode mode, int reg) {
	char regs[100];
	if(reg == -1) sprintf(regs, "ebx*4");
	else sprintf(regs, "%d", reg * 4);
	switch(mode) {
	case dreg: case areg:
		break;
	case aind: case ainc: case adec:
		emit("mov edx,[__areg+%s]\n",regs);
		if(mode == adec) {
			/* Compensate for byte-sized stack ops */
			if(size == 1) {
				if(reg == -1) {
					emit("cmp bl,7\n");
					emit("adc edx,byte -2\n");
				} else if(reg == 7) {
					emit("sub edx,byte 2\n");
				} else {
					emit("dec edx\n");
				}
			} else {
				emit("sub edx,byte %d\n", size);
			}
		}
		break;
	case adsp:
		emit("movsx edx,word[esi]\n");
		emit("add esi,byte 2\n");
		emit("add edx,[__areg+%s]\n", regs);
		break;
	case axdp:
		emit("call decode_ext\n");
		emit("add edx,[__areg+%s]\n", regs);
		break;
	case absw:
		emit("movsx edx,word[esi]\n");
		emit("add esi,byte 2\n");
		break;
	case absl:
		emit("mov edx,dword[esi]\n");
		emit("add esi,byte 4\n");
		emit("rol edx,16\n");
		break;
	case pcdp:
		emit("movsx edx,word[esi]\n");
		emit("add edx,esi\n");
		emit("sub edx,ebp\n");
		emit("add esi,byte 2\n");
		break;
	case pcxd:
		emit("call decode_ext\n");
		emit("add edx,esi\n");
		emit("sub edx,ebp\n");
		emit("sub edx,byte 2\n");
		break;
	case immd:
		break;
	default:
		emit("#error ea_step_precalc\n");
		break;
	}
}

static void pcrel_adjust(int size) {
  /* This should be called instead of "call readmemory" for pc relative modes */
  /* Since it's pc relative, it does not need to read memory map, just fetch from
     the program rom, and it does not even need to check the offset in the fetch
     table (pc relative -> in the same page normally !) */

  emit("add edx, ebp\n");
  if (size == 1) {
    // byte swap (only for byte size)
    emit("xor edx,1\n");
    emit("movsx ecx,byte[edx]\n");
  } else if (size == 2) {
    emit("movsx ecx,word[edx]\n");
  } else if (size == 4) {
    emit("movsx ecx,word[edx]\n");
    emit("shl   ecx,16\n");
    emit("mov   cx,word[edx+2]\n");
  } else {
    fprintf(stderr,"size %d\n",size);
    exit(1);
  }
  /* Don't forget to put emit("sub edx, ebp") if you need edx after this !!! */
}

static void pcrel_restore_dx(int size) {
  if (size == 1)
    emit("xor edx,1\n"); // back to normal state
  emit("sub edx, ebp\n");
}

static void ea_step_read(int size, enum eamode mode, int reg) {
	char regs[100];
	if(reg == -1) sprintf(regs, "ebx*4");
	else sprintf(regs, "%d", reg * 4);
	switch(mode) {
	case dreg: emit("mov ecx,[__dreg+%s]\n", regs); break;
	case areg: emit("mov ecx,[__areg+%s]\n", regs); break;
	case aind: case ainc: case adec:
	case adsp: case axdp:
	case absw: case absl:
		emit("call readmemory%s\n", sizename[size]);
		break;
	case pcdp: case pcxd:
	  pcrel_adjust(size);
	  break;
	case immd:
		switch(size) {
		case 1:
		case 2:
			emit("mov cx,[esi]\n");
			emit("add esi,byte 2\n");
			break;
		case 4:
			emit("mov ecx,[esi]\n");
			emit("add esi,byte 4\n");
			emit("rol ecx,16\n");
			break;
		default:
			emit("#error ea_step_read\n");
			break;
		}
		break;
	default:
		emit("#error ea_step_read\n");
		break;
	}
}

/*
** Special case for when you need to load a word and sign-extend it.
** This cuts some fat out of a few instructions (i.e. MOVEA).
*/
static void ea_step_read_signword(enum eamode mode, int reg) {
	char regs[100];
	if(reg == -1) sprintf(regs, "ebx*4");
	else sprintf(regs, "%d", reg * 4);
	switch(mode) {
	case dreg: emit("movsx ecx,word[__dreg+%s]\n", regs); break;
	case areg: emit("movsx ecx,word[__areg+%s]\n", regs); break;
	case aind: case ainc: case adec:
	case adsp: case axdp:
	case absw: case absl:
		emit("call readmemory%s\n", sizename[2]);
		emit("movsx ecx,cx\n");
		break;
	case pcdp: case pcxd:
	  // emit(".infinite_2: jmp short .infinite_2\n");
          emit("add edx, ebp\n");
		emit("movsx ecx,word[edx]\n");
		break;
	case immd:
		emit("movsx ecx,word[esi]\n");
		emit("add esi,byte 2\n");
		break;
	default:
		emit("#error ea_step_read_signword\n");
		break;
	}
}

static void ea_step_write(int size, enum eamode mode, int reg) {
	char regs[100];
	if(reg == -1) sprintf(regs, "ebx*4");
	else sprintf(regs, "%d", reg * 4);
	switch(mode) {
	case dreg:
		emit("mov [__dreg+%s],%s\n", regs, x86cx[size]);
		break;
	case aind: case ainc: case adec:
	case adsp: case axdp:
	case absw: case absl:
		emit("call writememory%s\n", sizename[size]);
		break;
	default:
		emit("#error ea_step_write\n");
		break;
	}
}

static void ea_step_postcalc(int size, enum eamode mode, int reg) {
	char regs[100];
	if(reg == -1) sprintf(regs, "ebx*4");
	else sprintf(regs, "%d", reg * 4);
	switch(mode) {
	case ainc:
		/* Compensate for byte-sized stack ops */
		if(size == 1) {
			if(reg == -1) {
				emit("cmp bl,7\n");
				emit("sbb edx,byte -2\n");
			} else if(reg == 7) {
				emit("add edx,byte 2\n");
			} else {
				emit("inc edx\n");
			}
		} else {
			emit("add edx,byte %d\n", size);
		}
		/* Fall through */
	case adec:
		/* Store already-predecremented address */
		emit("mov [__areg+%s],edx\n", regs);
		break;
	case dreg: case areg:
	case aind: case adsp: case axdp:
	case absw: case absl:
	case pcdp: case pcxd:
	case immd:
		break;
	default:
		emit("#error ea_step_postcalc\n");
		break;
	}
}

/* Combined EA routines */

static void ea_load(int size, enum eamode mode, int reg) {
	ea_step_precalc (size, mode, reg);
	ea_step_read    (size, mode, reg);
	ea_step_postcalc(size, mode, reg);
}

static void ea_load_signword(enum eamode mode, int reg) {
	ea_step_precalc      (2, mode, reg);
	ea_step_read_signword(   mode, reg);
	ea_step_postcalc     (2, mode, reg);
}

static void ea_store(int size, enum eamode mode, int reg) {
	ea_step_precalc (size, mode, reg);
	ea_step_write   (size, mode, reg);
	ea_step_postcalc(size, mode, reg);
}

static void ea_rmw_load(int size, enum eamode mode, int reg) {
	ea_step_precalc (size, mode, reg);
	ea_step_read    (size, mode, reg);
}

static void ea_rmw_store(int size, enum eamode mode, int reg) {
	ea_step_write   (size, mode, reg);
	ea_step_postcalc(size, mode, reg);
}

static void ea_control(enum eamode mode, int reg) {
	ea_step_precalc (0, mode, reg);
}

static void main_ea_load(void) {
	ea_load(main_size, main_eamode, -1);
}

static void main_ea_load_signed(void) {
	if(main_size < 4) {
		ea_load_signword(main_eamode, -1);
	} else {
		ea_load(main_size, main_eamode, -1);
	}
}

static void main_ea_store(void) {
	ea_store(main_size, main_eamode, -1);
}

static void main_ea_rmw_load(void) {
	ea_rmw_load(main_size, main_eamode, -1);
}

static void main_ea_rmw_store(void) {
	ea_rmw_store(main_size, main_eamode, -1);
}

static void main_ea_control(void) {
	ea_control(main_eamode, -1);
}

/***************************************************************************/
/*
** Calculate cycles for main EA mode
** (68000, 68010)
*/
static int main_ea_cycles(void) {
	int l;
	if(main_size == 4) l = 4; else l = 0;
	switch(main_eamode) {
	case aind: return(l +  4);
	case ainc: return(l +  4);
	case adec: return(l +  6);
	case adsp: return(l +  8);
	case axdp: return(l + 10);
	case absw: return(l +  8);
	case absl: return(l + 12);
	case pcdp: return(l +  8);
	case pcxd: return(l + 10);
	case immd: return(l +  4);
	default:   break;
	}
	return 0;
}

/* Calculate cycles for main EA mode, without fetching (68010 only) */
static int main_ea_cycles_nofetch(void){
	switch(main_eamode) {
	case aind: return(2);
	case ainc: return(4);
	case adec: return(4);
	case adsp: return(4);
	case axdp: return(8);
	case absw: return(4);
	case absl: return(8);
	default:   break;
	}
	return 0;
}

/****************************************************************************
** PREFIXES / SUFFIXES
****************************************************************************/

/*
** Prefixes - stuff that appears before the instruction handling routines
*/
static void prefixes(void) {
	/* Basic stuff - banner, variable section, API */
	gen_banner();
	gen_variables();
	gen_interface();
	/* Internal functions - PC rebasing, I/O, etc. */
	gen_basefunction();
	gen_decode_ext();
	gen_readbw(1);
	gen_readbw(2);
	gen_readl();
	gen_writebw(1);
	gen_writebw(2);
	gen_writel();
	gen_group_12_exception();
	gen_privilege_violation();
	gen_flush_interrupts();
}

/*
** Suffixes - stuff that appears after the instruction handling routines and
** the jump table / loop info table
*/
static void suffixes(void) {
	emit("end\n");
}

/****************************************************************************
** INSTRUCTION HANDLING ROUTINES
****************************************************************************/

/* called 600 times (!) */
static void i_move(void) {
	int cycles;
	selective_usereg();
	main_ea_load();
	ea_store(main_size, main_destmode, main_reg);
	selftest(main_size);
	flags_v0();
	cycles = 4 + main_ea_cycles();
	switch(main_destmode) {
	case aind: if(main_size == 4) cycles += 4; cycles +=  4; break;
	case ainc: if(main_size == 4) cycles += 4; cycles +=  4; break;
	case adec: if(main_size == 4) cycles += 4; cycles +=  4; break;
	case adsp: if(main_size == 4) cycles += 4; cycles +=  8; break;
	case axdp: if(main_size == 4) cycles += 4; cycles += 10; break;
	case absw: if(main_size == 4) cycles += 4; cycles +=  8; break;
	case absl: if(main_size == 4) cycles += 4; cycles += 12; break;
	default:   break;
	}
	/* Calculate loop mode timings */
	if(cputype == 68010) {
		switch(main_eamode) {
		case dreg: case areg:
			switch(main_destmode) {
			case aind: case ainc:
				if(main_size <= 2) {
					loop_c_cycles = 2;
					loop_t_cycles = 10;
					loop_x_cycles = 8;
				}else{
					loop_c_cycles = 2;
					loop_t_cycles = 8;
					loop_x_cycles = 6;
				}
				break;
			default:break;
			}
			break;
		case aind: case ainc: case adec:
			switch(main_destmode) {
			case aind: case ainc:
				loop_c_cycles = 2;
				loop_t_cycles = 8;
				loop_x_cycles = 6;
				break;
			case adec:
				loop_c_cycles = 4;
				loop_t_cycles = 10;
				loop_x_cycles = 8;
				break;
			default:break;
			}
			break;
		default:break;
		}
	}
	ret_timing(cycles);
}

static void i_moveq(void) {
	emit("movsx ecx,bl\n");
	emit("mov [__dreg+%d],ecx\n", main_reg * 4);
	selftest(1);
	flags_v0();
	/* No loop mode */
	ret_timing(4);
}

static void op_to_areg(char *s) {
	selective_usereg();
	main_ea_load_signed(); /* sign extends to ecx where necessary */
	emit("%s [__areg+%d],ecx\n", s, main_reg * 4);
}

static void i_movea(void) {
	op_to_areg("mov");
	/* No loop mode */
	ret_timing(4 + main_ea_cycles());
}

/* ADDA or SUBA */
static void addsuba(char *op) {
	int base_cycles;
	op_to_areg(op);
	if(main_size==4){
		base_cycles=6;
		/* Register direct / immediate penalty (68000) */
		if(cputype==68000){
			switch(main_eamode){
			case areg:case dreg:case immd:
				base_cycles+=2;
				break;
			default:break;
			}
		}
	}else{
		base_cycles=8;
	}
	/* Calculate loop mode timings */
	if(cputype==68010){
		switch(main_eamode){
		case aind:case ainc:case adec:
			if(main_size<=2){
				loop_c_cycles = 6;
				loop_t_cycles = 12;
				loop_x_cycles = 10;
			}else{
				loop_c_cycles = 8;
				loop_t_cycles = 14;
				loop_x_cycles = 12;
			}
			break;
		default:break;
		}
	}
	ret_timing(base_cycles+main_ea_cycles());
}

static void i_adda(void){addsuba("add");}
static void i_suba(void){addsuba("sub");}

static void i_cmpa(void){
	op_to_areg("cmp");
	flags();
	/* Calculate loop mode timings */
	if(cputype==68010){
		switch(main_eamode){
		case aind:case ainc:case adec:
			if(main_size<=2){
				loop_c_cycles = 2;
				loop_t_cycles = 8;
				loop_x_cycles = 6;
			}else{
				loop_c_cycles = 4;
				loop_t_cycles = 10;
				loop_x_cycles = 6;
			}
			break;
		default:break;
		}
	}
	ret_timing(6+main_ea_cycles());
}

static void i_move_to_sr(void){
	main_size=2;
	privilegecheck();
	selective_usereg();
	main_ea_load();
	cx2sr();
	/* No loop mode; check PPL and trace flag */
	ret_timing_checkpoint(12+main_ea_cycles());
}

static void i_move_to_ccr(void){
	main_size=2;/* WEIRD! But it works! */
	selective_usereg();
	main_ea_load();
	cl2ccr();
	/* No loop mode */
	ret_timing(12+main_ea_cycles());
}

static void i_move_from_sr(void){
	int cycles;
	/* This is privileged on 68010 and up */
	if(cputype>=68010)privilegecheck();
	main_size=2;
	selective_usereg();
	sr2cx();
	main_ea_store();
	if(cputype==68010){
		cycles=8+main_ea_cycles_nofetch();
	}else{
		cycles=8+main_ea_cycles();
	}
	if((main_eamode==dreg)||(main_eamode==areg)){
		cycles-=2;
		/* Speed demon 68010 can do it in 2 fewer cycles... :p */
		if(cputype==68010)cycles-=2;
	}
	/* No loop mode */
	ret_timing(cycles);
}

/* 68000/68008 aren't supposed to have this */
static void i_move_from_ccr(void){
	int cycles;
	main_size=2;
	selective_usereg();
	ccr2cl();
	main_ea_store();
	if(cputype==68010){
		cycles=8+main_ea_cycles_nofetch();
	}else{
		cycles=8+main_ea_cycles();
	}
	if((main_eamode==dreg)||(main_eamode==areg)){
		cycles-=2;
		if(cputype==68010)cycles-=2;
	}
	/* No loop mode */
	ret_timing(cycles);
}

static void op_to_ccr(char*op){
	ccr2cl();
	emit("%s cl,[esi]\n",op);
	emit("add esi,byte 2\n");
	cl2ccr();
	/* No loop mode */
	ret_timing((cputype==68010)?16:20);
}

static void op_to_sr(char*op){
	privilegecheck();
	sr2cx();
	emit("%s cx,[esi]\n",op);
	emit("add esi,byte 2\n");
	cx2sr();
	/* No loop mode */
	/* Check PPL and trace flag */
	ret_timing_checkpoint((cputype==68010)?16:20);
}

static void  i_ori_ccr(void){op_to_ccr("or" );}
static void i_andi_ccr(void){op_to_ccr("and");}
static void i_eori_ccr(void){op_to_ccr("xor");}
static void  i_ori_sr (void){op_to_sr ("or" );}
static void i_andi_sr (void){op_to_sr ("and");}
static void i_eori_sr (void){op_to_sr ("xor");}

static void i_clr(void){
	int cycles=0;
	selective_usereg();
	emit("xor ecx,ecx\n");
	main_ea_store();
	if(cputype==68000){
		cycles=main_ea_cycles();
		if((main_eamode==dreg)||(main_eamode==areg)){
			cycles+=4;if(main_size==4)cycles+=4;
		}else{
			cycles+=6;if(main_size==4)cycles+=6;
		}
	}else if(cputype==68010){
		switch(main_eamode){
		case dreg:cycles= 4;break;
		case aind:cycles= 8;break;
		case ainc:cycles= 8;break;
		case adec:cycles=10;break;
		case adsp:cycles=12;break;
		case axdp:cycles=16;break;
		case absw:cycles=12;break;
		case absl:cycles=16;break;
		default:break;
		}
		if(main_size==4){
			cycles+=4;
			if(main_eamode==dreg)cycles-=2;
		}
		/* Calculate loop mode timings */
		switch(main_eamode){
		case aind:case ainc:case adec:
			loop_c_cycles = 2;
			loop_t_cycles = 10;
			loop_x_cycles = 8;
			break;
		default:break;
		}
	}
	emit("mov ax,4000h\n");
	ret_timing(cycles);
}

static void i_tst(void){
	selective_usereg();
	main_ea_load();
	/* Calculate loop mode timings */
	if(cputype==68010){
		switch(main_eamode){
		case aind:case ainc:case adec:
			if(main_size<=2){
				loop_c_cycles = 4;
				loop_t_cycles = 10;
				loop_x_cycles = 8;
			}else{
				loop_c_cycles = 6;
				loop_t_cycles = 12;
				loop_x_cycles = 8;
			}
			break;
		default:break;
		}
	}
	selftest(main_size);
	flags_v0();
	ret_timing(4+main_ea_cycles());
}

/* Always affects X flag
** (except for #,An which affects no flags whatsoever) */
static void op_quick(char*op){
	int cycles;
	selective_usereg();
	if(main_eamode==dreg){
		emit(
			"%s %s[__dreg+ebx*4],byte %d\n",
			op,sizename[main_size],quickvalue[main_qv]
		);
		flags();
		c2x();
		cycles=4;
	}else if(main_eamode==areg){
		emit(
			"%s dword[__areg+ebx*4],byte %d\n",
			op,quickvalue[main_qv]
		);
		cycles=4;
		/* SUBQ.W #,An incurs 4-cycle penalty (68000 only) */
		if(cputype==68000){
			if((main_size==2)&&(op[0]=='s'))cycles+=4;
		}
	}else{
		main_ea_rmw_load();
		emit(
			"%s %s,byte %d\n",op,x86cx[main_size],quickvalue[main_qv]
		);
		flags();
		c2x();
		main_ea_rmw_store();
		cycles=8+main_ea_cycles();
	}
	if(main_size==4)cycles+=4;
	/* No loop mode */
	ret_timing(cycles);
}

static void i_addq(void){op_quick("add");}
static void i_subq(void){op_quick("sub");}

static void op_to_dn(char*op,int affectx,int logical){
	int cycles;
	selective_usereg();
	main_ea_load();
	emit("%s [__dreg+%d],%s\n",
		op,main_reg*4,x86cx[main_size]
	);
	if(logical){
		flags_v0();
	}else{
		flags();
	}
	if(affectx)c2x();
	cycles=4+main_ea_cycles();
	if(main_size==4){
		cycles+=2;
		/* Register direct / immediate penalty (68000) */
		if((cputype==68000)&&(op[0]!='c')){
			switch(main_eamode){
			case areg:case dreg:case immd:
				cycles+=2;
				break;
			default:break;
			}
		}
	}
	/* Calculate loop mode timings */
	if(cputype==68010){
		switch(main_eamode){
		case aind:case ainc:case adec:
			switch(op[0]){
			case 'a':/* ADD, AND */
			case 'o':/* OR */
				loop_c_cycles = 8;
				loop_t_cycles = 14;
				loop_x_cycles = 12;
				break;
			case 's':/* SUB */
				if(main_size<=2){
					loop_c_cycles = 8;
					loop_t_cycles = 14;
					loop_x_cycles = 12;
				}else{
					loop_c_cycles = 6;
					loop_t_cycles = 12;
					loop_x_cycles = 10;
				}
				break;
			case 'c':/* CMP */
				loop_c_cycles = 4;
				loop_t_cycles = 10;
				loop_x_cycles = 8;
				if(main_size==4)loop_x_cycles = 6;
				break;
			default:break;
			}
			break;
		default:break;
		}
	}
	ret_timing(cycles);
}

static void i_cmp_dn(void){op_to_dn("cmp",0,0);}
static void i_add_dn(void){op_to_dn("add",1,0);}
static void i_sub_dn(void){op_to_dn("sub",1,0);}
static void i_and_dn(void){op_to_dn("and",0,1);}
static void i_or_dn (void){op_to_dn("or" ,0,1);}

static void op_to_ea(char*op,int logical){
	int cycles;
	selective_usereg();
	main_ea_rmw_load();
	emit(
		"%s %s,[__dreg+%d]\n",
		op,x86cx[main_size],main_reg*4
	);
	if(logical){
		flags_v0();
	}else{
		flags();
	}
	/* Logical instructions don't affect X flag */
	if(!logical)c2x();
	main_ea_rmw_store();
	cycles=8+main_ea_cycles();
	if(main_size==4)cycles+=4;
	/* EOR Dn,Dn takes fewer cycles than we'd expect */
	if((op[0]=='x')&&(main_eamode==dreg)){
		cycles-=4;
		if(cputype==68010)cycles-=2;
	}
	/* Calculate loop mode timings */
	if(cputype==68010){
		switch(main_eamode){
		case aind:case ainc:case adec:
			loop_c_cycles = 4;
			loop_t_cycles = 10;
			loop_x_cycles = 8;
			break;
		default:break;
		}
	}
	ret_timing(cycles);
}

static void i_eor_ea(void){op_to_ea("xor",1);}
static void i_add_ea(void){op_to_ea("add",0);}
static void i_sub_ea(void){op_to_ea("sub",0);}
static void i_and_ea(void){op_to_ea("and",1);}
static void i_or_ea (void){op_to_ea("or" ,1);}

/* called 144 times */
/*
** c1: Total cycles for #,Dn (byte/word)
** c2: Total cycles for #,Dn (long)
** c3: Basic cycles for #,M  (byte/word)
** c4: Basic cycles for #,M  (long)
*/
static void im_to_ea(char*op,int wback,int affectx,int logical,
	int c1,int c2,int c3,int c4){
	int cycles;
	selective_usereg();
	switch(main_size){
	case 1:
	case 2:
		emit("mov cx,[esi]\n");
		emit("add esi,byte 2\n");
		break;
	case 4:
		emit("mov ecx,[esi]\n");
		emit("rol ecx,16\n");
		emit("add esi,byte 4\n");
		break;
	default:break;
	}
	if(main_eamode==dreg){
		emit("%s [__dreg+ebx*4],%s\n",
			op,x86cx[main_size]
		);
		if(logical){
			flags_v0();
		}else{
			flags();
		}
		if(affectx)c2x();
		if(main_size<4)cycles=c1;else cycles=c2;
	}else{
		emit("push ecx\n");
		if(wback)main_ea_rmw_load();else main_ea_load();
		emit("%s %s,[esp]\n",op,x86cx[main_size]);
		if(logical){
			flags_v0();
		}else{
			flags();
		}
		if(affectx)c2x();
		emit("add esp,byte 4\n");
		if(wback)main_ea_rmw_store();
		if(main_size<4)cycles=c3;else cycles=c4;
		cycles+=main_ea_cycles();
	}
	/* No loop mode */
	ret_timing(cycles);
}

/* each called 24 times */
static void i_addi(void){im_to_ea("add",1,1,0,8,(cputype==68010)?14:16,12,20);}
static void i_subi(void){im_to_ea("sub",1,1,0,8,(cputype==68010)?14:16,12,20);}
static void i_cmpi(void){im_to_ea("cmp",0,0,0,8,(cputype==68010)?12:14, 8,12);}
static void i_andi(void){im_to_ea("and",1,0,1,8,(cputype==68010)?14:14,12,20);}
static void i_ori (void){im_to_ea("or" ,1,0,1,8,(cputype==68010)?14:16,12,20);}
static void i_eori(void){im_to_ea("xor",1,0,1,8,(cputype==68010)?14:16,12,20);}

static void flick_reg(char*op,int needxf,int affectx,int asl,int rotate){
	int cycles;
	char tmps[5];
	int i;
	usereg();
	cycles=6;
	if(main_size==4)cycles+=2;
	/* ASR doesn't need overflow checking */
	if(direction[main_dr]=='r')asl=0;
	if(main_ir==1){
		int myline=linenum;linenum++;
		emit("mov ecx,[__dreg+%d]\n",main_reg*4);
		emit("and ecx,byte 63\n");
		emit("jnz short ln%d\n",myline);
		/* The shift count was zero.  Strange things are about to
		** happen... */
		ea_load(main_size,dreg,-1);/* get data in eax */
		selftest(main_size);
		flags_v0();
		if(needxf){/* ROXL/ROXR: Set C flag equal to X */
			emit("and ah,0FEh\n");
			emit("or ah,[__xflag]\n");
		}
		ret_timing(cycles);
		/* Shift count non-zero */
		emit("ln%d:\n",myline);
		emit("sub edi,ecx\n");
		emit("sub edi,ecx\n");
		sprintf(tmps,"cl");
	}else{
		sprintf(tmps,"%d",quickvalue[main_reg]);
		cycles+=2*quickvalue[main_reg];
	}
	if(asl){
		switch(tmps[0]){
		case 'c':/* register shift count */
			emit("mov edx,[__dreg+ebx*4]\n");
			emit("mov al,0\n");/* overflow starts at 0 */
			emit("ln%d:\n",linenum);
			emit("add %s,%s\n",
				x86dx[main_size],x86dx[main_size]
			);
			emit("lahf\n");/* grab N,Z,C flags */
			emit("seto ch\n");
			emit("or al,ch\n");/* add overflow */
			emit("dec cl\n");
			emit("jnz short ln%d\n",linenum);linenum++;
			emit("mov [__dreg+ebx*4],%s\n",x86dx[main_size]);
			if(affectx){
				emit("mov cl,ah\n");
				emit("and cl,1\n");
				emit("mov [__xflag],cl\n");
			}
			break;
		case '1':/* immediate shift count ==1 */
			emit("sal %s[__dreg+ebx*4],1\n",
				sizename[main_size]
			);
			flags();
			if(affectx)c2x();
			break;
		default:/* immediate shift count >1 */
			emit("mov edx,[__dreg+ebx*4]\n");
			emit("mov al,0\n");/* overflow starts at 0 */
			for(i='1';i<=tmps[0];i++){
				emit("add %s,%s\n",
					x86dx[main_size],x86dx[main_size]
				);
				if(i==tmps[0]){
					/* grab N,Z,C flags */
					emit("lahf\n");
				}
				emit("seto ch\n");
				emit("or al,ch\n");/* add overflow */
			}
			emit("mov [__dreg+ebx*4],%s\n",x86dx[main_size]);
			if(affectx){
				emit("mov cl,ah\n");
				emit("and cl,1\n");
				emit("mov [__xflag],cl\n");
			}
			break;
		}
	}else{
		if(rotate){
			emit("mov edx,[__dreg+ebx*4]\n");
			if(needxf){
				emit("mov al,[__xflag]\n");
				emit("shr al,1\n");
			}else{
				emit("mov al,0\n");
			}
			emit("%s%c %s,%s\n",
				op,direction[main_dr],x86dx[main_size],tmps
			);
			emit("adc al,al\n");
			emit("or %s,%s\n",
				x86dx[main_size],x86dx[main_size]
			);
			emit("lahf\n");
			emit("or ah,al\n");
			if(affectx){
				emit("mov [__xflag],al\n");
			}
			emit("mov al,0\n");
			emit("mov [__dreg+ebx*4],%s\n",x86dx[main_size]);
		}else{
			if(needxf){
				emit("mov al,[__xflag]\n");
				emit("shr al,1\n");
			}else{
				emit("mov al,0\n");
			}
			emit("%s%c %s[__dreg+ebx*4],%s\n",
				op,direction[main_dr],sizename[main_size],tmps
			);
			emit("lahf\n");
			if(affectx)c2x();
		}
	}
	/* No loop mode */
	ret_timing(cycles);
}

static void i_lsx_reg(void){flick_reg("sh",0,1,0,0);}
static void i_asx_reg(void){flick_reg("sa",0,1,1,0);}
static void i_rox_reg(void){flick_reg("ro",0,0,0,1);}
static void i_rxx_reg(void){flick_reg("rc",1,1,0,1);}

static void flick_mem(char*op,int needxf,int affectx,int vf,int rotate){
	/* ASR doesn't need overflow checking */
	if(direction[main_dr]=='r')vf=0;
	main_size=2;
	selective_usereg();
	main_ea_rmw_load();
	if(needxf){
		emit("mov al,[__xflag]\n");
		emit("shr al,1\n");
	}else{
		if(rotate)emit("mov al,0\n");
	}
	emit("%s%c cx,1\n",op,direction[main_dr]);
	if(rotate){
		emit("adc al,al\n");
		emit("test cx,cx\n");
		emit("lahf\n");
		emit("or ah,al\n");
		if(affectx)emit("mov [__xflag],al\n");
		emit("mov al,0\n");
	}else{
		if(vf){
			flags();
		}else{
			flags_v0();
		}
		if(affectx)c2x();
	}
	main_ea_rmw_store();
	/* Calculate loop mode timings */
	if(cputype==68010){
		switch(main_eamode){
		case aind:case ainc:case adec:
			loop_c_cycles = 6;
			loop_t_cycles = 12;
			loop_x_cycles = 10;
			break;
		default:break;
		}
	}
	ret_timing(8+main_ea_cycles());
}

static void i_lsx_mem(void){flick_mem("sh",0,1,0,0);}
static void i_asx_mem(void){flick_mem("sa",0,1,1,0);}
static void i_rox_mem(void){flick_mem("ro",0,0,0,1);}
static void i_rxx_mem(void){flick_mem("rc",1,1,0,1);}

static int created_bra_b=0;
static void i_bra_b(void){
	if(!created_bra_b){emit("r_bra_b:\n");created_bra_b=1;}
	emit("movsx ebx,bl\n");
	emit("add esi,ebx\n");
	emit("xor ebx,ebx\n");
	ret_timing(10);
}

static int created_bra_w=0;
static void i_bra_w(void){
	if(!created_bra_w){emit("r_bra_w:\n");created_bra_w=1;}
	emit("movsx ebx,word[esi]\n");
	emit("add esi,ebx\n");
	emit("xor ebx,ebx\n");
	ret_timing(10);
}

static void i_bsr_b(void){
	emit("movsx ebx,bl\n");
	emit("mov ecx,esi\n");
	emit("sub ecx,ebp\n");
	emit("add esi,ebx\n");
	emit("xor ebx,ebx\n");
	ea_store(4,adec,7);
	ret_timing(18);
}

static void i_bsr_w(void){
	emit("movsx ebx,word[esi]\n");
	emit("mov ecx,esi\n");
	emit("add ecx,byte 2\n");
	emit("sub ecx,ebp\n");
	emit("add esi,ebx\n");
	emit("xor ebx,ebx\n");
	ea_store(4,adec,7);
	ret_timing(18);
}

static void i_bcc_b(void){
	getcondition(main_cc);
	emit("j%s near r_bra_b\n",optcc);
	ret_timing((cputype==68010)?6:8);
}

static void i_bcc_w(void){
	getcondition(main_cc);
	emit("j%s near r_bra_w\n",optcc);
	emit("add esi,byte 2\n");/* skip relative offset */
	ret_timing(12);
}

/* called once */
static void i_dbra(void){
	emit("r_dbra:\n");
	usereg();
	if(cputype==68010){
		emit("movsx ecx,word[esi]\n");
		emit("cmp ecx,byte -4\n");
		emit("je short r_loopmode_dbra\n");

		/* Regular DBRA */
		emit("r_regular_dbra:\n");
		emit("sub word[__dreg+ebx*4],byte 1\n");
		emit("jc short r_dbra_expire\n");
		emit("add esi,ecx\n");
		ret_timing(10);
		emit("r_dbra_expire:\n");
		emit("add esi,byte 2\n");
		ret_timing(16);

		/* Loop mode DBRA */
		emit("r_loopmode_dbra:\n");
		emit("cmp byte[__loopmode],0\n");
		emit("jnz short r_loopmode_dbra_inloop\n");
		emit("mov byte[__loopmode],1\n");
		emit("jmp short r_regular_dbra\n");
		emit("r_loopmode_dbra_inloop:\n");
		emit("sub word[__dreg+ebx*4],byte 1\n");
		emit("jc short r_loopmode_dbra_expire\n");

		/* Continue */
		emit("mov bx,word[esi-4]\n");
		emit("add esi,ecx\n");
		/* Subtract continuation cycles */
		emit("mov cl,byte[__looptbl+ebx]\n");
		emit("and ecx,byte 0Eh\n");
		emit("sub edi,ecx\n");
		ret_timing(0);

		/* Expire */
		emit("r_loopmode_dbra_expire:\n");
		emit("mov bx,word[esi-4]\n");
		emit("mov byte[__loopmode],0\n");
		/* Subtract continuation and extra expiration cycles */
		emit("mov cl,byte[__looptbl+ebx]\n");
		emit("mov ebx,ecx\n");
		emit("rol cl,2\n");
		emit("add esi,byte 2\n");
		emit("and ebx,byte 0Eh\n");
		emit("and ecx,byte 06h\n");
		emit("sub edi,ebx\n");
		emit("sub edi,ecx\n");
		ret_timing(0);

		/* Terminate (visited externally by i_dbcc) */
		emit("r_dbra_terminate:\n");
		emit("cmp word[esi],byte -4\n");
		emit("jne short r_dbra_terminate_regular\n");
		emit("cmp byte[__loopmode],0\n");
		emit("je short r_dbra_terminate_regular\n");
		emit("mov bx,word[esi-4]\n");
		emit("mov byte[__loopmode],0\n");
		/* Subtract termination cycles */
		emit("mov cl,byte[__looptbl+ebx]\n");
		emit("shr cl,3\n");
		emit("add esi,byte 2\n");
		emit("and ecx,byte 0Eh\n");
		emit("sub edi,ecx\n");
		ret_timing(0);
		emit("r_dbra_terminate_regular:\n");
		ret_timing(10);
	}else{
		emit("sub word[__dreg+ebx*4],byte 1\n");
		emit("jnc near r_bra_w\n");
		emit("add esi,byte 2\n");
		ret_timing(14);
	}
}

static void i_dbcc(void){
	getcondition(main_cc);
	emit("j%s near r_dbra\n",optrc);
	/* Terminate */
	if(cputype==68010){
		emit("jmp r_dbra_terminate\n");
	}else{
		emit("add esi,byte 2\n");
		ret_timing(12);
	}
}

static void i_scc(void){
	int cycles;
	main_size=1;
	selective_usereg();
	if(main_cc>1){
		if((main_eamode==dreg)||(main_eamode==areg)){
			if(cputype==68000){
				emit("xor ecx,ecx\n");
			}
			getcondition(main_cc);
			emit("set%s cl\n",optcc);
			if(cputype==68000){
				/* 2 extra cycles if it's true */
				emit("sub edi,ecx\n");
				emit("sub edi,ecx\n");
			}
			emit("neg cl\n");
			cycles=4;
		}else{
			getcondition(main_cc);
			emit("set%s cl\n",optcc);
			emit("neg cl\n");
			cycles=8;
		}
	}else{
		emit("mov cl,%d\n",(main_cc^1)*0xFF);
		if((main_eamode==dreg)||(main_eamode==areg)){
			cycles=4;
			if(cputype==68000){
				/* 2 extra cycles if it's true */
				if(!main_cc)cycles+=2;
			}
		}else{
			cycles=8;
		}
	}
	main_ea_store();
	if(cputype==68010){
		cycles+=main_ea_cycles_nofetch();
	}else{
		cycles+=main_ea_cycles();
	}
	/* No loop mode */
	ret_timing(cycles);
}

/* bit operations */

/* called 315 times */
/* 0=btst,1=bchg,2=bclr,3=bset */
static void bitop(int static_cycles){
	int cycles;
	selective_usereg();
	emit("and ecx,byte %d\n",(main_eamode==dreg)?31:7);
	if(main_eamode==dreg){
		main_size=4;
		if(!main_cc){/* BTST */
			emit("mov edx,1\n");
			emit("shl edx,cl\n");
			emit("test [__dreg+ebx*4],edx\n");
			flag_to_z("z");
		}else{
			emit("mov edx,1\n");
			emit("shl edx,cl\n");
			emit("mov ecx,[__dreg+ebx*4]\n");
			emit("test ecx,edx\n");
			flag_to_z("z");
			switch(main_cc){
			case 1:emit("xor ecx,edx\n");break;
			case 2:emit("not edx\nand ecx,edx\n");break;
			case 3:emit("or ecx,edx\n");break;
			default:break;
			}
			emit("mov [__dreg+ebx*4],ecx\n");
		}
		cycles=6+static_cycles;
		if(main_cc)cycles+=2;
		if(main_cc==2)cycles+=2;
	}else{
		main_size=1;
		if(!main_cc){
			emit("push ecx\n");
			main_ea_load();
			emit("mov edx,ecx\n");
			emit("pop ecx\n");
			emit("inc cl\n");
			emit("shr dl,cl\n");
			flag_to_z("nc");
		}else{
			emit("mov dl,1\n");
			emit("shl dl,cl\n");
			emit("push edx\n");
			main_ea_rmw_load();
			emit("xchg edx,[esp]\n");
			emit("test cl,dl\n");
			flag_to_z("z");
			switch(main_cc){
			case 1:emit("xor cl,dl\n");break;
			case 2:emit("not dl\nand cl,dl\n");break;
			case 3:emit("or cl,dl\n");break;
			default:break;
			}
			emit("pop edx\n");
			main_ea_rmw_store();
		}
		cycles=4+static_cycles+main_ea_cycles();
		if(main_cc)cycles+=4;
		if((cputype==68010)&&(main_cc==2))cycles+=2;
	}
	ret_timing(cycles);
}

/* called 35 times */
static void i_bitop_imm(void){
	emit("mov cl,[esi]\n");
	emit("add esi,byte 2\n");
	bitop(4);
}

/* called 280 times */
static void i_bitop_reg(void){
	emit("mov cl,byte[__dreg+%d]\n",main_reg*4);
	bitop(0);
}

static void i_jmp(void){
	int cycles=0;
	selective_usereg();
	main_ea_control();
	emit("mov esi,edx\n");
	perform_cached_rebase();
	switch(main_eamode){
	case aind:cycles= 8;break;
	case adsp:cycles=10;break;
	case axdp:cycles=14;break;
	case absw:cycles=10;break;
	case absl:cycles=12;break;
	case pcdp:cycles=10;break;
	case pcxd:cycles=14;break;
	default:break;
	}
	/* No loop mode */
	ret_timing(cycles);
}

static void i_jsr(void){
	int cycles=0;
	selective_usereg();
	main_ea_control();
	emit("mov ecx,esi\n");
	emit("sub ecx,ebp\n");
	emit("mov esi,edx\n");
	perform_cached_rebase();
	ea_store(4,adec,7);
	switch(main_eamode){
	case aind:cycles=16;break;
	case adsp:cycles=18;break;
	case axdp:cycles=22;break;
	case absw:cycles=18;break;
	case absl:cycles=20;break;
	case pcdp:cycles=18;break;
	case pcxd:cycles=22;break;
	default:break;
	}
	/* No loop mode */
	ret_timing(cycles);
}

static void i_rts(void){
	ea_load(4,ainc,7);
	emit("mov esi,ecx\n");
	perform_cached_rebase();
	/* No loop mode */
	ret_timing(16);
}

/* 68010 and higher */
static void i_rtd(void){
	emit("mov edx,[__a7]\n");
	emit("call readmemorydword\n");
	emit("movsx ebx,word[esi]\n");
	emit("add edx,ebx\n");
	emit("mov esi,ecx\n");
	emit("add edx,byte 4\n");
	emit("xor ebx,ebx\n");
	emit("mov [__a7],edx\n");
	perform_cached_rebase();
	/* No loop mode */
	ret_timing(16);
}

static void i_rtr(void){
	ea_load(2,ainc,7);
	cl2ccr();
	ea_load(4,ainc,7);
	emit("mov esi,ecx\n");
	perform_cached_rebase();
	/* No loop mode */
	ret_timing(20);
}

static void i_rte(void){
	int myline=linenum;
	linenum++;
	privilegecheck();
	if(cputype>=68010){
		/* Check stack frame format - must be 0xxx or 8xxx */
		emit("mov edx,[__a7]\n");
		emit("add edx,byte 6\n");
		emit("call readmemory%s\n",sizename[2]);
		emit("test ch,70h\n");
		emit("jnz short ln%d_formatok\n",myline);
		/* Generate Format Error exception where necessary */
		emit("mov edx,38h\n");
		emit("call group_1_exception\n");
		perform_cached_rebase();
		ret_timing(50);/* RTE, Illegal Format */
		emit("ln%d_formatok:\n",myline);

		/* Now _we_ check to make sure the format isn't 8xxx (since
		** that's not implemented yet). */
		emit("or ch,ch\n");
		emit("jns short ln%d_formatok2\n",myline);
		/* Double fault with error code 80000002h. */
		emit("mov ecx,80000002h\n");
		emit("or byte[__pc],1\n");
		emit("or byte[__interrupts],1\n");
		emit("jmp execexit\n");
		emit("ln%d_formatok2:\n",myline);

		/* Now RTE as usual */
		emit("sub edx,byte 6\n");
		emit("call readmemory%s\n",sizename[2]);
		emit("add edx,byte 2\n");
		cx2sr();
		emit("test ch,20h\n");
		emit("jz short ln%d_nosupe\n",myline);
		emit("add dword [__a7],byte 8\n");
		emit("jmp short ln%d_finish\n",myline);
		emit("ln%d_nosupe:\n",myline);
		emit("add dword [__asp],byte 8\n");
		emit("ln%d_finish:\n",myline);
		emit("call readmemory%s\n",sizename[4]);
		emit("mov esi,ecx\n");
		perform_cached_rebase();
		ret_timing_checkpoint(24);/* RTE with no trouble */
	}else{
		emit("mov edx,[__a7]\n");
		emit("call readmemory%s\n",sizename[2]);
		emit("add edx,byte 2\n");
		cx2sr();
		emit("test ch,20h\n");
		emit("jz short ln%d_nosupe\n",myline);
		emit("add dword [__a7],byte 6\n");
		emit("jmp short ln%d_finish\n",myline);
		emit("ln%d_nosupe:\n",myline);
		emit("add dword [__asp],byte 6\n");
		emit("ln%d_finish:\n",myline);
		emit("call readmemory%s\n",sizename[4]);
		emit("mov esi,ecx\n");
		perform_cached_rebase();
		ret_timing_checkpoint(20);
	}
}

static void i_lea(void){
	int cycles=0;
	selective_usereg();
	main_ea_control();
	emit("mov [__areg+%d],edx\n",main_reg*4);
	switch(main_eamode){
	case aind:cycles= 4;break;
	case adsp:cycles= 8;break;
	case axdp:cycles=12;break;
	case absw:cycles= 8;break;
	case absl:cycles=12;break;
	case pcdp:cycles= 8;break;
	case pcxd:cycles=12;break;
	default:break;
	}
	/* No loop mode */
	ret_timing(cycles);
}

static void i_pea(void){
	int cycles=0;
	selective_usereg();
	main_ea_control();
	emit("mov ecx,edx\n");
	ea_store(4,adec,7);
	switch(main_eamode){
	case aind:cycles=12;break;
	case adsp:cycles=16;break;
	case axdp:cycles=20;break;
	case absw:cycles=16;break;
	case absl:cycles=20;break;
	case pcdp:cycles=16;break;
	case pcxd:cycles=20;break;
	default:break;
	}
	/* No loop mode */
	ret_timing(cycles);
}

static void i_nop(void){
	/* No loop mode */
	ret_timing(4);
}

static void i_movem_control(void){
	int cycles=0;
	int myline=linenum;linenum+=2;
	emit("push eax\n");
	selective_usereg();
	emit("mov ax,[esi]\n");
	emit("add esi,byte 2\n");
	main_ea_control();
	emit("xor ebx,ebx\n");
	emit("ln%d:\n",myline);
	emit("shr eax,1\n");
	emit("jnc short ln%d\n",myline+1);
	if(main_dr==0){/*register->memory*/
		switch(main_eamode){
		case aind:cycles= 8;break;
		case adsp:cycles=12;break;
		case axdp:cycles=14;break;
		case absw:cycles=12;break;
		case absl:cycles=16;break;
		default:break;
		}
		emit("mov ecx,[__reg+ebx]\n");
		emit("call writememory%s\n",sizename[main_size]);
	}else{/*memory->register*/
		switch(main_eamode){
		case aind:cycles=12;
			if((cputype==68010)&&(main_size==4))cycles=24;
			break;
		case adsp:cycles=16;break;
		case axdp:cycles=18;break;
		case absw:cycles=16;break;
		case absl:cycles=20;break;
		case pcdp:cycles=16;break;
		case pcxd:cycles=18;break;
		default:break;
		}
		if (main_eamode == pcdp || main_eamode == pcxd) {
		  pcrel_adjust(main_size);
		  pcrel_restore_dx(main_size);
		} else
		  emit("call readmemory%s\n",sizename[main_size]);
		if(main_size==2)emit("movsx ecx,cx\n");
		emit("mov [__reg+ebx],ecx\n");
	}
	emit("add edx,byte %d\n",main_size);
	emit("sub edi,byte %d\n",main_size*2);
	emit("ln%d:\n",myline+1);
	emit("add ebx,byte 4\n");
	emit("cmp ebx,byte 64\n");
	emit("jne short ln%d\n",myline);
	emit("pop eax\n");
	ret_timing(cycles);
}

static void i_movem_postinc(void){
	int myline=linenum;linenum+=2;
	usereg();
	emit("push eax\n");
	emit("mov ax,[esi]\n");
	emit("add esi,byte 2\n");
	emit("mov edx,[__areg+ebx*4]\n");
	emit("push ebx\n");
	emit("xor ebx,ebx\n");
	emit("ln%d:\n",myline);
	emit("shr eax,1\n");
	emit("jnc short ln%d\n",myline+1);

	emit("call readmemory%s\n",sizename[main_size]);
	if(main_size==2)emit("movsx ecx,cx\n");
	emit("mov [__reg+ebx],ecx\n");
	emit("add edx,byte %d\n",main_size);
	emit("sub edi,byte %d\n",main_size*2);
	emit("ln%d:\n",myline+1);
	emit("add ebx,byte 4\n");
	emit("cmp ebx,byte 64\n");
	emit("jne short ln%d\n",myline);
	emit("pop ebx\n");
	emit("pop eax\n");
	emit("mov [__areg+ebx*4],edx\n");
	ret_timing(12);
}

static void i_movem_predec(void){
	int myline=linenum;linenum+=2;
	usereg();
	emit("push eax\n");
	emit("mov ax,[esi]\n");
	emit("add esi,byte 2\n");
	emit("mov edx,[__areg+ebx*4]\n");
	emit("push ebx\n");
	emit("mov ebx,60\n");
	emit("ln%d:\n",myline);
	emit("shr eax,1\n");
	emit("jnc short ln%d\n",myline+1);
	emit("mov ecx,[__reg+ebx]\n");
	emit("sub edx,byte %d\n",main_size);
	emit("sub edi,byte %d\n",main_size*2);
	emit("call writememory%s\n",sizename[main_size]);
	emit("ln%d:\n",myline+1);
	emit("sub ebx,byte 4\n");
	emit("jns short ln%d\n",myline);
	emit("pop ebx\n");
	emit("pop eax\n");
	emit("mov [__areg+ebx*4],edx\n");
	ret_timing(8);
}

static void i_link(void){
	usereg();
	emit("mov ecx,[__areg+ebx*4]\n");
	ea_store(4,adec,7);
	emit("mov ecx,[__a7]\n");
	emit("mov [__areg+ebx*4],ecx\n");
	emit("movsx edx,word[esi]\n");
	emit("add ecx,edx\n");
	emit("mov [__a7],ecx\n");
	emit("add esi,byte 2\n");
	ret_timing(16);
}

static void i_unlk(void){
	usereg();
	emit("mov ecx,[__areg+ebx*4]\n");
	emit("mov [__a7],ecx\n");
	ea_load(4,ainc,7);
	emit("mov [__areg+ebx*4],ecx\n");
	ret_timing(12);
}

static void i_move_from_usp(void){
	privilegecheck(); /* makes our job much easier... */
	usereg();
	emit("mov ecx,[__asp]\n");
	emit("mov [__areg+ebx*4],ecx\n");
	ret_timing((cputype==68010)?6:4);
}

static void i_move_to_usp(void){
	privilegecheck();
	usereg();
	emit("mov ecx,[__areg+ebx*4]\n");
	emit("mov [__asp],ecx\n");
	ret_timing((cputype==68010)?6:4);
}

static void i_trap(void){
	emit("and ebx,byte 0Fh\n");
	emit("lea edx,[80h+ebx*4]\n");
	emit("call group_2_exception\n");
	perform_cached_rebase();
	ret_timing((cputype==68010)?38:34);
}

static void i_trapv(void){
	int myline=linenum;linenum++;
	emit("test al,1\n");
	emit("jnz short ln%d\n",myline);
	ret_timing(4);
	emit("ln%d:\n",myline);
	emit("mov edx,1Ch\n");
	emit("call group_2_exception\n");
	perform_cached_rebase();
	ret_timing(4+((cputype==68010)?38:34));
}

static void i_stop(void){
	int myline=linenum;linenum++;
	privilegecheck();
	emit("mov cx,[esi]\n");
	emit("add esi,2\n");
	cx2sr();
	emit("or byte[__interrupts],1\n");
	/* Forfeit all remaining cycles */
	emit("sub edi,byte 4\n");
	emit("js short ln%d\n",myline);
	emit("xor edi,edi\n");
	emit("dec edi\n");
	emit("ln%d:\n",myline);
	ret_timing(0);
}

static void i_extbw(void){
	usereg();
	emit("movsx cx,byte[__dreg+ebx*4]\n");
	emit("mov [__dreg+ebx*4],cx\n");
	selftest(2);
	flags_v0();
	ret_timing(4);
}

static void i_extwl(void){
	usereg();
	emit("movsx ecx,word[__dreg+ebx*4]\n");
	emit("mov [__dreg+ebx*4],ecx\n");
	selftest(4);
	flags_v0();
	ret_timing(4);
}

static void i_swap(void){
	usereg();
	emit("mov ecx,[__dreg+ebx*4]\n");
	emit("rol ecx,16\n");
	emit("mov [__dreg+ebx*4],ecx\n");
	selftest(4);
	flags_v0();
	ret_timing(4);
}

/* if main_cc==1 then it's signed */
static void i_mul(void){
	int base_cycles;
	selective_usereg();
	main_size=2;
	main_ea_load();
	emit("mov eax,ecx\n");
	/* Finally!  Real MULS/MULU timing! */
	if(cputype==68000){
		emit("mov dl,0\n");
		emit("mov bl,16\n");
		emit("ln%d:\n",linenum);
		emit("add cx,cx\n");
		if(main_cc==1){
			/* MULS: count the number of 10 or 01 pairs */
			emit("seto dh\n");
			emit("add dl,dh\n");
		}else{
			/* MULU: count the number of 1s */
			emit("adc dl,0\n");
		}
		emit("dec bl\n");
		emit("jnz ln%d\n",linenum);linenum++;
		emit("and edx,byte 127\n");
		emit("sub edi,edx\n");
		emit("sub edi,edx\n");
	}
	emit("%smul word[__dreg+%d]\n",
		(main_cc==1)?"i":"",main_reg*4
	);
	emit("shl edx,16\n");
	emit("and eax,0FFFFh\n");
	emit("mov ecx,edx\n");
	emit("or ecx,eax\n");
	flags_v0();
	emit("mov [__dreg+%d],ecx\n",main_reg*4);
	if(cputype==68010){
		/* Maximum is 42 signed, 40 unsigned */
		base_cycles=36;
		if(main_cc)base_cycles+=2;
	}else{
		/* 38+2n, signed or unsigned, maximum is 70 */
		base_cycles=38;
	}
	/* No loop mode */
	ret_timing(base_cycles+main_ea_cycles());
}

/* if main_cc=1 then it's signed */
static void i_div(void){
	int base_cycles;
	int myline=linenum;linenum+=2;
	selective_usereg();
	main_size=2;
	main_ea_load();
	emit("test cx,cx\n");
	emit("jnz short ln%d\n",myline);
	/* Forgot to put on our Division by Zero Suit... */
	emit("mov edx,14h\n");
	emit("call group_2_exception\n");
	perform_cached_rebase();
	base_cycles=38;
	if(cputype==68010)base_cycles+=4;
	ret_timing(base_cycles+main_ea_cycles());
	emit("ln%d:\n",myline);myline++;
	if(main_cc){
		emit("movsx ecx,cx\n");
	}else{
		emit("and ecx,0FFFFh\n");
	}
	emit("mov eax,[__dreg+%d]\n",main_reg*4);
	if(main_cc){
		emit("mov edx,eax\n");
		emit("sar edx,31\n");
	}else{
		emit("xor edx,edx\n");
	}
	emit("%sdiv ecx\n",main_cc?"i":"");
	if(main_cc){
		emit("mov ecx,eax\n");
		emit("sar cx,15\n");
		emit("or ecx,ecx\n");
		emit("je short ln%d\n",linenum);
		emit("inc ecx\n");
		emit("jne short ln%d\n",myline);
		emit("ln%d:\n",linenum);linenum++;
		emit("and eax,0FFFFh\n");
	}else{
		emit("test eax,0FFFF0000h\n");
		emit("jnz short ln%d\n",myline);
	}
	emit("shl edx,16\n");
	emit("mov dx,ax\n");
	emit("test dx,dx\n");
	flags_v0();
	emit("mov [__dreg+%d],edx\n",main_reg*4);
	if(cputype==68010){
		base_cycles=108;
		if(main_cc)base_cycles=122;
	}else{
		/* Varies from 142-158 signed, 126-140 unsigned */
		base_cycles=133;
		if(main_cc)base_cycles=150;
	}
	ret_timing(base_cycles+main_ea_cycles());
	/* Overflow */
	emit("ln%d:\n",myline);myline++;
	emit("mov ax,1\n");
	/* No loop mode */
	ret_timing(base_cycles+main_ea_cycles());
}

static void i_neg(void){
	int cycles;
	selective_usereg();
	cycles=4;
	if(main_size==4)cycles=6;
	if(main_eamode==dreg){
		emit("neg %s[__dreg+ebx*4]\n",
			sizename[main_size]
		);
		flags();
		c2x();
	}else{
		cycles*=2;
		main_ea_rmw_load();
		emit("neg %s\n",x86cx[main_size]);
		flags();
		c2x();
		main_ea_rmw_store();
		cycles+=main_ea_cycles();
	}
	/* Calculate loop mode timings */
	if(cputype==68010){
		switch(main_eamode){
		case aind:case ainc:case adec:
			loop_c_cycles = 4;
			loop_t_cycles = 10;
			loop_x_cycles = 8;
			break;
		default:break;
		}
	}
	ret_timing(cycles);
}

static void i_negx(void){
	int cycles;
	selective_usereg();
	cycles=4;
	if(main_size==4)cycles=6;
	if(main_eamode==dreg){
		emit("mov cl,[__xflag]\n");
		emit("shr cl,1\n");
		if(main_size==1)emit("mov cl,0\n");
		else emit("mov ecx,0\n");
		emit("sbb %s,[__dreg+ebx*4]\n",x86cx[main_size]);
		emit("mov edx,eax\n");
		flags();c2x();
		adjzero("dh");
		emit("mov [__dreg+ebx*4],%s\n",x86cx[main_size]);
	}else{
		cycles*=2;
		main_ea_rmw_load();
		emit("push ebx\n");
		emit("mov bl,[__xflag]\n");
		emit("shr bl,1\n");
		if(main_size==1)emit("mov bl,0\n");
		else emit("mov ebx,0\n");
		emit("sbb %s,%s\n",x86bx[main_size],x86cx[main_size]);
		emit("mov ecx,ebx\n");
		emit("mov ebx,eax\n");
		flags();c2x();
		adjzero("bh");
		emit("pop ebx\n");
		main_ea_rmw_store();
		cycles+=main_ea_cycles();
	}
	/* Calculate loop mode timings */
	if(cputype==68010){
		switch(main_eamode){
		case aind:case ainc:case adec:
			loop_c_cycles = 4;
			loop_t_cycles = 10;
			loop_x_cycles = 8;
			break;
		default:break;
		}
	}
	ret_timing(cycles);
}

static void i_nbcd(void){
	int cycles;
	main_size=1;
	selective_usereg();
	main_ea_rmw_load();
	/* Get the X flag into carry */
	emit("mov cl,[__xflag]\n");
	emit("shr cl,1\n");
	/* Save the previous Z flag in CH */
	emit("mov ch,ah\n");
	/* Perform the BCD subtraction */
	emit("mov al,0\n");
	emit("sbb al,cl\n");
	emit("das\n");
	/* Save result in CL */
	emit("mov cl,al\n");
	/* Set flags - V undefined */
	flags_v0();
	c2x();
	/* Adjust for non-changing Z (previous Z flag in CH) */
	adjzero("ch");
	main_ea_rmw_store();
	if(main_eamode==dreg){
		cycles=6;
	}else{
		cycles=8+main_ea_cycles();
	}
	/* Calculate loop mode timings */
	if(cputype==68010){
		switch(main_eamode){
		case aind:case ainc:case adec:
			loop_c_cycles = 6;
			loop_t_cycles = 12;
			loop_x_cycles = 10;
			break;
		default:break;
		}
	}
	ret_timing(cycles);
}

static void i_tas(void){
	int cycles;
	main_size=1;
	selective_usereg();
	main_ea_rmw_load();
	selftest(1);
	flags_v0();
	emit("or cl,80h\n");
	main_ea_rmw_store();
	if((main_eamode==dreg)||(main_eamode==areg)){
		cycles=4;
	}else{
		cycles=14+main_ea_cycles();
	}
	/* No loop mode */
	ret_timing(cycles);
}

static void i_not(void){
	int cycles;
	selective_usereg();
	cycles=4;
	if(main_size==4)cycles=6;
	if(main_eamode==dreg){
		emit("xor %s[__dreg+ebx*4],byte -1\n",
			sizename[main_size]
		);
		flags_v0();
	}else{
		cycles*=2;
		main_ea_rmw_load();
		emit("xor %s,byte -1\n",x86cx[main_size]);
		flags_v0();
		main_ea_rmw_store();
		cycles+=main_ea_cycles();
	}
	/* Calculate loop mode timings */
	if(cputype==68010){
		switch(main_eamode){
		case aind:case ainc:case adec:
			loop_c_cycles = 4;
			loop_t_cycles = 10;
			loop_x_cycles = 8;
			break;
		default:break;
		}
	}
	ret_timing(cycles);
}

/* main_reg=rx, main_dr is 0 or 32 (rx a/d), main_ir is 0 or 32 (ry a/d) */
static void i_exg(void){
	usereg();
	emit("mov ecx,[__reg+%d]\n",(main_reg*4)+main_dr);
	emit("mov edx,[__reg+%d+ebx*4]\n",main_ir);
	emit("mov [__reg+%d],edx\n",(main_reg*4)+main_dr);
	emit("mov [__reg+%d+ebx*4],ecx\n",main_ir);
	/* No loop mode */
	ret_timing(6);
}

static void i_cmpm(void){
	usereg();
	ea_load(main_size,ainc,-1);/* Keep this in order */
	emit("mov eax,ecx\n");
	ea_load(main_size,ainc,main_reg);
	emit("cmp %s,%s\n",x86cx[main_size],x86ax[main_size]);
	flags();
	/* Calculate loop mode timings */
	if(cputype==68010){
		if(main_size<=2){
			loop_c_cycles = 2;
			loop_t_cycles = 8;
			loop_x_cycles = 6;
		}else{
			loop_c_cycles = 4;
			loop_t_cycles = 10;
			loop_x_cycles = 6;
		}
	}
	ret_timing((main_size==4)?20:12);
}

static void opx_dreg(char*op,char*adjust){
	int cycles;
	usereg();
	emit("mov ch,ah\n");/* Save old Z flag in CH */
	emit("mov cl,[__xflag]\n");
	emit("shr cl,1\n");/* X -> x86 carry */
	emit("mov eax,[__dreg+%d]\n",main_reg*4);
	emit("%s %s,[__dreg+ebx*4]\n",op,x86ax[main_size]);
	if(adjust[0]){
		emit("%s\n",adjust);
	}
	emit("mov [__dreg+%d],%s\n",main_reg*4,x86ax[main_size]);
	emit("lahf\n");
	if(adjust[0]){
		emit("mov al,0\n");
	}else{
		emit("seto al\n");
	}
	c2x();
	adjzero("ch");
	if(main_size<=2){
		cycles=4;
		if(adjust[0])cycles=6;
	}else{
		cycles=8;
		if(cputype==68010)cycles=6;
	}
	/* No loop mode */
	ret_timing(cycles);
}

static void opx_adec(char*op,char*adjust){
	int cycles;
	usereg();
	ea_load(main_size,adec,-1);/* Keep this in order */
	emit("mov ebx,ecx\n");
	ea_rmw_load(main_size,adec,main_reg);
	emit("xchg ecx,eax\n");/* flags -> ECX, dest -> EAX */
	emit("mov cl,[__xflag]\n");
	emit("shr cl,1\n");/* X -> x86 carry */
	emit("%s %s,%s\n",op,x86ax[main_size],x86bx[main_size]);
	if(adjust[0]){
		emit("%s\n",adjust);
	}
	emit("mov ebx,eax\n");
	emit("lahf\n");
	if(adjust[0]){
		emit("mov al,0\n");
	}else{
		emit("seto al\n");
	}
	c2x();
	adjzero("ch");
	emit("mov ecx,ebx\n");
	emit("xor ebx,ebx\n");
	ea_rmw_store(main_size,adec,main_reg);
	if(main_size<=2){
		cycles=18;
	}else{
		cycles=30;
	}
	/* Calculate loop mode timings */
	if(cputype==68010){
		if(adjust[0]){
			loop_c_cycles = 6;
			loop_t_cycles = 12;
			loop_x_cycles = 10;
		}else{
			if(main_size<=2){
				loop_c_cycles = 4;
				loop_t_cycles = 10;
				loop_x_cycles = 8;
			}else{
				loop_c_cycles = 2;
				loop_t_cycles = 8;
				loop_x_cycles = 6;
			}
		}
	}
	ret_timing(cycles);
}

static void i_addx_dreg(void){opx_dreg("adc","");}
static void i_addx_adec(void){opx_adec("adc","");}
static void i_subx_dreg(void){opx_dreg("sbb","");}
static void i_subx_adec(void){opx_adec("sbb","");}
static void i_abcd_dreg(void){main_size=1;opx_dreg("adc","daa");}
static void i_abcd_adec(void){main_size=1;opx_adec("adc","daa");}
static void i_sbcd_dreg(void){main_size=1;opx_dreg("sbb","das");}
static void i_sbcd_adec(void){main_size=1;opx_adec("sbb","das");}

static void i_movep_mem2reg(void){
	int cycles;
	usereg();
	emit("movsx edx,word[esi]\n");
	emit("add esi,byte 2\n");
	emit("add edx,[__areg+ebx*4]\n");
	emit("call readmemorybyte\n");
	emit("mov bh,cl\n");
	emit("add edx,byte 2\n");
	emit("call readmemorybyte\n");
	emit("mov bl,cl\n");
	if(main_size==2){
		emit("mov [__dreg+%d],bx\n",main_reg*4);
		cycles=16;
	}else{
		emit("add edx,byte 2\n");
		emit("shl ebx,16\n");
		emit("call readmemorybyte\n");
		emit("mov bh,cl\n");
		emit("add edx,byte 2\n");
		emit("call readmemorybyte\n");
		emit("mov bl,cl\n");
		emit("mov [__dreg+%d],ebx\n",main_reg*4);
		emit("xor ebx,ebx\n");
		cycles=24;
	}
	/* No loop mode */
	ret_timing(cycles);
}

static void i_movep_reg2mem(void){
	int cycles;
	usereg();
	emit("movsx edx,word[esi]\n");
	emit("add esi,byte 2\n");
	emit("add edx,[__areg+ebx*4]\n");
	emit("mov ebx,[__dreg+%d]\n",main_reg*4);
	if(main_size==4)emit("rol ebx,16\n");
	emit("mov cl,bh\n");
	emit("call writememorybyte\n");
	emit("add edx,byte 2\n");
	emit("mov cl,bl\n");
	emit("call writememorybyte\n");
	if(main_size==4){
		emit("add edx,byte 2\n");
		emit("rol ebx,16\n");
		emit("mov cl,bh\n");
		emit("call writememorybyte\n");
		emit("add edx,byte 2\n");
		emit("mov cl,bl\n");
		emit("call writememorybyte\n");
		cycles=24;
	}else{
		cycles=16;
	}
	emit("xor ebx,ebx\n");
	/* No loop mode */
	ret_timing(cycles);
}

static void i_chk(void){
	int cycles;
	int myline=linenum;linenum++;
	selective_usereg();
	main_ea_load();
	emit("cmp %s[__dreg+%d],byte 0\n",
		sizename[main_size],main_reg*4
	);
	emit("mov ax,8000h\n");
	emit("jl short ln%d\n",myline);
	emit("cmp [__dreg+%d],%s\n",
		main_reg*4,x86cx[main_size]
	);
	emit("mov ax,0\n");
	emit("jg short ln%d\n",myline);
	cycles=10;
	if(cputype==68010)cycles=8;
	ret_timing(cycles+main_ea_cycles());
	/* Out of bounds, so generate CHK exception */
	emit("ln%d:",myline);
	emit("mov edx,18h\n");
	emit("call group_2_exception\n");
	perform_cached_rebase();
	cycles=40;
	if(cputype==68010)cycles=44;
	/* No loop mode */
	ret_timing(cycles+main_ea_cycles());
}

static int created_illegal=0;
static void i_illegal(void){
	if(!created_illegal){emit("r_illegal:\n");created_illegal=1;}
	emit("sub esi,byte 2\n");
	emit("mov edx,10h\n");
	emit("call group_1_exception\n");
	perform_cached_rebase();
	ret_timing((cputype==68010)?38:34);
}

/* Breakpoint - notify hardware */
static void i_bkpt(void){
	int myline=linenum;
	linenum++;
	emit("mov ecx,[__bkpthandler]\n");
	emit("or ecx,ecx\n");
	emit("jz ln%d\n",myline);
	airlock_exit();
	emit("call ecx\n");
	airlock_enter();
	emit("ln%d:\n",myline);
	emit("mov edx,10h\n");
	emit("call group_1_exception\n");
	perform_cached_rebase();
	ret_timing((cputype==68010)?38:34);
}

static void i_aline(void){
	emit("sub esi,byte 2\n");
	emit("mov edx,28h\n");
	emit("call group_1_exception\n");
	perform_cached_rebase();
	/* This is just a guess */
	ret_timing((cputype==68010)?38:34);
}

static void i_fline(void){
	emit("sub esi,byte 2\n");
	emit("mov edx,2Ch\n");
	emit("call group_1_exception\n");
	perform_cached_rebase();
	/* This is just a guess */
	ret_timing((cputype==68010)?38:34);
}

static void i_reset(void){
	privilegecheck();
	emit("mov ecx,[__resethandler]\n");
	emit("or ecx,ecx\n");
	emit("jz near invalidins\n");
	airlock_exit();
	emit("call ecx\n");
	airlock_enter();
	ret_timing((cputype==68010)?130:132);
}

static void i_movec_c_to_r(void){
	int myline=linenum;linenum++;
	privilegecheck();
	emit("mov bx,word[esi]\n");
	emit("mov edx,ebx\n");
	emit("shr ebx,12\n");

	emit("and edx,0FFFh\n");
	emit("jnz short ln%d\n",linenum);
	emit("mov cl,[__sfc]\n");
	emit("and ecx,byte 7\n");
	emit("jmp short ln%d\n",myline);
	emit("ln%d:\n",linenum);linenum++;

	emit("cmp edx,byte 1\n");
	emit("jnz short ln%d\n",linenum);
	emit("mov cl,[__dfc]\n");
	emit("and ecx,byte 7\n");
	emit("jmp short ln%d\n",myline);
	emit("ln%d:\n",linenum);linenum++;

	emit("cmp edx,0800h\n");
	emit("jnz short ln%d\n",linenum);
	emit("mov ecx,[__asp]\n");
	emit("jmp short ln%d\n",myline);
	emit("ln%d:\n",linenum);linenum++;

	emit("cmp edx,0801h\n");
	emit("jnz short ln%d\n",linenum);
	emit("mov ecx,[__vbr]\n");
	emit("jmp short ln%d\n",myline);
	emit("ln%d:\n",linenum);linenum++;

	emit("jmp r_illegal\n");

	emit("ln%d:\n",myline);
	emit("add esi,byte 2\n");
	emit("mov dword[__reg+ebx*4],ecx\n");
	ret_timing(12);
}

static void i_movec_r_to_c(void){
	int myline=linenum;linenum++;
	privilegecheck();
	emit("mov bx,word[esi]\n");
	emit("mov edx,ebx\n");
	emit("shr ebx,12\n");
	emit("mov ecx,dword[__reg+ebx*4]\n");

	emit("and edx,0FFFh\n");
	emit("jnz short ln%d\n",linenum);
	emit("and cl,7\n");
	emit("mov [__sfc],cl\n");
	emit("jmp short ln%d\n",myline);
	emit("ln%d:\n",linenum);linenum++;

	emit("cmp edx,byte 1\n");
	emit("jnz short ln%d\n",linenum);
	emit("and cl,7\n");
	emit("mov [__dfc],cl\n");
	emit("jmp short ln%d\n",myline);
	emit("ln%d:\n",linenum);linenum++;

	emit("cmp edx,0800h\n");
	emit("jnz short ln%d\n",linenum);
	emit("mov [__asp],ecx\n");
	emit("jmp short ln%d\n",myline);
	emit("ln%d:\n",linenum);linenum++;

	emit("cmp edx,0801h\n");
	emit("jnz short ln%d\n",linenum);
	emit("mov [__vbr],ecx\n");
	emit("jmp short ln%d\n",myline);
	emit("ln%d:\n",linenum);linenum++;

	emit("jmp r_illegal\n");

	emit("ln%d:\n",myline);
	emit("add esi,byte 2\n");
	ret_timing(10);
}

#if 0
/* called not-quite-so-many times as i_move */
static void i_moves(void){
	int cycles;
	int unitsize = main_size == 4 ? 2 : main_size;
	int myline=linenum;
	linenum++;
	selective_usereg();
	emit("push esi\n"); /* save in case it's invalid */
	emit("movzx ecx,word[esi]\n");
	emit("add esi,byte 2\n");
	ea_step_precalc(main_size,main_eamode,-1); /* edx=address */
	emit("shr ecx,12\n");
	emit("jc short ln%d_write\n",myline);
	/* read */
	emit("cmp byte[__sfc],1\n");
	emit("je ln%d_read_userdata\n");
	emit("cmp byte[__sfc],2\n");
	emit("je ln%d_read_userprogram\n");
	emit("cmp byte[__sfc],5\n");
	emit("je ln%d_read_superdata\n");
	emit("cmp byte[__sfc],6\n");
	emit("je ln%d_read_superprogram\n");

	/*
	** Generic address space read routine; SFC in {0,3,4,7}
	*/
	emit("cmp dword[__fc_read%s],byte 0\n",sizename[unitsize]);
	emit("je short ln%d_inv\n",myline);
	emit("push ecx\n");
	emit("push edx\n");
	airlock_exit();
	emit("mov al,[__sfc]\n");
	emit("and eax,byte 7\n");
	if(use_stack) {
		emit("push edx\n");
		emit("push eax\n");
	}
	emit("call dword[__fc_read%s]\n");
	if(use_stack) {
		emit("add esp,byte 8\n");
	}
	emit("mov ecx,[esp+%d]\n",airlock_stacksize+4);
	switch(main_size) {
	case 1:emit("mov [__reg+ecx*4],al\n");break;
	case 2:emit("mov [__reg+ecx*4],ax\n");break;
	case 4:emit("mov [__reg+ecx*4+2],ax\n");
		emit("mov edx,[esp+%d]\n",airlock_stacksize);
		emit("add edx,byte 2\n");
		emit("mov al,[__sfc]\n");
		emit("and eax,byte 7\n");
		if(use_stack) {
			emit("push edx\n");
			emit("push eax\n");
		}
		emit("call dword[__fc_read%s]\n");
		if(use_stack) {
			emit("add esp,byte 8\n");
		}
		emit("mov ecx,[esp+%d]\n",airlock_stacksize+4);
		emit("mov [__reg+ecx*4],ax\n");
		break;
	}
	airlock_enter();
	emit("pop edx\n");
	emit("pop ecx\n");
	ea_step_postcalc(main_size,main_eamode,-1); /* edx=address */
	emit("jmp ln%d_end\n");

	ret_timing(cycles);
}
#endif

/****************************************************************************
** DECODE ROUTINES
****************************************************************************/

static int  rproc [0x10000];
static byte unique[0x10000];
static int  cease_decode;

static int test(int n, int m, int op) {
	int t;
	if((n & m) != op) return 0;
	for(t = op & 0xF000; t < n; t++) {
		if((!unique[t]) && ((t & m) == (n & m))) {
			rproc[n] = t;
			return 2;
		}
	}
	unique[n] = (m >> 16) & 1;
	rproc[n] = n;
	t = (m ^ 0xFFFF) & 0xFFF;
	if(!t) {
		emit("; Opcode %04X\n", n);
	} else {
		emit("; Opcodes %04X - %04X\n", n, op + t);
	}
/*	align(4);*/
	emit("%c%03X:\n", ((n >> 12) & 0xF) + 'K', n & 0xFFF);
	routine_counter++;
	return 1;
}

/* Instruction definition routine */
static void idef(
	int n, int mask, int op, void(*proc)(void)
) {
	if(cease_decode) return;
	cease_decode = test(n, mask, op);
	if(cease_decode == 1) {
		if(cputype == 68010) {
			loop_c_cycles = 10;
			loop_t_cycles = 10;
			loop_x_cycles = 16;
		}
		proc();
		if(cputype == 68010) {
			if(loop_c_cycles > 14) {
				fprintf(stderr,
					"Bad news: instruction %04X:\n"
					"loop_c_cycles (%d) exceeds limit\n",
					n, loop_c_cycles
				);
				exit(1);
			}
			if(loop_t_cycles > 14) {
				fprintf(stderr,
					"Bad news: instruction %04X:\n"
					"loop_t_cycles (%d) exceeds limit\n",
					n, loop_t_cycles
				);
				exit(1);
			}
			if(loop_x_cycles > (loop_c_cycles + 6)) {
				fprintf(stderr,
					"Bad news: instruction %04X:\n"
					"loop_c_cycles (%d) and "
					"loop_x_cycles (%d) too far apart\n",
					n, loop_c_cycles, loop_x_cycles
				);
				exit(1);
			}
			loop_x_cycles -= loop_c_cycles;
			loopinfo[n] =
				(((loop_c_cycles     ) & 0x0E)  |
				 ((loop_t_cycles << 3) & 0x70)) |
				(((loop_x_cycles << 6) & 0x80)  |
				 ((loop_x_cycles >> 2) & 0x01));
		}
	}
}

/* Batch idef for all addressing modes */
static void eadef_all(
	int n, int m, int op, void(*proc)(void)
) {
	if(cease_decode) return;
	main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc);
	main_eamode = areg; idef(n, m | 0x38, op | 0x08, proc);
	main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
	main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc);
	main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc);
	main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
	main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
	main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
	main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
	main_eamode = pcdp; idef(n, m | 0x3F, op | 0x3A, proc);
	main_eamode = pcxd; idef(n, m | 0x3F, op | 0x3B, proc);
	main_eamode = immd; idef(n, m | 0x3F, op | 0x3C, proc);
}

/* Batch idef for all addressing modes, excluding Address Register Direct
**  mode when the operand size is 1 */
static void eadef_all_nobyteaddress(
	int n, int m, int op, void(*proc)(void)
) {
	if(cease_decode) return;
	main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc);
	if(main_size != 1) {
		main_eamode = areg; idef(n, m | 0x38, op | 0x08, proc);
	}
	main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
	main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc);
	main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc);
	main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
	main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
	main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
	main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
	main_eamode = pcdp; idef(n, m | 0x3F, op | 0x3A, proc);
	main_eamode = pcxd; idef(n, m | 0x3F, op | 0x3B, proc);
	main_eamode = immd; idef(n, m | 0x3F, op | 0x3C, proc);
}

/* Batch idef for all data addressing modes */
static void eadef_data(
	int n, int m, int op, void(*proc)(void)
) {
	if(cease_decode) return;
	main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc);
	main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
	main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc);
	main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc);
	main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
	main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
	main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
	main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
	main_eamode = pcdp; idef(n, m | 0x3F, op | 0x3A, proc);
	main_eamode = pcxd; idef(n, m | 0x3F, op | 0x3B, proc);
	main_eamode = immd; idef(n, m | 0x3F, op | 0x3C, proc);
}

/* Batch idef for all alterable addressing modes, excluding Address Register
**  Direct mode when the operand size is 1 */
static void eadef_alterable_nobyteaddress(
	int n, int m, int op, void(*proc)(void)
) {
	if(cease_decode) return;
	main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc);
	if(main_size != 1) {
		main_eamode = areg; idef(n, m | 0x38, op | 0x08, proc);
	}
	main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
	main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc);
	main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc);
	main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
	main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
	main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
	main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
}

/* Batch idef for all data alterable addressing modes */
static void eadef_data_alterable(
	int n, int m, int op, void(*proc)(void)
) {
	if(cease_decode) return;
	main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc);
	main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
	main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc);
	main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc);
	main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
	main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
	main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
	main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
}

/* Batch idef for all memory alterable addressing modes */
static void eadef_memory_alterable(
	int n, int m, int op, void(*proc)(void)
) {
	if(cease_decode) return;
	main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
	main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc);
	main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc);
	main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
	main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
	main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
	main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
}

/* Batch idef for all control addressing modes */
static void eadef_control(
	int n, int m, int op, void(*proc)(void)
) {
	if(cease_decode) return;
	main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
	main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
	main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
	main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
	main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
	main_eamode = pcdp; idef(n, m | 0x3F, op | 0x3A, proc);
	main_eamode = pcxd; idef(n, m | 0x3F, op | 0x3B, proc);
}

/* Batch idef for all control alterable addressing modes */
static void eadef_control_alterable(
	int n, int m, int op, void(*proc)(void)
) {
	if(cease_decode) return;
	main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc);
	main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc);
	main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc);
	main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc);
	main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc);
}

/* Batch eadef for MOVE instructions */
static void defmove(int majorop, int n) {
	if(cease_decode) return;
	for(main_reg = 0; main_reg < 8; main_reg++) {
		word w = majorop | (main_reg << 9);
		main_destmode = dreg; eadef_all(n, 0xFFC0, 0x000 | w, i_move);
		main_destmode = aind; eadef_all(n, 0xFFC0, 0x080 | w, i_move);
		main_destmode = ainc; eadef_all(n, 0xFFC0, 0x0C0 | w, i_move);
		main_destmode = adec; eadef_all(n, 0xFFC0, 0x100 | w, i_move);
		main_destmode = adsp; eadef_all(n, 0xFFC0, 0x140 | w, i_move);
		main_destmode = axdp; eadef_all(n, 0xFFC0, 0x180 | w, i_move);
	}
	main_destmode = absw; eadef_all(n, 0xFFC0, 0x1C0 | majorop, i_move);
	main_destmode = absl; eadef_all(n, 0xFFC0, 0x3C0 | majorop, i_move);
}

/***************************************************************************/

static void decode0(int n) {
	cease_decode = 0;
	for(sizedef = 0; sizedef < 3; sizedef++) {
		main_size = 1 << sizedef;
		eadef_data_alterable(n, 0xFFC0, 0x0000 | (sizedef << 6), i_ori);
		eadef_data_alterable(n, 0xFFC0, 0x0200 | (sizedef << 6), i_andi);
		eadef_data_alterable(n, 0xFFC0, 0x0400 | (sizedef << 6), i_subi);
		eadef_data_alterable(n, 0xFFC0, 0x0600 | (sizedef << 6), i_addi);
		eadef_data_alterable(n, 0xFFC0, 0x0A00 | (sizedef << 6), i_eori);
		eadef_data_alterable(n, 0xFFC0, 0x0C00 | (sizedef << 6), i_cmpi);
		/* Not quite ready for prime time yet */
/*		if(cputype >= 68010) {
			eadef_memory_alterable(n, 0xFFC0, 0x0E00 | (sizedef << 6), i_moves);
		}*/
	}
	/*
	** Bit operations
	** BTST (main_cc 0) works with all data addressing modes; the others
	** require data alterable addressing modes
	*/
	for(main_cc = 0; main_cc < 4; main_cc++) {
		void (*eadef)(int n, int m, int op, void(*proc)(void)) =
			main_cc ? eadef_data_alterable : eadef_data;
		eadef(n, 0xFFC0, 0x0800 | (main_cc << 6), i_bitop_imm);
		for(main_reg = 0; main_reg < 8; main_reg++) {
			eadef(n, 0xFFC0,
				0x0100 | (main_cc << 6) | (main_reg << 9),
				i_bitop_reg
			);
		}
	}
	idef(n, 0xFFFF, 0x003C, i_ori_ccr );
	idef(n, 0xFFFF, 0x023C, i_andi_ccr);
	idef(n, 0xFFFF, 0x0A3C, i_eori_ccr);
	idef(n, 0xFFFF, 0x007C, i_ori_sr  );
	idef(n, 0xFFFF, 0x027C, i_andi_sr );
	idef(n, 0xFFFF, 0x0A7C, i_eori_sr );
	for(main_reg=0;main_reg<8;main_reg++)for(main_size=2;main_size<=4;main_size+=2){
		idef(n,0xFFF8,0x0108|((main_size&4)<<4)|(main_reg<<9),i_movep_mem2reg);
		idef(n,0xFFF8,0x0188|((main_size&4)<<4)|(main_reg<<9),i_movep_reg2mem);
	}
}

static void decode1(int n) {
	cease_decode = 0;
	main_size = 1;
	defmove(0x1000, n);
}

static void decode2(int n) {
	cease_decode = 0;
	main_size = 4;
	defmove(0x2000, n);
	for(main_reg = 0; main_reg < 8; main_reg++) {
		eadef_all(n, 0xFFC0, 0x2040 | (main_reg << 9), i_movea);
	}
}

static void decode3(int n) {
	cease_decode = 0;
	main_size = 2;
	defmove(0x3000, n);
	for(main_reg = 0; main_reg < 8; main_reg++) {
		eadef_all(n, 0xFFC0, 0x3040 | (main_reg << 9), i_movea);
	}
}

static void decode4(int n) {
	cease_decode = 0;
	eadef_data_alterable(n, 0xFFC0, 0x40C0, i_move_from_sr);
	if(cputype >= 68010) {
		eadef_data_alterable(n, 0xFFC0, 0x42C0, i_move_from_ccr);
	}
	eadef_data(n, 0xFFC0, 0x44C0, i_move_to_ccr);
	eadef_data(n, 0xFFC0, 0x46C0, i_move_to_sr );
	eadef_control(n, 0xFFC0, 0x4EC0, i_jmp);
	eadef_control(n, 0xFFC0, 0x4E80, i_jsr);
	for(main_reg=0;main_reg<8;main_reg++)eadef_control(n,0xFFC0,0x41C0|(main_reg<<9),i_lea);
	main_size=2;for(main_reg=0;main_reg<8;main_reg++)eadef_data(n,0xFFC0,0x4100|(main_reg<<9),i_chk);
	eadef_control(n,0xFFC0,0x4840,i_pea);
	for(sizedef = 0; sizedef < 3; sizedef++) {
		main_size = 1 << sizedef;
		eadef_data_alterable(n, 0xFFC0, 0x4200 | (sizedef << 6), i_clr);
		eadef_data_alterable(n, 0xFFC0, 0x4A00 | (sizedef << 6), i_tst);
	}
	idef(n, 0xFFFF, 0x4E70, i_reset);
	idef(n, 0xFFFF, 0x4E71, i_nop);
	idef(n, 0xFFFF, 0x4E72, i_stop);
	idef(n, 0xFFFF, 0x4E73, i_rte);
	idef(n, 0xFFFF, 0x4E75, i_rts);
	idef(n, 0xFFFF, 0x4E76, i_trapv);
	idef(n, 0xFFFF, 0x4E77, i_rtr);
	if(cputype >= 68010) {
		idef(n, 0xFFFF, 0x4E74, i_rtd);
		idef(n, 0xFFFF, 0x4E7A, i_movec_c_to_r);
		idef(n, 0xFFFF, 0x4E7B, i_movec_r_to_c);
	}
	main_dr=0;for(sizedef=0;sizedef<2;sizedef++){main_size=1<<(sizedef+1);eadef_control_alterable(n,0xFFC0,0x4880|(main_dr<<10)|(sizedef<<6),i_movem_control);}
	main_dr=1;for(sizedef=0;sizedef<2;sizedef++){main_size=1<<(sizedef+1);eadef_control          (n,0xFFC0,0x4880|(main_dr<<10)|(sizedef<<6),i_movem_control);}
	for(sizedef = 0; sizedef < 2; sizedef++) {
		main_size = 1 << (sizedef + 1);
		idef(n, 0xFFF8, 0x4C98 | (sizedef << 6), i_movem_postinc);
		idef(n, 0xFFF8, 0x48A0 | (sizedef << 6), i_movem_predec );
	}
	idef(n, 0xFFF8, 0x4E50, i_link);
	idef(n, 0xFFF8, 0x4E58, i_unlk);
	idef(n, 0xFFF0, 0x4E40, i_trap);
	idef(n, 0xFFF8, 0x4E60, i_move_to_usp);
	idef(n, 0xFFF8, 0x4E68, i_move_from_usp);
	idef(n, 0xFFF8, 0x4840, i_swap);
	idef(n, 0xFFF8, 0x4880, i_extbw);
	idef(n, 0xFFF8, 0x48C0, i_extwl);
	for(sizedef = 0; sizedef < 3; sizedef++) {
		main_size = 1 << sizedef;
		eadef_data_alterable(n, 0xFFC0, 0x4000 | (sizedef << 6),
			i_negx);
		eadef_data_alterable(n, 0xFFC0, 0x4400 | (sizedef << 6),
			i_neg);
		eadef_data_alterable(n, 0xFFC0, 0x4600 | (sizedef << 6),
			i_not);
	}
	eadef_data_alterable(n, 0xFFC0, 0x4800, i_nbcd);
	eadef_data_alterable(n, 0xFFC0, 0x4AC0, i_tas);
	if(cputype == 68010) idef(n, 0xFFF8, 0x4848, i_bkpt);
	idef(n, 0xFFFF, 0x4AFA, i_illegal);
	idef(n, 0xFFFF, 0x4AFB, i_illegal);
	idef(n, 0xFFFF, 0x4AFC, i_illegal);
}

static void decode5(int n) {
	cease_decode = 0;
	for(sizedef = 0; sizedef < 3; sizedef++) {
		main_size = 1 << sizedef;
		for(main_qv = 0; main_qv < 8; main_qv++) {
			word w = (sizedef << 6) | (main_qv << 9);
			eadef_alterable_nobyteaddress(n, 0xFFC0, 0x5000 | w,
				i_addq);
			eadef_alterable_nobyteaddress(n, 0xFFC0, 0x5100 | w,
				i_subq);
		}
	}
	for(main_cc = 0x2; main_cc <= 0xF; main_cc++) {
		idef(n, 0xFFF8, 0x50C8 | (main_cc << 8), i_dbcc);
	}
	idef(n, 0xFFF8, 0x51C8, i_dbra);
	main_size = 1;
	for(main_cc = 0x0; main_cc <= 0xF; main_cc++) {
		eadef_data_alterable(n, 0xFFC0, 0x50C0 | (main_cc << 8),
			i_scc);
	}
}

static void decode6(int n){
	cease_decode=0;
	idef(n,0x1FFFF,0x6000,i_bra_w);
	idef(n,0x1FFFF,0x6100,i_bsr_w);
	for(main_cc=0x2;main_cc<=0xF;main_cc++){
		idef(n,0x1FFFF,0x6000|(main_cc<<8),i_bcc_w);
	}
	idef(n,0x0FF00,0x6000,i_bra_b);
	idef(n,0x0FF00,0x6100,i_bsr_b);
	for(main_cc=0x2;main_cc<=0xF;main_cc++){
		idef(n,0x0FF00,0x6000|(main_cc<<8),i_bcc_b);
	}
}

static void decode7(int n){
	cease_decode=0;
	for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFF00,0x7000|(main_reg<<9),i_moveq);
}

static void decode8(int n){
	cease_decode=0;
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_data             (n,0xFFC0,0x8000|(main_reg<<9)|(sizedef<<6),i_or_dn );}}
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_memory_alterable (n,0xFFC0,0x8100|(main_reg<<9)|(sizedef<<6),i_or_ea );}}
	for(main_cc=0;main_cc<2;main_cc++)for(main_reg=0;main_reg<8;main_reg++)eadef_data(n,0xFFC0,0x80C0|(main_reg<<9)|(main_cc<<8),i_div);
	main_size=1;
	for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0x8100|(main_reg<<9),i_sbcd_dreg);
	for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0x8108|(main_reg<<9),i_sbcd_adec);
}

static void decode9(int n){
	cease_decode=0;
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_all_nobyteaddress(n,0xFFC0,0x9000|(main_reg<<9)|(sizedef<<6),i_sub_dn);}}
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_memory_alterable (n,0xFFC0,0x9100|(main_reg<<9)|(sizedef<<6),i_sub_ea);}}
	main_size=2;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0x90C0|(main_reg<<9),i_suba);
	main_size=4;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0x91C0|(main_reg<<9),i_suba);
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0x9100|(main_reg<<9)|(sizedef<<6),i_subx_dreg);}
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0x9108|(main_reg<<9)|(sizedef<<6),i_subx_adec);}
}

static void decodeA(int n){
	cease_decode=0;
	idef(n,0xF000,0xA000,i_aline);
}

static void decodeB(int n){
	cease_decode=0;
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_all_nobyteaddress(n,0xFFC0,0xB000|(main_reg<<9)|(sizedef<<6),i_cmp_dn);}}
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_data_alterable   (n,0xFFC0,0xB100|(main_reg<<9)|(sizedef<<6),i_eor_ea);}}
	main_size=2;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0xB0C0|(main_reg<<9),i_cmpa);
	main_size=4;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0xB1C0|(main_reg<<9),i_cmpa);
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xB108|(sizedef<<6)|(main_reg<<9),i_cmpm);}
}

static void decodeC(int n){
	cease_decode=0;
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_data             (n,0xFFC0,0xC000|(main_reg<<9)|(sizedef<<6),i_and_dn);}}
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_memory_alterable (n,0xFFC0,0xC100|(main_reg<<9)|(sizedef<<6),i_and_ea);}}
	for(main_cc=0;main_cc<2;main_cc++)for(main_reg=0;main_reg<8;main_reg++)eadef_data(n,0xFFC0,0xC0C0|(main_reg<<9)|(main_cc<<8),i_mul);
	main_dr=0 ;main_ir=0 ;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC140|(main_reg<<9),i_exg);
	main_dr=32;main_ir=32;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC148|(main_reg<<9),i_exg);
	main_dr=0 ;main_ir=32;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC188|(main_reg<<9),i_exg);
	main_size=1;
	for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC100|(main_reg<<9),i_abcd_dreg);
	for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC108|(main_reg<<9),i_abcd_adec);
}

static void decodeD(int n){
	cease_decode=0;
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_all_nobyteaddress(n,0xFFC0,0xD000|(main_reg<<9)|(sizedef<<6),i_add_dn);}}
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_memory_alterable (n,0xFFC0,0xD100|(main_reg<<9)|(sizedef<<6),i_add_ea);}}
	main_size=2;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0xD0C0|(main_reg<<9),i_adda);
	main_size=4;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0xD1C0|(main_reg<<9),i_adda);
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xD100|(main_reg<<9)|(sizedef<<6),i_addx_dreg);}
	for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xD108|(main_reg<<9)|(sizedef<<6),i_addx_adec);}
}

static void decodeE(int n){
	cease_decode=0;
	for(main_reg=0;main_reg<8;main_reg++)
	for(main_dr=0;main_dr<2;main_dr++)
	for(main_ir=0;main_ir<2;main_ir++)
	for(sizedef=0;sizedef<3;sizedef++){
		main_size=1<<sizedef;
		idef(n,0xFFF8,0xE000|(main_reg<<9)|(main_dr<<8)|(sizedef<<6)|(main_ir<<5),i_asx_reg);
		idef(n,0xFFF8,0xE008|(main_reg<<9)|(main_dr<<8)|(sizedef<<6)|(main_ir<<5),i_lsx_reg);
		idef(n,0xFFF8,0xE010|(main_reg<<9)|(main_dr<<8)|(sizedef<<6)|(main_ir<<5),i_rxx_reg);
		idef(n,0xFFF8,0xE018|(main_reg<<9)|(main_dr<<8)|(sizedef<<6)|(main_ir<<5),i_rox_reg);
	}
	for(main_dr=0;main_dr<2;main_dr++){
		eadef_memory_alterable(n,0xFFC0,0xE0C0|(main_dr<<8),i_asx_mem);
		eadef_memory_alterable(n,0xFFC0,0xE2C0|(main_dr<<8),i_lsx_mem);
		eadef_memory_alterable(n,0xFFC0,0xE4C0|(main_dr<<8),i_rxx_mem);
		eadef_memory_alterable(n,0xFFC0,0xE6C0|(main_dr<<8),i_rox_mem);
	}
}

static void decodeF(int n) {
	cease_decode = 0;
	idef(n, 0xF000, 0xF000, i_fline);
}

static void (*(decodetable[16]))(int n) =
{decode0, decode1, decode2, decode3, decode4, decode5, decode6, decode7,
 decode8, decode9, decodeA, decodeB, decodeC, decodeD, decodeE, decodeF};

/***************************************************************************/

/* Build a jump table entry (including loop info for 68010) */
static void tableentry(int last, int rl) {
	if(last == -1){
		emit("dd r_illegal-top");
	} else {
		emit("dd %c%03X-top",
			((last >> 12) & 0xF) + 'K', last & 0xFFF
		);
	}
	if(rl > 1) emit("+%u", ((dword)(rl - 1)) << 24);
	emit("\n");
	if(cputype == 68010) emit("db %d\n", loopinfo[last]);
}

/* Return the next parameter (or NULL if there isn't one */
static char *getparameter(int *ip, int argc, char **argv) {
	int i;
	(*ip)++;
	i = (*ip);
	if(i >= argc) {
		fprintf(stderr, "Invalid use of %s option\n", argv[i - 1]);
		return NULL;
	}
	return argv[i];
}

int main(int argc, char **argv) {
	int i, j, last, rl, bank;
	char *codefilename = NULL;
	char default_sourcename[10];

	fprintf(stderr, "STARSCREAM version " VERSION "\n");

	/* Read options from the command line */
	for(i = 1; i < argc; i++) {
		char *a = argv[i];
		if(*a == '-') {
			a++;
			       if(!strcmp("regcall"    , a)) { use_stack = 0;
			} else if(!strcmp("stackcall"  , a)) { use_stack = 1;
			} else if(!strcmp("nohog"      , a)) { hog = 0;
			} else if(!strcmp("hog"        , a)) { hog = 1;
			} else if(!strcmp("addressbits", a)) {
				int n;
				char *s = getparameter(&i, argc, argv);
				if(!s) return 1;
				n = atol(s);
				if(n < 1 || n > 32) {
					fprintf(stderr,
						"Invalid number of address "
						"bits: \"%s\"\n", argv[i]
					);
					return 1;
				}
				addressbits = n;
			} else if(!strcmp("cputype"    , a)) {
				int n;
				char *s = getparameter(&i, argc, argv);
				if(!s) return 1;
				n = atol(s);
				switch(n) {
				case 68000:
				case 68010:
				case 68020:
					cputype = n;
					break;
				default:
					fprintf(stderr,
						"Invalid CPU type: \"%s\"\n",
						argv[i]
					);
					return 1;
				}
			} else if(!strcmp("name"       , a)) {
				sourcename = getparameter(&i, argc, argv);
				if(!sourcename) return 1;
			} else {
				fprintf(stderr,
					"\nUnrecognized option: \"%s\"\n",
					argv[i]
				);
				return 1;
			}
		} else {
			if(codefilename) {
				fprintf(stderr,
					"\n\"%s\": only one output filename "
					"is allowed\n",
					argv[i]
				);
				return 1;
			}
			codefilename = argv[i];
		}
	}

	if(!codefilename) {
		fprintf(stderr, "usage: %s outputfile [options]\n", argv[0]);
		fprintf(stderr, "see STARDOC.TXT for details\n");
		return 1;
	}

	/* Set default options where applicable */
	if(use_stack   < 0) use_stack = 1;
	if(hog         < 0) hog       = 0;
	if(cputype     < 0) cputype   = 68000;
	if(addressbits < 0) {
		if(cputype <= 68010) addressbits = 24;
		else                 addressbits = 32;
	}
	if(!sourcename) {
		sprintf(default_sourcename, "s%d", cputype);
		sourcename = default_sourcename;
	}

	/* Prepare to generate the code file */
	linenum = 0;
	fflush(stdout);
	fflush(stderr);
	codefile = fopen(codefilename, "w");
	if(!codefile) {
		perror(codefilename);
		return 1;
	}

	fprintf(stderr, "Generating \"%s\" with the following options:\n",
		codefilename
	);
	optiondump(stderr, " *  ");
	prefixes();
	for(i = 0; i < 0x10000; i++) rproc[i] = -1;
	/* Clear loop timings for 68010 */
	if(cputype == 68010) {
		for(i = 0; i < 0x10000; i++) loopinfo[i] = 0xDB;
	}

	/*
	** Decode instructions
	** (this is where the vast majority of the code is emitted)
	*/
	fprintf(stderr, "Decoding instructions: ");
	for(bank = 0; bank <= 0xF; bank++) {
		int bankend = (bank + 1) << 12;
		void (*decoderoutine)(int n) = decodetable[bank];
		fprintf(stderr, "%X", bank);
		fflush(stderr);
		for(i = bank << 12; i < bankend; i++) decoderoutine(i);
	}
	fprintf(stderr, " done\n");

	/*
	** Build the main jump table (all CPUs) / loop info table (68010)
	*/
	fprintf(stderr, "Building table: ");
	emit("section .bss\n");
	emit("bits 32\n");
	align(4);
	emit("__jmptbl resb 262144\n");
	if(cputype == 68010) emit("__looptbl resb 65536\n");
	emit("section .data\n");
	emit("bits 32\n");
	align(4);
	emit("__jmptblcomp:\n");
	last = -2;
	rl = 0;
	for(i = 0; i < 0x10000; i++) {
		j = rproc[i];
		if(j == last){
			if(rl == 256) {
				tableentry(last, rl);
				rl = 1;
			} else {
				rl++;
			}
		} else {
			if(rl) tableentry(last, rl);
			rl = 1;
			last = j;
		}
	}
	tableentry(last, rl);
	align(4);

	/* Finish up */
	suffixes();
	fprintf(stderr, "done\n");
	fprintf(stderr, "routine_counter = %d\n", routine_counter);
	fclose(codefile);
	return 0;
}
