/************************************************************************/
/*			KEGS: Apple //gs Emulator			*/
/*			Copyright 2002 by Kent Dickey			*/
/*									*/
/*		This code is covered by the GNU GPL			*/
/*									*/
/*	The KEGS web page is kegs.sourceforge.net			*/
/*	You may contact the author at: kadickey@alumni.princeton.edu	*/
/************************************************************************/

const char rcsid_engine_c_c[] = "@(#)$KmKId: engine_c.c,v 1.57 2004-12-03 23:51:01-05 kentd Exp $";

#include "defc.h"
#include "protos_engine_c.h"

#if 0
# define LOG_PC
/* define FCYCS_PTR_FCYCLES_ROUND_SLOW to get accurate 1MHz write to slow mem*/
/*  this might help joystick emulation in some Apple //gs games like */
/*  Madness */
# define FCYCS_PTR_FCYCLES_ROUND_SLOW	FCYCLES_ROUND; *fcycs_ptr = fcycles;
#endif

#ifndef FCYCS_PTR_FCYCLES_ROUND_SLOW
# define FCYCS_PTR_FCYCLES_ROUND_SLOW
#endif

extern int halt_sim;
extern int g_code_red;
extern int g_ignore_halts;
extern int g_user_halt_bad;
extern double g_fcycles_stop;
extern double g_last_vbl_dcycs;
extern double g_cur_dcycs;
extern int g_wait_pending;
extern int g_irq_pending;
extern int g_testing;
extern int g_num_brk;
extern int g_num_cop;
extern byte *g_slow_memory_ptr;
extern byte *g_memory_ptr;
extern byte *g_rom_fc_ff_ptr;
extern byte *g_rom_cards_ptr;
extern byte *g_dummy_memory1_ptr;

extern int g_num_breakpoints;
extern word32 g_breakpts[];

extern Pc_log *g_log_pc_ptr;
extern Pc_log *g_log_pc_start_ptr;
extern Pc_log *g_log_pc_end_ptr;

extern Data_log *g_log_data_ptr;
extern Data_log *g_log_data_start_ptr;
extern Data_log *g_log_data_end_ptr;

int size_tab[] = {
#include "size_c.h"
};

int bogus[] = {
	0,
#include "op_routs.h"
};

#define FINISH(arg1, arg2)	g_ret1 = arg1; g_ret2 = arg2; goto finish;
#define INC_KPC_1	kpc = (kpc & 0xff0000) + ((kpc + 1) & 0xffff);
#define INC_KPC_2	kpc = (kpc & 0xff0000) + ((kpc + 2) & 0xffff);
#define INC_KPC_3	kpc = (kpc & 0xff0000) + ((kpc + 3) & 0xffff);
#define INC_KPC_4	kpc = (kpc & 0xff0000) + ((kpc + 4) & 0xffff);

#define CYCLES_PLUS_1	fcycles += fplus_1;
#define CYCLES_PLUS_2	fcycles += fplus_2;
#define CYCLES_PLUS_3	fcycles += fplus_3;
#define CYCLES_PLUS_4	fcycles += (fplus_1 + fplus_3);
#define CYCLES_PLUS_5	fcycles += (fplus_2 + fplus_3);
#define CYCLES_MINUS_1	fcycles -= fplus_1;
#define CYCLES_MINUS_2	fcycles -= fplus_2;

#define CYCLES_FINISH	fcycles = g_fcycles_stop + fplus_1;

#define FCYCLES_ROUND	fcycles = (int)(fcycles + fplus_x_m1);

#ifdef LOG_PC
# define LOG_PC_MACRO()							\
		tmp_pc_ptr = g_log_pc_ptr++;				\
		tmp_pc_ptr->dbank_kpc = (dbank << 24) + kpc;		\
		tmp_pc_ptr->instr = (opcode << 24) + arg_ptr[1] +	\
			(arg_ptr[2] << 8) + (arg_ptr[3] << 16);		\
		tmp_pc_ptr->psr_acc = ((psr & ~(0x82)) << 16) + acc +	\
			(neg << 23) + ((!zero) << 17);			\
		tmp_pc_ptr->xreg_yreg = (xreg << 16) + yreg;		\
		tmp_pc_ptr->stack_direct = (stack << 16) + direct;	\
		tmp_pc_ptr->dcycs = fcycles + g_last_vbl_dcycs - fplus_2; \
		if(g_log_pc_ptr >= g_log_pc_end_ptr) {			\
			/*halt2_printf("log_pc oflow %f\n", tmp_pc_ptr->dcycs);*/ \
			g_log_pc_ptr = g_log_pc_start_ptr;		\
		}

