 /* 
  * UAE - The Un*x Amiga Emulator
  * 
  * m68k emulation
  *
  * (c) 1995 Bernd Schmidt
  */

#include "sysconfig.h"
#include "sysdeps.h"

#include "config.h"
#include "options.h"
#include "events.h"
#include "gui.h"
#include "memory.h"
#include "custom.h"
#include "newcpu.h"
#include "ersatz.h"
#include "readcpu.h"
#include "blitter.h"
#include "debug.h"
#include "autoconf.h"
#include "compiler.h"

#define RELY_ON_LOADSEG_DETECTION

#ifdef USE_COMPILER

#include <sys/mman.h>

char *address_space, *good_address_map;

code_execfunc exec_me;
UBYTE nr_bbs_to_run = 1;
int nr_bbs_start = 40;

static int compile_failure;
static int quiet_compile = 1;
int i_want_to_die = 1;
static int n_compiled = 0;
static int n_max_comp = 99999999;
static CPTR call_only_me = 0;

int patched_syscalls = 0;

static int count_bits(UWORD v)
{
    int bits = 0;
    while (v != 0) {
	if (v & 1)
	    bits++;
	v >>= 1;
    }
    return bits;
}

static UWORD bitswap(UWORD v)
{
    UWORD newv = 0;
    UWORD m1 = 1, m2 = 0x8000;
    int i;
    
    for (i = 0; i < 16; i++) {
	if (v & m1)
	    newv |= m2;
	m2 >>= 1;
	m1 <<= 1;
    }
    return newv;
}

static long long compiled_hits = 0;

/* @@@ FIXME: a defragmenter would be nice for this, but since we flush
 * the cache all the time anyway to deal with LoadSegs() */

/* 16K areas with 512 byte blocks */
#define SUBUNIT_ORDER 9
#define PAGE_SUBUNIT (1 << SUBUNIT_ORDER)
#define PAGE_ALLOC_UNIT (PAGE_SUBUNIT * 32)

static int zerofd;
static int zeroff;
static struct code_page *first_code_page;

static struct code_page *new_code_page(void)
{
    struct code_page *ncp;
    
    ncp = (struct code_page *)mmap(NULL, PAGE_ALLOC_UNIT, 
				   PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE,
				   zerofd, zeroff);
    zeroff += PAGE_ALLOC_UNIT;
    if (ncp) {
	ncp->next = first_code_page;
	first_code_page = ncp;
	ncp->allocmask = 1; /* what a waste */
    }
    return ncp;
}

#define NUM_HASH 1024
#define NUM_FUNCTIONS 8192
#define HASH_MASK (NUM_HASH-1)

static int SCAN_MARK = 5; /* Number of calls after which to scan a function */
static int COMPILE_MARK = 50; /* Number of calls after which to compile a function */

static struct hash_entry cpu_hash[NUM_HASH];
static struct hash_block lru_first_block;
static struct hash_entry lru_first_hash;
static struct hash_entry *freelist_hash;
static struct hash_block *freelist_block;
static struct hash_entry hash_entries[NUM_FUNCTIONS];

static int m68k_scan_func(struct hash_entry *);
static int m68k_compile_block(struct hash_block *);

static char *alloc_code(struct hash_block *hb, int ninsns)
{
    struct code_page *cp;
    long int allocsize = (ninsns * 32 + PAGE_SUBUNIT-1) & ~(PAGE_SUBUNIT-1);
    ULONG allocmask;
    int allocbits;
    int j;
    int last_bit;

    if (allocsize >= (PAGE_ALLOC_UNIT - (1 << SUBUNIT_ORDER)))
	return NULL;
    allocbits = (allocsize >> SUBUNIT_ORDER);
    allocmask = (1 << allocbits) - 1;
    
    for (cp = first_code_page; cp != NULL; cp = cp->next) {
	ULONG thispage_alloc = cp->allocmask;
	for (j = 1; j < (33 - allocbits); j++) {
	    if ((cp->allocmask & (allocmask << j)) == 0) {
		goto found_page;
	    }
	}
    }

    /* Nothing large enough free: make a new page */
    cp = new_code_page();
    if (cp == NULL)
	return NULL;
    j = 1;
    
found_page:
    /* See whether there is in fact more space for us. If so, allocate all of
     * it. compile_block() will free everything it didn't need. */

    allocmask <<= j;
    last_bit = allocbits + j;
    while (last_bit < 32 && (cp->allocmask & (1 << last_bit)) == 0) {
	allocmask |= 1 << last_bit;
	allocsize += PAGE_SUBUNIT;
	last_bit++;
    }

    hb->page_allocmask = allocmask;
    hb->cpage = cp;
    cp->allocmask |= allocmask;
    hb->compile_start = ((char *)cp + (j << SUBUNIT_ORDER));
    hb->alloclen = allocsize;
    return hb->compile_start;
}

static int remove_hash_from_lists(struct hash_entry *h)
{
    if (h->locked || h->cacheflush)
	return 0;

    h->lru_next->lru_prev = h->lru_prev;
    h->lru_prev->lru_next = h->lru_next;
    
    h->next->prev = h->prev;
    h->prev->next = h->next;
    return 1;
}

static void forget_block(struct hash_block *hb)
{
    struct hash_entry *h = hb->he_first;
    
    hb->lru_next->lru_prev = hb->lru_prev;
    hb->lru_prev->lru_next = hb->lru_next;

    hb->lru_next = freelist_block;
    freelist_block = hb;

    if (hb->cpage != NULL)
	fprintf(stderr, "Discarding block with code. Tsk.\n");

    do {
	struct hash_entry *next = h->next_same_block;
	h->block = NULL;
	h->execute = NULL;
	h->next_same_block = NULL;
	h = next;
    } while (h != hb->he_first);
}

static void kill_lru_block(void)
{
    struct hash_block *hb = lru_first_block.lru_next;
    struct hash_entry *h = hb->he_first;
    
    hb->lru_next->lru_prev = hb->lru_prev;
    hb->lru_prev->lru_next = hb->lru_next;

    hb->lru_next = freelist_block;
    freelist_block = hb;
    
    if (hb->cpage != NULL) {
	hb->cpage->allocmask &= ~hb->page_allocmask;
    }
    do {
	struct hash_entry *next = h->next_same_block;
	if (remove_hash_from_lists(h)) {
	    h->next_same_block = freelist_hash;
	    freelist_hash = h;
	} else {
	    h->block = NULL;
	    h->next_same_block = NULL;
	    h->execute = NULL;
	}
	h = next;
    } while (h != hb->he_first);
}

static void lru_touch_block(struct hash_block *h)
{
    h->lru_next->lru_prev = h->lru_prev;
    h->lru_prev->lru_next = h->lru_next;
    
    h->lru_next = &lru_first_block;
    h->lru_prev = lru_first_block.lru_prev;
    h->lru_prev->lru_next = h;
    lru_first_block.lru_prev = h;    
}

static int check_block(struct hash_block *hb)
{
#ifndef RELY_ON_LOADSEG_DETECTION
    struct hash_entry *h = hb->he_first;
    
    do {
	struct hash_entry *next = h->next_same_block;
	if (h->matchword != *(ULONG *)get_real_address(h->addr))
	    return 0;
	h = next;
    } while (h != hb->he_first);
#endif
    return 1;
}

ULONG flush_icache(void)
{
    struct hash_block *hb = lru_first_block.lru_next;
    
    while (hb != &lru_first_block) {
	struct hash_block *next = hb->lru_next;
	if (hb->cpage != NULL) {
	    /* Address in chipmem? Then forget about block*/
	    if ((hb->he_first->addr & ~0xF80000) != 0xF80000) {
		hb->cpage->allocmask &= ~hb->page_allocmask;
		hb->cpage = NULL;
		forget_block(hb);
	    }
	}
	hb = next;
    }
    return regs.d[0];
}

void possible_loadseg(void)
{
    fprintf(stderr, "Possible LoadSeg() detected\n");
    flush_icache();
}

static struct hash_block *new_block(void)
{
    struct hash_block *b = freelist_block;
    
    if (b != NULL) {
	freelist_block = b->lru_next;
    } else
	b = (struct hash_block *)malloc(sizeof *b);
    b->nrefs = 0;
    b->cpage = NULL;
    b->he_first = NULL;
    b->translated = b->untranslatable = b->allocfailed = 0;
    return b;
}

static struct hash_entry *get_free_hash(void)
{
    struct hash_entry *h;

    for (;;) {
	h = freelist_hash;
	if (h != NULL) {
	    freelist_hash = h->next_same_block;
	    break;
	} 
	h = lru_first_hash.lru_next;
	if (h->block == NULL) {
	    remove_hash_from_lists(h);
	    break;
	}
	kill_lru_block();
    }
    h->block = NULL;
    h->ncalls = 0;
    h->locked = h->cacheflush = 0;
    h->execute = NULL;
    return h;
}

static struct hash_entry *new_hash(CPTR addr)
{
    struct hash_entry *h = get_free_hash();
    
    h->addr = addr;

    /* Chain the new node */
    h->prev = cpu_hash + ((addr >> 1) & HASH_MASK);
    h->next = h->prev->next;
    h->next->prev = h->prev->next = h;

    h->lru_next = &lru_first_hash;
    h->lru_prev = lru_first_hash.lru_prev;
    h->lru_prev->lru_next = h;
    lru_first_hash.lru_prev = h;
    
    h->next_same_block = NULL;

    return h;
}

static void lru_touch(struct hash_entry *h)
{
    if (0) {
    h->lru_next->lru_prev = h->lru_prev;
    h->lru_prev->lru_next = h->lru_next;
    
    h->lru_next = &lru_first_hash;
    h->lru_prev = lru_first_hash.lru_prev;
    h->lru_prev->lru_next = h;
    lru_first_hash.lru_prev = h;
    }
}

static struct hash_entry *find_hash(CPTR addr)
{
    struct hash_entry *h;
    struct hash_entry *h1 = cpu_hash + ((addr >> 1) & HASH_MASK);

    if (h1->next->addr == addr)
	return h1->next;
    
    for (h = h1->next; h != h1; h = h->next) {
	if (h->addr == addr) {
	    h->next->prev = h->prev; h->prev->next = h->next;
	    h->prev = h1;
	    h->next = h1->next;
	    h->next->prev = h->prev->next = h;
	    return h;
	}
    }
    return NULL;
}

static struct hash_entry *get_hash_for_func(CPTR addr)
{
    struct hash_entry *h = find_hash(addr);
    if (h == NULL)
	h = new_hash (addr);
    else
	lru_touch(h);
    return h;
}

static struct hash_entry *get_hash(CPTR addr)
{
    struct hash_entry *h = get_hash_for_func(addr);

    if (h->block == NULL) {
	if (++h->ncalls == SCAN_MARK) {
	    m68k_scan_func(h);
	}
    } else
	if (!h->block->untranslatable && h->block->nrefs++ == COMPILE_MARK) {
	    lru_touch_block(h->block);
	    if (m68k_compile_block(h->block)) {
		h->block->untranslatable = 1;
	    } else
		h->block->translated = 1;
	}

    return h;
}

void special_flush_hash(CPTR addr)
{
    struct hash_entry *h = get_hash_for_func(addr);
    
    h->cacheflush = 1;
}

static __inline__ void m68k_setpc_hash(CPTR newpc)
{
    struct hash_entry *h = get_hash(newpc);
    
    if (h->cacheflush)
	flush_icache();

    if (h->execute != NULL) {
	if ((h->addr & 0xF80000) == 0xF80000 || check_block(h->block)) {
	    compiled_hits++;
	    if (i_want_to_die && (call_only_me == 0 || call_only_me == newpc)) {
		exec_me = h->execute;
		nr_bbs_to_run = nr_bbs_start;
		regs.spcflags |= SPCFLAG_EXEC;
	    }
	} else 
	    flush_icache();
    }
    regs.pc = newpc;
    regs.pc_p = regs.pc_oldp = get_real_address(newpc);
}

static __inline__ void m68k_setpc_nohash(CPTR newpc)
{
#if 0
    /* This is probably not too good for efficiency... FIXME */
    struct hash_entry *h = find_hash(newpc);

    if (h != NULL && h->cacheflush)
	flush_icache();
#endif
    regs.pc = newpc;
    regs.pc_p = regs.pc_oldp = get_real_address(newpc);
}

void m68k_setpc(CPTR newpc)
{
    m68k_setpc_hash(newpc);    
}

void m68k_setpc_fast(CPTR newpc)
{
    m68k_setpc_nohash(newpc);    
}

void m68k_setpc_rte(CPTR newpc)
{
    m68k_setpc_nohash(newpc);
}

void m68k_setpc_bcc(CPTR newpc)
{
    m68k_setpc_hash(newpc);
}

static void hash_init(void)
{
    int i;
    struct hash_entry **hepp;
    
    for(i = 0; i < NUM_HASH; i++) {
	cpu_hash[i].next = cpu_hash[i].prev = cpu_hash + i;
	cpu_hash[i].lru_next = cpu_hash[i].lru_prev = NULL;
	cpu_hash[i].block = NULL;
	cpu_hash[i].locked = 0; cpu_hash[i].cacheflush = 0;
	cpu_hash[i].addr = -1;
    }
    hepp = &freelist_hash;
    for(i = 0; i < NUM_FUNCTIONS; i++) {
	*hepp = hash_entries + i;
	hash_entries[i].next_same_block = NULL;
	hash_entries[i].addr = -1;
	hepp = &hash_entries[i].next_same_block;
    }
    lru_first_hash.lru_next = lru_first_hash.lru_prev = &lru_first_hash;
    lru_first_block.lru_next = lru_first_block.lru_prev = &lru_first_block;
    
    freelist_block = NULL;
}

static void code_init(void)
{
    first_code_page = NULL;
    zerofd = open("/dev/zero", O_RDWR);
    zeroff = 0;
}

void compiler_init(void)
{
    int i;
    code_init();
    hash_init();
}

/* Help function for the scan routine */
static __inline__ int cc_flagmask(const int cc)
{
    switch(cc){
     case 0: return 0;                       /* T */
     case 1: return 0;                       /* F */
     case 2: return 5;                       /* HI */
     case 3: return 5;                       /* LS */
     case 4: return 1;                       /* CC */
     case 5: return 1;                       /* CS */
     case 6: return 4;                       /* NE */
     case 7: return 4;                       /* EQ */
     case 8: return 2;                       /* VC */
     case 9: return 2;                       /* VS */
     case 10:return 8;                       /* PL */
     case 11:return 8;                       /* MI */
     case 12:return 10;                      /* GE */
     case 13:return 10;                      /* LT */
     case 14:return 14;                      /* GT */
     case 15:return 14;                      /* LE */
    }
    abort();
    return 0;
}

static __inline__ void translate_step_over_ea(UBYTE **pcpp, amodes m, 
					      wordsizes size)
{
    switch (m) {
     case Areg:
     case Dreg:
     case Aind:
     case Aipi:
     case Apdi:
     case immi:
	break;

     case imm:
	if (size == sz_long)
	    goto is_long;
	/* fall through */
     case Ad16:
     case PC16:
     case imm0:
     case imm1:
     case absw:
	(*pcpp)+=2;
	break;
     case Ad8r:
     case PC8r:
	{
	    UWORD extra = *(*pcpp)++;
	    extra <<= 8;
	    extra |= *(*pcpp)++;
	    /* @@@ handle 68020 stuff here */
	}
	break;
     case absl:
     case imm2:
	is_long:
	(*pcpp) += 4;
	break;
    }
}

static struct instr *translate_getnextinsn(UBYTE **pcpp)
{
    UWORD opcode;
    struct instr *dp;
    
    opcode = *(*pcpp)++ << 8;
    opcode |= *(*pcpp)++;
    
    if (cpufunctbl[opcode] == op_illg) {
	opcode = 0x4AFC;
    }
    dp = table68k + opcode;
    if (dp->suse) {
	translate_step_over_ea(pcpp, dp->smode, dp->size);
    }
    if (dp->duse) {
	translate_step_over_ea(pcpp, dp->dmode, dp->size);
    }
    return dp;
}

#define CB_STACKSIZE 200
#define BB_STACKSIZE 200

static ULONG condbranch_stack[CB_STACKSIZE];
static int condbranch_src_stack[CB_STACKSIZE];

