Source to src/cpu/newcpu.c


Enter a symbol's name here to quickly find it.

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

#define MMUOP_DEBUG 2
#define DEBUG_CD32CDTVIO 0

#include "main.h"
#include "m68000.h"
#include "compat.h"
#include "sysconfig.h"
#include "sysdeps.h"
#include "hatari-glue.h"
#include "options_cpu.h"
#include "maccess.h"
#include "memory.h"
#include "newcpu.h"
#include "cpummu.h"
#include "cpummu030.h"
#ifdef WITH_SOFTFLOAT
#include "fpp-softfloat.h"
#else
#include "md-fpp.h"
#endif
#include "main.h"
#include "dsp.h"
#include "dimension.hpp"
#include "reset.h"
#include "cycInt.h"
#include "dialog.h"
#include "screen.h"
#include "video.h"
#include "log.h"
#include "debugui.h"
#include "debugcpu.h"


/* Opcode of faulting instruction */
static uae_u16 last_op_for_exception_3;
/* PC at fault time */
static uaecptr last_addr_for_exception_3;
/* Address that generated the exception */
static uaecptr last_fault_for_exception_3;
/* read (0) or write (1) access */
static int last_writeaccess_for_exception_3;
/* instruction (1) or data (0) access */
static int last_instructionaccess_for_exception_3;
int mmu_enabled, mmu_triggered;
int cpu_cycles;

const int areg_byteinc[] = { 1, 1, 1, 1, 1, 1, 1, 2 };
const int imm8_table[] = { 8, 1, 2, 3, 4, 5, 6, 7 };

int movem_index1[256];
int movem_index2[256];
int movem_next[256];

cpuop_func *cpufunctbl[65536];

int OpcodeFamily;
struct mmufixup mmufixup[2];

uae_u32 fpp_get_fpsr (void);

static struct cache030 icaches030[CACHELINES030];
static struct cache030 dcaches030[CACHELINES030];

void m68k_disasm_2 (TCHAR *buf, int bufsize, uaecptr addr, uaecptr *nextpc, int cnt, uae_u32 *seaddr, uae_u32 *deaddr, int safemode);

uae_u32 (*x_prefetch)(int);
uae_u32 (*x_next_iword)(void);
uae_u32 (*x_next_ilong)(void);
uae_u32 (*x_get_ilong)(int);
uae_u32 (*x_get_iword)(int);
uae_u32 (*x_get_ibyte)(int);
uae_u32 (*x_get_long)(uaecptr);
uae_u32 (*x_get_word)(uaecptr);
uae_u32 (*x_get_byte)(uaecptr);
void (*x_put_long)(uaecptr,uae_u32);
void (*x_put_word)(uaecptr,uae_u32);
void (*x_put_byte)(uaecptr,uae_u32);

uae_u32 (*x_cp_next_iword)(void);
uae_u32 (*x_cp_next_ilong)(void);
uae_u32 (*x_cp_get_long)(uaecptr);
uae_u32 (*x_cp_get_word)(uaecptr);
uae_u32 (*x_cp_get_byte)(uaecptr);
void (*x_cp_put_long)(uaecptr,uae_u32);
void (*x_cp_put_word)(uaecptr,uae_u32);
void (*x_cp_put_byte)(uaecptr,uae_u32);
uae_u32 (REGPARAM3 *x_cp_get_disp_ea_020)(uae_u32 base, int idx) REGPARAM;

// shared memory access functions
static void set_x_funcs (void)
{
    if (currprefs.cpu_model == 68040) {
        x_prefetch   = get_iword_mmu040;
        x_get_ilong  = get_ilong_mmu040;
        x_get_iword  = get_iword_mmu040;
        x_get_ibyte  = get_ibyte_mmu040;
        x_next_iword = next_iword_mmu040;
        x_next_ilong = next_ilong_mmu040;
        x_put_long   = put_long_mmu040;
        x_put_word   = put_word_mmu040;
        x_put_byte   = put_byte_mmu040;
        x_get_long   = get_long_mmu040;
        x_get_word   = get_word_mmu040;
        x_get_byte   = get_byte_mmu040;
    } else {
        x_prefetch   = get_iword_mmu030;
        x_get_ilong  = get_ilong_mmu030;
        x_get_iword  = get_iword_mmu030;
        x_get_ibyte  = get_ibyte_mmu030;
        x_next_iword = next_iword_mmu030;
        x_next_ilong = next_ilong_mmu030;
        x_put_long   = put_long_mmu030;
        x_put_word   = put_word_mmu030;
        x_put_byte   = put_byte_mmu030;
        x_get_long   = get_long_mmu030;
        x_get_word   = get_word_mmu030;
        x_get_byte   = get_byte_mmu030;
    }
    
    if (currprefs.mmu_model == 68030) {
        x_cp_put_long = put_long_mmu030_state;
        x_cp_put_word = put_word_mmu030_state;
        x_cp_put_byte = put_byte_mmu030_state;
        x_cp_get_long = get_long_mmu030_state;
        x_cp_get_word = get_word_mmu030_state;
        x_cp_get_byte = get_byte_mmu030_state;
        x_cp_next_iword = next_iword_mmu030_state;
        x_cp_next_ilong = next_ilong_mmu030_state;
        x_cp_get_disp_ea_020 = get_disp_ea_020_mmu030;
    } else {
        x_cp_put_long = x_put_long;
        x_cp_put_word = x_put_word;
        x_cp_put_byte = x_put_byte;
        x_cp_get_long = x_get_long;
        x_cp_get_word = x_get_word;
        x_cp_get_byte = x_get_byte;
        x_cp_next_iword = x_next_iword;
        x_cp_next_ilong = x_next_ilong;
        x_cp_get_disp_ea_020 = x_get_disp_ea_020;
    }
}

void flush_cpu_caches_040(uae_u16 opcode)
{
    int cache = (opcode >> 6) & 3;
    if (!(cache & 2))
        return;
    set_cpu_caches(true);
}

void set_cpu_caches (bool flush)
{
	int i;

	if (currprefs.cpu_model == 68030) {
		if (regs.cacr & 0x08) { // clear instr cache
			for (i = 0; i < CACHELINES030; i++) {
				icaches030[i].valid[0] = 0;
				icaches030[i].valid[1] = 0;
				icaches030[i].valid[2] = 0;
				icaches030[i].valid[3] = 0;
			}
            regs.cacr &= ~0x08;
		}
		if (regs.cacr & 0x04) { // clear entry in instr cache
			icaches030[(regs.caar >> 4) & (CACHELINES030 - 1)].valid[(regs.caar >> 2) & 3] = 0;
			regs.cacr &= ~0x04;
		}
		if (regs.cacr & 0x800) { // clear data cache
			for (i = 0; i < CACHELINES030; i++) {
				dcaches030[i].valid[0] = 0;
				dcaches030[i].valid[1] = 0;
				dcaches030[i].valid[2] = 0;
				dcaches030[i].valid[3] = 0;
			}
			regs.cacr &= ~0x800;
		}
		if (regs.cacr & 0x400) { // clear entry in data cache
			dcaches030[(regs.caar >> 4) & (CACHELINES030 - 1)].valid[(regs.caar >> 2) & 3] = 0;
			regs.cacr &= ~0x400;
		}
#if 0 // FIXME
	} else if (currprefs.cpu_model == 68040) {
        if (ConfigureParams.System.bRealtime) {
            if (regs.cacr & 0x8000) {
                flush_icache(0, -1);
                x_prefetch   = getc_iword_mmu040;
                x_get_ilong  = getc_ilong_mmu040;
                x_get_iword  = getc_iword_mmu040;
                x_get_ibyte  = getc_ibyte_mmu040;
                x_next_iword = nextc_iword_mmu040;
                x_next_ilong = nextc_ilong_mmu040;
            } else {
                x_prefetch   = get_iword_mmu040;
                x_get_ilong  = get_ilong_mmu040;
                x_get_iword  = get_iword_mmu040;
                x_get_ibyte  = get_ibyte_mmu040;
                x_next_iword = next_iword_mmu040;
                x_next_ilong = next_ilong_mmu040;
            }
        }
#endif
	}
}

static uae_u32 REGPARAM2 op_illg_1 (uae_u32 opcode)
{
    op_illg (opcode);
    return 4;
}
static uae_u32 REGPARAM2 op_unimpl_1 (uae_u32 opcode)
{
    if ((opcode & 0xf000) == 0xf000 || currprefs.cpu_model < 68060)
        op_illg (opcode);
    else
        op_unimpl (opcode);
    return 4;
}

void build_cpufunctbl (void)
{
	int i, opcnt;
	unsigned long opcode;
	const struct cputbl *tbl = 0;
	int lvl;

	switch (currprefs.cpu_model)
	{
	case 68040:
		lvl = 4;
		tbl = op_smalltbl_31_ff;
		break;
	case 68030:
		lvl = 3;
        tbl = op_smalltbl_32_ff;
		break;
	default:
		changed_prefs.cpu_model = currprefs.cpu_model = 68030;
        lvl = 3;
        tbl = op_smalltbl_32_ff;
	}

	if (tbl == 0) {
		write_log ("no CPU emulation cores available CPU=%d!", currprefs.cpu_model);
        DebugUI();
	}

	for (opcode = 0; opcode < 65536; opcode++)
		cpufunctbl[opcode] = op_illg_1;
	for (i = 0; tbl[i].handler != NULL; i++) {
		opcode = tbl[i].opcode;
		cpufunctbl[opcode] = tbl[i].handler;
	}

	opcnt = 0;
	for (opcode = 0; opcode < 65536; opcode++) {
		cpuop_func *f;

		if (table68k[opcode].mnemo == i_ILLG)
			continue;
		if (table68k[opcode].clev > lvl) {
			continue;
		}

		if (table68k[opcode].handler != -1) {
			int idx = table68k[opcode].handler;
			f = cpufunctbl[idx];
			if (f == op_illg_1)
                DebugUI();
			cpufunctbl[opcode] = f;
			opcnt++;
		}
	}
	write_log ("Building CPU, %d opcodes (%d %d)\n",
		opcnt, lvl, currprefs.cpu_compatible ? 1 : 0);
	write_log ("CPU=%d, MMU=%d, FPU=%d ($%02x)\n",
               currprefs.cpu_model, currprefs.mmu_model,
               currprefs.fpu_model, currprefs.fpu_revision);
	set_cpu_caches (0);
	if (currprefs.mmu_model) {
        if (currprefs.cpu_model >= 68040) {
            mmu_reset ();
            mmu_set_tc (regs.tcr);
            mmu_set_super (regs.s != 0);
        } else {
            mmu030_reset(1);
        }
    }
}

static void prefs_changed_cpu (void) {
	currprefs.cpu_model = changed_prefs.cpu_model;
	currprefs.fpu_model = changed_prefs.fpu_model;
    currprefs.fpu_revision = changed_prefs.fpu_revision;
	currprefs.mmu_model = changed_prefs.mmu_model;
	currprefs.cpu_compatible = changed_prefs.cpu_compatible;
}