# define LOG_DATA_MACRO(in_addr, in_val, in_size)			\
		g_log_data_ptr->dcycs = fcycles + g_last_vbl_dcycs;	\
		g_log_data_ptr->addr = in_addr;				\
		g_log_data_ptr->val = in_val;				\
		g_log_data_ptr->size = in_size;				\
		g_log_data_ptr++;					\
		if(g_log_data_ptr >= g_log_data_end_ptr) {		\
			g_log_data_ptr = g_log_data_start_ptr;		\
		}

#else
# define LOG_PC_MACRO()
# define LOG_DATA_MACRO(addr, val, size)
/* Just do nothing */
#endif


#define	GET_1BYTE_ARG	arg = arg_ptr[1];
#define	GET_2BYTE_ARG	arg = arg_ptr[1] + (arg_ptr[2] << 8);
#define	GET_3BYTE_ARG	arg = arg_ptr[1] + (arg_ptr[2] << 8) + (arg_ptr[3]<<16);

/* HACK HACK HACK */
#define	UPDATE_PSR(dummy, old_psr)				\
	if(psr & 0x100) {					\
		psr |= 0x30;					\
		stack = 0x100 + (stack & 0xff);			\
	}							\
	if((old_psr ^ psr) & 0x10) {				\
		if(psr & 0x10) {				\
			xreg = xreg & 0xff;			\
			yreg = yreg & 0xff;			\
		}						\
	}							\
	if(((psr & 0x4) == 0) && g_irq_pending) {		\
		FINISH(RET_IRQ, 0);				\
	}							\
	if((old_psr ^ psr) & 0x20) {				\
		goto recalc_accsize;				\
	}

extern Page_info page_info_rd_wr[];
extern word32 slow_mem_changed[];

#define GET_MEMORY8(addr,dest)					\
	addr_latch = (addr);					\
	CYCLES_PLUS_1;						\
	stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff);	\
	wstat = PTR2WORD(stat) & 0xff;				\
	ptr = stat - wstat + ((addr) & 0xff);			\
	if(wstat & (1 << (31 - BANK_IO_BIT))) {			\
		fcycles_tmp1 = fcycles;				\
		dest = get_memory8_io_stub((addr), stat,	\
				&fcycles_tmp1, fplus_x_m1);	\
		fcycles = fcycles_tmp1;				\
	} else {						\
		dest = *ptr;					\
	}

#define GET_MEMORY(addr,dest)	GET_MEMORY8(addr, dest)

#define GET_MEMORY16(addr, dest, in_bank)			\
	save_addr = addr;					\
	stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff);	\
	wstat = PTR2WORD(stat) & 0xff;				\
	ptr = stat - wstat + ((addr) & 0xff);			\
	if((wstat & (1 << (31 - BANK_IO_BIT))) || (((addr) & 0xff) == 0xff)) { \
		fcycles_tmp1 = fcycles;				\
		dest = get_memory16_pieces_stub((addr), stat,	\
			&fcycles_tmp1, fplus_ptr, in_bank);	\
		fcycles = fcycles_tmp1;				\
	} else {						\
		CYCLES_PLUS_2;					\
		dest = ptr[0] + (ptr[1] << 8);			\
	}							\
	addr_latch = save_addr;

#define GET_MEMORY24(addr, dest, in_bank)			\
	save_addr = addr;					\
	stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff);	\
	wstat = PTR2WORD(stat) & 0xff;				\
	ptr = stat - wstat + ((addr) & 0xff);			\
	if((wstat & (1 << (31 - BANK_IO_BIT))) || (((addr) & 0xfe) == 0xfe)) { \
		fcycles_tmp1 = fcycles;				\
		dest = get_memory24_pieces_stub((addr), stat,	\
			&fcycles_tmp1, fplus_ptr, in_bank);	\
		fcycles = fcycles_tmp1;				\
	} else {						\
		CYCLES_PLUS_3;					\
		dest = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16);	\
	}							\
	addr_latch = save_addr;