struct bb_info {
    struct hash_entry *h;
    CPTR stopaddr;
    int can_compile_last;
    struct bb_info *bb_next1, *bb_next2;
    int flags_live_at_end;
    int flags_live_at_start;
    int first_iip, last_iip;
} bb_stack[BB_STACKSIZE];

static int top_bb;

static CPTR bcc_target_stack[BB_STACKSIZE];

static int new_bcc_target(CPTR addr)
{
    int i;
    
    for (i = 0; i < top_bb; i++)
	if (bcc_target_stack[i] == addr)
	    return 1;

    if (top_bb == BB_STACKSIZE)
	return 0;
    bcc_target_stack[top_bb++] = addr;
    return 1;
}

static int bcc_compfn(const void *a, const void *b)
{
    CPTR *a1 = (CPTR *)a, *b1 = (CPTR *)b;
    
    if (*a1 == *b1)
	printf("BUG!!\n");
    
    if (*a1 < *b1)
	return 1;
    return -1;
}

static int bb_compfn(const void *a, const void *b)
{
    struct bb_info *a1 = (struct bb_info *)a, *b1 = (struct bb_info *)b;
    
    if (a1->h->addr == b1->h->addr)
	printf("BUG!!\n");
    
    if (a1->h->addr < b1->h->addr)
	return -1;
    return 1;
}

static int find_basic_blocks(struct hash_entry *h)
{
    int current_bb = 0;

    top_bb = 0;
    bcc_target_stack[0] = h->addr;
    new_bcc_target(h->addr);

    while (top_bb > current_bb) {
	CPTR addr = bcc_target_stack[current_bb];
	int ninsns = 0;
	UBYTE *realpc = get_real_address(addr);
	UBYTE *rpc_start = realpc;
	
	for(;;) {
	    CPTR thisinsn_addr = (realpc - rpc_start) + addr;
	    UBYTE *rpc_save = realpc;
	    struct instr *dp = translate_getnextinsn(&realpc);
	    CPTR nextinsn_addr = (realpc - rpc_start) + addr;

	    if (dp->mnemo == i_RTS || dp->mnemo == i_RTE 
		|| dp->mnemo == i_RTR || dp->mnemo == i_RTD
		|| dp->mnemo == i_JMP || dp->mnemo == i_ILLG) 
	    {
		break;
	    }
	    
	    if (dp->mnemo == i_BSR || dp->mnemo == i_JSR) {
		if (!new_bcc_target(nextinsn_addr))
		    return 0;
		break;
	    }

	    if (dp->mnemo == i_DBcc) {
		CPTR newaddr = thisinsn_addr + 2 + (WORD)((*(rpc_save+2) << 8) | *(rpc_save+3));
		if (!new_bcc_target(nextinsn_addr))
		    return 0;
		if (!new_bcc_target(newaddr))
		    return 0;
		break;
	    }
	    
	    if (dp->mnemo == i_Bcc) {
		CPTR newaddr;
		if (dp->smode == imm1)
		    newaddr = thisinsn_addr + 2 + (WORD)((*(rpc_save+2) << 8) | *(rpc_save+3));
		else
		    newaddr = thisinsn_addr + 2 + (BYTE)dp->sreg;
		
		if (dp->cc != 0)
		    if (!new_bcc_target(nextinsn_addr))
			return 0;
		if (!new_bcc_target(newaddr))
		    return 0;
		break;
	    }
	}
	current_bb++;
    }

    qsort(bcc_target_stack, top_bb, sizeof (CPTR), bcc_compfn);

    return 1;
}

static int m68k_scan_func(struct hash_entry *h)
{
    int i;
    struct hash_block *found_block;
    struct hash_entry **hepp;
    
    if (!find_basic_blocks(h))
	return 0;

    found_block = NULL;
    
    for (i = 0; i < top_bb; i++) {
	struct hash_entry *h = get_hash_for_func(bcc_target_stack[i]);
	bb_stack[i].h = h;
	/* if (h->block != NULL && h->block != found_block) {
	    if (found_block == NULL) {
		if (h->block->cpage != NULL)
		    fprintf(stderr, "Found compiled code\n");
		else
		    found_block = h->block;
	    } else {
		fprintf(stderr, "Multiple blocks found.\n");
		if (h->block->cpage == NULL)
		    forget_block(h->block);
		else if (found_block->cpage == NULL) {
		    forget_block(found_block);
		    found_block = h->block;
		} else
		    fprintf(stderr, "Bad case.\n");
	    }
	}*/
    }
    if (found_block == NULL) {
	found_block = new_block();

	found_block->lru_next = &lru_first_block;
	found_block->lru_prev = lru_first_block.lru_prev;
	found_block->lru_prev->lru_next = found_block;
	lru_first_block.lru_prev = found_block;
    }

    hepp = &found_block->he_first;
    found_block->he_first = NULL;
    for (i = 0; i < top_bb; i++) {
	struct bb_info *bb = bb_stack + i;

	if (bb->h->block == NULL) {
	    bb->h->block = found_block;
	    *hepp = bb->h;
	    hepp = &bb->h->next_same_block;
	}
    }
    *hepp = found_block->he_first;
    return 1;
}

struct ea_reg_info {
    enum { eat_reg, eat_imem, eat_amem, eat_const } ea_type;
    int regs_set:16;
    int regs_used:16;
    int nr_scratch;
    ULONG temp1, temp2;
};

#define MAX_TRANSLATE 2048
struct insn_info_struct {
    CPTR address;
    struct instr *dp;
    int flags_set;
    int flags_used;
    int flags_live_at_end;
    int jump_target;
    int jumps_to;
    char *compiled_jumpaddr; /* Address to use for jumps to this insn */
    char *compiled_fillin;   /* Address where to put offset if this is a Bcc */
    int regs_set:16;
    int regs_used:16;
    int stop_translation:1;
    int sync_cache:1;
    int sync_flags:1;
    int ccuser_follows:1;
} insn_info [MAX_TRANSLATE];

#define EA_LOAD 1
#define EA_STORE 2
#define EA_IN_REG 4 /* Not quite sure yet what this flag will mean... :) */
#if 0
static void analyze_ea_for_insn(amodes mode, int reg, wordsizes size,
				struct ea_reg_info *eai,
				UBYTE **pcpp, CPTR pca,
				int ea_purpose)
{
    UBYTE *p = *pcpp;

    switch(mode) {
     case Dreg:
	eai->ea_type = eat_reg;
	if (size != sz_long && (ea_purpose & EA_STORE))
	    ea_purpose |= EA_LOAD;
	if (ea_purpose & EA_LOAD)
	    eai->regs_used |= 1 << reg;
	if (ea_purpose & EA_STORE)
	    eai->regs_set |= 1 << reg;
	break;
	
     case Areg:
	eai->ea_type = eat_reg;
	if (size != sz_long && (ea_purpose & EA_STORE))
	    printf("Areg != long\n");
	if (ea_purpose & EA_LOAD)
	    eai->regs_used |= 1 << (8+reg);
	if (ea_purpose & EA_STORE)
	    eai->regs_set |= 1 << (8+reg);
	break;
	
     case Ad16:
     case Aind:
     case Apdi:
     case Aipi:
	eai->ea_type = eat_imem;
	eai->regs_used |= 1 << (8+reg);
	break;

     case Ad8r:
	eai->ea_type = eat_imem;
	pii->regs_used |= 1 << (8+reg);

	eai->temp = (UWORD)((*p << 8) | *(p+1));
	r = (eai->temp & 0x7000) >> 12;
	(*pcpp) += 2; p += 2;	
	
	if (eai->temp1 & 0x8000)
	    pii->regs_used |= 1 << (8+r);
	else
	    pii->regs_used |= 1 << r;
	break;

     case PC8r:
	eai->ea_type = eat_imem;
	eai->temp1 = (UWORD)((*p << 8) | *(p+1));
	eai->temp2 = pca + (BYTE)eai->temp1;
	(*pcpp) += 2; p += 2;
	r = (eai->temp1 & 0x7000) >> 12;

	if (eai->temp1 & 0x8000)
	    pii->regs_used |= 1 << (8+r);
	else
	    pii->regs_used |= 1 << r;
	break;
	
     case PC16:	
	eai->ea_type = eat_amem;
	eai->temp1 = pca + (WORD)((*p << 8) | *(p+1));
	(*pcpp) += 2;
	break;
	
     case absw:
	eai->ea_type = eat_amem;
	eai->temp1 = (WORD)((*p << 8) | *(p+1));
	(*pcpp) += 2;
	break;

     case absl:
	eai->ea_type = eat_amem;
	eai->temp1 = (LONG)((*p << 24) | (*(p+1) << 16)
				   | (*(p+2) << 8) | *(p+3));
	(*pcpp) += 4;
	break;

     case imm:
	if (size == sz_long)
	    goto imm2_const;
	if (size == sz_word)
	    goto imm1_const;
	
	/* fall through */
     case imm0:
	eai->ea_type = eat_imm;
	eai->temp1 = (BYTE)*(p+1);
	(*pcpp) += 2;
	break;

     case imm1:
	imm1_const:
	eai->ea_type = eat_imm;
	eai->temp1 = (WORD)((*p << 8) | *(p+1));
	(*pcpp) += 2;
	break;

     case imm2:
	imm2_const:
	eai->ea_type = eat_imm;
	eai->temp1 = (LONG)((*p << 24) | (*(p+1) << 16) | (*(p+2) << 8) | *(p+3));
	(*pcpp) += 4;
	break;

     case immi:
	eai->ea_type = eat_imm;
	eai->temp1 = (BYTE)reg;
	break;

     default:
	break;
    }
}
#endif
static struct bb_info *find_bb(struct hash_entry *h)
{
    int i;
    
    for (i = 0; i < top_bb; i++)
	if (bb_stack[i].h == h)
	    return bb_stack + i;
    if (!quiet_compile)
	fprintf(stderr, "BB not found!\n");
    return NULL;
}

static int m68k_scan_block(struct hash_block *hb, int *movem_count)
{
    struct hash_entry *h = hb->he_first;
    int i, iip, last_iip;
    
    top_bb = 0;
    
    do {
	struct bb_info *bb = bb_stack + top_bb;
	bb->h = h;
	bb->bb_next1 = NULL;
	bb->bb_next2 = NULL;
	h = h->next_same_block;
	top_bb++;
    } while (h != hb->he_first);
    
    qsort(bb_stack, top_bb, sizeof (struct bb_info), bb_compfn);

    *movem_count = 0;

    iip = 0;
    for (i = 0; i < top_bb; i++) {
	struct bb_info *bb = bb_stack + i;
	UBYTE *realpc = get_real_address(bb->h->addr);
	UBYTE *rpc_start = realpc;
	CPTR stop_addr = 0;
	int live_at_start = 31, may_clear_las = 31;
	struct insn_info_struct *prev_ii = NULL;

	if (i < top_bb - 1)
	    stop_addr = (bb+1)->h->addr;
	bb->first_iip = iip;

	for (;;) {
	    struct insn_info_struct *thisii = insn_info + iip;
	    CPTR thisinsn_addr = (realpc - rpc_start) + bb->h->addr;
	    UBYTE *rpc_save = realpc;
	    struct instr *dp = translate_getnextinsn(&realpc);
	    CPTR nextinsn_addr = (realpc - rpc_start) + bb->h->addr;
	    
	    int fset = dp->flagdead == -1 ? 31 : dp->flagdead;
	    int fuse = dp->flaglive == -1 ? 31 : dp->flaglive;
	    
	    if (thisinsn_addr == stop_addr) {
		bb->bb_next1 = find_bb (find_hash (thisinsn_addr));
		break;
	    }
	    
	    if (dp->mnemo == i_Scc || dp->mnemo == i_Bcc || dp->mnemo == i_DBcc) {
		fset = 0, fuse = cc_flagmask(dp->cc);
		if (prev_ii && dp->mnemo != i_Scc) /* Don't use Scc here: ea can cause an exit */
		    prev_ii->ccuser_follows = 1;
	    }

	    may_clear_las &= ~fuse;
	    live_at_start &= ~(fset & may_clear_las);
	    
	    thisii->dp = dp;
	    thisii->address = thisinsn_addr;
	    thisii->stop_translation = 0;
	    thisii->ccuser_follows = 0;
/*	    thisii->have_reginfo = 0;*/
	    thisii->jump_target = 0;
	    thisii->sync_cache = thisii->sync_flags = 0;
	    thisii->flags_set = fset;
	    thisii->flags_used = fuse;
	    thisii->regs_set = 0;
	    thisii->regs_used = 0;
	    iip++;
	    if (iip == MAX_TRANSLATE)
		return 0;

	    if (dp->mnemo == i_RTS || dp->mnemo == i_RTE 
		|| dp->mnemo == i_RTR || dp->mnemo == i_RTD
		|| dp->mnemo == i_JMP || dp->mnemo == i_ILLG
		|| dp->mnemo == i_BSR || dp->mnemo == i_JSR)
	    {
		thisii->flags_used = 31;
		thisii->regs_used = 65535;
		thisii->stop_translation = 1;
		break;
	    }
	    
	    if (dp->mnemo == i_DBcc) {
		CPTR newaddr = thisinsn_addr + 2 + (WORD)((*(rpc_save+2) << 8) | *(rpc_save+3));
		bb->can_compile_last = 1;
		bb->bb_next1 = find_bb (find_hash (newaddr));
		if (bb->bb_next1 == NULL)
		    thisii->stop_translation = 1;
		bb->bb_next2 = find_bb (find_hash (nextinsn_addr));
		if (bb->bb_next2 == NULL)
		    thisii->stop_translation = 1;
		thisii->regs_used = 65535;
		break;
	    }
	    
	    if (dp->mnemo == i_Bcc) {
		CPTR newaddr;
		if (dp->smode == imm1)
		    newaddr = thisinsn_addr + 2 + (WORD)((*(rpc_save+2) << 8) | *(rpc_save+3));
		else
		    newaddr = thisinsn_addr + 2 + (BYTE)dp->sreg;
		bb->can_compile_last = 1;
		bb->bb_next1 = find_bb(get_hash_for_func(newaddr));
		if (bb->bb_next1 == NULL)
		    thisii->stop_translation = 1;
		if (dp->cc != 0) {
		    bb->bb_next2 = find_bb(get_hash_for_func(nextinsn_addr));
		    if (bb->bb_next2 == NULL)
			thisii->stop_translation = 1;
		}
		thisii->regs_used = 65535;
		break;
	    }

	    if (dp->mnemo == i_MVMLE || dp->mnemo == i_MVMEL) {
		UWORD regmask = (*(rpc_save + 2) << 8) | (*(rpc_save + 3));
		*movem_count += count_bits(regmask);
		if (dp->dmode == Apdi)
		    regmask = bitswap(regmask);
		if (dp->mnemo == i_MVMLE)
		    thisii->regs_used = regmask;
		else
		    thisii->regs_set = regmask;
	    }

	    prev_ii = thisii;
	}
	bb->last_iip = iip - 1;
	bb->flags_live_at_start = live_at_start;
    }
    last_iip = iip;

    for (i = 0; i < top_bb; i++) {
	struct bb_info *bb = bb_stack + i;
	int mnemo;
	int current_live;
	struct instr *dp;
	
	iip = bb->last_iip;
	mnemo = insn_info[iip].dp->mnemo;

	/* Fix up branches */
	if (mnemo == i_DBcc || mnemo == i_Bcc) {
	    if (bb->bb_next1 != NULL) {
		insn_info[bb->last_iip].jumps_to = bb->bb_next1->first_iip;
		insn_info[bb->bb_next1->first_iip].jump_target = 1;
	    }
	}
	/* And take care of flag life information */
	dp = insn_info[iip].dp;
	if (insn_info[iip].stop_translation)
	    current_live = 31;
	else if (dp->mnemo == i_DBcc || dp->mnemo == i_Bcc) {
	    current_live = 0;
	    if (bb->bb_next1 != NULL)
		current_live |= bb->bb_next1->flags_live_at_start;
	    if (bb->bb_next2 != NULL)
		current_live |= bb->bb_next2->flags_live_at_start;
	} else {
	    if (bb->bb_next1 == NULL && bb->bb_next2 == NULL)
		fprintf(stderr, "Can't happen\n");
	    current_live = 0;
	    if (bb->bb_next1 != NULL)
		current_live |= bb->bb_next1->flags_live_at_start;
	    if (bb->bb_next2 != NULL)
		current_live |= bb->bb_next2->flags_live_at_start;
	}

	do {
	    insn_info[iip].flags_live_at_end = current_live;
	    current_live &= ~insn_info[iip].flags_set;
	    current_live |= insn_info[iip].flags_used;
	} while (iip-- != bb->first_iip);

	if (bb->flags_live_at_start != current_live && !quiet_compile)
	    fprintf(stderr, "Fascinating!\n");
	bb->flags_live_at_start = current_live;
    }
    return last_iip;
}