void check_prefs_changed_cpu (void)
{
	bool changed = 0;

    if (changed
		|| currprefs.cpu_model != changed_prefs.cpu_model
		|| currprefs.fpu_model != changed_prefs.fpu_model
        || currprefs.fpu_revision != changed_prefs.fpu_revision
		|| currprefs.mmu_model != changed_prefs.mmu_model
		|| currprefs.cpu_compatible != changed_prefs.cpu_compatible) {

			prefs_changed_cpu ();
			build_cpufunctbl ();
			changed = 1;
	}

	if (currprefs.cpu_idle != changed_prefs.cpu_idle) {
		currprefs.cpu_idle = changed_prefs.cpu_idle;
	}
	if (changed)
		set_special (SPCFLAG_MODE_CHANGE);

}

void init_m68k (void)
{
	int i;

	prefs_changed_cpu ();
    
	for (i = 0 ; i < 256 ; i++) {
		int j;
		for (j = 0 ; j < 8 ; j++) {
			if (i & (1 << j)) break;
		}
		movem_index1[i] = j;
		movem_index2[i] = 7-j;
		movem_next[i] = i & (~(1 << j));
	}

	write_log ("Building CPU table for configuration: %d", currprefs.cpu_model);
	regs.address_space_mask = 0xffffffff;

    if (currprefs.fpu_model > 0)
		write_log ("/%d", currprefs.fpu_model);
    if (currprefs.cpu_compatible)
		write_log (" prefetch");
	write_log ("\n");

	read_table68k ();
	do_merges ();

	write_log ("%d CPU functions\n", nr_cpuop_funcs);

	build_cpufunctbl ();
	set_x_funcs ();
}

struct regstruct regs;
struct flag_struct regflags;

#define get_word_debug(o) DBGMemory_ReadWord (o)
#define get_iword_debug(o) DBGMemory_ReadWord (o)
#define get_ilong_debug(o) DBGMemory_ReadLong (o)

static uaecptr ShowEA (void *f, uaecptr pc, uae_u16 opcode, int reg, amodes mode, wordsizes size, TCHAR *buf, uae_u32 *eaddr, int safemode)
{
    uae_u16 dp;
    uae_s8 disp8;
    uae_s16 disp16;
    int r;
    uae_u32 dispreg;
    uaecptr addr = pc;
    uae_s32 offset = 0;
    TCHAR buffer[80];
    
    switch (mode){
        case Dreg:
            _stprintf (buffer, _T("D%d"), reg);
            break;
        case Areg:
            _stprintf (buffer, _T("A%d"), reg);
            break;
        case Aind:
            _stprintf (buffer, _T("(A%d)"), reg);
            addr = regs.regs[reg + 8];
            break;
        case Aipi:
            _stprintf (buffer, _T("(A%d)+"), reg);
            addr = regs.regs[reg + 8];
            break;
        case Apdi:
            _stprintf (buffer, _T("-(A%d)"), reg);
            addr = regs.regs[reg + 8];
            break;
        case Ad16:
        {
            TCHAR offtxt[80];
            disp16 = get_iword_debug (pc); pc += 2;
            if (disp16 < 0)
                _stprintf (offtxt, _T("-$%04x"), -disp16);
            else
                _stprintf (offtxt, _T("$%04x"), disp16);
            addr = m68k_areg (regs, reg) + disp16;
            _stprintf (buffer, _T("(A%d, %s) == $%08x"), reg, offtxt, addr);
        }
            break;
        case Ad8r:
            dp = get_iword_debug (pc); pc += 2;
            disp8 = dp & 0xFF;
            r = (dp & 0x7000) >> 12;
            dispreg = dp & 0x8000 ? m68k_areg (regs, r) : m68k_dreg (regs, r);
            if (!(dp & 0x800)) dispreg = (uae_s32)(uae_s16)(dispreg);
            dispreg <<= (dp >> 9) & 3;
            
            if (dp & 0x100) {
                uae_s32 outer = 0, disp = 0;
                uae_s32 base = m68k_areg (regs, reg);
                TCHAR name[10];
                _stprintf (name, _T("A%d, "), reg);
                if (dp & 0x80) { base = 0; name[0] = 0; }
                if (dp & 0x40) dispreg = 0;
                if ((dp & 0x30) == 0x20) { disp = (uae_s32)(uae_s16)get_iword_debug (pc); pc += 2; }
                if ((dp & 0x30) == 0x30) { disp = get_ilong_debug (pc); pc += 4; }
                base += disp;
                
                if ((dp & 0x3) == 0x2) { outer = (uae_s32)(uae_s16)get_iword_debug (pc); pc += 2; }
                if ((dp & 0x3) == 0x3) { outer = get_ilong_debug (pc); pc += 4; }
                
                if (!(dp & 4)) base += dispreg;
                if ((dp & 3) && !safemode) base = get_ilong_debug (base);
                if (dp & 4) base += dispreg;
                
                addr = base + outer;
                _stprintf (buffer, _T("(%s%c%d.%c*%d+%d)+%d == $%08x"), name,
                           dp & 0x8000 ? 'A' : 'D', (int)r, dp & 0x800 ? 'L' : 'W',
                           1 << ((dp >> 9) & 3),
                           disp, outer, addr);
            } else {
                addr = m68k_areg (regs, reg) + (uae_s32)((uae_s8)disp8) + dispreg;
                _stprintf (buffer, _T("(A%d, %c%d.%c*%d, $%02x) == $%08x"), reg,
                           dp & 0x8000 ? 'A' : 'D', (int)r, dp & 0x800 ? 'L' : 'W',
                           1 << ((dp >> 9) & 3), disp8, addr);
            }
            break;
        case PC16:
            disp16 = get_iword_debug (pc); pc += 2;
            addr += (uae_s16)disp16;
            _stprintf (buffer, _T("(PC,$%04x) == $%08x"), disp16 & 0xffff, addr);
            break;
        case PC8r:
            dp = get_iword_debug (pc); pc += 2;
            disp8 = dp & 0xFF;
            r = (dp & 0x7000) >> 12;
            dispreg = dp & 0x8000 ? m68k_areg (regs, r) : m68k_dreg (regs, r);
            if (!(dp & 0x800)) dispreg = (uae_s32)(uae_s16)(dispreg);
            dispreg <<= (dp >> 9) & 3;
            
            if (dp & 0x100) {
                uae_s32 outer = 0, disp = 0;
                uae_s32 base = addr;
                TCHAR name[10];
                _stprintf (name, _T("PC, "));
                if (dp & 0x80) { base = 0; name[0] = 0; }
                if (dp & 0x40) dispreg = 0;
                if ((dp & 0x30) == 0x20) { disp = (uae_s32)(uae_s16)get_iword_debug (pc); pc += 2; }
                if ((dp & 0x30) == 0x30) { disp = get_ilong_debug (pc); pc += 4; }
                base += disp;
                
                if ((dp & 0x3) == 0x2) { outer = (uae_s32)(uae_s16)get_iword_debug (pc); pc += 2; }
                if ((dp & 0x3) == 0x3) { outer = get_ilong_debug (pc); pc += 4; }
                
                if (!(dp & 4)) base += dispreg;
                if ((dp & 3) && !safemode) base = get_ilong_debug (base);
                if (dp & 4) base += dispreg;
                
                addr = base + outer;
                _stprintf (buffer, _T("(%s%c%d.%c*%d+%d)+%d == $%08x"), name,
                           dp & 0x8000 ? 'A' : 'D', (int)r, dp & 0x800 ? 'L' : 'W',
                           1 << ((dp >> 9) & 3),
                           disp, outer, addr);
            } else {
                addr += (uae_s32)((uae_s8)disp8) + dispreg;
                _stprintf (buffer, _T("(PC, %c%d.%c*%d, $%02x) == $%08x"), dp & 0x8000 ? 'A' : 'D',
                           (int)r, dp & 0x800 ? 'L' : 'W',  1 << ((dp >> 9) & 3),
                           disp8, addr);
            }
            break;
        case absw:
            addr = (uae_s32)(uae_s16)get_iword_debug (pc);
            _stprintf (buffer, _T("$%08x"), addr);
            pc += 2;
            break;
        case absl:
            addr = get_ilong_debug (pc);
            _stprintf (buffer, _T("$%08x"), addr);
            pc += 4;
            break;
        case imm:
            switch (size){
                case sz_byte:
                    _stprintf (buffer, _T("#$%02x"), (get_iword_debug (pc) & 0xff));
                    pc += 2;
                    break;
                case sz_word:
                    _stprintf (buffer, _T("#$%04x"), (get_iword_debug (pc) & 0xffff));
                    pc += 2;
                    break;
                case sz_long:
                    _stprintf(buffer, _T("#$%08x"), (get_ilong_debug(pc)));
                    pc += 4;
                    break;
                case sz_single:
                {
                    fptype fp;
                    to_single(&fp, get_ilong_debug(pc));
                    _stprintf(buffer, _T("#%s"), fp_print(&fp));
                    pc += 4;
                }
                    break;
                case sz_double:
                {
                    fptype fp;
                    to_double(&fp, get_ilong_debug(pc), get_ilong_debug(pc + 4));
                    _stprintf(buffer, _T("#%s"), fp_print(&fp));
                    pc += 8;
                }
                    break;
                case sz_extended:
                {
                    fptype fp;
                    to_exten(&fp, get_ilong_debug(pc), get_ilong_debug(pc + 4), get_ilong_debug(pc + 8));
                    _stprintf(buffer, _T("#%s"), fp_print(&fp));
                    pc += 12;
                    break;
                }
                case sz_packed:
                    _stprintf(buffer, _T("#$%08x%08x%08x"), get_ilong_debug(pc), get_ilong_debug(pc + 4), get_ilong_debug(pc + 8));
                    pc += 12;
                    break;
                default:
                    break;
            }
            break;
        case imm0:
            offset = (uae_s32)(uae_s8)get_iword_debug (pc);
            _stprintf (buffer, _T("#$%02x"), (uae_u32)(offset & 0xff));
            addr = pc + 2 + offset;
            pc += 2;
            break;
        case imm1:
            offset = (uae_s32)(uae_s16)get_iword_debug (pc);
            buffer[0] = 0;
            _stprintf (buffer, _T("#$%04x"), (uae_u32)(offset & 0xffff));
            addr = pc + offset;
            pc += 2;
            break;
        case imm2:
            offset = (uae_s32)get_ilong_debug (pc);
            _stprintf (buffer, _T("#$%08x"), (uae_u32)offset);
            addr = pc + offset;
            pc += 4;
            break;
        case immi:
            offset = (uae_s32)(uae_s8)(reg & 0xff);
            _stprintf (buffer, _T("#$%08x"), (uae_u32)offset);
            addr = pc + offset;
            break;
        default:
            break;
    }
    if (buf == 0)
        f_out (f, _T("%s"), buffer);
    else
        _tcscat (buf, buffer);
    if (eaddr)
        *eaddr = addr;
    return pc;
}