#define GET_MEMORY_DIRECT_PAGE16(addr, dest)				\
	save_addr = addr;						\
	if(psr & 0x100) {						\
		if((direct & 0xff) == 0) {				\
			save_addr = (save_addr & 0xff) + direct;	\
		}							\
	}								\
	if((psr & 0x100) && (((addr) & 0xff) == 0xff)) {		\
		GET_MEMORY8(save_addr, getmem_tmp);			\
		save_addr = (save_addr + 1) & 0xffff;			\
		if((direct & 0xff) == 0) {				\
			save_addr = (save_addr & 0xff) + direct;	\
		}							\
		GET_MEMORY8(save_addr, dest);				\
		dest = (dest << 8) + getmem_tmp;			\
	} else {							\
		GET_MEMORY16(save_addr, dest, 1);			\
	}


#define PUSH8(arg)						\
	SET_MEMORY8(stack, arg);				\
	stack--;						\
	if(psr & 0x100) {					\
		stack = 0x100 | (stack & 0xff);			\
	}							\
	stack = stack & 0xffff;

#define PUSH16(arg)						\
	if((stack & 0xfe) == 0) {				\
		/* stack will cross page! */			\
		PUSH8((arg) >> 8);				\
		PUSH8(arg);					\
	} else {						\
		stack -= 2;					\
		stack = stack & 0xffff;				\
		SET_MEMORY16(stack + 1, arg, 1);		\
	}

#define PUSH16_UNSAFE(arg)					\
	save_addr = (stack - 1) & 0xffff;			\
	stack -= 2;						\
	if(psr & 0x100) {					\
		stack = 0x100 | (stack & 0xff);			\
	}							\
	stack = stack & 0xffff;					\
	SET_MEMORY16(save_addr, arg, 1);

#define PUSH24_UNSAFE(arg)					\
	save_addr = (stack - 2) & 0xffff;			\
	stack -= 3;						\
	if(psr & 0x100) {					\
		stack = 0x100 | (stack & 0xff);			\
	}							\
	stack = stack & 0xffff;					\
	SET_MEMORY24(save_addr, arg, 1);

#define PULL8(dest)						\
	stack++;						\
	if(psr & 0x100) {					\
		stack = 0x100 | (stack & 0xff);			\
	}							\
	stack = stack & 0xffff;					\
	GET_MEMORY8(stack, dest);

#define PULL16(dest)						\
	if((stack & 0xfe) == 0xfe) {	/* page cross */	\
		PULL8(dest);					\
		PULL8(pull_tmp);				\
		dest = (pull_tmp << 8) + dest;			\
	} else {						\
		GET_MEMORY16(stack + 1, dest, 1);		\
		stack = (stack + 2) & 0xffff;			\
		if(psr & 0x100) {				\
			stack = 0x100 | (stack & 0xff);		\
		}						\
	}

#define PULL16_UNSAFE(dest)					\
	stack = (stack + 1) & 0xffff;				\
	if(psr & 0x100) {					\
		stack = 0x100 | (stack & 0xff);			\
	}							\
	GET_MEMORY16(stack, dest, 1);				\
	stack = (stack + 1) & 0xffff;				\
	if(psr & 0x100) {					\
		stack = 0x100 | (stack & 0xff);			\
	}

#define PULL24(dest)						\
	if((stack & 0xfc) == 0xfc) {	/* page cross */	\
		PULL8(dest);					\
		PULL8(pull_tmp);				\
		pull_tmp = (pull_tmp << 8) + dest;		\
		PULL8(dest);					\
		dest = (dest << 16) + pull_tmp;			\
	} else {						\
		GET_MEMORY24(stack + 1, dest, 1);		\
		stack = (stack + 3) & 0xffff;			\
		if(psr & 0x100) {				\
			stack = 0x100 | (stack & 0xff);		\
		}						\
	}

#define SET_MEMORY8(addr, val)						\
	LOG_DATA_MACRO(addr, val, 8);					\
	CYCLES_PLUS_1;							\
	stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff);		\
	wstat = PTR2WORD(stat) & 0xff;					\
	ptr = stat - wstat + ((addr) & 0xff);				\
	if(wstat) {							\
		fcycles_tmp1 = fcycles;					\
		set_memory8_io_stub((addr), val, stat, &fcycles_tmp1,	\
				fplus_x_m1);				\
		fcycles = fcycles_tmp1;					\
	} else {							\
		*ptr = val;						\
	}


#define SET_MEMORY16(addr, val, in_bank)				\
	LOG_DATA_MACRO(addr, val, 16);					\
	stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff);		\
	wstat = PTR2WORD(stat) & 0xff;					\
	ptr = stat - wstat + ((addr) & 0xff);				\
	if((wstat) || (((addr) & 0xff) == 0xff)) {			\
		fcycles_tmp1 = fcycles;					\
		set_memory16_pieces_stub((addr), (val),			\
			&fcycles_tmp1, fplus_1, fplus_x_m1, in_bank);	\
		fcycles = fcycles_tmp1;					\
	} else {							\
		CYCLES_PLUS_2;						\
		ptr[0] = (val);						\
		ptr[1] = (val) >> 8;					\
	}