static char *compile_current_addr;
static char *compile_last_addr;

static __inline__ void assemble(UBYTE a)
{
    if (compile_current_addr < compile_last_addr) {
	*compile_current_addr++ = a;
    } else {
	compile_failure = 1;
    }
}

static __inline__ void assemble_ulong(ULONG a)
{
    assemble(a);
    assemble(a >> 8);
    assemble(a >> 16);
    assemble(a >> 24);
}

static __inline__ void assemble_uword(UWORD a)
{
    assemble(a);
    assemble(a >> 8);
}

static __inline__ void assemble_long(void *a)
{
    assemble_ulong((ULONG)a);
}

static __inline__ void compile_org(char *addr)
{
    compile_current_addr = addr;
}

static __inline__ char *compile_here(void)
{
    return compile_current_addr;
}

#define r_EAX 0
#define r_ECX 1
#define r_EDX 2
#define r_EBX 3
#define r_ESP 4
#define r_EBP 5
#define r_ESI 6
#define r_EDI 7

#define r_AH 0x84
#define r_CH 0x85
#define r_DH 0x86
#define r_BH 0x87

#define ALL_X86_REGS 255
#define ADDRESS_X86_REGS ((1 << r_EBP) | (1 << r_ESI) | (1 << r_EDI))
#define DATA_X86_REGS ((1 << r_EAX) | (1 << r_EDX) | (1 << r_EBX) | (1 << r_ECX))

#define BO_NORMAL 0
#define BO_SWAPPED_LONG 1
#define BO_SWAPPED_WORD 2

struct register_mapping {
    int dreg_map[8], areg_map[8]; /* 68000 register cache */
    int x86_const_offset[8];
    int x86_dirty[8];
    int x86_cache_reg[8]; /* Regs used for the 68000 register cache */
    int x86_cr_type[8]; /* Caching data or address register? */
    int x86_locked[8]; /* Regs used for some purpose */
    int x86_byteorder[8];
    int x86_verified[8];
};

/*
 * First, code to compile some primitive x86 instructions
 */

static void compile_lea_reg_with_offset(int dstreg, int srcreg, ULONG srcoffs)
{
    assemble(0x8D);
    if (srcreg == -2) {
	assemble(0x05 + 8*dstreg);
	assemble_ulong(srcoffs);
    } else if ((LONG)srcoffs >= -128 && (LONG)srcoffs <= 127) {
	assemble(0x40 + 8*dstreg + srcreg);
	assemble(srcoffs);
    } else {
	assemble(0x80 + 8*dstreg + srcreg);
	assemble_ulong(srcoffs);
    }
}

static void compile_move_reg_reg(int dstreg, int srcreg, wordsizes size)
{
    if (size == sz_byte
	&& (((1 << dstreg) & DATA_X86_REGS) == 0
	    || ((1 << srcreg) & DATA_X86_REGS) == 0))
    {
	fprintf(stderr, "Moving wrong register types!\n");
    }
    if (size == sz_word)
	assemble(0x66);
    if (size == sz_byte)
	assemble(0x88);
    else
	assemble(0x89);
    assemble(0xC0 + dstreg + 8*srcreg);
}

static void compile_move_between_reg_mem_regoffs(int dstreg, int srcreg,
						 ULONG srcoffs, wordsizes size,
						 int code)
{
    if (size == sz_byte && (dstreg & 0x80) != 0)
	dstreg &= ~0x80;
    else if ((size == sz_byte
	      && ((1 << dstreg) & DATA_X86_REGS) == 0)
	     || (size != sz_byte && (dstreg & 0x80) != 0))
    {
	fprintf(stderr, "Moving wrong register types!\n");
    }
    if (size == sz_word)
	assemble(0x66);
    if (size == sz_byte)
	assemble(code);
    else
	assemble(code + 1);
    
    if (srcreg == -2) {
	assemble(0x05 + 8*dstreg);
	assemble_ulong(srcoffs);
    } else if ((LONG)srcoffs >= -128 && (LONG)srcoffs <= 127) {
	assemble(0x40 + 8*dstreg + srcreg);
	assemble(srcoffs);
    } else {
	assemble(0x80 + 8*dstreg + srcreg);
	assemble_ulong(srcoffs);
    }
}

static void compile_move_reg_from_mem_regoffs(int dstreg, int srcreg, 
					      ULONG srcoffs, wordsizes size)
{
    compile_move_between_reg_mem_regoffs(dstreg, srcreg, srcoffs, size, 0x8A);
}

static void compile_move_reg_to_mem_regoffs(int dstreg, ULONG dstoffs, 
					    int srcreg, wordsizes size)
{
    compile_move_between_reg_mem_regoffs(srcreg, dstreg, dstoffs, size, 0x88);
}

static void compile_byteswap(int x86r, wordsizes size, int save_flags)
{
    switch(size) {
     case sz_word:
	if (save_flags)
	    assemble(0x9C);
	assemble(0x66); /* rolw $8,x86r */
	assemble(0xC1);
	assemble(0xC0 + x86r);
	assemble(8);
	if (save_flags)
	    assemble(0x9D);
	break;
     case sz_long:
	assemble(0x0F); /* bswapl x86r */
	assemble(0xC8+x86r);
	break;
     default:
	break;
    }
}

static void compile_force_byteorder(struct register_mapping *map, int x86r,
				    int desired_bo, int save_flags)
{
    if (x86r == -2 || map->x86_byteorder[x86r] == desired_bo)
	return;
    
    if (map->x86_byteorder[x86r] == BO_SWAPPED_LONG)
	compile_byteswap(x86r, sz_long, save_flags);
    else if (map->x86_byteorder[x86r] == BO_SWAPPED_WORD)
	compile_byteswap(x86r, sz_word, save_flags);
    
    if (desired_bo == BO_SWAPPED_LONG)
	compile_byteswap(x86r, sz_long, save_flags);
    else if (desired_bo == BO_SWAPPED_WORD)
	compile_byteswap(x86r, sz_word, save_flags);
    map->x86_byteorder[x86r] = desired_bo;
}

/* Add a constant offset to a x86 register. If it's in the cache, make sure
 * we update the const_offset value. The flags are unaffected by this */

static void compile_offset_reg(struct register_mapping *map, int x86r, 
			       ULONG offset)
{
    int cached_68k;
    
    if (offset == 0 || x86r == -1 || x86r == -2)
	return;

    compile_force_byteorder(map, x86r, BO_NORMAL, 1);
    cached_68k = map->x86_cache_reg[x86r];
    if (cached_68k != -1) {
	map->x86_const_offset[x86r] -= offset;
	map->x86_dirty[x86r] = 1;
    }
    compile_lea_reg_with_offset(x86r, x86r, offset);
}

static int get_unused_x86_register(struct register_mapping *map)
{
    int x86r;
    for (x86r = 0; x86r < 24; x86r++) {
	if (map->x86_cache_reg[x86r] != -1)
	    continue;
	if (map->x86_locked[x86r] > 0)
	    continue;
	
	map->x86_verified[x86r] = 0;
	map->x86_byteorder[x86r] = BO_NORMAL;
	return x86r;
    }
    return -1;
}

/*
 * sync_reg() may not touch the flags
 * If may_clobber is 1 and the reg had an offset, the reg will be offsetted
 * by this function
 */
static void sync_reg(struct register_mapping *map, int x86r, void *m68kr, 
		     ULONG offset, int dirty, int may_clobber)
{
    compile_force_byteorder(map, x86r, BO_NORMAL, 1);
    if (offset != 0) {
	if (may_clobber) {
	    compile_lea_reg_with_offset(x86r, x86r, offset);
	    dirty = 1;
	} else {
	    int tmpr = get_unused_x86_register(map);
	    if (tmpr != -1) { 
		compile_lea_reg_with_offset(tmpr, x86r, offset);
		x86r = tmpr;
		dirty = 1;
	    } else {
		compile_lea_reg_with_offset(x86r, x86r, offset);
		assemble(0x89);          /* movl x86r,m68kr */
		assemble(0x05 + (x86r << 3)); 
		assemble_long(m68kr);
		compile_lea_reg_with_offset(x86r, x86r, -offset);
		return;
	    }
	}
    }

    if (dirty) {
	assemble(0x89);          /* movl x86r,m68kr */
	assemble(0x05 + (x86r << 3)); 
	assemble_long(m68kr);
    }
}

static void sync_reg_cache(struct register_mapping *map, int flush)
{
    int i;

    for (i = 0; i < 8; i++) {
	int cr68k = map->x86_cache_reg[i];
	if (cr68k != -1) {
	    if (map->x86_cr_type[i] == 1) {
		sync_reg(map, i, regs.d + cr68k, map->x86_const_offset[i], map->x86_dirty[i], 1);
		if (flush)
		    map->dreg_map[cr68k] = -1;
	    } else {
		sync_reg(map, i, regs.a + cr68k, map->x86_const_offset[i], map->x86_dirty[i], 1);
		if (flush)
		    map->areg_map[cr68k] = -1;
	    }
	    if (flush)
		map->x86_cache_reg[i] = -1;
	    map->x86_const_offset[i] = 0;
	}
    }
    memset(map->x86_dirty, 0, sizeof map->x86_dirty);
}

static void remove_x86r_from_cache(struct register_mapping *map, int x86r, 
				   int may_clobber)
{
    int j;
    int reg_68k;
    
    if (x86r == -1)
	return;

    reg_68k = map->x86_cache_reg[x86r];
    
    if (reg_68k != -1) {
	if (map->x86_cr_type[x86r] == 1) {
	    map->dreg_map[reg_68k] = -1;
	    sync_reg(map, x86r, regs.d + reg_68k, map->x86_const_offset[x86r],
		     map->x86_dirty[x86r], may_clobber);
	} else {
	    map->areg_map[reg_68k] = -1;
	    sync_reg(map, x86r, regs.a + reg_68k,  map->x86_const_offset[x86r],
		     map->x86_dirty[x86r], may_clobber);
	}
    }
    map->x86_dirty[x86r] = 0;
    map->x86_cache_reg[x86r] = -1;
    map->x86_const_offset[x86r] = 0;
    map->x86_verified[x86r] = 0;
    map->x86_byteorder[x86r] = BO_NORMAL;
}

static int get_free_x86_register(struct register_mapping *map,
				 int preferred_mask)
{
    int cnt;
    for (cnt = 0; cnt < 24; cnt++) {
	int x86r = cnt & 7;
	/* In the first two passes, try to get one of the preferred regs */
	if (cnt < 16 && ((1 << x86r) & preferred_mask) == 0)
	    continue;
	/* In the first pass, don't discard any registers from the cache */
	if (cnt < 8 && map->x86_cache_reg[x86r] != -1)
	    continue;
	/* Never use locked registers */
	if (map->x86_locked[x86r] > 0)
	    continue;

	remove_x86r_from_cache(map, x86r, 1);
	return x86r;
    }
    printf("Out of registers!\n");
    return -1;
}

static int get_typed_x86_register(struct register_mapping *map,
				  int preferred_mask)
{
    int cnt;
    for (cnt = 0; cnt < 16; cnt++) {
	int x86r = cnt & 7;
	/* Get one of the preferred regs */
	if (((1 << x86r) & preferred_mask) == 0)
	    continue;
	/* In the first pass, don't discard any registers from the cache */
	if (cnt < 8 && map->x86_cache_reg[x86r] != -1)
	    continue;
	/* Never use locked registers */
	if (map->x86_locked[x86r] > 0)
	    continue;

	remove_x86r_from_cache(map, x86r, 1);
	return x86r;
    }
    printf("Out of type registers!\n");
    return -1;
}

static void compile_unlock_reg(struct register_mapping *map, int reg)
{
    if (reg >= 0) {
	map->x86_locked[reg]--;
    }
}

static int get_and_lock_68k_reg(struct register_mapping *map, int reg, int is_dreg,
				int preferred, int no_offset)
{
    int x86r;
    int *regmap;
    ULONG *reghome;
    
    if (is_dreg)
	regmap = map->dreg_map, reghome = regs.d;
    else
	regmap = map->areg_map, reghome = regs.a;
    
    if (preferred == 0)
	preferred = ALL_X86_REGS;
    
    x86r = regmap[reg];
    if (x86r == -1) {
	x86r = get_free_x86_register(map, preferred);
	assemble(0x8B); assemble(0x05 + (x86r << 3)); /* movl regs.d[reg],x86r */
	assemble_long(reghome + reg);
	map->x86_cache_reg[x86r] = reg;
	map->x86_cr_type[x86r] = is_dreg;
	map->x86_const_offset[x86r] = 0;
	map->x86_dirty[x86r] = 0;
	map->x86_verified[x86r] = 0;
	map->x86_byteorder[x86r] = BO_NORMAL;
	regmap[reg] = x86r;
    } else if (map->x86_locked[x86r] > 0) {
	/* Register was in cache, and was locked. Need to make a copy */
	int newr = get_free_x86_register(map, preferred);
	int old_dirty = 0;
	int old_verified;
	int old_bo;
	
	if (map->x86_const_offset[x86r] == 0) {
	    compile_move_reg_reg(newr, x86r, sz_long);
	} else {
	    compile_force_byteorder(map, x86r, BO_NORMAL, 1);
	    compile_lea_reg_with_offset(newr, x86r, map->x86_const_offset[x86r]);
	    old_dirty = 1;
	}
	/* Remove old reg from cache... */
	map->x86_cache_reg[x86r] = -1;
	map->x86_cr_type[x86r] = is_dreg;
	map->x86_const_offset[x86r] = 0;
	old_dirty |= map->x86_dirty[x86r];
	old_verified = map->x86_verified[x86r];
	old_bo = map->x86_byteorder[x86r];
	map->x86_verified[x86r] = 0;
	map->x86_dirty[x86r] = 0;
	x86r = newr;
	/* ... and make the new one the cache register */
	map->x86_cache_reg[x86r] = reg;
	map->x86_cr_type[x86r] = is_dreg;
	map->x86_const_offset[x86r] = 0;
	map->x86_dirty[x86r] = old_dirty;
	map->x86_verified[x86r] = old_verified;
	map->x86_byteorder[x86r] = old_bo;
	regmap[reg] = x86r;
    }
    map->x86_locked[x86r]++;
    if (no_offset && map->x86_const_offset[x86r] != 0) {
	compile_force_byteorder(map, x86r, BO_NORMAL, 1);
	compile_lea_reg_with_offset(x86r, x86r, map->x86_const_offset[x86r]);
	map->x86_const_offset[x86r] = 0;
	map->x86_dirty[x86r] = 1;
    }
    return x86r;
}

/*
 * Move a constant to a register. Don't anything if we already have a
 * register, even if it is offset by a constant
 */

static int compile_force_const_reg(struct register_mapping *map, int x86r,
				   ULONG *offs, int desired)
{
    int newr = x86r;

    if (newr == -2) {
	if (desired == 0)
	    newr = get_free_x86_register(map, ALL_X86_REGS);
	else
	    newr = get_typed_x86_register(map, desired);

	assemble(0xB8 + newr);
	assemble_ulong(*offs);
	*offs = 0;
    }
    map->x86_locked[newr]++;
    return newr;
}