static void activate_trace(void)
{
    unset_special (SPCFLAG_TRACE);
    set_special (SPCFLAG_DOTRACE);
}

void check_t0_trace(void)
{
    if (regs.t0 && currprefs.cpu_model >= 68020) {
        unset_special (SPCFLAG_TRACE);
        set_special (SPCFLAG_DOTRACE);
    }
}

void REGPARAM2 MakeSR (void)
{
	regs.sr = ((regs.t1 << 15) | (regs.t0 << 14)
		| (regs.s << 13) | (regs.m << 12) | (regs.intmask << 8)
		| (GET_XFLG () << 4) | (GET_NFLG () << 3)
		| (GET_ZFLG () << 2) | (GET_VFLG () << 1)
		|  GET_CFLG ());
}

static void MakeFromSR_x(int t0trace)
{
	int oldm = regs.m;
	int olds = regs.s;
    int oldt0 = regs.t0;
    int oldt1 = regs.t1;

	SET_XFLG ((regs.sr >> 4) & 1);
	SET_NFLG ((regs.sr >> 3) & 1);
	SET_ZFLG ((regs.sr >> 2) & 1);
	SET_VFLG ((regs.sr >> 1) & 1);
	SET_CFLG (regs.sr & 1);
	if (regs.t1 == ((regs.sr >> 15) & 1) &&
		regs.t0 == ((regs.sr >> 14) & 1) &&
		regs.s  == ((regs.sr >> 13) & 1) &&
		regs.m  == ((regs.sr >> 12) & 1) &&
		regs.intmask == ((regs.sr >> 8) & 7))
		return;
	regs.t1 = (regs.sr >> 15) & 1;
	regs.t0 = (regs.sr >> 14) & 1;
	regs.s  = (regs.sr >> 13) & 1;
	regs.m  = (regs.sr >> 12) & 1;
	regs.intmask = (regs.sr >> 8) & 7;
	if (currprefs.cpu_model >= 68020) {
		if (olds != regs.s) {
			if (olds) {
				if (oldm)
					regs.msp = m68k_areg (regs, 7);
				else
					regs.isp = m68k_areg (regs, 7);
				m68k_areg (regs, 7) = regs.usp;
			} else {
				regs.usp = m68k_areg (regs, 7);
				m68k_areg (regs, 7) = regs.m ? regs.msp : regs.isp;
			}
		} else if (olds && oldm != regs.m) {
			if (oldm) {
				regs.msp = m68k_areg (regs, 7);
				m68k_areg (regs, 7) = regs.isp;
			} else {
				regs.isp = m68k_areg (regs, 7);
				m68k_areg (regs, 7) = regs.msp;
			}
		}
	} else {
		regs.t0 = regs.m = 0;
		if (olds != regs.s) {
			if (olds) {
				regs.isp = m68k_areg (regs, 7);
				m68k_areg (regs, 7) = regs.usp;
			} else {
				regs.usp = m68k_areg (regs, 7);
				m68k_areg (regs, 7) = regs.isp;
			}
		}
	}
	if (currprefs.mmu_model)
		mmu_set_super (regs.s != 0);

	doint ();
	if (regs.t1 || regs.t0) {
		set_special (SPCFLAG_TRACE);
	} else {
		/* Keep SPCFLAG_DOTRACE, we still want a trace exception for
		SR-modifying instructions (including STOP).  */
		unset_special (SPCFLAG_TRACE);
    }
    // Stop SR-modification does not generate T0
    // If this SR modification set Tx bit, no trace until next instruction.
    if ((oldt0 && t0trace && currprefs.cpu_model >= 68020) || oldt1) {
        // Always trace if Tx bits were already set, even if this SR modification cleared them.
        activate_trace();
    }
}

void REGPARAM2 MakeFromSR_T0(void)
{
    MakeFromSR_x(1);
}
void REGPARAM2 MakeFromSR(void)
{
    MakeFromSR_x(0);
    
}

static void exception_check_trace (int nr)
{
    unset_special (SPCFLAG_TRACE | SPCFLAG_DOTRACE);
    if (regs.t1 && !regs.t0) {
        /* trace stays pending if exception is div by zero, chk,
         * trapv or trap #x
         */
        if (nr == 5 || nr == 6 || nr == 7 || (nr >= 32 && nr <= 47))
            set_special (SPCFLAG_DOTRACE);
    }
    regs.t1 = regs.t0 = 0;
}

static void exception_debug (int nr)
{
#ifdef DEBUGGER
	if (!exception_debugging)
		return;
	printf ("Exception %d, PC=%08X\n", nr, M68K_GETPC);
    DebugUI();
#endif
}

void cpu_halt (int e)
{
	write_log("HALT!");
    DebugUI();
}

static void Exception_build_stack_frame (uae_u32 oldpc, uae_u32 currpc, uae_u32 ssw, int nr, int format)
{
    int i;
   
#if 0
    if (nr < 24 || nr > 31) { // do not print debugging for interrupts
        write_log(_T("Building exception stack frame (format %X)\n"), format);
    }
#endif

    switch (format) {
        case 0x0: // four word stack frame
        case 0x1: // throwaway four word stack frame
            break;
        case 0x2: // six word stack frame
            m68k_areg (regs, 7) -= 4;
            x_put_long (m68k_areg (regs, 7), oldpc);
            break;
        case 0x3: // floating point post-instruction stack frame (68040)
            m68k_areg (regs, 7) -= 4;
            x_put_long (m68k_areg (regs, 7), regs.fp_ea);
            break;
        case 0x7: // access error stack frame (68040)

			for (i = 3; i >= 0; i--) {
				// WB1D/PD0,PD1,PD2,PD3
                m68k_areg (regs, 7) -= 4;
                x_put_long (m68k_areg (regs, 7), mmu040_move16[i]);
			}

            m68k_areg (regs, 7) -= 4;
            x_put_long (m68k_areg (regs, 7), 0); // WB1A
			m68k_areg (regs, 7) -= 4;
            x_put_long (m68k_areg (regs, 7), 0); // WB2D
            m68k_areg (regs, 7) -= 4;
            x_put_long (m68k_areg (regs, 7), regs.wb2_address); // WB2A
			m68k_areg (regs, 7) -= 4;
            x_put_long (m68k_areg (regs, 7), regs.wb3_data); // WB3D
            m68k_areg (regs, 7) -= 4;
            x_put_long (m68k_areg (regs, 7), regs.mmu_fault_addr); // WB3A

			m68k_areg (regs, 7) -= 4;
            x_put_long (m68k_areg (regs, 7), regs.mmu_fault_addr); // FA
            
			m68k_areg (regs, 7) -= 2;
            x_put_word (m68k_areg (regs, 7), 0);
            m68k_areg (regs, 7) -= 2;
            x_put_word (m68k_areg (regs, 7), regs.wb2_status);
            regs.wb2_status = 0;
            m68k_areg (regs, 7) -= 2;
            x_put_word (m68k_areg (regs, 7), regs.wb3_status);
            regs.wb3_status = 0;

			m68k_areg (regs, 7) -= 2;
			x_put_word (m68k_areg (regs, 7), ssw);
            m68k_areg (regs, 7) -= 4;
            x_put_long (m68k_areg (regs, 7), regs.mmu_effective_addr);
            break;
        case 0x9: // coprocessor mid-instruction stack frame (68020, 68030)
            m68k_areg (regs, 7) -= 4;
            x_put_long (m68k_areg (regs, 7), regs.fp_ea);
            m68k_areg (regs, 7) -= 4;
            x_put_long (m68k_areg (regs, 7), regs.fp_opword);
            m68k_areg (regs, 7) -= 4;
            x_put_long (m68k_areg (regs, 7), oldpc);
            break;
        case 0x8: // bus and address error stack frame (68010)
            write_log(_T("Exception stack frame format %X not implemented\n"), format);
            return;
        case 0x4: // floating point unimplemented stack frame (68LC040, 68EC040)
				// or 68060 bus access fault stack frame
			m68k_areg (regs, 7) -= 4;
			x_put_long (m68k_areg (regs, 7), ssw);
			m68k_areg (regs, 7) -= 4;
			x_put_long (m68k_areg (regs, 7), oldpc);
			break;
		case 0xB: // long bus cycle fault stack frame (68020, 68030)
			// We always use B frame because it is easier to emulate,
			// our PC always points at start of instruction but A frame assumes
			// it is + 2 and handling this properly is not easy.
			// Store state information to internal register space
			for (i = 0; i < mmu030_idx + 1; i++) {
				m68k_areg (regs, 7) -= 4;
				x_put_long (m68k_areg (regs, 7), mmu030_ad[i].val);
			}
			while (i < 9) {
                uae_u32 v = 0;
				m68k_areg (regs, 7) -= 4;
                // mmu030_idx is always small enough if instruction is FMOVEM.
                if (mmu030_state[1] & MMU030_STATEFLAG1_FMOVEM) {
                    if (i == 7)
                        v = mmu030_fmovem_store[0];
                    else if (i == 8)
                        v = mmu030_fmovem_store[1];
                }
                x_put_long (m68k_areg (regs, 7), v);
                i++;
			}
			 // version & internal information (We store index here)
			m68k_areg (regs, 7) -= 2;
			x_put_word (m68k_areg (regs, 7), mmu030_idx);
			// 3* internal registers
			m68k_areg (regs, 7) -= 2;
			x_put_word (m68k_areg (regs, 7), mmu030_state[2]);
			m68k_areg (regs, 7) -= 2;
			x_put_word (m68k_areg (regs, 7), mmu030_state[1]);
			m68k_areg (regs, 7) -= 2;
			x_put_word (m68k_areg (regs, 7), mmu030_state[0]);
			// data input buffer = fault address
			m68k_areg (regs, 7) -= 4;
			x_put_long (m68k_areg (regs, 7), regs.mmu_fault_addr);
			// 2xinternal
			m68k_areg (regs, 7) -= 2;
			x_put_word (m68k_areg (regs, 7), 0);
			m68k_areg (regs, 7) -= 2;
			x_put_word (m68k_areg (regs, 7), 0);
			// stage b address
			m68k_areg (regs, 7) -= 4;
			x_put_long (m68k_areg (regs, 7), mm030_stageb_address);
			// 2xinternal
			m68k_areg (regs, 7) -= 4;
			x_put_long (m68k_areg (regs, 7), mmu030_disp_store[1]);
		/* fall through */
		case 0xA: // short bus cycle fault stack frame (68020, 68030)
			m68k_areg (regs, 7) -= 4;
			x_put_long (m68k_areg (regs, 7), mmu030_disp_store[0]);
			m68k_areg (regs, 7) -= 4;
			 // Data output buffer = value that was going to be written
			x_put_long (m68k_areg (regs, 7), (mmu030_state[1] & MMU030_STATEFLAG1_MOVEM1) ? mmu030_data_buffer : mmu030_ad[mmu030_idx].val);
			m68k_areg (regs, 7) -= 4;
			x_put_long (m68k_areg (regs, 7), mmu030_opcode);  // Internal register (opcode storage)
			m68k_areg (regs, 7) -= 4;
			x_put_long (m68k_areg (regs, 7), regs.mmu_fault_addr); // data cycle fault address
			m68k_areg (regs, 7) -= 2;
			x_put_word (m68k_areg (regs, 7), 0);  // Instr. pipe stage B
			m68k_areg (regs, 7) -= 2;
			x_put_word (m68k_areg (regs, 7), 0);  // Instr. pipe stage C
			m68k_areg (regs, 7) -= 2;
			x_put_word (m68k_areg (regs, 7), ssw);
			m68k_areg (regs, 7) -= 2;
			x_put_word (m68k_areg (regs, 7), 0);  // Internal register
			break;
		default:
            write_log(_T("Unknown exception stack frame format: %X\n"), format);
            return;
    }
    m68k_areg (regs, 7) -= 2;
    x_put_word (m68k_areg (regs, 7), (format << 12) | (nr * 4));
    m68k_areg (regs, 7) -= 4;
    x_put_long (m68k_areg (regs, 7), currpc);
    m68k_areg (regs, 7) -= 2;
    x_put_word (m68k_areg (regs, 7), regs.sr);
}