#define SET_MEMORY24(addr, val, in_bank)				\
	LOG_DATA_MACRO(addr, val, 24);					\
	stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff);		\
	wstat = PTR2WORD(stat) & 0xff;					\
	ptr = stat - wstat + ((addr) & 0xff);				\
	if((wstat) || (((addr) & 0xfe) == 0xfe)) {			\
		fcycles_tmp1 = fcycles;					\
		set_memory24_pieces_stub((addr), (val), 		\
			&fcycles_tmp1, fplus_ptr, in_bank);		\
		fcycles = fcycles_tmp1;					\
	} else {							\
		CYCLES_PLUS_3;						\
		ptr[0] = (val);						\
		ptr[1] = (val) >> 8;					\
		ptr[2] = (val) >> 16;					\
	}

void
check_breakpoints(word32 addr)
{
	int	count;
	int	i;

	count = g_num_breakpoints;
	for(i = 0; i < count; i++) {
		if((g_breakpts[i] & 0xffffff) == addr) {
			halt2_printf("Hit breakpoint at %06x\n", addr);
		}
	}
}

word32
get_memory8_io_stub(word32 addr, byte *stat, double *fcycs_ptr,
			double fplus_x_m1)
{
	double	fcycles;
	word32	wstat;
	byte	*ptr;

	wstat = PTR2WORD(stat) & 0xff;
	if(wstat & BANK_BREAK) {
		check_breakpoints(addr);
	}
	fcycles = *fcycs_ptr;
	if(wstat & BANK_IO2_TMP) {
		FCYCLES_ROUND;
		*fcycs_ptr = fcycles;
		return get_memory_io((addr), fcycs_ptr);
	} else {
		ptr = stat - wstat + (addr & 0xff);
		return *ptr;
	}
}

word32
get_memory16_pieces_stub(word32 addr, byte *stat, double *fcycs_ptr,
		Fplus	*fplus_ptr, int in_bank)
{
	byte	*ptr;
	double	fcycles, fcycles_tmp1;
	double	fplus_1;
	double	fplus_x_m1;
	word32	addrp1;
	word32	wstat;
	word32	addr_latch;
	word32	ret;
	word32	tmp1;

	fcycles = *fcycs_ptr;
	fplus_1 = fplus_ptr->plus_1;
	fplus_x_m1 = fplus_ptr->plus_x_minus_1;
	GET_MEMORY8(addr, tmp1);
	addrp1 = addr + 1;
	if(in_bank) {
		addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);
	}
	GET_MEMORY8(addrp1, ret);
	*fcycs_ptr = fcycles;
	return (ret << 8) + (tmp1);
}

word32
get_memory24_pieces_stub(word32 addr, byte *stat, double *fcycs_ptr,
		Fplus *fplus_ptr, int in_bank)
{
	byte	*ptr;
	double	fcycles, fcycles_tmp1;
	double	fplus_1;
	double	fplus_x_m1;
	word32	addrp1, addrp2;
	word32	wstat;
	word32	addr_latch;
	word32	ret;
	word32	tmp1;
	word32	tmp2;

	fcycles = *fcycs_ptr;
	fplus_1 = fplus_ptr->plus_1;
	fplus_x_m1 = fplus_ptr->plus_x_minus_1;
	GET_MEMORY8(addr, tmp1);
	addrp1 = addr + 1;
	if(in_bank) {
		addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);
	}
	GET_MEMORY8(addrp1, tmp2);
	addrp2 = addr + 2;
	if(in_bank) {
		addrp2 = (addr & 0xff0000) + (addrp2 & 0xffff);
	}
	GET_MEMORY8(addrp2, ret);
	*fcycs_ptr = fcycles;
	return (ret << 16) + (tmp2 << 8) + tmp1;
}

