/*
 * Copyright (c) 2002-2003 NONAKA Kimihiro
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

//#include "compiler.h"
//#include "dosio.h"
#include "cpu.h"
#include "ia32.mcr"

#include "inst_table.h"

#if defined(ENABLE_TRAP)
#include "trap/steptrap.h"
#endif

#if defined(SUPPORT_ASYNC_CPU)
#include "timing.h"
#include "nevent.h"
#include "pccore.h"
#include	"iocore.h"
#include	"sound/sound.h"
#include	"sound/beep.h"
#include	"sound/fmboard.h"
#include	"sound/soundrom.h"
#include	"cbus/mpu98ii.h"
#if defined(SUPPORT_SMPU98)
#include	"cbus/smpu98.h"
#endif
#endif
#include "../i386_np21.h"

#ifndef __cplusplus
sigjmp_buf exec_1step_jmpbuf;
#endif
//UINT32 realclock;

#if defined(IA32_INSTRUCTION_TRACE)
typedef struct {
	CPU_REGS		regs;
	disasm_context_t	disasm;

	BYTE			op[MAX_PREFIX + 2];
	int			opbytes;
} ia32_context_t;

#define	NCTX	1024

ia32_context_t ctx[NCTX];
int ctx_index = 0;

int cpu_inst_trace = 0;
#endif

#if defined(DEBUG)
int cpu_debug_rep_cont = 0;
CPU_REGS cpu_debug_rep_regs;
#endif

extern SINT32 __exception_set;
extern UINT32 __exception_pc;
extern UINT64 __exception_code;

#define I386_TRACE_DATA_BIT_USERDATA_SET	0x80000000
#define I386_TRACE_DATA_BIT_OP32			0x00000001
#define I386_TRACE_DATA_BIT_RET				0x00000040
#define I386_TRACE_DATA_BIT_RETF			0x00000050
#define I386_TRACE_DATA_BIT_IRET			0x00000060
#define I386_TRACE_DATA_BIT_JMP				0x00000080
#define I386_TRACE_DATA_BIT_JMP_COND		0x00000090
#define I386_TRACE_DATA_BIT_CALL			0x00000100
#define I386_TRACE_DATA_BIT_INT				0x10000000
#define I386_TRACE_DATA_BIT_IRQ				0x20000000
#define I386_TRACE_DATA_BIT_EXCEPTION		0x40000000

static __inline__ void __FASTCALL check_exception(bool is_debugging)
{
//	if(!(is_debugging)) return;
	__UNLIKELY_IF(__exception_set != 0) {
		device_debugger->exception_code = __exception_code;
		device_debugger->exception_pc = __exception_pc;
		device_debugger->exception_happened = true;
		device_debugger->add_cpu_trace_exception(__exception_code);
		__exception_set = 0;
	}
	//device_cpu->check_interrupts();
}

void
exec_1step(void)
{
	int prefix;
	UINT32 op;
	bool is_debugging = ((device_debugger != NULL) && (device_debugger->now_debugging)) ? true : false; 
	CPU_PREV_EIP = CPU_EIP;
	CPU_STATSAVE.cpu_inst = CPU_STATSAVE.cpu_inst_default;
	__exception_set = 0;
#if defined(ENABLE_TRAP)
	steptrap(CPU_CS, CPU_EIP);
#endif

#if defined(IA32_INSTRUCTION_TRACE)
	ctx[ctx_index].regs = CPU_STATSAVE.cpu_regs;
	if (cpu_inst_trace) {
		disasm_context_t *d = &ctx[ctx_index].disasm;
		UINT32 eip = CPU_EIP;
		int rv;

		rv = disasm(&eip, d);
		if (rv == 0) {
			char buf[256];
			char tmp[32];
			int len = d->nopbytes > 8 ? 8 : d->nopbytes;
			int i;

			buf[0] = '\0';
			for (i = 0; i < len; i++) {
				snprintf(tmp, sizeof(tmp), "%02x ", d->opbyte[i]);
				milstr_ncat(buf, tmp, sizeof(buf));
			}
			for (; i < 8; i++) {
				milstr_ncat(buf, "   ", sizeof(buf));
			}
			VERBOSE(("%04x:%08x: %s%s", CPU_CS, CPU_EIP, buf, d->str));

			buf[0] = '\0';
			for (; i < d->nopbytes; i++) {
				snprintf(tmp, sizeof(tmp), "%02x ", d->opbyte[i]);
				milstr_ncat(buf, tmp, sizeof(buf));
				if ((i % 8) == 7) {
					VERBOSE(("             : %s", buf));
					buf[0] = '\0';
				}
			}
			if ((i % 8) != 0) {
				VERBOSE(("             : %s", buf));
			}
		}
	}
	ctx[ctx_index].opbytes = 0;
#endif
	UINT32 old_eip = CPU_PREV_EIP;
	UINT32 old_addr = 0;
	
	for (prefix = 0; prefix < MAX_PREFIX; prefix++) {
		GET_PCBYTE(op);
//#ifdef USE_DEBUGGER
		UINT32 op_size = I386_TRACE_DATA_BIT_USERDATA_SET; 
		if (prefix == 0) {
			__LIKELY_IF(device_debugger != NULL) {
				device_debugger->add_cpu_trace(codefetch_address);
				op_size |= ((CPU_INST_AS32) ? I386_TRACE_DATA_BIT_OP32 : 0);
//				device_debugger->add_cpu_trace_userdata(op_size, (I386_TRACE_DATA_BIT_USERDATA_SET | I386_TRACE_DATA_BIT_OP32));
			}
		}
//#endif
#if defined(IA32_INSTRUCTION_TRACE)
		ctx[ctx_index].op[prefix] = op;
		ctx[ctx_index].opbytes++;
#endif
		// ToDo: more accurate call trace.
		if((op >= 0x70) && (op <= 0x7f)) {
			op_size |= I386_TRACE_DATA_BIT_JMP_COND;
		} else {
			switch(op) {
			case 0x9a: //CAll
				{
					descriptor_t *sdp = &CPU_CS_DESC;
					old_addr = sdp->u.seg.segbase + old_eip;
				}
				op_size |= I386_TRACE_DATA_BIT_CALL;
				break;
			case 0xc2: // RET far
			case 0xc3:
				{
					descriptor_t *sdp = &CPU_CS_DESC;
					old_addr = sdp->u.seg.segbase + old_eip;
				}
				op_size |= I386_TRACE_DATA_BIT_RET;
				break;
			case 0xca: // RET far
			case 0xcb:
				{
					descriptor_t *sdp = &CPU_CS_DESC;
					old_addr = sdp->u.seg.segbase + old_eip;
				}
				op_size |= I386_TRACE_DATA_BIT_RET;
				break;
			case 0xcf:
				{
					descriptor_t *sdp = &CPU_CS_DESC;
					old_addr = sdp->u.seg.segbase + old_eip;
				}
				op_size |= I386_TRACE_DATA_BIT_IRET;
				break;
			case 0xe8: //CAll
				{
					descriptor_t *sdp = &CPU_CS_DESC;
					old_addr = sdp->u.seg.segbase + old_eip;
				}
				op_size |= I386_TRACE_DATA_BIT_CALL;
			break;
			case 0xe9: //JMP
			case 0xea: //JMP16
			case 0xeb: //JMP
				op_size |= I386_TRACE_DATA_BIT_JMP;
				break;
			case 0xe3:
				op_size |= I386_TRACE_DATA_BIT_JMP_COND;
				break;
			case 0xf1:
				op_size |= I386_TRACE_DATA_BIT_INT;
				break;
			}
		}
		device_debugger->add_cpu_trace_userdata(op_size, 0xf000ffff);
	
		/* prefix */
		if (insttable_info[op] & INST_PREFIX) {
			(*insttable_1byte[0][op])();
			switch(op) {
			case 0x9a: //CAll
			case 0xe8: //CAll
				{
					descriptor_t *sdp = &CPU_CS_DESC;
					uint32_t new_addr = sdp->u.seg.segbase + CPU_EIP;
					device_debugger->add_cpu_trace_call(old_addr, new_addr);
				}
				break;
			case 0xc2: // RET far
			case 0xc3:
			case 0xca: // RET far
			case 0xcb:
			case 0xcf: // iRET
				{
					// ToDo: Collect intr num.
					device_debugger->add_cpu_trace_return(old_addr);
				}
				break;
			}
			continue;
		}
		break;
	}
	if (prefix == MAX_PREFIX) {
		EXCEPTION(UD_EXCEPTION, 0);
	}