// 68030 MMU
static void Exception_mmu030 (int nr, uaecptr oldpc)
{
    uae_u32 currpc = m68k_getpc (), newpc;
    
    exception_debug (nr);
    MakeSR ();
    
    if (!regs.s) {
        regs.usp = m68k_areg (regs, 7);
        m68k_areg(regs, 7) = regs.m ? regs.msp : regs.isp;
        regs.s = 1;
        mmu_set_super (1);
    }
 
#if 0
    if (nr < 24 || nr > 31) { // do not print debugging for interrupts
        write_log (_T("Exception_mmu030: Exception %i: %08x %08x %08x\n"),
                   nr, currpc, oldpc, regs.mmu_fault_addr);
    }
#endif

#if 0
	write_log (_T("Exception %d -> %08x\n", nr, newpc));
#endif


    newpc = x_get_long (regs.vbr + 4 * nr);

	if (regs.m && nr >= 24 && nr < 32) { /* M + Interrupt */
        Exception_build_stack_frame(oldpc, currpc, regs.mmu_ssw, nr, 0x0);
        MakeSR(); /* this sets supervisor bit in status reg */
        regs.m = 0; /* clear the M bit (but frame 0x1 uses sr with M bit set) */
        regs.msp = m68k_areg (regs, 7);
        m68k_areg (regs, 7) = regs.isp;
        Exception_build_stack_frame (oldpc, currpc, regs.mmu_ssw, nr, 0x1);
    } else if (nr ==5 || nr == 6 || nr == 7 || nr == 9 || nr == 56) {
        Exception_build_stack_frame (oldpc, currpc, regs.mmu_ssw, nr, 0x2);
    } else if (nr == 2) {
        Exception_build_stack_frame (oldpc, currpc, regs.mmu_ssw, nr,  0xB);
    } else if (nr == 3) {
		regs.mmu_fault_addr = last_fault_for_exception_3;
		mmu030_state[0] = mmu030_state[1] = 0;
		mmu030_data_buffer = 0;
        Exception_build_stack_frame (last_fault_for_exception_3, currpc, MMU030_SSW_RW | MMU030_SSW_SIZE_W | (regs.s ? 6 : 2), nr,  0xA);
    } else if (nr >= 48 && nr < 55) {
        if (regs.fpu_exp_pre) {
            Exception_build_stack_frame(oldpc, regs.instruction_pc, 0, nr, 0x0);
        } else { /* mid-instruction */
            Exception_build_stack_frame(regs.fpiar, currpc, 0, nr, 0x9);
        }
    } else {
        Exception_build_stack_frame (oldpc, currpc, regs.mmu_ssw, nr, 0x0);
    }
    
	if (newpc & 1) {
		if (nr == 2 || nr == 3)
			cpu_halt (2);
		else
			exception3_read(regs.ir, newpc);
		return;
	}
	m68k_setpci (newpc);
	exception_check_trace (nr);
}


// 68040 MMU
static void Exception_mmu (int nr, uaecptr oldpc)
{
	uae_u32 currpc = m68k_getpc (), newpc;

	exception_debug (nr);
	MakeSR ();

	if (!regs.s) {
		regs.usp = m68k_areg (regs, 7);
        if (currprefs.cpu_model >= 68020 && currprefs.cpu_model < 68060) {
			m68k_areg (regs, 7) = regs.m ? regs.msp : regs.isp;
		} else {
			m68k_areg (regs, 7) = regs.isp;
		}
		regs.s = 1;
		mmu_set_super (1);
	}
    
	newpc = x_get_long (regs.vbr + 4 * nr);
#if 0
	write_log (_T("Exception %d: %08x -> %08x\n"), nr, currpc, newpc);
#endif

	if (nr == 2) { // bus error
        //write_log (_T("Exception_mmu %08x %08x %08x\n"), currpc, oldpc, regs.mmu_fault_addr);
        if (currprefs.mmu_model == 68040)
			Exception_build_stack_frame(oldpc, currpc, regs.mmu_ssw, nr, 0x7);
		else
			Exception_build_stack_frame(regs.mmu_fault_addr, currpc, regs.mmu_fslw, nr, 0x4);
	} else if (nr == 3) { // address error
        Exception_build_stack_frame(last_fault_for_exception_3, currpc, 0, nr, 0x2);
		write_log (_T("Exception %d (%x) at %x!\n"), nr, last_fault_for_exception_3, currpc);
	} else if (nr == 5 || nr == 6 || nr == 7 || nr == 9) {
        Exception_build_stack_frame(oldpc, currpc, regs.mmu_ssw, nr, 0x2);
	} else if (regs.m && nr >= 24 && nr < 32) { /* M + Interrupt */
        Exception_build_stack_frame(oldpc, currpc, regs.mmu_ssw, nr, 0x0);
        MakeSR(); /* this sets supervisor bit in status reg */
        regs.m = 0; /* clear the M bit (but frame 0x1 uses sr with M bit set) */
        regs.msp = m68k_areg (regs, 7);
        m68k_areg (regs, 7) = regs.isp;
        Exception_build_stack_frame (oldpc, currpc, regs.mmu_ssw, nr, 0x1);
	} else if (nr == 60 || nr == 61) {
        Exception_build_stack_frame(oldpc, regs.instruction_pc, regs.mmu_ssw, nr, 0x0);
    } else if (nr >= 48 && nr <= 55) {
        if (regs.fpu_exp_pre) {
            if (currprefs.cpu_model == 68060 && nr == 55 && regs.fp_unimp_pend == 2) { // packed decimal real
                Exception_build_stack_frame(regs.fp_ea, regs.instruction_pc, 0, nr, 0x2);
            } else {
                Exception_build_stack_frame(oldpc, regs.instruction_pc, 0, nr, 0x0);
            }
        } else { /* post-instruction */
            if (currprefs.cpu_model == 68060 && nr == 55 && regs.fp_unimp_pend == 2) { // packed decimal real
                Exception_build_stack_frame(regs.fp_ea, currpc, 0, nr, 0x2);
            } else {
                Exception_build_stack_frame(oldpc, currpc, 0, nr, 0x3);
            }
        }
    } else if (nr == 11 && regs.fp_unimp_ins) {
        regs.fp_unimp_ins = false;
        if ((currprefs.cpu_model == 68060 && (currprefs.fpu_model == 0 || (regs.pcr & 2))) ||
            (currprefs.cpu_model == 68040 && currprefs.fpu_model == 0)) {
            Exception_build_stack_frame(regs.fp_ea, currpc, regs.instruction_pc, nr, 0x4);
        } else {
            Exception_build_stack_frame(regs.fp_ea, currpc, regs.mmu_ssw, nr, 0x2);
        }
	} else {
        Exception_build_stack_frame(oldpc, currpc, regs.mmu_ssw, nr, 0x0);
	}
    
	if (newpc & 1) {
		if (nr == 2 || nr == 3)
			cpu_halt (2);
		else
			exception3_read (regs.ir, newpc);
		return;
	}
	m68k_setpci (newpc);
	exception_check_trace (nr);
}


/* Handle exceptions. */
static void ExceptionX (int nr, uaecptr address)
{
    if (currprefs.cpu_model == 68030)
        Exception_mmu030 (nr, m68k_getpc ());
    else
        Exception_mmu (nr, m68k_getpc ());
}

void REGPARAM2 Exception_cpu(int nr)
{
    bool t0 = currprefs.cpu_model >= 68020 && regs.t0;
    ExceptionX (nr, -1);
    // check T0 trace
    if (t0) {
        activate_trace();
    }
}
void REGPARAM2 Exception (int nr)
{
    ExceptionX (nr, -1);
}
void REGPARAM2 ExceptionL (int nr, uaecptr address)
{
    ExceptionX (nr, address);
}

static void do_interrupt (int nr, int Pending)
{
	regs.stopped = 0;
	unset_special (SPCFLAG_STOP);
	assert (nr < 8 && nr >= 0);

	/* On Hatari, only video ints are using SPCFLAG_INT (see m68000.c) */
	Exception (nr + 24);

	regs.intmask = nr;
	doint ();

	set_special (SPCFLAG_INT);
}


void m68k_reset (int hardreset)
{
    regs.spcflags &= (SPCFLAG_MODE_CHANGE | SPCFLAG_BRK);
	regs.ipl = regs.ipl_pin = 0;
	regs.s = 1;
	regs.m = 0;
	regs.stopped = 0;
	regs.t1 = 0;
	regs.t0 = 0;
	SET_ZFLG (0);
	SET_XFLG (0);
	SET_CFLG (0);
	SET_VFLG (0);
	SET_NFLG (0);
	regs.intmask = 7;
	regs.vbr = regs.sfc = regs.dfc = 0;
	regs.irc = 0xffff;
	
	m68k_areg (regs, 7) = get_long (0);
	m68k_setpc (get_long (4));

	fpu_reset ();

	regs.caar = regs.cacr = 0;
	regs.itt0 = regs.itt1 = regs.dtt0 = regs.dtt1 = 0;
	regs.tcr = regs.mmusr = regs.urp = regs.srp = regs.buscr = 0;

	mmufixup[0].reg = -1;
	mmufixup[1].reg = -1;
	if (currprefs.mmu_model) {
        if (currprefs.cpu_model >= 68040) {
            mmu_reset ();
            mmu_set_tc (regs.tcr);
            mmu_set_super (regs.s != 0);
        }
        else {
            mmu030_reset(hardreset);
        }
    }

	regs.pcr = 0;
}

void REGPARAM2 op_unimpl (uae_u16 opcode)
{
    static int warned;
    if (warned < 20) {
        write_log (_T("68060 unimplemented opcode %04X, PC=%08x\n"), opcode, regs.instruction_pc);
        warned++;
    }
    ExceptionL (61, regs.instruction_pc);
}