static int compile_extend_long(struct register_mapping *map, int x86r,
			       ULONG *srcoffs, wordsizes size)
{
    compile_force_byteorder(map, x86r, BO_NORMAL, 1);
    if (size != sz_long) {
	if (x86r == -2) {
	    ULONG offs = *srcoffs;
	    if (size == sz_byte)
		offs = (LONG)(BYTE)offs;
	    else if (size == sz_word)
		offs = (LONG)(WORD)offs;
	    *srcoffs = offs;
	    return x86r;
	} else if (*srcoffs != 0) {
	    int newr = get_free_x86_register(map, ALL_X86_REGS);
	    compile_lea_reg_with_offset(newr, x86r, *srcoffs);
	    *srcoffs = 0;
	    x86r = newr;
	} else if (map->x86_locked[x86r] != 0) {
	    int newr = get_free_x86_register(map, ALL_X86_REGS);
	    assemble(0x0F);
	    if (size == sz_byte) {
		assemble(0xBE);
	    } else {
		assemble(0xBF);
	    }
	    assemble(0xC0 + newr*8 + x86r);
	    x86r = newr;
	    goto extended;
	}
	
	if (x86r == r_EAX && size == sz_word) {
	    assemble(0x98); /* cwtl */
	} else {
	    assemble(0x0F);
	    if (size == sz_byte) {
		assemble(0xBE);
	    } else {
		assemble(0xBF);
	    }
	    assemble(0xC0 + x86r*9);
	}
    }
    extended:
    if (x86r >= 0)
	map->x86_locked[x86r]++;
    return x86r;
}

/*
 * Either move a constant into a register, or get rid of a constant offset
 * for a register
 */

static int compile_move_const_reg(struct register_mapping *map, int x86r,
				  ULONG *offs, int desired)
{
    int newr = x86r;
    if (newr == -2) {
	if (desired == 0)
	    newr = get_free_x86_register(map, ALL_X86_REGS);
	else
	    newr = get_typed_x86_register(map, desired);
	assemble(0xB8 + newr);
	assemble_ulong(*offs);
    } else {
	compile_offset_reg(map, x86r, *offs);
    }
    *offs = 0;
    map->x86_locked[newr]++;
    return newr;
}

static int compile_move_to_modereg(struct register_mapping *map, int x86r, 
				   wordsizes size)
{
    int newr = x86r;
    
    if (size == sz_byte && ((1 << x86r) & DATA_X86_REGS) == 0) {
	newr = get_typed_x86_register(map, DATA_X86_REGS);
	compile_force_byteorder(map, x86r, BO_NORMAL, 1);
	if (((1 << newr) & DATA_X86_REGS) == 0)
	    printf("Can't get data register for byte value\n");
	assemble(0x89);
	assemble(0xC0 + newr*8 + x86r);
    }
    map->x86_locked[newr]++;
    return newr;
}

/*
 * This structure holds information about predec/postinc addressing modes.
 */

struct pid_undo {
    int used;
    int x86r[2];
    int m68kr[2];
    int dirty[2];
    ULONG offs[2];
};

static void add_undo(struct pid_undo *pud, int x86r, int m68kr, ULONG offs,
		     int dirty)
{
    int i;
    for (i = 0; i < pud->used; i++)
	if (pud->m68kr[i] == m68kr)
	    return;
    pud->m68kr[i] = m68kr;
    pud->x86r[i] = x86r;
    pud->offs[i] = offs;
    pud->dirty[i] = dirty;
    pud->used++;
}

struct ea_info {
    int reg;
    amodes mode;
    wordsizes size;
    int regs_locked;    /* The regs locked for this ea by compile_prepareea() */
    int locked_regs[3];
    int address_reg;    /* The x86 reg holding the address, or -1 if ea doesn't refer to memory
			 * -2 if it refers to memory, but only with a constant address */
    ULONG addr_const_off; /* Constant offset to the address */
    int flags;            /* Extra info. Contains the dp field of d8r modes */
    int purpose;
    int data_reg;         /* The x86 reg that holds the data. -1 if data is not present yet. 
			   * -2 if data is constant */
    ULONG data_const_off;
};

static void init_eainfo(struct ea_info *eai)
{
    eai->regs_locked = 0;
    eai->address_reg = -1;
    eai->addr_const_off = 0;
    eai->data_reg = -1;
    eai->data_const_off = 0;
}

/*
 * Load all the registers absolutely needed to calculate and verify thea
 * address. Load other registers if convenient.
 * This contains a fair amount of magic to get the register cache working right.
 */

static void compile_prepareea(struct register_mapping *map, amodes mode,
			      int reg, wordsizes size, UBYTE **pcpp, CPTR pca,
			      struct ea_info *eai, int ea_purpose,
			      struct pid_undo *pud, int pidmult)
{
    int pdival = size == sz_byte && reg != 7 ? 1 : size == sz_long ? 4 : 2;
    UBYTE *p = *pcpp;
    UWORD dp;
    int r;
    int x86r, tmpr;

    pdival *= pidmult;
    
    init_eainfo(eai);
    eai->mode = mode;
    eai->size = size;
    eai->reg = reg;
    
    switch(mode){
     case Dreg:
	if (size != sz_long && (ea_purpose & EA_STORE))
	    ea_purpose |= EA_LOAD;
	/* Is the register in the cache, or do we need it there? If so, lock it.
	 * This will make sure we are the only ones who have it locked, so we
	 * can mess with it without expecting surprises. 
	 * Get it without a constant offset. */
	x86r = map->dreg_map[reg];
	if (x86r != -1 || (ea_purpose & EA_LOAD)) {
	    eai->regs_locked = 1;
	    x86r = eai->locked_regs[0] = get_and_lock_68k_reg(map, reg, 1, 0, 1);
	}
	/* And if we need to manipulate it with byte sizes, we ought to get it
	 * into an appropriate register */
	if (size == sz_byte && x86r != -1 && (ea_purpose & (EA_LOAD|EA_STORE))
	    && ((1 << x86r) & DATA_X86_REGS) == 0) 
	{
	    int newr = get_typed_x86_register(map, DATA_X86_REGS);
	    compile_force_byteorder(map, x86r, BO_NORMAL, 0);
	    compile_move_reg_reg(newr, x86r, sz_long);
	    map->x86_locked[newr]++;
	    compile_unlock_reg(map, x86r);
	    eai->locked_regs[0] = newr;
	    
	    /* We need to update the register cache, otherwise storeea
	     * will mark the new one dirty, but that is never checked. */
	    map->x86_const_offset[newr] = map->x86_const_offset[x86r];
	    map->x86_dirty[newr] = map->x86_dirty[x86r];
	    map->x86_cache_reg[x86r] = -1;
	    map->x86_cache_reg[newr] = reg;
	    map->x86_cr_type[newr] = 1;
	    map->dreg_map[reg] = newr;
	}
	if (eai->regs_locked == 1) {
	    eai->data_reg = eai->locked_regs[0];
	    eai->data_const_off = map->x86_const_offset[eai->data_reg];
	}
	break;
	
     case Areg:
	if (size != sz_long && (ea_purpose & EA_STORE))
	    printf("Areg != long\n");
	x86r = map->areg_map[reg];
	if (x86r != -1 || (ea_purpose & EA_LOAD)) {
	    eai->regs_locked = 1;
	    x86r = eai->locked_regs[0] = get_and_lock_68k_reg(map, reg, 0, 0, 1);
	}
	/* And if we need to get the byte part, we ought to get it
	 * into an appropriate register */
	if (size == sz_byte && x86r != -1 && (ea_purpose & EA_LOAD)
	    && ((1 << x86r) & DATA_X86_REGS) == 0)
	{
	    int newr = get_typed_x86_register(map, DATA_X86_REGS);
	    compile_force_byteorder(map, x86r, BO_NORMAL, 0);
	    compile_move_reg_reg(newr, x86r, sz_long);
	    map->x86_locked[newr]++;
	    compile_unlock_reg(map, x86r);
	    eai->locked_regs[0] = newr;
	}
	if (eai->regs_locked == 1) {
	    eai->data_reg = eai->locked_regs[0];
	    eai->data_const_off = map->x86_const_offset[eai->data_reg];
	}
	break;
	
     case Ad16:
	eai->addr_const_off = (WORD)((*p << 8) | *(p+1));
	(*pcpp) += 2; p += 2;
	x86r = eai->locked_regs[0] = eai->address_reg = get_and_lock_68k_reg(map, reg, 0, ADDRESS_X86_REGS, 0);
	compile_force_byteorder(map, x86r, BO_NORMAL, 0);
	eai->addr_const_off += map->x86_const_offset[x86r];
	eai->regs_locked = 1;
	break;

     case Aind:
	x86r = eai->locked_regs[0] = eai->address_reg = get_and_lock_68k_reg(map, reg, 0, ADDRESS_X86_REGS, 0);
	compile_force_byteorder(map, x86r, BO_NORMAL, 0);
	eai->addr_const_off = map->x86_const_offset[x86r];
	eai->regs_locked = 1;
	break;
	
     case Apdi:
	x86r = eai->locked_regs[0] = eai->address_reg = get_and_lock_68k_reg(map, reg, 0, ADDRESS_X86_REGS, 0);
	compile_force_byteorder(map, x86r, BO_NORMAL, 0);
	/* 
	 * Add this reg with its current offset to the undo buffer.
	 * Since we have locked it, we are certain that it will not be
	 * modified at least before generate_possible_exits() has done its
	 * job.
	 */
	add_undo(pud, x86r, reg, map->x86_const_offset[x86r], map->x86_dirty[x86r]);
	map->x86_const_offset[x86r] -= pdival;
	eai->addr_const_off = map->x86_const_offset[x86r];
	eai->regs_locked = 1;
	break;
	
     case Aipi:
	x86r = eai->locked_regs[0] = eai->address_reg = get_and_lock_68k_reg(map, reg, 0, ADDRESS_X86_REGS, 0);
	compile_force_byteorder(map, x86r, BO_NORMAL, 0);
	add_undo(pud, x86r, reg, map->x86_const_offset[x86r], map->x86_dirty[x86r]);
	eai->addr_const_off = map->x86_const_offset[x86r];
	map->x86_const_offset[x86r] += pdival;
	eai->regs_locked = 1;
	break;

     case Ad8r:
	dp = (WORD)((*p << 8) | *(p+1));
	r = (dp & 0x7000) >> 12;
	(*pcpp) += 2; p += 2;	
	
	tmpr = get_and_lock_68k_reg(map, reg, 0, ADDRESS_X86_REGS, 0);
	x86r = get_and_lock_68k_reg(map, r, dp & 0x8000 ? 0 : 1, 0, 1);
	compile_force_byteorder(map, x86r, BO_NORMAL, 0);
	compile_force_byteorder(map, tmpr, BO_NORMAL, 0);
	eai->locked_regs[0] = eai->address_reg = get_free_x86_register(map, ADDRESS_X86_REGS);
	map->x86_locked[eai->address_reg]++;
	
	eai->addr_const_off = map->x86_const_offset[tmpr] + (BYTE)dp;
	r = (dp & 0x7000) >> 12;

	if (dp & 0x800) {
	    if ((LONG)eai->addr_const_off >= -128 && (LONG)eai->addr_const_off <= 127) {
		assemble(0x8D); 
		assemble(0x44 + eai->address_reg*8); /* leal disp8(dispreg,basereg),addrreg */
		assemble(x86r*8 + tmpr);
		assemble(eai->addr_const_off);
	    } else {
		assemble(0x8D);
		assemble(0x84 + eai->address_reg*8); /* leal disp32(dispreg,basereg),addrreg */
		assemble(x86r*8 + tmpr);
		assemble_ulong(eai->addr_const_off);
	    }
	    eai->addr_const_off = 0;
	} else {
	    assemble(0x0F); assemble(0xBF);
	    assemble(0xC0 + x86r + eai->address_reg*8); /* movswl dispreg,addrreg */
	    assemble(0x03); assemble(0xC0 + tmpr + eai->address_reg*8); /* addl basereg,addrreg */
	}
	compile_unlock_reg(map, x86r);
	compile_unlock_reg(map, tmpr);
	eai->regs_locked = 1;
	break;

     case PC8r:
	dp = (WORD)((*p << 8) | *(p+1));
	(*pcpp) += 2; p += 2;
	r = (dp & 0x7000) >> 12;
	eai->addr_const_off = pca + (BYTE)dp;
	if (dp & 0x800) {
	    eai->locked_regs[0] = eai->address_reg = get_and_lock_68k_reg(map, r, dp & 0x8000 ? 0 : 1, 0, 1);
	} else {
	    eai->regs_locked = 2;
	    tmpr = get_and_lock_68k_reg(map, r, dp & 0x8000 ? 0 : 1, 0, 1);
	    compile_force_byteorder(map, tmpr, BO_NORMAL, 0);
	    eai->locked_regs[0] = eai->address_reg = get_free_x86_register(map, ADDRESS_X86_REGS);
	    map->x86_locked[eai->address_reg]++;

	    assemble(0x0F); assemble(0xBF);
	    assemble(0xC0 + tmpr + eai->address_reg*8); /* movswl dispreg,addrreg */
	    compile_unlock_reg(map, tmpr);
	}
	eai->regs_locked = 1;
	break;
	
     case PC16:
	eai->addr_const_off = pca + (WORD)((*p << 8) | *(p+1));
	eai->address_reg = -2;
	(*pcpp) += 2; p += 2;
	break;
	
     case absw:
	eai->addr_const_off = (WORD)((*p << 8) | *(p+1));
	eai->address_reg = -2;
	(*pcpp) += 2; p += 2;
	break;

     case absl:
	eai->addr_const_off = (LONG)((*p << 24) | (*(p+1) << 16)
				   | (*(p+2) << 8) | *(p+3));
	eai->address_reg = -2;
	(*pcpp) += 4; p += 4;
	break;

     case imm:
	if (size == sz_long)
	    goto imm2_const;
	if (size == sz_word)
	    goto imm1_const;
	
	/* fall through */
     case imm0:
	eai->data_const_off = (BYTE)*(p+1);
	eai->data_reg = -2;
	(*pcpp) += 2; p += 2;
	break;

     case imm1:
	imm1_const:
	eai->data_const_off = (WORD)((*p << 8) | *(p+1));
	eai->data_reg = -2;
	(*pcpp) += 2; p += 2;
	break;

     case imm2:
	imm2_const:
	eai->data_const_off = (LONG)((*p << 24) | (*(p+1) << 16)
				   | (*(p+2) << 8) | *(p+3));
	eai->data_reg = -2;
	(*pcpp) += 4; p += 4;
	break;

     case immi:
	eai->data_const_off = (BYTE)reg;
	eai->data_reg = -2;
	break;

     default:
	break;
    }
    eai->purpose = ea_purpose;
}

static int compile_fetchea(struct register_mapping *map, struct ea_info *eai,
			   ULONG *reg_offset)
{
    int x86r;
    ULONG constant;
    
    if (eai->data_reg != -1) {
	*reg_offset = eai->data_const_off;
	return eai->data_reg;
    }
    
    if (eai->mode == Dreg || eai->mode == Areg || eai->mode == immi || eai->mode == imm
	|| eai->mode == imm0 || eai->mode == imm1 || eai->mode == imm2
	|| eai->address_reg == -1)
	printf("BUG\n");

    *reg_offset = 0;
    if (eai->size == sz_byte)
	x86r = get_typed_x86_register(map, DATA_X86_REGS);
    else
	x86r = get_free_x86_register(map, ALL_X86_REGS);
    map->x86_locked[x86r]++;

    compile_force_byteorder(map, eai->address_reg, BO_NORMAL, 0);
    compile_move_reg_from_mem_regoffs(x86r, eai->address_reg, 
				      (ULONG)(eai->addr_const_off + address_space),
				      eai->size);
    
    switch (eai->size) {
     case sz_byte: map->x86_byteorder[x86r] = BO_NORMAL; break;
     case sz_word: map->x86_byteorder[x86r] = BO_SWAPPED_WORD; break;
     case sz_long: map->x86_byteorder[x86r] = BO_SWAPPED_LONG; break;
    }
    return x86r;
}