void
set_memory8_io_stub(word32 addr, word32 val, byte *stat, double *fcycs_ptr,
		double fplus_x_m1)
{
	double	fcycles;
	word32	setmem_tmp1;
	word32	tmp1, tmp2;
	byte	*ptr;
	word32	wstat;

	wstat = PTR2WORD(stat) & 0xff;
	if(wstat & (1 << (31 - BANK_BREAK_BIT))) {
		check_breakpoints(addr);
	}
	ptr = stat - wstat + ((addr) & 0xff);				\
	fcycles = *fcycs_ptr;
	if(wstat & (1 << (31 - BANK_IO2_BIT))) {
		FCYCLES_ROUND;
		*fcycs_ptr = fcycles;
		set_memory_io((addr), val, fcycs_ptr);
	} else if(wstat & (1 << (31 - BANK_SHADOW_BIT))) {
		FCYCS_PTR_FCYCLES_ROUND_SLOW;
		tmp1 = (addr & 0xffff);
		setmem_tmp1 = g_slow_memory_ptr[tmp1];
		*ptr = val;
		if(setmem_tmp1 != ((val) & 0xff)) {
			g_slow_memory_ptr[tmp1] = val;
			slow_mem_changed[tmp1 >> CHANGE_SHIFT] |=
				(1 << (31-((tmp1 >> SHIFT_PER_CHANGE) & 0x1f)));
		}
	} else if(wstat & (1 << (31 - BANK_SHADOW2_BIT))) {
		FCYCS_PTR_FCYCLES_ROUND_SLOW;
		tmp2 = (addr & 0xffff);
		tmp1 = 0x10000 + tmp2;
		setmem_tmp1 = g_slow_memory_ptr[tmp1];
		*ptr = val;
		if(setmem_tmp1 != ((val) & 0xff)) {
			g_slow_memory_ptr[tmp1] = val;
			slow_mem_changed[tmp2 >>CHANGE_SHIFT] |=
				(1 <<(31-((tmp2 >> SHIFT_PER_CHANGE) & 0x1f)));
		}
	} else {
		/* breakpoint only */
		*ptr = val;
	}
}

void
set_memory16_pieces_stub(word32 addr, word32 val, double *fcycs_ptr,
		double fplus_1, double fplus_x_m1, int in_bank)
{
	byte	*ptr;
	byte	*stat;
	double	fcycles, fcycles_tmp1;
	word32	addrp1;
	word32	wstat;

	fcycles = *fcycs_ptr;
	SET_MEMORY8(addr, val);
	addrp1 = addr + 1;
	if(in_bank) {
		addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);
	}
	SET_MEMORY8(addrp1, val >> 8);

	*fcycs_ptr = fcycles;
}

void
set_memory24_pieces_stub(word32 addr, word32 val, double *fcycs_ptr,
		Fplus	*fplus_ptr, int in_bank)
{
	byte	*ptr;
	byte	*stat;
	double	fcycles, fcycles_tmp1;
	double	fplus_1;
	double	fplus_x_m1;
	word32	addrp1, addrp2;
	word32	wstat;

	fcycles = *fcycs_ptr;
	fplus_1 = fplus_ptr->plus_1;
	fplus_x_m1 = fplus_ptr->plus_x_minus_1;
	SET_MEMORY8(addr, val);
	addrp1 = addr + 1;
	if(in_bank) {
		addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);
	}
	SET_MEMORY8(addrp1, val >> 8);
	addrp2 = addr + 2;
	if(in_bank) {
		addrp2 = (addr & 0xff0000) + (addrp2 & 0xffff);
	}
	SET_MEMORY8(addrp2, val >> 16);

	*fcycs_ptr = fcycles;
}


word32
get_memory_c(word32 addr, int cycs)
{
	byte	*stat;
	byte	*ptr;
	double	fcycles, fcycles_tmp1;
	double	fplus_1;
	double	fplus_x_m1;
	word32	addr_latch;
	word32	wstat;
	word32	ret;

	fcycles = 0;
	fplus_1 = 0;
	fplus_x_m1 = 0;
	GET_MEMORY8(addr, ret);
	return ret;
}

word32
get_memory16_c(word32 addr, int cycs)
{
	double	fcycs;

	fcycs = 0;
	return get_memory_c(addr, fcycs) +
			(get_memory_c(addr+1, fcycs) << 8);
}

word32
get_memory24_c(word32 addr, int cycs)
{
	double	fcycs;

	fcycs = 0;
	return get_memory_c(addr, fcycs) +
			(get_memory_c(addr+1, fcycs) << 8) +
			(get_memory_c(addr+2, fcycs) << 16);
}