uae_u32 REGPARAM2 op_illg (uae_u32 opcode)
{
	uaecptr pc = m68k_getpc ();
	static int warned;

	if (warned < 20) {
		write_log ("Illegal instruction: %04x at %08X\n", opcode, pc);
		warned++;
	}

	if ((opcode & 0xF000)==0xA000)
		Exception (10);
	else 
	if ((opcode & 0xF000)==0xF000)
		Exception (11);
	else
		Exception (4);
	return 4;
}

#ifdef CPUEMU_0

// 68030 (68851) MMU instructions only
bool mmu_op30 (uaecptr pc, uae_u32 opcode, uae_u16 extra, uaecptr extraa)
{
    bool fline = false;
    
    if (extra & 0x8000) {
        fline = mmu_op30_ptest (pc, opcode, extra, extraa);
    } else if ((extra&0xE000)==0x2000 && (extra & 0x1C00)) {
        fline = mmu_op30_pflush (pc, opcode, extra, extraa);
    } else if ((extra&0xE000)==0x2000 && !(extra & 0x1C00)) {
        fline = mmu_op30_pload (pc, opcode, extra, extraa);
    } else {
        fline = mmu_op30_pmove (pc, opcode, extra, extraa);
    }
    
    if (fline) {
        m68k_setpc(pc);
        op_illg(opcode);
    }
    return fline;
}

void mmu_op (uae_u32 opcode, uae_u32 extra)
{
	if (currprefs.cpu_model) {
		mmu_op_real (opcode, extra);
		return;
	}
#if MMUOP_DEBUG > 1
	write_log ("mmu_op %04X PC=%08X\n", opcode, m68k_getpc ());
#endif
	if ((opcode & 0xFE0) == 0x0500) {
		/* PFLUSH */
		regs.mmusr = 0;
#if MMUOP_DEBUG > 0
		write_log ("PFLUSH\n");
#endif
		return;
	} else if ((opcode & 0x0FD8) == 0x548) {
		if (currprefs.cpu_model < 68060) { /* PTEST not in 68060 */
			/* PTEST */
#if MMUOP_DEBUG > 0
			write_log ("PTEST\n");
#endif
			return;
		}
	}
#if MMUOP_DEBUG > 0
	write_log ("Unknown MMU OP %04X\n", opcode);
#endif
	m68k_setpc (m68k_getpc () - 2);
	op_illg (opcode);
}

#endif

static void do_trace (void)
{
	if (regs.t0 && currprefs.cpu_model >= 68020) {
        // this is obsolete
        return;
    }
    if (regs.t1) {
        activate_trace();
    }
}

void doint (void)
{
	if (currprefs.cpu_compatible)
		set_special (SPCFLAG_INT);
	else
		set_special (SPCFLAG_DOINT);
}

#define IDLETIME (currprefs.cpu_idle * sleep_resolution / 700)

/*
 * Handle special flags
 */

static int vpos = 0;

static int do_specialties (int cycles)
{
	if (regs.spcflags & SPCFLAG_DOTRACE)
		Exception (9);

    /* Handle the STOP instruction */
    if ( regs.spcflags & SPCFLAG_STOP ) {
        while (regs.spcflags & SPCFLAG_STOP) {

            /* Take care of quit event if needed */
            if (regs.spcflags & SPCFLAG_BRK)
                return 1;
        
            M68000_AddCycles(cpu_cycles);

            /* It is possible one or more ints happen at the same time */
            /* We must process them during the same cpu cycle until the special INT flag is set */
            while (PendingInterrupt.time <=0 && PendingInterrupt.pFunction) {
                /* 1st, we call the interrupt handler */
                CALL_VAR(PendingInterrupt.pFunction);
                
                if ((regs.spcflags & (SPCFLAG_BRK | SPCFLAG_MODE_CHANGE))) {
                    unset_special (SPCFLAG_BRK | SPCFLAG_MODE_CHANGE);
                    // SPCFLAG_BRK breaks STOP condition, need to prefetch
                    m68k_resumestopped ();
                    return 1;
                }

                if (currprefs.cpu_idle && ((regs.spcflags & SPCFLAG_STOP)) == SPCFLAG_STOP) {
                    /* sleep 1ms if STOP-instruction is executed */
                    if (1) {
                        static int sleepcnt, lvpos;
                        if (vpos != lvpos) {
                            sleepcnt--;
                            lvpos = vpos;
                            if (sleepcnt < 0) {
                                    /*sleepcnt = IDLETIME / 2; */  /* Laurent : badly removed for now */
                                host_sleep_ms(1);
                            }
                        }
                    }
                }
            }
        }
	}

	if (regs.spcflags & SPCFLAG_TRACE)
		do_trace ();

	if (regs.spcflags & SPCFLAG_DOINT) {
		unset_special (SPCFLAG_DOINT);
		set_special (SPCFLAG_INT);
	}

    if (regs.spcflags & SPCFLAG_DEBUGGER)
		DebugCpu_Check();

	if ((regs.spcflags & (SPCFLAG_BRK | SPCFLAG_MODE_CHANGE))) {
		unset_special(SPCFLAG_MODE_CHANGE);
		return 1;
	}
    
	return 0;
}

#ifndef CPUEMU_0

#else

static int lastRegsS = 0;

// Previous MMU 68030
static void m68k_run_mmu030 (void)
{
	uae_u16 opcode;
	uaecptr pc;
	struct flag_struct f;
	f.cznv = 0;
	f.x    = 0;
    int intr             = 0;
    int lastintr         = 0;
	mmu030_opcode_stageb = -1;
retry:
	TRY (prb) {
		for (;;) {
			int cnt;
insretry:
			pc = regs.instruction_pc = m68k_getpc ();
			f.cznv = regflags.cznv;
			f.x    = regflags.x;
            
			mmu030_state[0] = mmu030_state[1] = mmu030_state[2] = 0;
			mmu030_opcode = -1;
			if (mmu030_opcode_stageb < 0) {
				opcode = get_iword_mmu030 (0);
			} else {
				opcode = mmu030_opcode_stageb;
				mmu030_opcode_stageb = -1;
			}

			mmu030_opcode = opcode;
			mmu030_ad[0].done = false;

            Uint64 beforeCycles = nCyclesMainCounter;
			cnt = 50;
			for (;;) {
				opcode = mmu030_opcode;
				mmu030_idx = 0;
				mmu030_retry = false;
				cpu_cycles = (*cpufunctbl[opcode])(opcode);
				cnt--; // so that we don't get in infinite loop if things go horribly wrong
				if (!mmu030_retry)
					break;
				if (cnt < 0) {
					cpu_halt (9);
					break;
				}
				if (mmu030_retry && mmu030_opcode == -1)
					goto insretry; // urgh
			}

			mmu030_opcode = -1;
            
            M68000_AddCycles(cpu_cycles);
            cpu_cycles = nCyclesMainCounter - beforeCycles;
            
			DSP_Run(cpu_cycles);
            i860_Run(cpu_cycles);

			/* We can have several interrupts at the same time before the next CPU instruction */
			/* We must check for pending interrupt and call do_specialties_interrupt() only */
			/* if the cpu is not in the STOP state. Else, the int could be acknowledged now */
			/* and prevent exiting the STOP state when calling do_specialties() after. */
			/* For performance, we first test PendingInterruptCount, then regs.spcflags */
			while ( ( PendingInterrupt.time <= 0 ) && ( PendingInterrupt.pFunction ) && ( ( regs.spcflags & SPCFLAG_STOP ) == 0 ) ) {
				CALL_VAR(PendingInterrupt.pFunction);		/* call the interrupt handler */
			}

            /* Previous: for now we poll the interrupt pins with every instruction.
             * TODO: only do this when an actual interrupt is active to not
             * unneccessarily slow down emulation.
             */
            intr = intlev ();
            if (intr>regs.intmask || (intr==7 && intr>lastintr))
                do_interrupt (intr, false);
            lastintr = intr;
            
            if(lastRegsS != regs.s) {
                host_realtime(!(regs.s));
                lastRegsS = regs.s;
            }

            if (regs.spcflags & ~SPCFLAG_INT) {
				if (do_specialties (cpu_cycles))
					return;
			}
		}
	} CATCH (prb) {

		regflags.cznv = f.cznv;
		regflags.x    = f.x;

		m68k_setpci (regs.instruction_pc);

		if (mmufixup[0].reg >= 0) {
			m68k_areg (regs, mmufixup[0].reg) = mmufixup[0].value;
			mmufixup[0].reg = -1;
		}
		if (mmufixup[1].reg >= 0) {
			m68k_areg (regs, mmufixup[1].reg) = mmufixup[1].value;
			mmufixup[1].reg = -1;
		}

		TRY (prb2) {
			Exception (prb);
		} CATCH (prb2) {
            write_log("[FATAL] double fault\n");
            m68k_reset (1); // auto-reset CPU
			//cpu_halt (1);
			return;
		} ENDTRY
	} ENDTRY
    goto retry;
}

/* Aranym MMU 68040  */
static void m68k_run_mmu040 (void)
{
	uae_u16 opcode;
	struct flag_struct f;
	f.cznv = 0;
	f.x    = 0;
	uaecptr pc;
    int intr = 0;
    int lastintr = 0;
	
	for (;;) {
	TRY (prb) {
		for (;;) {
			f.cznv = regflags.cznv;
			f.x = regflags.x;
			mmu_restart = true;
			pc = regs.instruction_pc = m68k_getpc ();
        
            Uint64 beforeCycles = nCyclesMainCounter;
			mmu_opcode = -1;
			mmu_opcode = opcode = x_prefetch (0);
			cpu_cycles = (*cpufunctbl[opcode])(opcode);
            M68000_AddCycles(cpu_cycles);
            
            cpu_cycles = nCyclesMainCounter - beforeCycles;

			DSP_Run(cpu_cycles);
            i860_Run(cpu_cycles);

			/* We can have several interrupts at the same time before the next CPU instruction */
			/* We must check for pending interrupt and call do_specialties_interrupt() only */
			/* if the cpu is not in the STOP state. Else, the int could be acknowledged now */
			/* and prevent exiting the STOP state when calling do_specialties() after. */
			/* For performance, we first test PendingInterruptCount, then regs.spcflags */
			while ( ( PendingInterrupt.time <= 0 ) && ( PendingInterrupt.pFunction ) && ( ( regs.spcflags & SPCFLAG_STOP ) == 0 ) ) {
				CALL_VAR(PendingInterrupt.pFunction);		/* call the interrupt handler */
			}

            /* Previous: for now we poll the interrupt pins with every instruction.
             * TODO: only do this when an actual interrupt is active to not
             * unneccessarily slow down emulation.
             */
            intr = intlev ();
            if (intr>regs.intmask || (intr==7 && intr>lastintr))
                do_interrupt (intr, false);
            lastintr = intr;
            
            if(lastRegsS != regs.s) {
                host_realtime(!(regs.s));
                lastRegsS = regs.s;
            }
            
			if (regs.spcflags & ~SPCFLAG_INT) {
				if (do_specialties (cpu_cycles))
					return;
			}
		} // end of for(;;)
	} CATCH (prb) {

		if (mmu_restart) {
			/* restore state if instruction restart */
			regflags.cznv = f.cznv;
			regflags.x = f.x;
			m68k_setpci (regs.instruction_pc);
		}

		if (mmufixup[0].reg >= 0) {
			m68k_areg (regs, mmufixup[0].reg) = mmufixup[0].value;
			mmufixup[0].reg = -1;
		}

		TRY (prb2) {
			Exception (prb);
		} CATCH (prb2) {
            write_log("[FATAL] double fault\n");
            m68k_reset (1); // auto-reset CPU
            //cpu_halt (1);
            return;
		} ENDTRY

	} ENDTRY
	}
}