#if defined(IA32_INSTRUCTION_TRACE)
	if (op == 0x0f) {
		BYTE op2;
		op2 = cpu_codefetch(CPU_EIP);
		ctx[ctx_index].op[prefix + 1] = op2;
		ctx[ctx_index].opbytes++;
	}
	ctx_index = (ctx_index + 1) % NELEMENTS(ctx);
#endif
	
	/* normal / rep, but not use */
	if (!(insttable_info[op] & INST_STRING) || !CPU_INST_REPUSE) {
#if defined(DEBUG)
		cpu_debug_rep_cont = 0;
#endif
		(*insttable_1byte[CPU_INST_OP32][op])();
		check_exception(is_debugging);
		switch(op) {
		case 0x9a: //CAll
		case 0xe8: //CAll
			{
				descriptor_t *sdp = &CPU_CS_DESC;
				uint32_t new_addr = sdp->u.seg.segbase + CPU_EIP;
				device_debugger->add_cpu_trace_call(old_addr, new_addr);
			}
			break;
		case 0xc2: // RET far
		case 0xc3:
		case 0xca: // RET far
		case 0xcb:
		case 0xcf: // iRET
			{
				// ToDo: Collect intr num.
				device_debugger->add_cpu_trace_return(old_addr);
			}
			break;
		}
		return;
	}

	/* rep */
	CPU_WORKCLOCK(5);