void
set_memory_c(word32 addr, word32 val, int cycs)
{
	byte	*stat;
	byte	*ptr;
	double	fcycles, fcycles_tmp1;
	double	fplus_1;
	double	fplus_x_m1;
	word32	wstat;

	fcycles = g_cur_dcycs - g_last_vbl_dcycs;
	fplus_1 = 0;
	fplus_x_m1 = 0;
	SET_MEMORY8(addr, val);
}

void
set_memory16_c(word32 addr, word32 val, int cycs)
{
	byte	*stat;
	byte	*ptr;
	double	fcycles, fcycles_tmp1;
	double	fplus_1, fplus_2;
	double	fplus_x_m1;
	word32	wstat;

	fcycles = g_cur_dcycs - g_last_vbl_dcycs;
	fplus_1 = 0;
	fplus_2 = 0;
	fplus_x_m1 = 0;
	SET_MEMORY16(addr, val, 0);
}

void
set_memory24_c(word32 addr, word32 val, int cycs)
{
	set_memory_c(addr, val, 0);
	set_memory_c(addr + 1, val >> 8, 0);
	set_memory_c(addr + 2, val >> 16, 0);
}

word32
do_adc_sbc8(word32 in1, word32 in2, word32 psr, int sub)
{
	word32	sum, carry, overflow;
	word32	zero;
	int	decimal;

	overflow = 0;
	decimal = psr & 8;
	if(sub) {
		in2 = (in2 ^ 0xff);
	}
	if(!decimal) {
		sum = (in1 & 0xff) + in2 + (psr & 1);
		overflow = ((sum ^ in2) >> 1) & 0x40;
	} else {
		/* decimal */
		sum = (in1 & 0xf) + (in2 & 0xf) + (psr & 1);
		if(sub) {
			if(sum < 0x10) {
				sum = (sum - 0x6) & 0xf;
			}
		} else {
			if(sum >= 0xa) {
				sum = (sum - 0xa) | 0x10;
			}
		}

		sum = (in1 & 0xf0) + (in2 & 0xf0) + sum;
		overflow = ((sum >> 2) ^ (sum >> 1)) & 0x40;
		if(sub) {
			if(sum < 0x100) {
				sum = (sum + 0xa0) & 0xff;
			}
		} else {
			if(sum >= 0xa0) {
				sum += 0x60;
			}
		}
	}

	zero = ((sum & 0xff) == 0);
	carry = (sum >= 0x100);
	if((in1 ^ in2) & 0x80) {
		overflow = 0;
	}

	psr = psr & (~0xc3);
	psr = psr + (sum & 0x80) + overflow + (zero << 1) + carry;

	return (psr << 16) + (sum & 0xff);
}

word32
do_adc_sbc16(word32 in1, word32 in2, word32 psr, int sub)
{
	word32	sum, carry, overflow;
	word32	tmp1, tmp2;
	word32	zero;
	int	decimal;

	overflow = 0;
	decimal = psr & 8;
	if(!decimal) {
		if(sub) {
			in2 = (in2 ^ 0xffff);
		}
		sum = in1 + in2 + (psr & 1);
		overflow = ((sum ^ in2) >> 9) & 0x40;
	} else {
		/* decimal */
		if(sub) {
			tmp1 = do_adc_sbc8(in1 & 0xff, in2 & 0xff, psr, sub);
			psr = (tmp1 >> 16);
			tmp2 = do_adc_sbc8((in1 >> 8) & 0xff,
						(in2 >> 8) & 0xff, psr, sub);
			in2 = (in2 ^ 0xfffff);
		} else {
			tmp1 = do_adc_sbc8(in1 & 0xff, in2 & 0xff, psr, sub);
			psr = (tmp1 >> 16);
			tmp2 = do_adc_sbc8((in1 >> 8) & 0xff,
						(in2 >> 8) &0xff, psr, sub);
		}
		sum = ((tmp2 & 0xff) << 8) + (tmp1 & 0xff) +
					(((tmp2 >> 16) & 1) << 16);
		overflow = (tmp2 >> 16) & 0x40;
	}

	zero = ((sum & 0xffff) == 0);
	carry = (sum >= 0x10000);
	if((in1 ^ in2) & 0x8000) {
		overflow = 0;
	}

	psr = psr & (~0xc3);
	psr = psr + ((sum & 0x8000) >> 8) + overflow + (zero << 1) + carry;

	return (psr << 16) + (sum & 0xffff);
}

int	g_ret1;
int	g_ret2;