#endif /* CPUEMU_0 */

int in_m68k_go = 0;

#if 0
static void exception2_handle (uaecptr addr, uaecptr fault)
{
	last_addr_for_exception_3 = addr;
	last_fault_for_exception_3 = fault;
	last_writeaccess_for_exception_3 = 0;
	last_instructionaccess_for_exception_3 = 0;
	Exception (2);
}
#endif

void m68k_go (int may_quit)
{
	int hardboot = 1;

	if (in_m68k_go || !may_quit) {
		write_log ("Bug! m68k_go is not reentrant.\n");
        DebugUI();
	}
    
	in_m68k_go++;
	for (;;) {
		void (*run_func)(void);
		
		if (regs.spcflags & SPCFLAG_BRK) {
			unset_special(SPCFLAG_BRK);
				break;
		}

			hardboot = 0;

#ifdef DEBUGGER
		if (debugging)
			debug ();
#endif
        
	set_x_funcs ();
        run_func=currprefs.cpu_model == 68040 ? m68k_run_mmu040 : m68k_run_mmu030;
		run_func ();
	}
	in_m68k_go--;
}


#if 1
TCHAR* buf_out (TCHAR *buffer, int *bufsize, const TCHAR *format, ...)
{
    va_list parms;
    
    if (buffer == NULL)
    {
        return NULL;
    }
    
    va_start (parms, format);
    vsnprintf (buffer, (*bufsize) - 1, format, parms);
    va_end (parms);
    *bufsize -= _tcslen (buffer);
    
    return buffer + _tcslen (buffer);
}
#endif

static const TCHAR *ccnames[] =
{
    _T("T "),_T("F "),_T("HI"),_T("LS"),_T("CC"),_T("CS"),_T("NE"),_T("EQ"),
    _T("VC"),_T("VS"),_T("PL"),_T("MI"),_T("GE"),_T("LT"),_T("GT"),_T("LE")
};
static const TCHAR *fpccnames[] =
{
    _T("F"),
    _T("EQ"),
    _T("OGT"),
    _T("OGE"),
    _T("OLT"),
    _T("OLE"),
    _T("OGL"),
    _T("OR"),
    _T("UN"),
    _T("UEQ"),
    _T("UGT"),
    _T("UGE"),
    _T("ULT"),
    _T("ULE"),
    _T("NE"),
    _T("T"),
    _T("SF"),
    _T("SEQ"),
    _T("GT"),
    _T("GE"),
    _T("LT"),
    _T("LE"),
    _T("GL"),
    _T("GLE"),
    _T("NGLE"),
    _T("NGL"),
    _T("NLE"),
    _T("NLT"),
    _T("NGE"),
    _T("NGT"),
    _T("SNE"),
    _T("ST")
};
static const TCHAR *fpuopcodes[] =
{
    _T("FMOVE"),
    _T("FINT"),
    _T("FSINH"),
    _T("FINTRZ"),
    _T("FSQRT"),
    NULL,
    _T("FLOGNP1"),
    NULL,
    _T("FETOXM1"),
    _T("FTANH"),
    _T("FATAN"),
    NULL,
    _T("FASIN"),
    _T("FATANH"),
    _T("FSIN"),
    _T("FTAN"),
    _T("FETOX"),	// 0x10
    _T("FTWOTOX"),
    _T("FTENTOX"),
    NULL,
    _T("FLOGN"),
    _T("FLOG10"),
    _T("FLOG2"),
    NULL,
    _T("FABS"),
    _T("FCOSH"),
    _T("FNEG"),
    NULL,
    _T("FACOS"),
    _T("FCOS"),
    _T("FGETEXP"),
    _T("FGETMAN"),
    _T("FDIV"),		// 0x20
    _T("FMOD"),
    _T("FADD"),
    _T("FMUL"),
    _T("FSGLDIV"),
    _T("FREM"),
    _T("FSCALE"),
    _T("FSGLMUL"),
    _T("FSUB"),
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    _T("FSINCOS"),	// 0x30
    _T("FSINCOS"),
    _T("FSINCOS"),
    _T("FSINCOS"),
    _T("FSINCOS"),
    _T("FSINCOS"),
    _T("FSINCOS"),
    _T("FSINCOS"),
    _T("FCMP"),
    NULL,
    _T("FTST"),
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
};

static const TCHAR *movemregs[] =
{
    _T("D0"),
    _T("D1"),
    _T("D2"),
    _T("D3"),
    _T("D4"),
    _T("D5"),
    _T("D6"),
    _T("D7"),
    _T("A0"),
    _T("A1"),
    _T("A2"),
    _T("A3"),
    _T("A4"),
    _T("A5"),
    _T("A6"),
    _T("A7"),
    _T("FP0"),
    _T("FP1"),
    _T("FP2"),
    _T("FP3"),
    _T("FP4"),
    _T("FP5"),
    _T("FP6"),
    _T("FP7"),
    _T("FPIAR"),
    _T("FPSR"),
    _T("FPCR")
};

static void addmovemreg (TCHAR *out, int *prevreg, int *lastreg, int *first, int reg, int fpmode)
{
    TCHAR *p = out + _tcslen (out);
    if (*prevreg < 0) {
        *prevreg = reg;
        *lastreg = reg;
        return;
    }
    if (reg < 0 || fpmode == 2 || (*prevreg) + 1 != reg || (reg & 8) != ((*prevreg & 8))) {
        _stprintf (p, _T("%s%s"), (*first) ? _T("") : _T("/"), movemregs[*lastreg]);
        p = p + _tcslen (p);
        if (*lastreg != *prevreg) {
            if ((*lastreg) + 2 == reg) {
                _stprintf(p, _T("/%s"), movemregs[*prevreg]);
            } else if ((*lastreg) != (*prevreg)) {
                _stprintf(p, _T("-%s"), movemregs[*prevreg]);
            }
        }
        *lastreg = reg;
        *first = 0;
    }
    *prevreg = reg;
}

static void movemout (TCHAR *out, uae_u16 mask, int mode, int fpmode)
{
    unsigned int dmask, amask;
    int prevreg = -1, lastreg = -1, first = 1;
    int i;
    
    if (mode == Apdi && !fpmode) {
        uae_u8 dmask2;
        uae_u8 amask2;
        
        amask2 = mask & 0xff;
        dmask2 = (mask >> 8) & 0xff;
        dmask = 0;
        amask = 0;
        for (i = 0; i < 8; i++) {
            if (dmask2 & (1 << i))
                dmask |= 1 << (7 - i);
            if (amask2 & (1 << i))
                amask |= 1 << (7 - i);
        }
    } else {
        dmask = mask & 0xff;
        amask = (mask >> 8) & 0xff;
        if (fpmode == 1 && mode != Apdi) {
            uae_u8 dmask2 = dmask;
            dmask = 0;
            for (i = 0; i < 8; i++) {
                if (dmask2 & (1 << i))
                    dmask |= 1 << (7 - i);
            }
        }
    }
    if (fpmode) {
        while (dmask) { addmovemreg(out, &prevreg, &lastreg, &first, movem_index1[dmask] + (fpmode == 2 ? 24 : 16), fpmode); dmask = movem_next[dmask]; }
    } else {
        while (dmask) { addmovemreg (out, &prevreg, &lastreg, &first, movem_index1[dmask], fpmode); dmask = movem_next[dmask]; }
        while (amask) { addmovemreg (out, &prevreg, &lastreg, &first, movem_index1[amask] + 8, fpmode); amask = movem_next[amask]; }
    }
    addmovemreg(out, &prevreg, &lastreg, &first, -1, fpmode);
}

static const TCHAR *fpsizes[] = {
    _T("L"),
    _T("S"),
    _T("X"),
    _T("P"),
    _T("W"),
    _T("D"),
    _T("B"),
    _T("P")
};
static const int fpsizeconv[] = {
    sz_long,
    sz_single,
    sz_extended,
    sz_packed,
    sz_word,
    sz_double,
    sz_byte,
    sz_packed
};

static void disasm_size (TCHAR *instrname, struct instr *dp)
{
    if (dp->unsized) {
        _tcscat(instrname, _T(" "));
        return;
    }
    switch (dp->size)
    {
        case sz_byte:
            _tcscat (instrname, _T(".B "));
            break;
        case sz_word:
            _tcscat (instrname, _T(".W "));
            break;
        case sz_long:
            _tcscat (instrname, _T(".L "));
            break;
        default:
            _tcscat (instrname, _T(" "));
            break;
    }
}