#if defined(DEBUG)
	if (!cpu_debug_rep_cont) {
		cpu_debug_rep_cont = 1;
		cpu_debug_rep_regs = CPU_STATSAVE.cpu_regs;
	}
#endif
	if (!CPU_INST_AS32) {
		if (CPU_CX != 0) {
			if (!(insttable_info[op] & REP_CHECKZF)) {
				/* rep */
				for (;;) {
					(*insttable_1byte[CPU_INST_OP32][op])();
					check_exception(is_debugging);
					if (--CPU_CX == 0) {
#if defined(DEBUG)
						cpu_debug_rep_cont = 0;
#endif
						break;
					}
					if (CPU_REMCLOCK <= 0) {
						CPU_EIP = CPU_PREV_EIP;
						break;
					}
					if(device_cpu->check_interrupts()) {
						CPU_EIP = CPU_PREV_EIP;
						break;
					}
				}
			} else if (CPU_INST_REPUSE != 0xf2) {
				/* repe */
				for (;;) {
					(*insttable_1byte[CPU_INST_OP32][op])();
					check_exception(is_debugging);
					if (--CPU_CX == 0 || CC_NZ) {
#if defined(DEBUG)
						cpu_debug_rep_cont = 0;
#endif
						break;
					}
					if (CPU_REMCLOCK <= 0) {
						CPU_EIP = CPU_PREV_EIP;
						break;
					}
					if(device_cpu->check_interrupts()) {
						CPU_EIP = CPU_PREV_EIP;
						break;
					}
				}
			} else {
				/* repne */
				for (;;) {
					(*insttable_1byte[CPU_INST_OP32][op])();
					check_exception(is_debugging);
					if (--CPU_CX == 0 || CC_Z) {
#if defined(DEBUG)
						cpu_debug_rep_cont = 0;
#endif
						break;
					}
					if (CPU_REMCLOCK <= 0) {
						CPU_EIP = CPU_PREV_EIP;
						break;
					}
					if(device_cpu->check_interrupts()) {
						CPU_EIP = CPU_PREV_EIP;
						break;
					}
				}
			}
		}
	} else {
		if (CPU_ECX != 0) {
			if (!(insttable_info[op] & REP_CHECKZF)) {
				/* rep */
				for (;;) {
					(*insttable_1byte[CPU_INST_OP32][op])();
					check_exception(is_debugging);
					if (--CPU_ECX == 0) {
#if defined(DEBUG)
						cpu_debug_rep_cont = 0;
#endif
						break;
					}
					if (CPU_REMCLOCK <= 0) {
						CPU_EIP = CPU_PREV_EIP;
						break;
					}
					if(device_cpu->check_interrupts()) {
						CPU_EIP = CPU_PREV_EIP;
						break;
					}
				}
			} else if (CPU_INST_REPUSE != 0xf2) {
				/* repe */
				for (;;) {
					(*insttable_1byte[CPU_INST_OP32][op])();
					check_exception(is_debugging);
					if (--CPU_ECX == 0 || CC_NZ) {
#if defined(DEBUG)
						cpu_debug_rep_cont = 0;
#endif
						break;
					}
					if (CPU_REMCLOCK <= 0) {
						CPU_EIP = CPU_PREV_EIP;
						break;
					}
					if(device_cpu->check_interrupts()) {
						CPU_EIP = CPU_PREV_EIP;
						break;
					}
				}
			} else {
				/* repne */
				for (;;) {
					(*insttable_1byte[CPU_INST_OP32][op])();
					check_exception(is_debugging);
					if (--CPU_ECX == 0 || CC_Z) {
#if defined(DEBUG)
						cpu_debug_rep_cont = 0;
#endif
						break;
					}
					if (CPU_REMCLOCK <= 0) {
						CPU_EIP = CPU_PREV_EIP;
						break;
					}
					if(device_cpu->check_interrupts()) {
						CPU_EIP = CPU_PREV_EIP;
						break;
					}
				}
			}
		}
	}
}