static void compile_storeea(struct register_mapping *map, struct ea_info *eai,
			     int valuereg, ULONG valueoffset)
{
    ULONG constant;
    int newr, cacher;
    
    if (eai->mode == Dreg) {
	/* Easy case first: just put the reg in the register cache */
	if (eai->size == sz_long) {
	    newr = compile_move_const_reg(map, valuereg, &valueoffset, 0);
	    compile_unlock_reg(map, valuereg);
	    if (valueoffset != 0)
		printf("Hoppla?\n");
	    /*
	     * Two checks whether registers are already in the cache.
	     */
	    if (map->x86_cache_reg[newr] != -1 
		&& (map->x86_cache_reg[newr] != eai->reg
		    || map->x86_cr_type[newr] != 1))
	    {
		remove_x86r_from_cache(map, newr, 0);
	    }
	    if (map->dreg_map[eai->reg] != -1
		&& map->dreg_map[eai->reg] != newr) 
	    {
		/* No need to write back */
		map->x86_cache_reg[map->dreg_map[eai->reg]] = -1;
	    }
	    map->x86_cache_reg[newr] = eai->reg;
	    map->x86_cr_type[newr] = 1;
	    map->x86_const_offset[newr] = valueoffset;
	    map->x86_dirty[newr] = 1;
	    map->dreg_map[eai->reg] = newr;
	    map->x86_verified[newr] = 0;
	    return;
	}
	
	if (eai->data_reg < 0)
	    printf("Don't have a data reg to move to!\n");

	compile_force_byteorder(map, eai->data_reg, BO_NORMAL, 1);
	compile_force_byteorder(map, valuereg, BO_NORMAL, 1);
	map->x86_verified[eai->data_reg] = 0;
	if (valuereg == -2) {
	    if (eai->size == sz_byte) {
		if (((1 << eai->data_reg) & DATA_X86_REGS) == 0)
		    printf("Uhoh - not moving to proper type reg\n");
		assemble(0xB0 + eai->data_reg);
		assemble(valueoffset);
	    } else {
		assemble(0x66); assemble(0xB8 + eai->data_reg);
		assemble_uword(valueoffset);
	    }
	} else {
	    /* Move the subword into the right place */
/* This shouldn't be necessary */
#if 0
	    newr = compile_force_const_reg(map, valuereg, &valueoffset);
#else
	    newr = valuereg;
#endif
	    compile_unlock_reg(map, valuereg);
	    compile_move_reg_reg(eai->data_reg, newr, eai->size);
	}
	map->x86_dirty[eai->data_reg] = 1;
	return;
    } else if (eai->mode == Areg) {
	if (eai->size != sz_long)
	    printf("Areg put != long\n");

	newr = compile_force_const_reg(map, valuereg, &valueoffset, 0);
	compile_unlock_reg(map, valuereg);

	if (map->x86_cache_reg[newr] != -1 
	    && (map->x86_cache_reg[newr] != eai->reg
		|| map->x86_cr_type[newr] != 0))
	{
	    remove_x86r_from_cache(map, newr, 0);
	}
	if (map->areg_map[eai->reg] != -1
	    && map->areg_map[eai->reg] != newr)
	{
	    /* No need to write back */
	    map->x86_cache_reg[map->areg_map[eai->reg]] = -1;
	}
	map->x86_verified[newr] = 0;
	map->x86_cache_reg[newr] = eai->reg;
	map->x86_cr_type[newr] = 0;
	map->x86_const_offset[newr] = valueoffset;
	map->x86_dirty[newr] = 1;
	map->areg_map[eai->reg] = newr;
	return;
    }

    compile_offset_reg(map, valuereg, valueoffset);
    /* Correct the byteorder */
    if (valuereg != -2) {
	switch (eai->size) {
	 case sz_byte: compile_force_byteorder(map, valuereg, BO_NORMAL, 1); break;
	 case sz_word: compile_force_byteorder(map, valuereg, BO_SWAPPED_WORD, 1); break;
	 case sz_long: compile_force_byteorder(map, valuereg, BO_SWAPPED_LONG, 1); break;
	}
    } else {
	switch (eai->size) {
	 case sz_long:
	    valueoffset = (((valueoffset & 0xFF000000) >> 24)
			   | ((valueoffset & 0xFF0000) >> 8)
			   | ((valueoffset & 0xFF00) << 8)
			   | ((valueoffset & 0xFF) << 24));
	    break;
	 case sz_word:
	    valueoffset = (((valueoffset & 0xFF00) >> 8)
			   | ((valueoffset & 0xFF) << 8));
	    break;
	}
    }
    
    /* We may have the value either in valuereg or in valueoffset by now,
     * not in both (see call to compile_offset_reg() above) */
    
    if (valuereg != -2) {
	compile_move_reg_to_mem_regoffs(eai->address_reg,
					(ULONG)(eai->addr_const_off + address_space),
					valuereg, eai->size);
    } else {
	/* generate code to move valueoffset,eaoffset(eareg) */
	switch(eai->size) {                   
	 case sz_byte: assemble(0xC6); break;
	 case sz_word: assemble(0x66); /* fall through */
	 case sz_long: assemble(0xC7); break;
	}
	if (eai->address_reg == -2) { /* absolute or PC-relative */
	    assemble(0x05);
	    assemble_long(eai->addr_const_off + address_space);
	} else {
	    assemble(0x80 + eai->address_reg);
	    assemble_long(eai->addr_const_off + address_space);
	}
	switch(eai->size) {
	 case sz_byte: assemble(valueoffset); break;
	 case sz_word: assemble_uword(valueoffset); break;
	 case sz_long: assemble_ulong(valueoffset); break;
	}
    }
}

#define CE_STACK_SIZE 1000

static struct {
    struct register_mapping map;
    char *jmpoffs;
    ULONG address;
    int noflush:1;
} compile_exit_stack[CE_STACK_SIZE];

static int cesp;

static struct register_mapping current_exit_regmap;

static void generate_exit(struct register_mapping *map, int address)
{
    int i;
    
    if (map != NULL)
	sync_reg_cache (map, 1);
    assemble(0xB8); /* movl $new_pc,%eax */
    assemble_ulong(address);
    assemble(0xC3); /* RET */
}

static void copy_map_with_undo(struct register_mapping *dst, 
			       struct register_mapping *src,
			       struct pid_undo *pud)
{
    int i;
    *dst = *src;
    for (i = 0; i < pud->used; i++) {
	int m68kr = pud->m68kr[i];
	int x86r = pud->x86r[i];
	int old_cr = dst->areg_map[m68kr];
	if (old_cr != -1) {
	    dst->x86_cache_reg[old_cr] = -1;
	}
	dst->x86_cache_reg[x86r] = m68kr;
	dst->areg_map[m68kr] = x86r;
	dst->x86_cr_type[x86r] = 0;
	dst->x86_const_offset[x86r] = pud->offs[i];
	dst->x86_dirty[x86r] = pud->dirty[i];
    }
}

static void generate_possible_exit(struct register_mapping *map,
				   struct ea_info *eai, int iip,
				   struct pid_undo *pud)
{
    struct register_mapping exit_regmap;
    switch (eai->address_reg) {
     case -1:
	/* EA doesn't refer to memory */
	break;
     case -2:
	/* Only a constant offset */
	eai->addr_const_off &= (1<<24)-1;
	if (!good_address_map[eai->addr_const_off]) {
	    copy_map_with_undo(&exit_regmap, map, pud);
	    generate_exit(&exit_regmap, insn_info[iip].address);
	}
	break;
     default:
	if (map->x86_verified[eai->address_reg])
	    break;
	map->x86_verified[eai->address_reg] = 1;
	if (cesp == CE_STACK_SIZE) {
	    copy_map_with_undo(&exit_regmap, map, pud);
	    generate_exit(&exit_regmap, insn_info[iip].address);
	    break;
	}
	copy_map_with_undo(&compile_exit_stack[cesp].map, map, pud);
	compile_exit_stack[cesp].address = insn_info[iip].address;
	assemble(0x80); assemble(0xB8 + eai->address_reg); /* cmpb $0, good_address_map(x86r) */
	assemble_long(good_address_map + eai->addr_const_off);
	assemble(0);
	assemble(0x0F); assemble(0x84); /* JE finish */
	compile_exit_stack[cesp].jmpoffs = compile_here();
	compile_exit_stack[cesp].noflush = 0;
	assemble_ulong(0);
	cesp++;
	break;
    }
}

static void finish_exits(void)
{
    int i;
    for (i = 0; i < cesp; i++) {
	char *exitpoint = compile_here();
	char *nextpoint;

	if (compile_exit_stack[i].noflush)
	    generate_exit(NULL, compile_exit_stack[i].address);
	else
	    generate_exit(&compile_exit_stack[i].map, compile_exit_stack[i].address);
	nextpoint = compile_here();
	compile_org(compile_exit_stack[i].jmpoffs);
	assemble_ulong(exitpoint - (compile_exit_stack[i].jmpoffs + 4));
	compile_org(nextpoint);
    }
}

static void finish_condjumps(int lastiip)
{
    int iip;
    char *lastptr = compile_here();
    for (iip = 0; iip < lastiip; iip++) {
	char *fillin = insn_info[iip].compiled_fillin;
	if (fillin != NULL) {
	    compile_org(insn_info[iip].compiled_fillin);
	    assemble_ulong(insn_info[insn_info[iip].jumps_to].compiled_jumpaddr - (fillin + 4));
	}
    }
    compile_org(lastptr);
}

#define CC_X_FROM_86C 1
#define CC_C_FROM_86C 2
#define CC_Z_FROM_86Z 4
#define CC_V_FROM_86V 8 
#define CC_N_FROM_86N 16
#define CC_TEST_REG   32
#define CC_Z_FROM_86C 64
#define CC_SAHF       128
#define CC_TEST_CONST 256
#define CC_AFTER_RO   512
#define CC_AFTER_ROX  1024

#define CC68K_C 16
#define CC68K_V 8
#define CC68K_Z 4
#define CC68K_N 2
#define CC68K_X 1

static unsigned int cc_status;
static int cc_reg;
static ULONG cc_offset;
static wordsizes cc_size;

static void compile_do_cc_test_reg(struct register_mapping *map)
{
    compile_force_byteorder(map, cc_reg, BO_NORMAL, 1);
    if (cc_offset != 0)
	printf("Pull my finger\n");
    if (cc_size == sz_word) /* test ccreg */
	assemble(0x66);
    if (cc_size == sz_byte)
	assemble(0x84);
    else
	assemble(0x85);
    assemble(0xC0 + 9*cc_reg);
}

static int compile_flush_cc_cache(struct register_mapping *map, int status, 
				  int live_at_end, int user_follows,
				  int user_live_at_end, int user_ccval)
{
    int status_for_user = 0;

    if (user_follows) {
	int need_for_user = 0;
	int user_flagmask = cc_flagmask(user_ccval);

	if (user_flagmask & CC68K_C)
	    need_for_user |= CC_C_FROM_86C;
	if (user_flagmask & CC68K_Z)
	    need_for_user |= CC_Z_FROM_86Z;
	if (user_flagmask & CC68K_N)
	    need_for_user |= CC_N_FROM_86N;
	if (user_flagmask & CC68K_V)
	    need_for_user |= CC_V_FROM_86V;

	/* Check whether we can satisfy the user's needs in a simple way. */
	if ((need_for_user & status) == need_for_user)
	    status_for_user = status;
	else if (user_flagmask == CC68K_Z && status == CC_Z_FROM_86C)
	    status_for_user = status;
	else if (status == CC_TEST_REG && (user_flagmask & (CC68K_C|CC68K_V|CC68K_Z|CC68K_N)) != 0) {
	    if (cc_reg == -2) {
		status_for_user = CC_TEST_CONST;
	    } else {
		compile_do_cc_test_reg(map);
		status_for_user = status = (CC_C_FROM_86C | CC_Z_FROM_86Z | CC_N_FROM_86N | CC_V_FROM_86V);
	    }
	} else if (status == CC_AFTER_RO) {
	    /* We fake some information here... */
	    if (user_flagmask == CC68K_C && (user_live_at_end & ~CC68K_C) == 0)
		status = status_for_user = CC_C_FROM_86C;
	    else if (((user_flagmask | user_live_at_end) & CC68K_C) == 0) {
		status = CC_TEST_REG; user_live_at_end = CC68K_Z|CC68K_N|CC68K_V;
		status_for_user = (CC_C_FROM_86C | CC_Z_FROM_86Z | CC_N_FROM_86N | CC_V_FROM_86V);
	    } else
		status_for_user = CC_SAHF;
	} else if (status == CC_AFTER_ROX) {
	    if (user_flagmask == CC68K_C && (user_live_at_end & ~(CC68K_C|CC68K_X)) == 0)
		status = status_for_user = CC_C_FROM_86C;
	    else if (((user_flagmask | user_live_at_end) & (CC68K_C|CC68K_X)) == 0) {
		status = CC_TEST_REG; user_live_at_end = CC68K_Z|CC68K_N|CC68K_V;
		status_for_user = (CC_C_FROM_86C | CC_Z_FROM_86Z | CC_N_FROM_86N | CC_V_FROM_86V);
	    } else
		status_for_user = CC_SAHF;
	} else if (need_for_user != 0) {
	    /* No way to handle it easily */
	    status_for_user = CC_SAHF;
	}
	if (status_for_user != CC_SAHF)
	    live_at_end = user_live_at_end;
    }

    /*
     * Now store the flags which are live at the end of this insn and set by 
     * us into their home locations 
     */
    if (status == CC_TEST_REG) {
	if ((live_at_end & (CC68K_C|CC68K_V|CC68K_Z|CC68K_N)) == 0)
	    goto all_ok;

	if (cc_reg == -2) {
	    UBYTE f = 0;
	    if (cc_size == sz_byte) {
		f |= (cc_offset & 0x80) ? 0x80 : 0;
		f |= (cc_offset & 0xFF) == 0 ? 0x40 : 0;
	    } else if (cc_size == sz_byte) {
		f |= (cc_offset & 0x8000) ? 0x80 : 0;
		f |= (cc_offset & 0xFFFF) == 0 ? 0x40 : 0;
	    } else {
		f |= (cc_offset & 0x80000000) ? 0x80 : 0;
		f |= (cc_offset & 0xFFFFFFFF) == 0 ? 0x40 : 0;
	    }
	    assemble(0x66); assemble(0xC7); assemble(0x05); 
	    assemble_long((char*)&regflags);
	    assemble_uword(f);
	} else {
	    int tmpr = get_free_x86_register(map, ALL_X86_REGS);
	    compile_do_cc_test_reg(map);

	    /* pushfl; popl tmpr; movl tempr, regflags */
	    assemble(0x9C); assemble(0x58+tmpr);
	    compile_move_reg_to_mem_regoffs(-2, (ULONG)&regflags, tmpr, sz_word);
	}
    } else if (status == CC_Z_FROM_86C) {
	if ((live_at_end & CC68K_Z) != 0) {
	    int tmpr = get_typed_x86_register(map, DATA_X86_REGS);
	    assemble(0x9C);
	    /* setnc tmpr; shl $6, tmpr; andb $~0x40, regflags; orb tmpr, regflags */
	    assemble(0x0F); assemble(0x93); assemble(0xC0 + tmpr);
	    assemble(0xC0); assemble(4*8 + 0xC0 + tmpr); assemble(6);
	    assemble(0x80); assemble(0x05+0x20); assemble_long(&regflags); assemble((UBYTE)~0x40);
	    assemble(0x08); assemble(0x05+ tmpr*8); assemble_long(&regflags);
	    assemble(0x9D);
	}
    } else if (status == CC_AFTER_RO || status == CC_AFTER_ROX) {
	int tmpr = get_typed_x86_register(map, DATA_X86_REGS);
	assemble(0x9C);
	compile_do_cc_test_reg(map);
	/* pushfl; popl tmpr; movl tempr, regflags */
	assemble(0x9C); assemble(0x58+tmpr);
	assemble(0x9D);
	/* adc $0, tmpr */
	assemble(0x80); assemble(0xC0 + tmpr + 8*2); assemble(0);
	compile_move_reg_to_mem_regoffs(-2, (ULONG)&regflags, tmpr, sz_word);
	if (status == CC_AFTER_ROX)
	    compile_move_reg_to_mem_regoffs(-2, 2 + (ULONG)&regflags, tmpr, sz_word);
    } else if (status != 0) {
	assert((status & CC_TEST_REG) == 0);
	assert (status == (CC_C_FROM_86C | CC_Z_FROM_86Z | CC_N_FROM_86N | CC_X_FROM_86C | CC_V_FROM_86V)
		|| status == (CC_C_FROM_86C | CC_Z_FROM_86Z | CC_N_FROM_86N | CC_V_FROM_86V)
		|| status == CC_C_FROM_86C);
	    
	if ((status & CC_X_FROM_86C) == 0)
	    live_at_end &= ~CC68K_X;
	
	if (status == CC_C_FROM_86C && (live_at_end & CC68K_C) != 0) 
	    fprintf(stderr, "Shouldn't be needing C here!\n");
	else if (live_at_end) {
	    if ((live_at_end & CC68K_X) == 0)
		status &= ~CC_X_FROM_86C;

	    if (live_at_end) {
		int tmpr = get_free_x86_register(map, ALL_X86_REGS);
		/* pushfl; popl tmpr; movl tempr, regflags */
		assemble(0x9C); assemble(0x58+tmpr);
		compile_move_reg_to_mem_regoffs(-2, (ULONG)&regflags, tmpr, sz_word);
		
		if (status & CC_X_FROM_86C) {
		    compile_move_reg_to_mem_regoffs(-2, 2 + (ULONG)&regflags, tmpr, sz_word);
		}
	    }
	}
    }

    all_ok:
    return status_for_user;
}
    