void m68k_disasm_2 (TCHAR *buf, int bufsize, uaecptr pc, uaecptr *nextpc, int cnt, uae_u32 *seaddr, uae_u32 *deaddr, int safemode)
{
    uae_u32 seaddr2;
    uae_u32 deaddr2;
    
    if (buf)
        memset (buf, 0, bufsize * sizeof (TCHAR));
    if (!table68k)
        return;
    while (cnt-- > 0) {
        TCHAR instrname[100], *ccpt;
        int i;
        uae_u32 opcode;
        uae_u16 extra;
        struct mnemolookup *lookup;
        struct instr *dp;
        uaecptr oldpc;
        uaecptr m68kpc_illg = 0;
        bool illegal = false;
        
        seaddr2 = deaddr2 = 0;
        oldpc = pc;
        opcode = get_word_debug (pc);
        extra = get_word_debug (pc + 2);
        if (cpufunctbl[opcode] == op_illg_1 || cpufunctbl[opcode] == op_unimpl_1) {
            m68kpc_illg = pc + 2;
            illegal = true;
        }
        
        dp = table68k + opcode;
        if (dp->mnemo == i_ILLG) {
            illegal = false;
            opcode = 0x4AFC;
            dp = table68k + opcode;
        }
        for (lookup = lookuptab;lookup->mnemo != dp->mnemo; lookup++)
            ;
        
        buf = buf_out (buf, &bufsize, _T("%08X "), pc);
        
        pc += 2;
        
        if (lookup->friendlyname)
            _tcscpy (instrname, lookup->friendlyname);
        else
            _tcscpy (instrname, lookup->name);
        ccpt = _tcsstr (instrname, _T("cc"));
        if (ccpt != 0) {
            if ((opcode & 0xf000) == 0xf000)
                _tcscpy (ccpt, fpccnames[extra & 0x1f]);
            else
                _tcsncpy (ccpt, ccnames[dp->cc], 2);
        }
        disasm_size (instrname, dp);
        
        if (lookup->mnemo == i_MOVEC2 || lookup->mnemo == i_MOVE2C) {
            uae_u16 imm = extra;
            uae_u16 creg = imm & 0x0fff;
            uae_u16 r = imm >> 12;
            TCHAR regs[16];
            const TCHAR *cname = _T("?");
            int i;
            for (i = 0; m2cregs[i].regname; i++) {
                if (m2cregs[i].regno == creg)
                    break;
            }
            _stprintf (regs, _T("%c%d"), r >= 8 ? 'A' : 'D', r >= 8 ? r - 8 : r);
            if (m2cregs[i].regname)
                cname = m2cregs[i].regname;
            if (lookup->mnemo == i_MOVE2C) {
                _tcscat (instrname, regs);
                _tcscat (instrname, _T(","));
                _tcscat (instrname, cname);
            } else {
                _tcscat (instrname, cname);
                _tcscat (instrname, _T(","));
                _tcscat (instrname, regs);
            }
            pc += 2;
        } else if (lookup->mnemo == i_MVMEL) {
            uae_u16 mask = extra;
            pc += 2;
            pc = ShowEA (0, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, deaddr, safemode);
            _tcscat (instrname, _T(","));
            movemout (instrname, mask, dp->dmode, 0);
        } else if (lookup->mnemo == i_MVMLE) {
            uae_u16 mask = extra;
            pc += 2;
            movemout(instrname, mask, dp->dmode, 0);
            _tcscat(instrname, _T(","));
            pc = ShowEA(0, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, deaddr, safemode);
        } else if (lookup->mnemo == i_DIVL || lookup->mnemo == i_MULL) {
            TCHAR *p;
            pc = ShowEA(0, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &seaddr2, safemode);
            extra = get_word_debug(pc);
            pc += 2;
            p = instrname + _tcslen(instrname);
            if (extra & 0x0400)
                _stprintf(p, _T(",D%d:D%d"), extra & 7, (extra >> 12) & 7);
            else
                _stprintf(p, _T(",D%d"), (extra >> 12) & 7);
        } else if (lookup->mnemo == i_MOVES) {
            TCHAR *p;
            pc += 2;
            if (!(extra & 0x1000)) {
                pc = ShowEA(0, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &seaddr2, safemode);
                p = instrname + _tcslen(instrname);
                _stprintf(p, _T(",%c%d"), (extra & 0x8000) ? 'A' : 'D', (extra >> 12) & 7);
            } else {
                p = instrname + _tcslen(instrname);
                _stprintf(p, _T("%c%d,"), (extra & 0x8000) ? 'A' : 'D', (extra >> 12) & 7);
                pc = ShowEA(0, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &seaddr2, safemode);
            }
        } else if (lookup->mnemo == i_BFEXTS || lookup->mnemo == i_BFEXTU ||
                   lookup->mnemo == i_BFCHG || lookup->mnemo == i_BFCLR ||
                   lookup->mnemo == i_BFFFO || lookup->mnemo == i_BFINS ||
                   lookup->mnemo == i_BFSET || lookup->mnemo == i_BFTST) {
            TCHAR *p;
            int reg = -1;
            
            pc += 2;
            p = instrname + _tcslen(instrname);
            if (lookup->mnemo == i_BFEXTS || lookup->mnemo == i_BFEXTU || lookup->mnemo == i_BFFFO || lookup->mnemo == i_BFINS)
                reg = (extra >> 12) & 7;
            if (lookup->mnemo == i_BFINS)
                _stprintf(p, _T("D%d,"), reg);
            pc = ShowEA(0, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &seaddr2, safemode);
            _tcscat(instrname, _T(" {"));
            p = instrname + _tcslen(instrname);
            if (extra & 0x0800)
                _stprintf(p, _T("D%d"), (extra >> 6) & 7);
            else
                _stprintf(p, _T("%d"), (extra >> 6) & 31);
            _tcscat(instrname, _T(":"));
            p = instrname + _tcslen(instrname);
            if (extra & 0x0020)
                _stprintf(p, _T("D%d"), extra & 7);
            else
                _stprintf(p, _T("%d"), extra  & 31);
            _tcscat(instrname, _T("}"));
            p = instrname + _tcslen(instrname);
            if (lookup->mnemo == i_BFFFO || lookup->mnemo == i_BFEXTS || lookup->mnemo == i_BFEXTU)
                _stprintf(p, _T(",D%d"), reg);
        } else if (lookup->mnemo == i_CPUSHA || lookup->mnemo == i_CPUSHL || lookup->mnemo == i_CPUSHP ||
                   lookup->mnemo == i_CINVA || lookup->mnemo == i_CINVL || lookup->mnemo == i_CINVP) {
            if ((opcode & 0xc0) == 0xc0)
                _tcscat(instrname, _T("BC"));
            else if (opcode & 0x80)
                _tcscat(instrname, _T("IC"));
            else if (opcode & 0x40)
                _tcscat(instrname, _T("DC"));
            else
                _tcscat(instrname, _T("?"));
            if (lookup->mnemo == i_CPUSHL || lookup->mnemo == i_CPUSHP || lookup->mnemo == i_CINVL || lookup->mnemo == i_CINVP) {
                TCHAR *p = instrname + _tcslen(instrname);
                _stprintf(p, _T(",(A%d)"), opcode & 7);
            }
        } else if (lookup->mnemo == i_FPP) {
            TCHAR *p;
            int ins = extra & 0x3f;
            int size = (extra >> 10) & 7;
            
            pc += 2;
            if ((extra & 0xfc00) == 0x5c00) { // FMOVECR (=i_FPP with source specifier = 7)
                fptype fp;
                if (fpu_get_constant(&fp, extra & 0x3f))
                    _stprintf(instrname, _T("FMOVECR.X #%s,FP%d"), fp_print(&fp), (extra >> 7) & 7);
                else
                    _stprintf(instrname, _T("FMOVECR.X #?,FP%d"), (extra >> 7) & 7);
            } else if ((extra & 0x8000) == 0x8000) { // FMOVEM
                int dr = (extra >> 13) & 1;
                int mode;
                int dreg = (extra >> 4) & 7;
                int regmask, fpmode;
                
                if (extra & 0x4000) {
                    mode = (extra >> 11) & 3;
                    regmask = extra & 0xff;  // FMOVEM FPx
                    fpmode = 1;
                    _tcscpy(instrname, _T("FMOVEM.X "));
                } else {
                    mode = 0;
                    regmask = (extra >> 10) & 7;  // FMOVEM control
                    fpmode = 2;
                    _tcscpy(instrname, _T("FMOVEM.L "));
                    if (regmask == 1 || regmask == 2 || regmask == 4)
                        _tcscpy(instrname, _T("FMOVE.L "));
                }
                p = instrname + _tcslen(instrname);
                if (dr) {
                    if (mode & 1)
                        _stprintf(instrname, _T("D%d"), dreg);
                    else
                        movemout(instrname, regmask, dp->dmode, fpmode);
                    _tcscat(instrname, _T(","));
                    pc = ShowEA(0, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, deaddr, safemode);
                } else {
                    pc = ShowEA(0, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, deaddr, safemode);
                    _tcscat(instrname, _T(","));
                    p = instrname + _tcslen(instrname);
                    if (mode & 1)
                        _stprintf(p, _T("D%d"), dreg);
                    else
                        movemout(p, regmask, dp->dmode, fpmode);
                }
            } else {
                if (fpuopcodes[ins])
                    _tcscpy(instrname, fpuopcodes[ins]);
                else
                    _tcscpy(instrname, _T("F?"));
                
                if ((extra & 0xe000) == 0x6000) { // FMOVE to memory
                    int kfactor = extra & 0x7f;
                    _tcscpy(instrname, _T("FMOVE."));
                    _tcscat(instrname, fpsizes[size]);
                    _tcscat(instrname, _T(" "));
                    p = instrname + _tcslen(instrname);
                    _stprintf(p, _T("FP%d,"), (extra >> 7) & 7);
                    pc = ShowEA(0, pc, opcode, dp->dreg, dp->dmode, fpsizeconv[size], instrname, &deaddr2, safemode);
                    p = instrname + _tcslen(instrname);
                    if (size == 7) {
                        _stprintf(p, _T(" {D%d}"), (kfactor >> 4));
                    } else if (kfactor) {
                        if (kfactor & 0x40)
                            kfactor |= ~0x3f;
                        _stprintf(p, _T(" {%d}"), kfactor);
                    }
                } else {
                    if (extra & 0x4000) { // source is EA
                        _tcscat(instrname, _T("."));
                        _tcscat(instrname, fpsizes[size]);
                        _tcscat(instrname, _T(" "));
                        pc = ShowEA(0, pc, opcode, dp->dreg, dp->dmode, fpsizeconv[size], instrname, &seaddr2, safemode);
                    } else { // source is FPx
                        p = instrname + _tcslen(instrname);
                        _stprintf(p, _T(".X FP%d"), (extra >> 10) & 7);
                    }
                    p = instrname + _tcslen(instrname);
                    if ((extra & 0x4000) || (((extra >> 7) & 7) != ((extra >> 10) & 7)))
                        _stprintf(p, _T(",FP%d"), (extra >> 7) & 7);
                    if (ins >= 0x30 && ins < 0x38) { // FSINCOS
                        p = instrname + _tcslen(instrname);
                        _stprintf(p, _T(",FP%d"), extra & 7);
                    }
                }
            }
        } else if ((opcode & 0xf000) == 0xa000) {
            _tcscpy(instrname, _T("A-LINE"));
        } else {
            if (dp->suse) {
                pc = ShowEA (0, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, &seaddr2, safemode);
            }
            if (dp->suse && dp->duse)
                _tcscat (instrname, _T(","));
            if (dp->duse) {
                pc = ShowEA (0, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &deaddr2, safemode);
            }
        }
        
        for (i = 0; i < (int)(pc - oldpc) / 2 && i < 5; i++) {
            buf = buf_out (buf, &bufsize, _T("%04x "), get_word_debug (oldpc + i * 2));
        }
        while (i++ < 5)
            buf = buf_out (buf, &bufsize, _T("     "));
        
        if (illegal)
            buf = buf_out (buf, &bufsize, _T("[ "));
        buf = buf_out (buf, &bufsize, instrname);
        if (illegal)
            buf = buf_out (buf, &bufsize, _T(" ]"));
        
        if (ccpt != 0) {
            uaecptr addr2 = deaddr2 ? deaddr2 : seaddr2;
            if (deaddr)
                *deaddr = pc;
            if ((opcode & 0xf000) == 0xf000) {
                if (fpp_cond(dp->cc)) {
                    buf = buf_out(buf, &bufsize, _T(" == $%08x (T)"), addr2);
                } else {
                    buf = buf_out(buf, &bufsize, _T(" == $%08x (F)"), addr2);
                }
            } else {
                if (cctrue (dp->cc)) {
                    buf = buf_out (buf, &bufsize, _T(" == $%08x (T)"), addr2);
                } else {
                    buf = buf_out (buf, &bufsize, _T(" == $%08x (F)"), addr2);
                }
            }
        } else if ((opcode & 0xff00) == 0x6100) { /* BSR */
            if (deaddr)
                *deaddr = pc;
            buf = buf_out (buf, &bufsize, _T(" == $%08x"), seaddr2);
        }
        buf = buf_out (buf, &bufsize, _T("\n"));
        
        if (illegal)
            pc =  m68kpc_illg;
    }
    if (nextpc)
        *nextpc = pc;
    if (seaddr)
        *seaddr = seaddr2;
    if (deaddr)
        *deaddr = deaddr2;
}