void
fixed_memory_ptrs_init()
{
	/* set g_slow_memory_ptr, g_rom_fc_ff_ptr, g_dummy_memory1_ptr, */
	/*  and rom_cards_ptr */

	g_slow_memory_ptr = memalloc_align(128*1024, 0, 0);
	g_dummy_memory1_ptr = memalloc_align(256, 1024, 0);
	g_rom_fc_ff_ptr = memalloc_align(256*1024, 512, 0);
	g_rom_cards_ptr = memalloc_align(16*256, 256, 0);

#if 0
	printf("g_memory_ptr: %08x, dummy_mem: %08x, slow_mem_ptr: %08x\n",
		(word32)g_memory_ptr, (word32)g_dummy_memory1_ptr,
		(word32)g_slow_memory_ptr);
	printf("g_rom_fc_ff_ptr: %08x, g_rom_cards_ptr: %08x\n",
		(word32)g_rom_fc_ff_ptr, (word32)g_rom_cards_ptr);
	printf("page_info_rd = %08x, page_info_wr end = %08x\n",
		(word32)&(page_info_rd_wr[0]),
		(word32)&(page_info_rd_wr[PAGE_INFO_PAD_SIZE+0x1ffff].rd_wr));
#endif
}

word32
get_itimer()
{
#if defined(__i386) && defined(__GNUC__)
	/* Here's my bad ia32 asm code to do rdtsc */
	/* Linux source uses: */
	/* asm volatile("rdtsc" : "=a"(ret) : : "edx"); */
	/* asm volatile("rdtsc" : "=%eax"(ret) : : "%edx"); */

	/* GCC bug report 2001-03/msg00786.html used: */
	/*register word64 dtmp; */
	/*asm volatile ("rdtsc" : "=A" (dtmp)); */
	/*return (word32)dtmp; */

	register word32 ret;

	asm volatile ("rdtsc;movl %%eax,%0" : "=r"(ret) : : "%eax","%edx");

	return ret;
#else
# if defined(__POWERPC__) && defined(__GNUC__)
	register word32 ret;

	asm volatile ("mftb %0" : "=r"(ret));
	return ret;
# else
	return 0;
# endif
#endif
}

void
set_halt_act(int val)
{
	if(val == 1 && g_ignore_halts && !g_user_halt_bad) {
		g_code_red++;
	} else {
		halt_sim |= val;
		g_fcycles_stop = (double)0.0;
	}
}

void
clr_halt_act()
{
	halt_sim = 0;
}

word32
get_remaining_operands(word32 addr, word32 opcode, word32 psr, Fplus *fplus_ptr)
{
	byte	*stat;
	byte	*ptr;
	double	fcycles, fcycles_tmp1;
	double	fplus_1, fplus_2, fplus_3;
	double	fplus_x_m1;
	word32	addr_latch;
	word32	wstat;
	word32	save_addr;
	word32	arg;
	word32	addrp1;
	int	size;

	fcycles = 0;
	fplus_1 = 0;
	fplus_2 = 0;
	fplus_3 = 0;
	fplus_x_m1 = 0;

	size = size_tab[opcode];

	addrp1 = (addr & 0xff0000) + ((addr + 1) & 0xffff);
	switch(size) {
	case 0:
		arg = 0;	/* no args */
		break;
	case 1:
		GET_MEMORY8(addrp1, arg);
		break;		/* 1 arg, already done */
	case 2:
		GET_MEMORY16(addrp1, arg, 1);
		break;
	case 3:
		GET_MEMORY24(addrp1, arg, 1);
		break;
	case 4:
		if(psr & 0x20) {
			GET_MEMORY8(addrp1, arg);
		} else {
			GET_MEMORY16(addrp1, arg, 1);
		}
		break;
	case 5:
		if(psr & 0x10) {
			GET_MEMORY8(addrp1, arg);
		} else {
			GET_MEMORY16(addrp1, arg, 1);
		}
		break;
	default:
		printf("Unknown size: %d\n", size);
		arg = 0;
		exit(-2);
	}

	return arg;
}