static char *compile_condbranch(struct register_mapping *map, int iip,
				int new_cc_status)
{
    int cc = insn_info[iip].dp->cc;
    int flagsused = cc_flagmask(cc);
    int flagsneeded = 0;
    char *undo_pointer = compile_here();

    if (flagsused & CC68K_C)
	flagsneeded |= CC_C_FROM_86C;
    if (flagsused & CC68K_Z)
	flagsneeded |= CC_Z_FROM_86Z;
    if (flagsused & CC68K_N)
	flagsneeded |= CC_N_FROM_86N;
    if (flagsused & CC68K_V)
	flagsneeded |= CC_V_FROM_86V;

    if (new_cc_status == CC_SAHF) {
	int tmpr = get_free_x86_register(map, ALL_X86_REGS);
	compile_move_reg_from_mem_regoffs(tmpr, -2, (ULONG)&regflags, sz_long);
	assemble(0x66); assemble(0x50+tmpr); assemble(0x66); assemble(0x9D);
	new_cc_status = CC_C_FROM_86C|CC_Z_FROM_86Z|CC_N_FROM_86N|CC_V_FROM_86V;
    } else if (new_cc_status == CC_TEST_CONST) {
	int n,z;
	switch(cc_size) {
	 case sz_byte: n = ((BYTE)cc_offset) < 0; z = ((BYTE)cc_offset) == 0; break;
	 case sz_word: n = ((WORD)cc_offset) < 0; z = ((WORD)cc_offset) == 0; break;
	 case sz_long: n = ((LONG)cc_offset) < 0; z = ((LONG)cc_offset) == 0; break;
	}
#define Bcc_TRUE 0
#define Bcc_FALSE 1
	flagsneeded = 0;
	new_cc_status = 0;
	switch (cc) {
	 case 2: cc = !z ? Bcc_TRUE : Bcc_FALSE; break; /* !CFLG && !ZFLG */
	 case 3: cc = z ? Bcc_TRUE : Bcc_FALSE; break; /* CFLG || ZFLG */
	 case 4: cc = Bcc_TRUE; break; /* !CFLG */
	 case 5: cc = Bcc_FALSE; break; /* CFLG */
	 case 6: cc = !z ? Bcc_TRUE : Bcc_FALSE; break; /* !ZFLG */
	 case 7: cc = z ? Bcc_TRUE : Bcc_FALSE; break; /* ZFLG */
	 case 8: cc = Bcc_TRUE; break; /* !VFLG */
	 case 9: cc = Bcc_FALSE; break; /* VFLG */
	 case 10:cc = !n ? Bcc_TRUE : Bcc_FALSE; break; /* !NFLG */
	 case 11:cc = n ? Bcc_TRUE : Bcc_FALSE; break; /* NFLG */
	 case 12:cc = !n ? Bcc_TRUE : Bcc_FALSE; break; /* NFLG == VFLG */
	 case 13:cc = n ? Bcc_TRUE : Bcc_FALSE; break; /* NFLG != VFLG */
	 case 14:cc = !n && !z ? Bcc_TRUE : Bcc_FALSE; break; /* !ZFLG && (NFLG == VFLG) */
	 case 15:cc = n || z ? Bcc_TRUE : Bcc_FALSE; break; /* ZFLG || (NFLG != VFLG) */
	}
    } else if (new_cc_status == CC_Z_FROM_86C) {
	if (cc == 6 || cc == 7) {
	    cc = (cc - 2) ^ 1;
	    /* Fake... */
	    flagsneeded = new_cc_status = CC_C_FROM_86C;
	} else if (cc != 0 && cc != 1)
	    printf("Groan!\n");
    }
    
    if (cc == 1)
	return NULL;
    
    if ((flagsneeded & new_cc_status) == flagsneeded) {
	char *result;
	/* We can generate a simple branch */
	if (cc == 0)
	    assemble(0xE9);
	else
	    assemble(0x0F);
	switch(cc) {
	 case 2: assemble(0x87); break;          /* HI */
	 case 3: assemble(0x86); break;          /* LS */
	 case 4: assemble(0x83); break;          /* CC */
	 case 5: assemble(0x82); break;          /* CS */
	 case 6: assemble(0x85); break;          /* NE */
	 case 7: assemble(0x84); break;          /* EQ */
	 case 8: assemble(0x81); break;          /* VC */
	 case 9: assemble(0x80); break;          /* VS */
	 case 10:assemble(0x89); break;          /* PL */
	 case 11:assemble(0x88); break;          /* MI */
	 case 12:assemble(0x8D); break;          /* GE */
	 case 13:assemble(0x8C); break;          /* LT */
	 case 14:assemble(0x8F); break;          /* GT */
	 case 15:assemble(0x8E); break;          /* LE */
	}
	result = compile_here();
	assemble_ulong(0);
	return result;
    }
    printf("Uhhuh.\n");
    return NULL;
}

static void compile_handle_bcc(struct register_mapping *map, int iip, 
			       int new_cc_status)
{
    insn_info[iip].compiled_fillin = compile_condbranch(map, iip, new_cc_status);
}

static void compile_handle_dbcc(struct register_mapping *map, int iip, 
				int new_cc_status, int dreg)
{
    int cc = insn_info[iip].dp->cc;
    int flagsused = cc_flagmask(cc);
    int flagsneeded = 0;
    char *undo_pointer = compile_here();
    char *fillin1 = compile_condbranch(map, iip, new_cc_status);
    
    /* subw $1,dreg; jnc ... */
    assemble(0x66); assemble(0x83); assemble(0x05 + 5*8);
    assemble_long(regs.d + dreg);
    assemble(1);
    assemble(0x0F); assemble(0x83);
    insn_info[iip].compiled_fillin = compile_here();
    assemble_ulong(0);
    if (fillin1 != NULL) {
	char *oldp = compile_here();
	compile_org(fillin1);
	assemble_ulong(oldp - (fillin1+4));
	compile_org(oldp);
    }
}

static void handle_bit_insns(struct register_mapping *map, struct ea_info *srcea,
			     struct ea_info *dstea, instrmnem optype)
{
    int srcreg, dstreg, dstreg2;
    ULONG srcoffs, dstoffs;
    int code = (optype == i_BTST ? 0
		: optype == i_BSET ? 1
		: optype == i_BCLR ? 2
		: /* optype == i_BCHG */ 3);

    srcreg = compile_fetchea(map, srcea, &srcoffs);
    if (srcreg >= 0) {
	compile_offset_reg(map, srcreg, srcoffs);
	srcoffs = 0;
    }
    /* Fake some EA info... */
    if (dstea->data_reg == -2) {
	dstreg2 = compile_fetchea(map, dstea, &dstoffs);
	dstreg = compile_move_const_reg(map, dstreg2, &dstoffs, 0);
	compile_unlock_reg(map, dstreg2);
	dstea->data_const_off = 0;
	dstea->data_reg = dstreg;
    } else if (dstea->data_reg >= 0) {
	compile_offset_reg(map, dstea->data_reg, dstea->data_const_off);
	dstea->data_const_off = 0;
    }
    compile_force_byteorder(map, srcreg, BO_NORMAL, 0);
    if (srcreg != -2) {
	remove_x86r_from_cache(map, srcreg, 0);
	/* andl $something,srcreg */
	assemble(0x83); assemble(0xC0 + 4*8 + srcreg);
	if (dstea->size == sz_byte)
	    assemble(7);
	else
	    assemble(31);
    } else
	if (dstea->size == sz_byte)
	    srcoffs &= 7;
	else
	    srcoffs &= 31;

    /* Areg isn't possible here */
    if (dstea->mode == Dreg && dstea->data_reg == -1) {
	if (srcreg == -2) {
	    assemble(0x0F); assemble(0xBA); assemble(5 + 8*(4 + code));
	    assemble_long(regs.d + dstea->reg);
	    assemble(srcoffs);
	} else {
	    assemble(0x0F); assemble(0xA3 + 8*code);
	    assemble(5 + srcreg*8);
	    assemble_long(regs.d + dstea->reg);
	}
    } else if (dstea->data_reg >= 0) {
	compile_force_byteorder(map, dstea->data_reg, BO_NORMAL, 0);
	if (srcreg == -2) {
	    assemble(0x0F); assemble(0xBA); assemble(0xC0 + dstea->data_reg + 8*(4 + code));
	    assemble(srcoffs);
	} else {
	    assemble(0x0F); assemble(0xA3 + 8*code);
	    assemble(0xC0 + dstea->data_reg + srcreg*8);
	}
	if (optype != i_BTST)
	    map->x86_dirty[dstea->data_reg] = 1;
    } else {
	int addr_code = dstea->address_reg == -2 ? 5 : dstea->address_reg + 0x80;
	/* We have an address in memory */
	if (dstea->data_reg != -1)
	    printf("Things don't look good in handle_bit_insns\n");
	if (srcreg == -2) {
	    assemble(0x0F); assemble(0xBA); 
	    assemble(addr_code + 8*(4 + code));
	    assemble_long(address_space + dstea->addr_const_off);
	    assemble(srcoffs);
	} else {
	    assemble(0x0F); assemble(0xA3 + 8*code);
	    assemble(addr_code + srcreg*8);
	    assemble_long(address_space + dstea->addr_const_off);
	}
	
    }
    cc_status = CC_Z_FROM_86C;
}

static int do_rotshi = 1;

static void handle_rotshi(struct register_mapping *map, int iip, 
			  UBYTE *realpc, CPTR current_addr)
{
    struct pid_undo pub;
    struct ea_info eai;
    int amode_reg = insn_info[iip].dp->sreg;
    int amode_mode = insn_info[iip].dp->smode;
    wordsizes size = insn_info[iip].dp->size;
    int shiftcount;
    int mnemo = insn_info[iip].dp->mnemo;
    int shiftcode;

    int srcreg, srcreg2;
    ULONG srcoffs;
    
    switch(mnemo) {
     case i_ASLW: shiftcount = 1; mnemo = i_ASL; break;
     case i_ASRW: shiftcount = 1; mnemo = i_ASR; break;
     case i_LSLW: shiftcount = 1; mnemo = i_LSL; break;
     case i_LSRW: shiftcount = 1; mnemo = i_LSR; break;
     case i_ROLW: shiftcount = 1; mnemo = i_ROL; break;
     case i_RORW: shiftcount = 1; mnemo = i_ROR; break;
     case i_ROXLW:shiftcount = 1; mnemo = i_ROXL;break;
     case i_ROXRW:shiftcount = 1; mnemo = i_ROXR;break;
     default:
	if (insn_info[iip].dp->smode != immi) {
	    generate_exit(map, insn_info[iip].address);
	    return;
	}
	amode_reg = insn_info[iip].dp->dreg;
	amode_mode = insn_info[iip].dp->dmode;
	shiftcount = insn_info[iip].dp->sreg;
	break;
    }
    if ((mnemo == i_LSL || mnemo == i_LSR || mnemo == i_ASR || mnemo == i_ASL)
	&& (insn_info[iip].flags_live_at_end & CC68K_V) != 0) {
	generate_exit(map, insn_info[iip].address);
	return;
    }
    if (mnemo == i_ROR || mnemo == i_ROL || mnemo == i_ROXR || mnemo == i_ROXL) {
	if ((insn_info[iip].flags_live_at_end & CC68K_V) != 0) {
	    generate_exit(map, insn_info[iip].address);
	    return;
	}
    }
    if (mnemo == i_ROXR || mnemo == i_ROXL) {
	remove_x86r_from_cache(map, r_EAX, 1);
	map->x86_locked[r_EAX]++;
	compile_move_reg_from_mem_regoffs(r_AH, -2, 2 + (ULONG)&regflags,
					  sz_byte);
    }
    compile_prepareea(map, amode_mode, amode_reg, size,
		      &realpc, current_addr,
		      &eai, EA_LOAD|EA_STORE, &pub, 1);
    
    generate_possible_exit(map, &eai, iip, &pub);
    
    srcreg = compile_fetchea(map, &eai, &srcoffs);
    srcreg2 = compile_move_const_reg(map, srcreg, &srcoffs, insn_info[iip].dp->size == sz_byte ? DATA_X86_REGS : 0);
    compile_unlock_reg(map, srcreg);

    compile_force_byteorder(map, srcreg2, BO_NORMAL, 0);

    switch (mnemo) {
     case i_ASL:
	shiftcode = 4; cc_status = CC_C_FROM_86C | CC_Z_FROM_86Z | CC_N_FROM_86N | CC_V_FROM_86V | CC_X_FROM_86C;
	break;
     case i_LSL:
	shiftcode = 4; cc_status = CC_C_FROM_86C | CC_Z_FROM_86Z | CC_N_FROM_86N | CC_V_FROM_86V | CC_X_FROM_86C;
	break;	
     case i_LSR:
	shiftcode = 5; cc_status = CC_C_FROM_86C | CC_Z_FROM_86Z | CC_N_FROM_86N | CC_V_FROM_86V | CC_X_FROM_86C;
	break;
     case i_ASR:
	shiftcode = 7; cc_status = CC_C_FROM_86C | CC_Z_FROM_86Z | CC_N_FROM_86N | CC_V_FROM_86V | CC_X_FROM_86C;
	break;
     case i_ROR:
	shiftcode = 1; cc_status = CC_AFTER_RO;
	break;
     case i_ROL:
	shiftcode = 0; cc_status = CC_AFTER_RO;
	break;
     case i_ROXL:
	shiftcode = 2; assemble(0x9E); /* SAHF */ cc_status = CC_AFTER_ROX;
	break;
     case i_ROXR:
	shiftcode = 3; assemble(0x9E); /* SAHF */ cc_status = CC_AFTER_ROX;
	break;
    }

    if (size == sz_word)
	assemble(0x66);
    assemble((shiftcount == 1 ? 0xD0 : 0xC0) + (size == sz_byte ? 0 : 1));
    assemble(shiftcode*8+0xC0 + srcreg);
    if (shiftcount != 1) assemble(shiftcount);
    cc_offset = 0; cc_size = size; cc_reg = srcreg;
    
    compile_storeea(map, &eai, srcreg, 0);
}

static ULONG testmask = 0xF80000, testval = 0xF80000;