void m68k_disasm_ea (uaecptr addr, uaecptr *nextpc, int cnt, uae_u32 *seaddr, uae_u32 *deaddr)
{
    TCHAR *buf;
    
    buf = xmalloc (TCHAR, (MAX_LINEWIDTH + 1) * cnt);
    if (!buf)
        return;
    m68k_disasm_2 (buf, (MAX_LINEWIDTH + 1) * cnt, addr, nextpc, cnt, seaddr, deaddr, 1);
    xfree (buf);
}
void m68k_disasm (uaecptr addr, uaecptr *nextpc, int cnt)
{
    TCHAR *buf;
    
    buf = xmalloc (TCHAR, (MAX_LINEWIDTH + 1) * cnt);
    if (!buf)
        return;
    m68k_disasm_2 (buf, (MAX_LINEWIDTH + 1) * cnt, addr, nextpc, cnt, NULL, NULL, 0);
    printf (_T("%s"), buf);
    xfree (buf);
}

/*************************************************************
Disasm the m68kcode at the given address into instrname
and instrcode
*************************************************************/
void sm68k_disasm (TCHAR *instrname, TCHAR *instrcode, uaecptr addr, uaecptr *nextpc)
{
    TCHAR *ccpt;
    uae_u32 opcode;
    struct mnemolookup *lookup;
    struct instr *dp;
    uaecptr pc, oldpc;
    
    pc = oldpc = addr;
    opcode = get_word_debug (pc);
    if (cpufunctbl[opcode] == op_illg_1) {
        opcode = 0x4AFC;
    }
    dp = table68k + opcode;
    for (lookup = lookuptab;lookup->mnemo != dp->mnemo; lookup++);
    
    pc += 2;
    
    _tcscpy (instrname, lookup->name);
    ccpt = _tcsstr (instrname, _T("cc"));
    if (ccpt != 0) {
        _tcsncpy (ccpt, ccnames[dp->cc], 2);
    }
    switch (dp->size){
        case sz_byte: _tcscat (instrname, _T(".B ")); break;
        case sz_word: _tcscat (instrname, _T(".W ")); break;
        case sz_long: _tcscat (instrname, _T(".L ")); break;
        default: _tcscat (instrname, _T("   ")); break;
    }
    
    if (dp->suse) {
        pc = ShowEA (0, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, NULL, 0);
    }
    if (dp->suse && dp->duse)
        _tcscat (instrname, _T(","));
    if (dp->duse) {
        pc = ShowEA (0, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, NULL, 0);
    }
    if (instrcode)
    {
        int i;
        for (i = 0; i < (int)(pc - oldpc) / 2; i++)
        {
            _stprintf (instrcode, _T("%04x "), get_iword_debug (oldpc + i * 2));
            instrcode += _tcslen (instrcode);
        }
    }
    if (nextpc)
        *nextpc = pc;
}

struct cpum2c m2cregs[] = {
	{ 0, "SFC" },
	{ 1, "DFC" },
	{ 2, "CACR" },
	{ 3, "TC" },
	{ 4, "ITT0" },
	{ 5, "ITT1" },
	{ 6, "DTT0" },
	{ 7, "DTT1" },
	{ 8, "BUSC" },
	{ 0x800, "USP" },
	{ 0x801, "VBR" },
	{ 0x802, "CAAR" },
	{ 0x803, "MSP" },
	{ 0x804, "ISP" },
	{ 0x805, "MMUS" },
	{ 0x806, "URP" },
	{ 0x807, "SRP" },
	{ 0x808, "PCR" },
	{ -1, NULL }
};

void m68k_dumpstate_2 (uaecptr pc, uaecptr *nextpc)
{
    int i, j;
    
    for (i = 0; i < 8; i++){
        printf (_T("  D%d %08X "), i, m68k_dreg (regs, i));
        if ((i & 3) == 3) printf (_T("\n"));
    }
    for (i = 0; i < 8; i++){
        printf (_T("  A%d %08X "), i, m68k_areg (regs, i));
        if ((i & 3) == 3) printf (_T("\n"));
    }
    if (regs.s == 0)
        regs.usp = m68k_areg (regs, 7);
    if (regs.s && regs.m)
        regs.msp = m68k_areg (regs, 7);
    if (regs.s && regs.m == 0)
        regs.isp = m68k_areg (regs, 7);
    j = 2;
    printf (_T("USP  %08X ISP  %08X "), regs.usp, regs.isp);
    for (i = 0; m2cregs[i].regno>= 0; i++) {
        if (!movec_illg (m2cregs[i].regno)) {
            if (!_tcscmp (m2cregs[i].regname, _T("USP")) || !_tcscmp (m2cregs[i].regname, _T("ISP")))
                continue;
            if (j > 0 && (j % 4) == 0)
                printf (_T("\n"));
            printf (_T("%-4s %08X "), m2cregs[i].regname, val_move2c (m2cregs[i].regno));
            j++;
        }
    }
    if (j > 0)
        printf (_T("\n"));
    printf (_T("T=%d%d S=%d M=%d X=%d N=%d Z=%d V=%d C=%d IMASK=%d STP=%d\n"),
                   regs.t1, regs.t0, regs.s, regs.m,
                   GET_XFLG (), GET_NFLG (), GET_ZFLG (),
                   GET_VFLG (), GET_CFLG (),
                   regs.intmask, regs.stopped);
#ifdef FPUEMU
    if (currprefs.fpu_model) {
        uae_u32 fpsr;
        for (i = 0; i < 8; i++){
            printf (_T("FP%d: %s "), i, fp_print(&regs.fp[i]));
            if ((i & 3) == 3)
                printf (_T("\n"));
        }
        fpsr = fpp_get_fpsr ();
        printf (_T("FPSR: %08X FPCR: %04x FPIAR: %08x N=%d Z=%d I=%d NAN=%d\n"),
                       fpsr, regs.fpcr, regs.fpiar,
                       (fpsr & 0x8000000) != 0,
                       (fpsr & 0x4000000) != 0,
                       (fpsr & 0x2000000) != 0,
                       (fpsr & 0x1000000) != 0);
    }
#endif
    if (currprefs.mmu_model == 68030) {
        printf (_T("SRP: %llX CRP: %llX\n"), srp_030, crp_030);
        printf (_T("TT0: %08X TT1: %08X TC: %08X\n"), tt0_030, tt1_030, tc_030);
    }
    if (currprefs.cpu_compatible && currprefs.cpu_model == 68000) {
        struct instr *dp;
        struct mnemolookup *lookup1, *lookup2;
        dp = table68k + regs.irc;
        for (lookup1 = lookuptab; lookup1->mnemo != dp->mnemo; lookup1++);
        dp = table68k + regs.ir;
        for (lookup2 = lookuptab; lookup2->mnemo != dp->mnemo; lookup2++);
        printf (_T("Prefetch %04x (%s) %04x (%s) Chip latch %08X\n"), regs.irc, lookup1->name, regs.ir, lookup2->name, regs.chipset_latch_rw);
    }
    
    if (pc != 0xffffffff) {
        m68k_disasm (pc, nextpc, 1);
        if (nextpc)
            printf (_T("Next PC: %08x\n"), *nextpc);
    }
}
void m68k_dumpstate (uaecptr *nextpc)
{
    m68k_dumpstate_2 (m68k_getpc (), nextpc);
}

static void exception3f (uae_u32 opcode, uaecptr addr, bool writeaccess, bool instructionaccess, bool notinstruction, uaecptr pc, bool plus2)
{
    if (currprefs.cpu_model >= 68040)
        addr &= ~1;
    if (currprefs.cpu_model >= 68020) {
        if (pc == 0xffffffff)
            last_addr_for_exception_3 = regs.instruction_pc;
        else
            last_addr_for_exception_3 = pc;
    } else if (pc == 0xffffffff) {
        last_addr_for_exception_3 = m68k_getpc ();
        if (plus2)
            last_addr_for_exception_3 += 2;
    } else {
        last_addr_for_exception_3 = pc;
    }
    last_fault_for_exception_3 = addr;
    last_op_for_exception_3 = opcode;
    last_writeaccess_for_exception_3 = writeaccess;
    last_instructionaccess_for_exception_3 = instructionaccess;
    Exception (3);
}

void exception3_read(uae_u32 opcode, uaecptr addr)
{
    exception3f (opcode, addr, false, 0, false, 0xffffffff, false);
}
void exception3i (uae_u32 opcode, uaecptr addr)
{
    exception3f (opcode, addr, 0, 1, false, 0xffffffff, true);
}
void exception3b (uae_u32 opcode, uaecptr addr, bool w, bool i, uaecptr pc)
{
    exception3f (opcode, addr, w, i, false, pc, true);
}

void exception2 (uaecptr addr, bool read, int size, uae_u32 fc)
{
    if (currprefs.mmu_model == 68030) {
        uae_u32 flags = size == 1 ? MMU030_SSW_SIZE_B : (size == 2 ? MMU030_SSW_SIZE_W : MMU030_SSW_SIZE_L);
        mmu030_page_fault (addr, read, flags, fc);
    } else {
        mmu_bus_error (addr, 0, fc, read == false, size, true);
    }
}

void cpureset (void) {
	uaecptr pc;
	uaecptr ksboot = 0xf80002 - 2; /* -2 = RESET hasn't increased PC yet */
	uae_u16 ins;

	if (currprefs.cpu_compatible) {
		return;
	}
    
	pc = m68k_getpc ();
	/* panic, RAM is going to disappear under PC */
	ins = get_word (pc + 2);
	if ((ins & ~7) == 0x4ed0) {
		int reg = ins & 7;
		uae_u32 addr = m68k_areg (regs, reg);
		write_log ("reset/jmp (ax) combination emulated -> %x\n", addr);
		if (addr < 0x80000)
			addr += 0xf80000;
		m68k_setpc (addr - 2);
		return;
	}
	write_log ("M68K RESET PC=%x, rebooting..\n", pc);
	m68k_setpc (ksboot);
}


void m68k_setstopped (void)
{
	regs.stopped = 1;
	/* A traced STOP instruction drops through immediately without
	actually stopping.  */
	if ((regs.spcflags & SPCFLAG_DOTRACE) == 0)
		set_special (SPCFLAG_STOP);
	else
		m68k_resumestopped ();
}

void m68k_resumestopped (void)
{
	if (!regs.stopped)
		return;
	regs.stopped = 0;
	unset_special (SPCFLAG_STOP);
}