#define FETCH_OPCODE							\
	addr = kpc;							\
	CYCLES_PLUS_2;							\
	stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff);		\
	wstat = PTR2WORD(stat) & 0xff;					\
	ptr = stat - wstat + ((addr) & 0xff);				\
	arg_ptr = ptr;							\
	opcode = *ptr;							\
	if((wstat & (1 << (31-BANK_IO_BIT))) || ((addr & 0xff) > 0xfc)) {\
		if(wstat & BANK_BREAK) {				\
			check_breakpoints(addr);			\
		}							\
		if((addr & 0xfffff0) == 0x00c700) {			\
			if(addr == 0xc700) {				\
				FINISH(RET_C700, 0);			\
			} else if(addr == 0xc70a) {			\
				FINISH(RET_C70A, 0);			\
			} else if(addr == 0xc70d) {			\
				FINISH(RET_C70D, 0);			\
			}						\
		}							\
		if(wstat & (1 << (31 - BANK_IO2_BIT))) {		\
			FCYCLES_ROUND;					\
			fcycles_tmp1 = fcycles;				\
			opcode = get_memory_io((addr), &fcycles_tmp1);	\
			fcycles = fcycles_tmp1;				\
		} else {						\
			opcode = *ptr;					\
		}							\
		arg = get_remaining_operands(addr, opcode, psr, fplus_ptr);\
		arg_ptr = (byte *)&tmp_bytes;				\
		arg_ptr[1] = arg;					\
		arg_ptr[2] = arg >> 8;					\
		arg_ptr[3] = arg >> 16;					\
	}

int
enter_engine(Engine_reg *engine_ptr)
{
	register byte	*ptr;
	byte	*arg_ptr;
	Pc_log	*tmp_pc_ptr;
	byte	*stat;
	word32	wstat;
	word32	arg;
	register word32	kpc;
	register word32	acc;
	register word32	xreg;
	register word32	yreg;
	word32	stack;
	word32	dbank;
	register word32	direct;
	register word32	psr;
	register word32	zero;
	register word32	neg;
	word32	getmem_tmp;
	word32	save_addr;
	word32	pull_tmp;
	word32	tmp_bytes;
	double	fcycles;
	Fplus	*fplus_ptr;
	double	fplus_1;
	double	fplus_2;
	double	fplus_3;
	double	fplus_x_m1;
	double	fcycles_tmp1;

	word32	opcode;
	register word32	addr;
	word32	addr_latch;
	word32	tmp1, tmp2;


	tmp_pc_ptr = 0;

	kpc = engine_ptr->kpc;
	acc = engine_ptr->acc;
	xreg = engine_ptr->xreg;
	yreg = engine_ptr->yreg;
	stack = engine_ptr->stack;
	dbank = engine_ptr->dbank;
	direct = engine_ptr->direct;
	psr = engine_ptr->psr;
	fcycles = engine_ptr->fcycles;
	fplus_ptr = engine_ptr->fplus_ptr;
	zero = !(psr & 2);
	neg = (psr >> 7) & 1;

	fplus_1 = fplus_ptr->plus_1;
	fplus_2 = fplus_ptr->plus_2;
	fplus_3 = fplus_ptr->plus_3;
	fplus_x_m1 = fplus_ptr->plus_x_minus_1;

	g_ret1 = 0;
	g_ret2 = 0;

recalc_accsize:
	if(psr & 0x20) {
		while(fcycles <= g_fcycles_stop) {
#if 0
			if((neg & ~1) || (psr & (~0x1ff))) {
				halt_printf("psr = %04x\n", psr);
			}
#endif

			FETCH_OPCODE;

			LOG_PC_MACRO();

			switch(opcode) {
			default:
				halt_printf("acc8 unk op: %02x\n", opcode);
				arg = 9
#define ACC8
#include "defs_instr.h"
				* 2;
				break;
#include "8inst_c.h"
				break;
			}
		}
	} else {
		while(fcycles <= g_fcycles_stop) {
			FETCH_OPCODE;
			LOG_PC_MACRO();

			switch(opcode) {
			default:
				halt_printf("acc16 unk op: %02x\n", opcode);
				arg = 9
#undef ACC8
#include "defs_instr.h"
				* 2;
				break;
#include "16inst_c.h"
				break;
			}
		}
	}

finish:
	engine_ptr->kpc = kpc;
	engine_ptr->acc = acc;
	engine_ptr->xreg = xreg;
	engine_ptr->yreg = yreg;
	engine_ptr->stack = stack;
	engine_ptr->dbank = dbank;
	engine_ptr->direct = direct;
	engine_ptr->fcycles = fcycles;

	psr = psr & (~0x82);
	psr |= (neg << 7);
	psr |= ((!zero) << 1);

	engine_ptr->psr = psr;

	return (g_ret1 << 28) + g_ret2;
}


int	g_engine_c_mode = 1;

int defs_instr_start_8 = 0;
int defs_instr_end_8 = 0;
int defs_instr_start_16 = 0;
int defs_instr_end_16 = 0;
int op_routs_start = 0;
int op_routs_end = 0;