static int m68k_compile_block(struct hash_block *hb)
{
    int movem_extra = 0;
    int last_iip = m68k_scan_block(hb, &movem_extra);
    struct register_mapping map;
    int i, iip, szflag;
    UBYTE *realpc_start = NULL;
    struct bb_info *current_bb;
    int cc_status_for_bcc = CC_SAHF;
    
    cesp = 0;

    if (n_compiled > n_max_comp)
	return 1;
    else if (n_compiled++ == n_max_comp)
	printf("X\n");

    cc_status = 0; compile_failure = 0;

    /* Kickstart ROM address? */
    if ((hb->he_first->addr & 0xF80000) != 0xF80000
	&& 0 && !patched_syscalls)
	return 1;

    if (alloc_code (hb, last_iip + movem_extra) == NULL) {
	hb->allocfailed = 1;
	return 0;
    }
    compile_org(hb->compile_start);
    compile_last_addr = (char *)hb->compile_start + hb->alloclen;
    
    /* m68k_scan_block() will leave this all set up */
    current_bb = bb_stack;
    
    for (i = 0; i < 8; i++) {
	map.dreg_map[i] = map.areg_map[i] = -1;
	map.x86_dirty[i] = 0;
	map.x86_cache_reg[i] = -1;
	map.x86_cr_type[i] = 0;
	map.x86_const_offset[i] = 0;
	map.x86_verified[i] = 0;
	map.x86_byteorder[i] = BO_NORMAL;
    }
    
    for (iip = 0; iip < last_iip && !compile_failure; iip++) {
	UBYTE *realpc;
	int srcreg, dstreg, srcreg2, dstreg2;
	ULONG srcoffs, dstoffs;
	struct ea_info eainfo[4];
	CPTR current_addr;
	struct pid_undo pub;
	
	/* Set up locks for a new insn. We don't bother to clear this
	 * properly after compiling one insn. */
	for (i = 0; i < 8; i++)
	    map.x86_locked[i] = i == r_ESP ? 1 : 0;

	pub.used = 0;
	current_addr = insn_info[iip].address + 2;

	if (iip == current_bb->first_iip) {
	    sync_reg_cache(&map, 1);
	    if (!quiet_compile)
		printf("Compiling %08lx\n", current_bb->h->addr);
	    realpc_start = get_real_address(current_bb->h->addr);
	    current_bb->h->execute = (code_execfunc)compile_here();
	    current_bb->h->matchword = *(ULONG *)realpc_start;
	    cc_status_for_bcc = CC_SAHF;
	}

	realpc = realpc_start + (current_addr - current_bb->h->addr);	

	insn_info[iip].compiled_jumpaddr = compile_here();	    
	insn_info[iip].compiled_fillin = NULL;
	
	if (insn_info[iip].jump_target) {
	    if (cesp == CE_STACK_SIZE) {
		generate_exit(NULL, insn_info[iip].address);
		compile_failure = 1;
	    } else {
		assemble(0xFE); assemble(0x05 + 8*1); assemble_long(&nr_bbs_to_run);
		assemble(0x0F); assemble(0x84); /* JE finish */
		compile_exit_stack[cesp].noflush = 1;
		compile_exit_stack[cesp].address = current_bb->h;
		compile_exit_stack[cesp].jmpoffs = compile_here();
		assemble_ulong(0);
		cesp++;
	    }
	}
	/*
	 * This will sort out all insns we can't compile, including
	 * conditional branches and jumps out of this block */
	if (insn_info[iip].stop_translation == 1) {
	    generate_exit(&map, insn_info[iip].address);
	    cc_status = 0;
	} else switch (insn_info[iip].dp->mnemo) {
	 case i_Bcc:
	    sync_reg_cache(&map, 0);
	    compile_handle_bcc(&map, iip, cc_status_for_bcc);
	    cc_status = 0;
	    break;

	 case i_DBcc:
	    sync_reg_cache(&map, 0);
	    remove_x86r_from_cache(&map, map.dreg_map[insn_info[iip].dp->sreg], 1);
	    compile_handle_dbcc(&map, iip, cc_status_for_bcc,
				insn_info[iip].dp->sreg);
	    cc_status = 0;
	    break;
#if 0
	 case i_Scc:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo, EA_STORE, &pub, 1);

	    generate_possible_exit(&map, eainfo, iip, &pub);
	    srcreg2 = get_;
	    compile_storeea(&map, eainfo + 0, -2, 0);

	    cc_status = 0;
	    break;
#endif
	 case i_ADD:
	 case i_SUB:
	 case i_CMP:
	 case i_CMPM:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo, EA_LOAD, &pub, 1);
	    compile_prepareea(&map, insn_info[iip].dp->dmode,
			      insn_info[iip].dp->dreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo + 1, EA_IN_REG | EA_LOAD | EA_STORE, 
			      &pub, 1);

	    generate_possible_exit(&map, eainfo, iip, &pub);
	    generate_possible_exit(&map, eainfo+1, iip, &pub);
	    
	    szflag = insn_info[iip].dp->size == sz_byte ? 0 : 1;
	    srcreg = compile_fetchea(&map, eainfo + 0, &srcoffs);
	    dstreg = compile_fetchea(&map, eainfo + 1, &dstoffs);
	    
	    srcreg2 = compile_move_const_reg(&map, srcreg, &srcoffs, insn_info[iip].dp->size == sz_byte ? DATA_X86_REGS : 0);
	    compile_unlock_reg(&map, srcreg);
	    compile_offset_reg(&map, dstreg, dstoffs);

	    compile_force_byteorder(&map, srcreg2, BO_NORMAL, 0);
	    compile_force_byteorder(&map, dstreg, BO_NORMAL, 0);

	    if (insn_info[iip].dp->size == sz_word)
		assemble(0x66);
	    switch (insn_info[iip].dp->mnemo) {
	     case i_ADD: assemble(0x00+szflag); break;
	     case i_SUB: assemble(0x28+szflag); break;
	     case i_AND: assemble(0x20+szflag); break;
	     case i_EOR: assemble(0x30+szflag); break;
	     case i_CMP: case i_CMPM: assemble(0x38+szflag); break;
	     case i_OR: assemble(0x08+szflag); break;
	    }
	    assemble(0xC0 + srcreg2*8 + dstreg);
	    if ((insn_info[iip].dp->mnemo != i_CMP)
		&& (insn_info[iip].dp->mnemo != i_CMPM))
		compile_storeea(&map, eainfo + 1, dstreg, 0);
	    switch (insn_info[iip].dp->mnemo) {
	     case i_ADD:
	     case i_SUB:
		cc_status = CC_X_FROM_86C | CC_Z_FROM_86Z |CC_C_FROM_86C |CC_V_FROM_86V |CC_N_FROM_86N;
		break;
	     case i_AND:
	     case i_EOR:
	     case i_OR:
	     case i_CMP:
	     case i_CMPM:
		cc_status = CC_Z_FROM_86Z |CC_C_FROM_86C |CC_V_FROM_86V |CC_N_FROM_86N;
		break;
	    }
	    break;

	 case i_ADDX:
	 case i_SUBX:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo, EA_LOAD, &pub, 1);
	    compile_prepareea(&map, insn_info[iip].dp->dmode,
			      insn_info[iip].dp->dreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo + 1, EA_IN_REG | EA_LOAD | EA_STORE, 
			      &pub, 1);

	    generate_possible_exit(&map, eainfo, iip, &pub);
	    generate_possible_exit(&map, eainfo+1, iip, &pub);
	    
	    szflag = insn_info[iip].dp->size == sz_byte ? 0 : 1;
	    srcreg = compile_fetchea(&map, eainfo + 0, &srcoffs);
	    dstreg = compile_fetchea(&map, eainfo + 1, &dstoffs);
	    
	    srcreg2 = compile_move_const_reg(&map, srcreg, &srcoffs, insn_info[iip].dp->size == sz_byte ? DATA_X86_REGS : 0);
	    compile_unlock_reg(&map, srcreg);
	    compile_offset_reg(&map, dstreg, dstoffs);

	    compile_force_byteorder(&map, srcreg2, BO_NORMAL, 0);
	    compile_force_byteorder(&map, dstreg, BO_NORMAL, 0);

	    /* bt $16, regflags ; get carry */
	    assemble(0x0F); assemble(0xBA); assemble(0x5+4*8); 
	    assemble_long(&regflags); assemble(0x10);
	    if (insn_info[iip].dp->size == sz_word)
		assemble(0x66);
	    switch (insn_info[iip].dp->mnemo) {
	     case i_ADDX: assemble(0x10+szflag); break;
	     case i_SUBX: assemble(0x18+szflag); break;
	    }
	    assemble(0xC0 + srcreg2*8 + dstreg);
	    compile_storeea(&map, eainfo + 1, dstreg, 0);
	    if (insn_info[iip].flags_live_at_end & CC68K_Z) {
		/* Darn. */
		int tmpr = get_free_x86_register(&map, ALL_X86_REGS);
		/* pushfl; popl tmpr */
		assemble(0x9C); assemble(0x58+tmpr);
		/* Magic! */
		/* andl tmpr, regflags; andl $~0x40,tmpr; orl tmpr, regflags */
		assemble(0x21); assemble(0x05 + 8*tmpr); assemble_long(&regflags);
		assemble(0x81); assemble(0xC0 + 8*4 + tmpr); assemble_ulong(~0x40);
		assemble(0x09); assemble(0x05 + 8*tmpr); assemble_long(&regflags);
		compile_move_reg_to_mem_regoffs(-2, 2 + (ULONG)&regflags, tmpr, sz_word);
		cc_status = 0;
	    } else {
		/* Lies! */
		cc_status = CC_X_FROM_86C | CC_Z_FROM_86Z |CC_C_FROM_86C |CC_V_FROM_86V |CC_N_FROM_86N;
	    }
	    break;

	 case i_MULU:
	 case i_MULS:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo, EA_LOAD, &pub, 1);
	    compile_prepareea(&map, insn_info[iip].dp->dmode,
			      insn_info[iip].dp->dreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo + 1, EA_IN_REG | EA_LOAD | EA_STORE, 
			      &pub, 1);

	    generate_possible_exit(&map, eainfo, iip, &pub);
	    generate_possible_exit(&map, eainfo+1, iip, &pub);
	    
	    srcreg = compile_fetchea(&map, eainfo + 0, &srcoffs);
	    dstreg = compile_fetchea(&map, eainfo + 1, &dstoffs);
	    
	    srcreg2 = compile_move_const_reg(&map, srcreg, &srcoffs, insn_info[iip].dp->size == sz_byte ? DATA_X86_REGS : 0);
	    compile_unlock_reg(&map, srcreg);
	    compile_offset_reg(&map, dstreg, dstoffs);

	    compile_force_byteorder(&map, srcreg2, BO_NORMAL, 0);
	    compile_force_byteorder(&map, dstreg, BO_NORMAL, 0);

	    /* Extend the regs properly */
	    remove_x86r_from_cache(&map, srcreg2, 0);
	    switch (insn_info[iip].dp->mnemo) {
	     case i_MULU:
		assemble(0x81); assemble(0xC0+4*8+srcreg2); assemble_ulong(0xFFFF);
		assemble(0x81); assemble(0xC0+4*8+dstreg); assemble_ulong(0xFFFF);
		break;
	     case i_MULS:
		assemble(0x0F); assemble(0xBF); assemble(0xC0 + 9*srcreg2);
		assemble(0x0F); assemble(0xBF); assemble(0xC0 + 9*dstreg);
		break;
	    }
	    /* and multiply */
	    assemble(0x0F); assemble(0xAF); assemble(0xC0 + 8*dstreg + srcreg2);
	    compile_storeea(&map, eainfo + 1, dstreg, 0);
	    cc_status = CC_TEST_REG;
	    cc_reg = dstreg;
	    cc_offset = 0;
	    cc_size = sz_long;
	    break;

	 case i_ADDA:
	 case i_SUBA:
	 case i_CMPA:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo, EA_LOAD, &pub, 1);
	    compile_prepareea(&map, insn_info[iip].dp->dmode,
			      insn_info[iip].dp->dreg, 
			      sz_long, &realpc, current_addr,
			      eainfo + 1, EA_IN_REG | EA_LOAD | EA_STORE, 
			      &pub, 1);

	    generate_possible_exit(&map, eainfo, iip, &pub);

	    srcreg2 = compile_fetchea(&map, eainfo + 0, &srcoffs);
	    dstreg = compile_fetchea(&map, eainfo + 1, &dstoffs);
	    srcreg = compile_move_const_reg(&map, srcreg2, &srcoffs, 0);
	    compile_unlock_reg(&map, srcreg2);
	    srcreg2 = compile_extend_long(&map, srcreg, &srcoffs, eainfo[0].size);
	    compile_unlock_reg(&map, srcreg);
	    compile_offset_reg(&map, dstreg, dstoffs);

	    compile_force_byteorder(&map, srcreg2, BO_NORMAL, 0);
	    compile_force_byteorder(&map, dstreg, BO_NORMAL, 0);

	    switch (insn_info[iip].dp->mnemo) {
	     case i_ADDA: assemble(0x01); break;
	     case i_SUBA: assemble(0x29); break;
	     case i_CMPA: assemble(0x39); break;
	    }
	    assemble(0xC0 + srcreg2*8 + dstreg);

	    if (insn_info[iip].dp->mnemo == i_CMPA) {
		cc_status = CC_Z_FROM_86Z |CC_C_FROM_86C |CC_V_FROM_86V |CC_N_FROM_86N;
	    } else {
		compile_storeea(&map, eainfo + 1, dstreg, 0);
		cc_status = 0;
	    }
	    break;

	 case i_MOVE:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo, EA_LOAD, &pub, 1);
	    compile_prepareea(&map, insn_info[iip].dp->dmode,
			      insn_info[iip].dp->dreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo + 1, EA_STORE, &pub, 1);

	    generate_possible_exit(&map, eainfo, iip, &pub);
	    generate_possible_exit(&map, eainfo + 1, iip, &pub);
	    
	    srcreg = compile_fetchea(&map, eainfo + 0, &srcoffs);
	    compile_storeea(&map, eainfo + 1, srcreg, srcoffs);
	    cc_status = CC_TEST_REG;
	    cc_reg = srcreg;
	    cc_offset = srcoffs;
	    cc_size = eainfo[0].size;
	    
	    break;

	 case i_MOVEA:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo, EA_LOAD, &pub, 1);
	    compile_prepareea(&map, insn_info[iip].dp->dmode,
			      insn_info[iip].dp->dreg,
			      sz_long, &realpc, current_addr,
			      eainfo + 1, EA_STORE, &pub, 1);

	    generate_possible_exit(&map, eainfo, iip, &pub);
	    
	    srcreg = compile_fetchea(&map, eainfo, &srcoffs);
	    srcreg2 = compile_extend_long(&map, srcreg, &srcoffs, eainfo[0].size);
	    compile_unlock_reg(&map, srcreg);
	    compile_storeea(&map, eainfo + 1, srcreg2, srcoffs);

	    cc_status = 0;
	    break;

	 case i_EXG:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg,
			      sz_long, &realpc, current_addr,
			      eainfo, EA_LOAD|EA_STORE, &pub, 1);
	    compile_prepareea(&map, insn_info[iip].dp->dmode,
			      insn_info[iip].dp->dreg,
			      sz_long, &realpc, current_addr,
			      eainfo + 1, EA_LOAD|EA_STORE, &pub, 1);

	    srcreg = compile_fetchea(&map, eainfo, &srcoffs);
	    dstreg = compile_fetchea(&map, eainfo+1, &dstoffs);
	    compile_unlock_reg(&map, srcreg);
	    compile_storeea(&map, eainfo + 1, srcreg, srcoffs);
	    compile_storeea(&map, eainfo, dstreg, dstoffs);

	    cc_status = 0;
	    break;
	    
	 case i_LINK:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      sz_long, &realpc, current_addr,
			      eainfo, EA_LOAD|EA_STORE, &pub, 1);
	    compile_prepareea(&map, insn_info[iip].dp->dmode,
			      insn_info[iip].dp->dreg,
			      sz_long, &realpc, current_addr,
			      eainfo + 1, EA_LOAD, &pub, 1);
	    compile_prepareea(&map, Apdi, 7, sz_long, &realpc, current_addr,
			      eainfo + 2, EA_STORE, &pub, 1);

	    generate_possible_exit(&map, eainfo+2, iip, &pub);

	    srcreg = compile_fetchea(&map, eainfo + 0, &srcoffs);
	    dstreg = compile_fetchea(&map, eainfo + 1, &dstoffs);
	    compile_storeea(&map, eainfo + 2, srcreg, srcoffs);

	    compile_prepareea(&map, Areg, 7, sz_long, &realpc, current_addr,
			      eainfo + 3, EA_STORE, &pub, 1);
	    srcreg = compile_fetchea(&map, eainfo + 3, &srcoffs);
	    compile_storeea(&map, eainfo + 0, srcreg, srcoffs);
	    /* @@@ 020 */
	    compile_storeea(&map, eainfo + 3, srcreg, srcoffs+(WORD)dstoffs); 
	    cc_status = 0;
	    break;
	    	    
	 case i_UNLK:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      sz_long, &realpc, current_addr,
			      eainfo, EA_LOAD|EA_STORE, &pub, 1);
	    compile_prepareea(&map, Aind,
			      insn_info[iip].dp->sreg, 
			      sz_long, &realpc, current_addr,
			      eainfo + 1, EA_LOAD, &pub, 1);

	    generate_possible_exit(&map, eainfo+1, iip, &pub);

	    compile_prepareea(&map, Areg, 7, sz_long, &realpc, current_addr,
			      eainfo + 3, EA_STORE, &pub, 1);
	    srcreg = compile_fetchea(&map, eainfo + 1, &srcoffs);
	    dstreg = compile_fetchea(&map, eainfo + 0, &dstoffs);
	    compile_storeea(&map, eainfo + 0, srcreg, srcoffs);
	    compile_storeea(&map, eainfo + 3, dstreg, dstoffs+4);
	    cc_status = 0;	    
	    break;

	 case i_OR:
	 case i_AND:
	 case i_EOR:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo, EA_LOAD, &pub, 1);
	    compile_prepareea(&map, insn_info[iip].dp->dmode,
			      insn_info[iip].dp->dreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo + 1, EA_IN_REG | EA_LOAD | EA_STORE, &pub, 1);

	    generate_possible_exit(&map, eainfo, iip, &pub);
	    generate_possible_exit(&map, eainfo + 1, iip, &pub);
	    
	    szflag = insn_info[iip].dp->size == sz_byte ? 0 : 1;
	    srcreg = compile_fetchea(&map, eainfo + 0, &srcoffs);
	    dstreg = compile_fetchea(&map, eainfo + 1, &dstoffs);
	    
	    srcreg2 = compile_move_const_reg(&map, srcreg, &srcoffs, insn_info[iip].dp->size == sz_byte ? DATA_X86_REGS : 0);
	    compile_unlock_reg(&map, srcreg);
	    compile_offset_reg(&map, dstreg, dstoffs);

	    compile_force_byteorder(&map, srcreg2, BO_NORMAL, 0);
	    compile_force_byteorder(&map, dstreg, BO_NORMAL, 0);

	    if (insn_info[iip].dp->size == sz_word)
		assemble(0x66);
	    switch (insn_info[iip].dp->mnemo) {
	     case i_AND: assemble(0x20+szflag); break;
	     case i_EOR: assemble(0x30+szflag); break;
	     case i_OR: assemble(0x08+szflag); break;
	    }
	    assemble(0xC0 + srcreg2*8 + dstreg);
	    compile_storeea(&map, eainfo + 1, dstreg, 0);
	    cc_status = CC_Z_FROM_86Z |CC_C_FROM_86C |CC_V_FROM_86V |CC_N_FROM_86N;
	    break;
	    
	 case i_BTST:
	 case i_BSET:
	 case i_BCLR:
	 case i_BCHG:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo, EA_LOAD, &pub, 1);
	    compile_prepareea(&map, insn_info[iip].dp->dmode,
			      insn_info[iip].dp->dreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo + 1, 0, &pub, 1);

	    generate_possible_exit(&map, eainfo, iip, &pub);
	    generate_possible_exit(&map, eainfo + 1, iip, &pub);

	    handle_bit_insns(&map, eainfo, eainfo + 1, insn_info[iip].dp->mnemo);
	    break;
	    
	 case i_TST:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo, EA_LOAD, &pub, 1);

	    generate_possible_exit(&map, eainfo, iip, &pub);
	    
	    srcreg = compile_fetchea(&map, eainfo + 0, &srcoffs);
	    cc_status = CC_TEST_REG;
	    cc_reg = srcreg;
	    cc_offset = srcoffs;
	    cc_size = eainfo[0].size;
	    break;
	    
	 case i_CLR:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo, EA_STORE, &pub, 1);

	    generate_possible_exit(&map, eainfo, iip, &pub);
	    
	    compile_storeea(&map, eainfo + 0, -2, 0);

	    cc_status = CC_TEST_REG;
	    cc_reg = -2;
	    cc_offset = 0;
	    cc_size = eainfo[0].size;
	    break;

	 case i_EXT:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size == sz_long ? sz_word : sz_byte,
			      &realpc, current_addr,
			      eainfo, EA_LOAD|EA_STORE, &pub, 1);
	    /* No exits - this is always a Dreg; fetchea will get it in a reg
	     * without offset */
	    srcreg = compile_fetchea(&map, eainfo + 0, &srcoffs);

	    compile_force_byteorder(&map, srcreg, BO_NORMAL, 0);

	    if (insn_info[iip].dp->size == sz_word)
		assemble(0x66);
	    assemble(0x0F);
	    if (insn_info[iip].dp->size == sz_long)
		assemble(0xBF);
	    else
		assemble(0xBE);
	    
	    assemble(0xC0 + 9*srcreg);
	    map.x86_dirty[srcreg] = 1;

	    cc_status = CC_TEST_REG;
	    cc_reg = srcreg;
	    cc_offset = srcoffs;
	    cc_size = eainfo[0].size;	    
	    break;

	 case i_NOT:
	 case i_NEG:
	    szflag = insn_info[iip].dp->size == sz_byte ? 0 : 1;

	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size,
			      &realpc, current_addr,
			      eainfo, EA_LOAD|EA_STORE, &pub, 1);

	    generate_possible_exit(&map, eainfo, iip, &pub);
	    
	    srcreg = compile_fetchea(&map, eainfo + 0, &srcoffs);	    
	    srcreg2 = compile_move_const_reg(&map, srcreg, &srcoffs, insn_info[iip].dp->size == sz_byte ? DATA_X86_REGS : 0);
	    compile_unlock_reg(&map, srcreg);

	    compile_force_byteorder(&map, srcreg2, BO_NORMAL, 0);

	    if (insn_info[iip].dp->size == sz_word)
		assemble(0x66);
	    assemble(0xF6 + szflag);
	    
	    assemble(0xC0 + srcreg2 + 8*(insn_info[iip].dp->mnemo == i_NOT ? 2 : 3));
	    compile_storeea(&map, eainfo, srcreg, 0);

	    cc_status = CC_TEST_REG;
	    cc_reg = srcreg;
	    cc_offset = 0;
	    cc_size = eainfo[0].size;	    
	    break;

	 case i_SWAP:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, sz_long,
			      &realpc, current_addr,
			      eainfo, EA_LOAD|EA_STORE, &pub, 1);
	    /* No exits - this is always a Dreg; fetchea will get it in a reg
	     * without offset */
	    srcreg = compile_fetchea(&map, eainfo + 0, &srcoffs);

	    compile_force_byteorder(&map, srcreg, BO_NORMAL, 0);
	    /* roll $16, srcreg */
	    assemble(0xC1); assemble(0xC0 + srcreg); assemble(16);

	    map.x86_dirty[srcreg] = 1;

	    cc_status = CC_TEST_REG;
	    cc_reg = srcreg;
	    cc_offset = srcoffs;
	    cc_size = eainfo[0].size;	    
	    break;
	    
	 case i_LEA:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo, 0, &pub, 1);
	    compile_prepareea(&map, insn_info[iip].dp->dmode,
			      insn_info[iip].dp->dreg,
			      sz_long, &realpc, current_addr,
			      eainfo + 1, EA_STORE, &pub, 1);
	    compile_storeea(&map, eainfo + 1, eainfo[0].address_reg, 
			    eainfo[0].addr_const_off);
	    cc_status = 0;
	    break;

	 case i_PEA:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      insn_info[iip].dp->size, &realpc, current_addr,
			      eainfo, 0, &pub, 1);
	    compile_prepareea(&map, Apdi, 7, sz_long, &realpc, current_addr,
			      eainfo + 1, EA_STORE, &pub, 1);

	    generate_possible_exit(&map, eainfo+1, iip, &pub);

	    compile_storeea(&map, eainfo + 1, eainfo[0].address_reg, 
			    eainfo[0].addr_const_off);
	    cc_status = 0;
	    break;

	 case i_MVMEL:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      sz_word, &realpc, current_addr,
			      eainfo, EA_LOAD, &pub, 1);
	    sync_reg_cache(&map, 0);
	    {
		/* Scratch 0 holds the registers while they are being moved
		 * from/to memory. Scratch 1 points at regs.d. Scratch 2
		 * points at the base addr in memory where to fetch data
		 * from
		 */
		int scratch0, scratch1, scratch2;
		UWORD mask = eainfo[0].data_const_off;
		int bits = count_bits(mask);
		int size = insn_info[iip].dp->size == sz_long ? 4 : 2;
		int i;
		UBYTE x86amode;
		ULONG current_offs = 0;
		
		/* !!! Note current_addr + 2 here! */
		compile_prepareea(&map, insn_info[iip].dp->dmode,
				  insn_info[iip].dp->dreg,
				  insn_info[iip].dp->size, &realpc, current_addr + 2,
				  eainfo + 1, EA_LOAD, &pub, bits);
		
		generate_possible_exit(&map, eainfo + 1, iip, &pub);

		scratch0 = get_free_x86_register(&map, ADDRESS_X86_REGS);
		map.x86_locked[scratch0]++;
		scratch1 = get_free_x86_register(&map, ADDRESS_X86_REGS);
		map.x86_locked[scratch1]++;
		scratch2 = get_free_x86_register(&map, ADDRESS_X86_REGS);
		map.x86_locked[scratch2]++;
		compile_force_byteorder(&map, eainfo[1].address_reg, BO_NORMAL, 0);
		
		compile_lea_reg_with_offset(scratch1, -2, (ULONG)regs.d);
		compile_lea_reg_with_offset(scratch2, eainfo[1].address_reg,
					    (ULONG)(address_space + eainfo[1].addr_const_off));

		for (i = 0; i < 16; i++) {
		    int r68k = i & 7;
		    ULONG *regp = i < 8 ? regs.d : regs.a;
		    int *cache68k = i < 8 ? map.dreg_map : map.areg_map;
		    if (mask & 1 
			&& (i < 8 
			    || insn_info[iip].dp->dmode != Aipi
			    || r68k != insn_info[iip].dp->dreg)) {
			int tmpr = cache68k[r68k];
			
			if (tmpr != -1) {
			    cache68k[r68k] = -1;
			    map.x86_cache_reg[tmpr] = -1;
			}
			compile_move_reg_from_mem_regoffs(scratch0, scratch2,
							  current_offs, insn_info[iip].dp->size);
			if (size == 2) {
			    assemble(0x66); /* rolw $8,scratch0 */
			    assemble(0xC1);
			    assemble(0xC0 + scratch0);
			    assemble(8);
			    assemble(0x0F); assemble(0xBF); /* extend */
			    assemble(0xC0 + 9*scratch0);
			} else {
			    assemble(0x0F); /* bswapl scratch0 */
			    assemble(0xC8 + scratch0);
			}
			compile_move_reg_to_mem_regoffs(scratch1, (char *)(regp + r68k) - (char *)regs.d,
							scratch0, sz_long);
		    }
		    if (mask & 1)
			current_offs += size;
		    mask >>= 1;
		}
	    }
	    cc_status = 0;
	    break;

	 case i_MVMLE:
	    compile_prepareea(&map, insn_info[iip].dp->smode,
			      insn_info[iip].dp->sreg, 
			      sz_word, &realpc, current_addr,
			      eainfo, EA_LOAD, &pub, 1);
	    sync_reg_cache(&map, 0);
	    {
		int scratch0,scratch1,scratch2;
		UWORD mask = eainfo[0].data_const_off;
		int bits = count_bits(mask);
		int size = insn_info[iip].dp->size == sz_long ? 4 : 2;
		int i;
		UBYTE x86amode;
		ULONG current_offs = 0;
		int addrareg = get_and_lock_68k_reg(&map, insn_info[iip].dp->dreg, 
						    0, 0, 1);
		compile_force_byteorder(&map, addrareg, BO_NORMAL, 0);
		if (insn_info[iip].dp->dmode == Apdi)
		    mask = bitswap(mask);
		/* !!! Note current_addr + 2 here! */
		compile_prepareea(&map, insn_info[iip].dp->dmode,
				  insn_info[iip].dp->dreg,
				  insn_info[iip].dp->size, &realpc, current_addr + 2,
				  eainfo + 1, EA_STORE, &pub, bits);
		
		generate_possible_exit(&map, eainfo + 1, iip, &pub);

		scratch0 = get_free_x86_register(&map, ADDRESS_X86_REGS);
		map.x86_locked[scratch0]++;
		scratch1 = get_free_x86_register(&map, ADDRESS_X86_REGS);
		map.x86_locked[scratch1]++;
		scratch2 = get_free_x86_register(&map, ADDRESS_X86_REGS);
		map.x86_locked[scratch2]++;

		compile_force_byteorder(&map, eainfo[1].address_reg, BO_NORMAL, 0);

		compile_lea_reg_with_offset(scratch1, -2, (ULONG)regs.d);
		compile_lea_reg_with_offset(scratch2, eainfo[1].address_reg,
					    (ULONG)(address_space + eainfo[1].addr_const_off));

		for (i = 0; i < 16; i++) {
		    int r68k = i & 7;
		    ULONG *regp = i < 8 ? regs.d : regs.a;
		    int *cache68k = i < 8 ? map.dreg_map : map.areg_map;
		    if (mask & 1) {
			/* move from 68k reg */
			if (i < 8 || r68k != insn_info[iip].dp->dreg) {
			    compile_move_reg_from_mem_regoffs(scratch0, scratch1, (char *)(regp + r68k) - (char *)regs.d,
							      sz_long);
			} else {
			    assemble(0x8B); assemble(0xC0 + 8*scratch0 + addrareg);
			}

			if (size == 2) {
			    assemble(0x66); /* rolw $8,scratch0 */
			    assemble(0xC1);
			    assemble(0xC0 + scratch0); assemble(8);
			} else {
			    assemble(0x0F); /* bswapl scratch0 */
			    assemble(0xC8 + scratch0);
			}
			compile_move_reg_to_mem_regoffs(scratch2, current_offs,
							scratch0, insn_info[iip].dp->size);
		    }
		    if (mask & 1)
			current_offs += size;
		    mask >>= 1;
		}
	    }
	    cc_status = 0;
	    break;

	 case i_ASL: case i_ASR: case i_LSL: case i_LSR: 
	 case i_ROL: case i_ROR: case i_ROXL:case i_ROXR:
	 case i_ASLW: case i_ASRW: case i_LSLW: case i_LSRW: 
	 case i_ROLW: case i_RORW: case i_ROXLW:case i_ROXRW:
	    if (do_rotshi) {
		handle_rotshi(&map, iip, realpc, current_addr);
		break;
	    }

	 default:
	    generate_exit(&map, insn_info[iip].address); cc_status = 0;
	    break;
	}
	if (insn_info[iip].ccuser_follows)
	    cc_status_for_bcc = compile_flush_cc_cache(&map, cc_status, 
				   insn_info[iip].flags_live_at_end,
				   1, insn_info[iip+1].flags_live_at_end,
				   insn_info[iip+1].dp->cc);
	else
	    cc_status_for_bcc = compile_flush_cc_cache(&map, cc_status, 
				   insn_info[iip].flags_live_at_end,
				   0, 0, 0);

	if (iip == current_bb->last_iip) {
	    current_bb++;
	}
    }
    if (compile_failure)
	goto oops;

    /* Compile all exits that we prepared earlier */
    finish_exits();
    if (compile_failure)
	goto oops;
    finish_condjumps(last_iip);
    {
	int needed_len = compile_here() - hb->compile_start;
	int allocsize = (needed_len + PAGE_SUBUNIT - 1) & ~(PAGE_SUBUNIT-1);
	ULONG allocmask;
	int allocbits;
	
	allocbits = (allocsize >> SUBUNIT_ORDER);
	allocmask = (1 << allocbits) - 1;
	while ((allocmask & hb->page_allocmask) != allocmask)
	    allocmask <<= 1;
	if ((hb->page_allocmask & ~allocmask) != 0 && !quiet_compile)
	    fprintf(stderr, "Gaining some bits: %08lx\n", hb->page_allocmask & ~allocmask);
	hb->cpage->allocmask &= ~hb->page_allocmask;
	hb->page_allocmask = allocmask;
	hb->cpage->allocmask |= allocmask;
    }
    return 0;
    
    oops:
    if (1 || !quiet_compile)
	fprintf(stderr, "Compile failed!\n");
    hb->cpage->allocmask &= ~hb->page_allocmask;
    hb->cpage = NULL;
    hb->untranslatable = 1;
    {
	struct hash_entry *h = hb->he_first;
	
	do {
	    h->execute = NULL;
	    h = h->next_same_block;
	} while (h != hb->he_first);
    }
    return 1;
}

/*
 * Why do compilers always have to be so complicated? And I thought GCC was
 * a mess...
 */

#endif /* USE_COMPILER */