#if 0
void
exec_allstep(void)
{
	int prefix;
	UINT32 op;
	void (*func)(void);
#if defined(SUPPORT_ASYNC_CPU)
	int firstflag = 1;
	UINT timing;
	UINT lcflag = 0;
	SINT32 oldremclock = CPU_REMCLOCK;
	static int remclock_mul = 1000;
	int remclockb = 0;
	int remclkcnt = 0x100;
	static int latecount = 0;
	static int latecount2 = 0;
	static int hltflag = 0;
	bool is_debugging = ((device_debugger != NULL) && (device_debugger->now_debugging)) ? true : false; 
	__exception_set = 0;
	
	if(latecount2==0){
		if(latecount > 0){
			//latecount--;
		}else if (latecount < 0){
			latecount++;
		}
	}
	latecount2 = (latecount2+1) & 0x1fff;
#endif
	
	do {

		CPU_PREV_EIP = CPU_EIP;
		CPU_STATSAVE.cpu_inst = CPU_STATSAVE.cpu_inst_default;

	#if defined(ENABLE_TRAP)
		steptrap(CPU_CS, CPU_EIP);
	#endif

	#if defined(IA32_INSTRUCTION_TRACE)
		ctx[ctx_index].regs = CPU_STATSAVE.cpu_regs;
		if (cpu_inst_trace) {
			disasm_context_t *d = &ctx[ctx_index].disasm;
			UINT32 eip = CPU_EIP;
			int rv;

			rv = disasm(&eip, d);
			if (rv == 0) {
				char buf[256];
				char tmp[32];
				int len = d->nopbytes > 8 ? 8 : d->nopbytes;
				int i;

				buf[0] = '\0';
				for (i = 0; i < len; i++) {
					snprintf(tmp, sizeof(tmp), "%02x ", d->opbyte[i]);
					milstr_ncat(buf, tmp, sizeof(buf));
				}
				for (; i < 8; i++) {
					milstr_ncat(buf, "   ", sizeof(buf));
				}
				VERBOSE(("%04x:%08x: %s%s", CPU_CS, CPU_EIP, buf, d->str));

				buf[0] = '\0';
				for (; i < d->nopbytes; i++) {
					snprintf(tmp, sizeof(tmp), "%02x ", d->opbyte[i]);
					milstr_ncat(buf, tmp, sizeof(buf));
					if ((i % 8) == 7) {
						VERBOSE(("             : %s", buf));
						buf[0] = '\0';
					}
				}
				if ((i % 8) != 0) {
					VERBOSE(("             : %s", buf));
				}
			}
		}
		ctx[ctx_index].opbytes = 0;
	#endif

		for (prefix = 0; prefix < MAX_PREFIX; prefix++) {
			GET_PCBYTE(op);
	#if defined(IA32_INSTRUCTION_TRACE)
			ctx[ctx_index].op[prefix] = op;
			ctx[ctx_index].opbytes++;
	#endif

			/* prefix */
			if (insttable_info[op] & INST_PREFIX) {
				(*insttable_1byte[0][op])();
				check_exception(is_debugging);	
				switch(op) {
				case 0x9a: //CAll
				case 0xe8: //CAll
					{
						descriptor_t *sdp = &CPU_CS_DESC;
						uint32_t new_addr = sdp->u.seg.segbase + CPU_EIP;
						device_debugger->add_cpu_trace_call(old_addr, new_addr);
					}
					break;
				case 0xcc: // INTr
				case 0xcd:
				case 0xce:
				case 0xf1:
					{
						// ToDo: Collect intr num.
						descriptor_t *sdp = &CPU_CS_DESC;
						uint32_t new_addr = sdp->u.seg.segbase + CPU_EIP;
						device_debugger->add_cpu_trace_call(old_addr, new_addr);
					}
					break;
				case 0xc2: // RET far
				case 0xc3:
				case 0xca: // RET far
				case 0xcb:
				case 0xcf: // iRET
					{
						// ToDo: Collect intr num.
						device_debugger->add_cpu_trace_return(old_addr);
					}
					break;
				}
				continue;
			}
			break;
		}
		if (prefix == MAX_PREFIX) {
			EXCEPTION(UD_EXCEPTION, 0);
		}
		check_exception(is_debugging);

	#if defined(IA32_INSTRUCTION_TRACE)
		if (op == 0x0f) {
			BYTE op2;
			op2 = cpu_codefetch(CPU_EIP);
			ctx[ctx_index].op[prefix + 1] = op2;
			ctx[ctx_index].opbytes++;
		}
		ctx_index = (ctx_index + 1) % NELEMENTS(ctx);
	#endif
	
		/* normal / rep, but not use */
		if (!(insttable_info[op] & INST_STRING) || !CPU_INST_REPUSE) {
	#if defined(DEBUG)
			cpu_debug_rep_cont = 0;
	#endif
			(*insttable_1byte[CPU_INST_OP32][op])();
			check_exception(is_debugging);
			switch(op) {
			case 0x9a: //CAll
			case 0xe8: //CAll
				{
					descriptor_t *sdp = &CPU_CS_DESC;
					uint32_t new_addr = sdp->u.seg.segbase + CPU_EIP;
					device_debugger->add_cpu_trace_call(old_addr, new_addr);
				}
				break;
			case 0xcc: // INTr
			case 0xcd:
			case 0xce:
			case 0xf1:
				{
					// ToDo: Collect intr num.
					descriptor_t *sdp = &CPU_CS_DESC;
					uint32_t new_addr = sdp->u.seg.segbase + CPU_EIP;
					device_debugger->add_cpu_trace_call(old_addr, new_addr);
				}
				break;
			case 0xc2: // RET far
			case 0xc3:
			case 0xca: // RET far
			case 0xcb:
			case 0xcf: // iRET
				{
					// ToDo: Collect intr num.
					device_debugger->add_cpu_trace_return(old_addr);
				}
				break;
			}
			goto cpucontinue; //continue;
		}

		/* rep */
		CPU_WORKCLOCK(5);
	#if defined(DEBUG)
		if (!cpu_debug_rep_cont) {
			cpu_debug_rep_cont = 1;
			cpu_debug_rep_regs = CPU_STATSAVE.cpu_regs;
		}
	#endif
		func = insttable_1byte[CPU_INST_OP32][op];
		if (!CPU_INST_AS32) {
			if (CPU_CX != 0) {
				if(CPU_CX==1){
					(*func)();
					check_exception(is_debugging);
					--CPU_CX;
				}else{
					if (!(insttable_info[op] & REP_CHECKZF)) {
						if(insttable_1byte_repfunc[CPU_INST_OP32][op]){
							(*insttable_1byte_repfunc[CPU_INST_OP32][op])(0);
							check_exception(is_debugging);
						}else{
							/* rep */
							for (;;) {
								(*func)();
								check_exception(is_debugging);
								if (--CPU_CX == 0) {
			#if defined(DEBUG)
									cpu_debug_rep_cont = 0;
			#endif
									break;
								}
								if (CPU_REMCLOCK <= 0) {
									CPU_EIP = CPU_PREV_EIP;
									break;
								}
								if(device_cpu->check_interrupts()) {
									CPU_EIP = CPU_PREV_EIP;
									break;
								}
							}
						}
					} else if (CPU_INST_REPUSE != 0xf2) {
						if(insttable_1byte_repfunc[CPU_INST_OP32][op]){
							(*insttable_1byte_repfunc[CPU_INST_OP32][op])(1);
							check_exception(is_debugging);
						}else{
							/* repe */
							for (;;) {
								(*func)();
								check_exception(is_debugging);
								if (--CPU_CX == 0 || CC_NZ) {
			#if defined(DEBUG)
									cpu_debug_rep_cont = 0;
			#endif
									break;
								}
								if (CPU_REMCLOCK <= 0) {
									CPU_EIP = CPU_PREV_EIP;
									break;
								}
								if(device_cpu->check_interrupts()) {
									CPU_EIP = CPU_PREV_EIP;
									break;
								}
							}
						}
					} else {
						if(insttable_1byte_repfunc[CPU_INST_OP32][op]){
							(*insttable_1byte_repfunc[CPU_INST_OP32][op])(2);
							check_exception(is_debugging);
						}else{
							/* repne */
							for (;;) {
								(*func)();
								check_exception(is_debugging);
								if (--CPU_CX == 0 || CC_Z) {
			#if defined(DEBUG)
									cpu_debug_rep_cont = 0;
			#endif
									break;
								}
								if (CPU_REMCLOCK <= 0) {
									CPU_EIP = CPU_PREV_EIP;
									break;
								}
								if(device_cpu->check_interrupts()) {
									CPU_EIP = CPU_PREV_EIP;
									break;
								}
							}
						}
					}
				}
			}
		} else {
			if (CPU_ECX != 0) {
				if(CPU_ECX==1){
					(*func)();
					check_exception(is_debugging);
					--CPU_ECX;
				}else{
					if (!(insttable_info[op] & REP_CHECKZF)) {
						if(insttable_1byte_repfunc[CPU_INST_OP32][op]){
							(*insttable_1byte_repfunc[CPU_INST_OP32][op])(0);
							check_exception(is_debugging);
						}else{
							/* rep */
							for (;;) {
								(*func)();
								check_exception(is_debugging);
								if (--CPU_ECX == 0) {
			#if defined(DEBUG)
									cpu_debug_rep_cont = 0;
			#endif
									break;
								}
								if (CPU_REMCLOCK <= 0) {
									CPU_EIP = CPU_PREV_EIP;
									break;
								}
								if(device_cpu->check_interrupts()) {
									CPU_EIP = CPU_PREV_EIP;
									break;
								}
							}
						}
					} else if (CPU_INST_REPUSE != 0xf2) {
						if(insttable_1byte_repfunc[CPU_INST_OP32][op]){
							(*insttable_1byte_repfunc[CPU_INST_OP32][op])(1);
							check_exception(is_debugging);
						}else{
							/* repe */
							for (;;) {
								(*func)();
								check_exception(is_debugging);
								if (--CPU_ECX == 0 || CC_NZ) {
			#if defined(DEBUG)
									cpu_debug_rep_cont = 0;
			#endif
									break;
								}
								if (CPU_REMCLOCK <= 0) {
									CPU_EIP = CPU_PREV_EIP;
									break;
								}
								if(device_cpu->check_interrupts()) {
									CPU_EIP = CPU_PREV_EIP;
									break;
								}
							}
						}
					} else {
						if(insttable_1byte_repfunc[CPU_INST_OP32][op]){
							(*insttable_1byte_repfunc[CPU_INST_OP32][op])(2);
							check_exception(is_debugging);
						}else{
							/* repne */
							for (;;) {
								(*func)();
								check_exception(is_debugging);
								if (--CPU_ECX == 0 || CC_Z) {
			#if defined(DEBUG)
									cpu_debug_rep_cont = 0;
			#endif
									break;
								}
								if (CPU_REMCLOCK <= 0) {
									CPU_EIP = CPU_PREV_EIP;
									break;
								}
								if(device_cpu->check_interrupts()) {
									CPU_EIP = CPU_PREV_EIP;
									break;
								}
							}
						}
					}
				}
			}
		}
cpucontinue:
#if defined(SUPPORT_ASYNC_CPU)
		// 非同期CPU処理
		if(np2cfg.asynccpu){
#define LATECOUNTER_THRESHOLD	6
#define LATECOUNTER_THRESHOLDM	2
			int realclock = 0;
			if(CPU_STAT_HLT){
				hltflag = pccore.multiple;
			}
			if(CPU_REMCLOCK >= 0 && !realclock && (remclkcnt > 0x7)){
				remclkcnt = 0;
				firstflag = 0;
				timing = timing_getcount_baseclock();
				if(timing!=0){
					if(!asynccpu_fastflag && !asynccpu_lateflag){
						if(remclock_mul < 100000) {
							latecount++;
							if(latecount > +LATECOUNTER_THRESHOLD){
								if(pccore.multiple > 4){
									UINT32 oldmultiple = pccore.multiple;
									if(pccore.multiple > 40){
										pccore.multiple-=3;
									}else if(pccore.multiple > 20){
										pccore.multiple-=2;
									}else{
										pccore.multiple-=1;
									}
									pccore.realclock = pccore.baseclock * pccore.multiple;
									nevent_changeclock(oldmultiple, pccore.multiple);
		
									sound_changeclock();
									pcm86_changeclock();
									beep_changeclock();
									mpu98ii_changeclock();
#if defined(SUPPORT_SMPU98)
									smpu98_changeclock();
#endif
									keyboard_changeclock();
									mouseif_changeclock();
									gdc_updateclock();
								}

								latecount = 0;
							}
						}
						asynccpu_lateflag = 1;

						CPU_REMCLOCK = 0;
						break;
					}
				}else{
					if(!hltflag && !asynccpu_lateflag && g_nevent.item[NEVENT_FLAMES].proc==screendisp && g_nevent.item[NEVENT_FLAMES].clock <= CPU_BASECLOCK){
						//CPU_REMCLOCK = 10000;
						//oldremclock = CPU_REMCLOCK;
						if(!asynccpu_fastflag){
							latecount--;
							if(latecount < -LATECOUNTER_THRESHOLDM){
								if(pccore.multiple < pccore.maxmultiple){
									UINT32 oldmultiple = pccore.multiple;
									pccore.multiple+=1;
									pccore.realclock = pccore.baseclock * pccore.multiple;
									nevent_changeclock(oldmultiple, pccore.multiple);
		
									sound_changeclock();
									pcm86_changeclock();
									beep_changeclock();
									mpu98ii_changeclock();
#if defined(SUPPORT_SMPU98)
									smpu98_changeclock();
#endif
									keyboard_changeclock();
									mouseif_changeclock();
									gdc_updateclock();
								}
								latecount = 0;
							}
							asynccpu_fastflag = 1;
						}
					}
					firstflag = 1;
				}
			}
			remclkcnt++;
		}
#else
		;
#endif

	} while (CPU_REMCLOCK > 0);
#if defined(SUPPORT_ASYNC_CPU)
	if(hltflag > 0) hltflag--;
#endif
}
#endif

