/*
 * Copyright (c) 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 "cpu.h"
#include "ia32.mcr"
#include "../../../i386_np21.h"
const char *exception_str[EXCEPTION_NUM] = {
	"DE_EXCEPTION",
	"DB_EXCEPTION",
	"NMI_EXCEPTION",
	"BP_EXCEPTION",
	"OF_EXCEPTION",
	"BR_EXCEPTION",
	"UD_EXCEPTION",
	"NM_EXCEPTION",
	"DF_EXCEPTION",
	"CoProcesser Segment Overrun",
	"TS_EXCEPTION",
	"NP_EXCEPTION",
	"SS_EXCEPTION",
	"GP_EXCEPTION",
	"PF_EXCEPTION",
	"Reserved",
	"MF_EXCEPTION",
	"AC_EXCEPTION",
	"MC_EXCEPTION",
	"XF_EXCEPTION",
};

static const int exctype[EXCEPTION_NUM] = {
	1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0,
};

static const int dftable[4][4] = {
	{ 0, 0, 0, 1, },
	{ 0, 1, 0, 1, },
	{ 0, 1, 1, 1, },
	{ 1, 1, 1, 1, },
};

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

void CPUCALL
exception(int num, int error_code)
{
#if defined(DEBUG)
	extern int cpu_debug_rep_cont;
	extern CPU_REGS cpu_debug_rep_regs;
#endif
	int errorp = 0;

	__ASSERT((unsigned int)num < EXCEPTION_NUM);

#if 0
	iptrace_out();
	debugwriteseg("execption.bin", &CPU_CS_DESC, CPU_PREV_EIP & 0xffff0000, 0x10000);
#endif

	VERBOSE(("exception: -------------------------------------------------------------- start"));
	VERBOSE(("exception: %s, error_code = %x at %04x:%08x", exception_str[num], error_code, CPU_CS, CPU_PREV_EIP));
	VERBOSE(("%s", cpu_reg2str()));
	VERBOSE(("code: %dbit(%dbit), address: %dbit(%dbit)", CPU_INST_OP32 ? 32 : 16, CPU_STATSAVE.cpu_inst_default.op_32 ? 32 : 16, CPU_INST_AS32 ? 32 : 16, CPU_STATSAVE.cpu_inst_default.as_32 ? 32 : 16));
#if defined(DEBUG)
	if (cpu_debug_rep_cont) {
		VERBOSE(("rep: original regs: ecx=%08x, esi=%08x, edi=%08x", cpu_debug_rep_regs.reg[CPU_ECX_INDEX].d, cpu_debug_rep_regs.reg[CPU_ESI_INDEX].d, cpu_debug_rep_regs.reg[CPU_EDI_INDEX].d));
	}
	VERBOSE(("%s", cpu_disasm2str(CPU_PREV_EIP)));
#endif
	__exception_set = 1;
	__exception_pc = device_cpu->get_pc();
	__exception_code = num;
	
	CPU_STAT_EXCEPTION_COUNTER_INC();
	__UNLIKELY_IF((CPU_STAT_EXCEPTION_COUNTER >= 3) 
	 || (CPU_STAT_EXCEPTION_COUNTER == 2 && CPU_STAT_PREV_EXCEPTION == DF_EXCEPTION)) {
		/* Triple fault */
		ia32_panic("exception: catch triple fault!");
	}

	switch (num) {
	case UD_EXCEPTION:	/* (F) 無効オペコード */
		//ia32_warning("warning: undefined op!");
		//{ // TEST!!
		//	UINT32 op[5];
		//	CPU_EIP = CPU_PREV_EIP;
		//	GET_PCBYTE(op[0]);
		//	GET_PCBYTE(op[1]);
		//	GET_PCBYTE(op[2]);
		//	GET_PCBYTE(op[3]);
		//	GET_PCBYTE(op[4]);
		//}
	case DE_EXCEPTION:	/* (F) 除算エラー */
	case DB_EXCEPTION:	/* (F/T) デバッグ */
	case BR_EXCEPTION:	/* (F) BOUND の範囲外 */
	case NM_EXCEPTION:	/* (F) デバイス使用不可 (FPU が無い) */
	case MF_EXCEPTION:	/* (F) 浮動小数点エラー */
		CPU_EIP = CPU_PREV_EIP;
		if (CPU_STATSAVE.cpu_stat.backout_sp)
			CPU_ESP = CPU_PREV_ESP;
		/*FALLTHROUGH*/
	case NMI_EXCEPTION:	/* (I) NMI 割り込み */
	case BP_EXCEPTION:	/* (T) ブレークポイント */
	case OF_EXCEPTION:	/* (T) オーバーフロー */
		errorp = 0;
		break;

	case DF_EXCEPTION:	/* (A) ダブルフォルト (errcode: 0) */
		errorp = 1;
		error_code = 0;
		break;

	case AC_EXCEPTION:	/* (F) アラインメントチェック (errcode: 0) */
		error_code = 0;
		/*FALLTHROUGH*/
	case TS_EXCEPTION:	/* (F) 無効 TSS (errcode) */
	case NP_EXCEPTION:	/* (F) セグメント不在 (errcode) */
	case SS_EXCEPTION:	/* (F) スタックセグメントフォルト (errcode) */
	case GP_EXCEPTION:	/* (F) 一般保護例外 (errcode) */
	case PF_EXCEPTION:	/* (F) ページフォルト (errcode) */
		CPU_EIP = CPU_PREV_EIP;
		if (CPU_STATSAVE.cpu_stat.backout_sp)
			CPU_ESP = CPU_PREV_ESP;
		errorp = 1;
		break;

	default:
		ia32_panic("exception: unknown exception (%d)", num);
		break;
	}
//	device_cpu->out_debug_log("EXCEPTION: %s PC=%08X", exception_str[num], device_cpu->get_pc());

	__UNLIKELY_IF(CPU_STAT_EXCEPTION_COUNTER >= 2) {
		if (dftable[exctype[CPU_STAT_PREV_EXCEPTION]][exctype[num]]) {
			num = DF_EXCEPTION;
			errorp = 1;
			error_code = 0;
		}
	}
	CPU_STAT_PREV_EXCEPTION = num;

	VERBOSE(("exception: ---------------------------------------------------------------- end"));

	interrupt(num, INTR_TYPE_EXCEPTION, errorp, error_code);
	CPU_STAT_EXCEPTION_COUNTER_CLEAR();
#ifdef __cplusplus
	throw(1);
#else
	siglongjmp(exec_1step_jmpbuf, 1);
#endif
}

/*
 * コール・ゲート・ディスクリプタ
 *
 *  31                                16 15 14 13 12       8 7   5 4       0
 * +------------------------------------+--+-----+----------+-----+---------+
 * |         オフセット 31..16          | P| DPL | 0 D 1 0 0|0 0 0|カウント | 4
 * +------------------------------------+--+-----+----------+-----+---------+
 *  31                                16 15                                0
 * +------------------------------------+-----------------------------------+
 * |        セグメント・セレクタ        |          オフセット 15..0         | 0
 * +------------------------------------+-----------------------------------+
 */

/*
 * 割り込みディスクリプタ
 *--
 * タスク・ゲート
 *
 *  31                                16 15 14 13 12       8 7             0
 * +------------------------------------+--+-----+----------+---------------+
 * |              Reserved              | P| DPL | 0 0 1 0 1|   Reserved    | 4
 * +------------------------------------+--+-----+----------+---------------+
 *  31                                16 15                                0
 * +------------------------------------+-----------------------------------+
 * |      TSS セグメント・セレクタ      |              Reserved             | 0
 * +------------------------------------+-----------------------------------+
 *--
 * 割り込み・ゲート
 *
 *  31                                16 15 14 13 12       8 7   5 4       0
 * +------------------------------------+--+-----+----------+-----+---------+
 * |         オフセット 31..16          | P| DPL | 0 D 1 1 0|0 0 0|Reserved | 4
 * +------------------------------------+--+-----+----------+-----+---------+
 *  31                                16 15                                0
 * +------------------------------------+-----------------------------------+
 * |        セグメント・セレクタ        |          オフセット 15..0         | 0
 * +------------------------------------+-----------------------------------+
 *--
 * トラップ・ゲート
 *
 *  31                                16 15 14 13 12       8 7   5 4       0
 * +------------------------------------+--+-----+----------+-----+---------+
 * |         オフセット 31..16          | P| DPL | 0 D 1 1 1|0 0 0|Reserved | 4
 * +------------------------------------+--+-----+----------+-----+---------+
 *  31                                16 15                                0
 * +------------------------------------+-----------------------------------+
 * |        セグメント・セレクタ        |          オフセット 15..0         | 0
 * +------------------------------------+-----------------------------------+
 *--
 * DPL        : ディスクリプタ特権レベル
 * オフセット : プロシージャ・エントリ・ポイントまでのオフセット
 * P          : セグメント存在フラグ
 * セレクタ   : ディスティネーション・コード・セグメントのセグメント・セレクタ
 * D          : ゲートのサイズ．0 = 16 bit, 1 = 32 bit
 */

static void CPUCALL interrupt_task_gate(const descriptor_t *gsdp, int intrtype, int errorp, int error_code);
static void CPUCALL interrupt_intr_or_trap(const descriptor_t *gsdp, int intrtype, int errorp, int error_code);

void CPUCALL
interrupt(int num, int intrtype, int errorp, int error_code)
{
	descriptor_t gsd;
	UINT idt_idx;
	UINT32 new_ip;
	UINT16 new_cs;
	int exc_errcode;

	VERBOSE(("interrupt: num = 0x%02x, intrtype = %s, errorp = %s, error_code = %08x", num, (intrtype == INTR_TYPE_EXTINTR) ? "external" : (intrtype == INTR_TYPE_EXCEPTION ? "exception" : "softint"), errorp ? "on" : "off", error_code));

//#ifdef I86_PSEUDO_BIOS
	__UNLIKELY_IF(/*(!CPU_STAT_PM || CPU_STAT_VM86) && */intrtype == INTR_TYPE_SOFTINTR && device_bios != NULL) {
//		uint16_t regs[8] = {CPU_AX, CPU_CX, CPU_DX, CPU_BX, CPU_SP, CPU_BP, CPU_SI, CPU_DI};
		uint32_t regs[10] = {CPU_EAX, CPU_ECX, CPU_EDX, CPU_EBX, CPU_ESP, CPU_EBP, CPU_ESI, CPU_EDI, 0, 0};
		const uint16_t sregs[4] = {CPU_ES, CPU_CS, CPU_SS, CPU_DS};
		int32_t ZeroFlag = ((CPU_FLAG & Z_FLAG) ? 1 : 0);
		int32_t CarryFlag = ((CPU_FLAG & C_FLAG) ? 1 : 0);
		int cycles = 0;
		uint64_t total_cycles = 0;
		//printf("%s\n", device_bios->get_device_name());
//		if (device_bios->bios_int_i86(num, regs, sregs, &ZeroFlag, &CarryFlag)) {
		if (device_bios->bios_int_ia32(num, regs, sregs, &ZeroFlag, &CarryFlag, &cycles, &total_cycles)) {
			CPU_EAX = regs[0];
			CPU_ECX = regs[1];
			CPU_EDX = regs[2];
			CPU_EBX = regs[3];
			CPU_ESP = regs[4];
			CPU_EBP = regs[5];
			CPU_ESI = regs[6];
			CPU_EDI = regs[7];
			if (ZeroFlag) {
				CPU_FLAG |=  Z_FLAG;
			} else {
				CPU_FLAG &= ~Z_FLAG;
			}
			if (CarryFlag) {
				CPU_FLAG |=  C_FLAG;
			} else {
				CPU_FLAG &= ~C_FLAG;
			}
			CPU_WORKCLOCK(1000); // temporary
			CPU_REMCLOCK -= cycles;
			/*if((regs[8] != 0x0000) || (regs[9] != 0x0000)) {
				uint32_t hi = regs[9];
				uint32_t lo = regs[8];
				uint32_t __addr = (hi << 16) | lo;
				CPU_EIP = __addr;
			}*/
			return;
		}
	}
//#endif

	CPU_SET_PREV_ESP();

	__UNLIKELY_IF(!CPU_STAT_PM) {
		/* real mode */
		CPU_WORKCLOCK(20);

		idt_idx = num * 4;
		__UNLIKELY_IF(idt_idx + 3 > CPU_IDTR_LIMIT) {
			VERBOSE(("interrupt: real-mode IDTR limit check failure (idx = 0x%04x, limit = 0x%08x", idt_idx, CPU_IDTR_LIMIT));
			EXCEPTION(GP_EXCEPTION, idt_idx + 2);
		}

		__UNLIKELY_IF((intrtype == INTR_TYPE_EXTINTR) && CPU_STAT_HLT) {
			VERBOSE(("interrupt: reset HTL in real mode"));
			CPU_EIP++;
			CPU_STAT_HLT = 0;
		}

		REGPUSH0(REAL_FLAGREG);
		REGPUSH0(CPU_CS);
		REGPUSH0(CPU_IP);

		CPU_EFLAG &= ~(T_FLAG | I_FLAG | AC_FLAG | RF_FLAG);
		CPU_TRAP = 0;

		new_ip = cpu_memoryread_w(CPU_IDTR_BASE + idt_idx);
		new_cs = cpu_memoryread_w(CPU_IDTR_BASE + idt_idx + 2);
		LOAD_SEGREG(CPU_CS_INDEX, new_cs);
		CPU_EIP = new_ip;
	} else {
		/* protected mode */
		CPU_WORKCLOCK(200);

		VERBOSE(("interrupt: -------------------------------------------------------------- start"));
		VERBOSE(("interrupt: old EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP));

#if defined(DEBUG)
		if (num == 0x80) {
			/* Linux, FreeBSD, NetBSD, OpenBSD system call */
			VERBOSE(("interrupt: syscall# = %d\n%s", CPU_EAX, cpu_reg2str()));
		}
#endif

		idt_idx = num * 8;
		exc_errcode = idt_idx + 2;
		if (intrtype == INTR_TYPE_EXTINTR)
			exc_errcode++;

		__UNLIKELY_IF(idt_idx + 7 > CPU_IDTR_LIMIT) {
			VERBOSE(("interrupt: IDTR limit check failure (idx = 0x%04x, limit = 0x%08x", idt_idx, CPU_IDTR_LIMIT));
			EXCEPTION(GP_EXCEPTION, exc_errcode);
		}

		/* load a gate descriptor from interrupt descriptor table */
		memset(&gsd, 0, sizeof(gsd));
		load_descriptor(&gsd, CPU_IDTR_BASE + idt_idx);
		__UNLIKELY_IF(!SEG_IS_VALID(&gsd)) {
			VERBOSE(("interrupt: gate descripter is invalid."));
			EXCEPTION(GP_EXCEPTION, exc_errcode);
		}
		__UNLIKELY_IF(!SEG_IS_SYSTEM(&gsd)) {
			VERBOSE(("interrupt: gate descriptor is not system segment."));
			EXCEPTION(GP_EXCEPTION, exc_errcode);
		}

		switch (gsd.type) {
		case CPU_SYSDESC_TYPE_TASK:
		case CPU_SYSDESC_TYPE_INTR_16:
		case CPU_SYSDESC_TYPE_INTR_32:
		case CPU_SYSDESC_TYPE_TRAP_16:
		case CPU_SYSDESC_TYPE_TRAP_32:
			break;

		default:
			VERBOSE(("interrupt: invalid gate type (%d)", gsd.type));
			EXCEPTION(GP_EXCEPTION, exc_errcode);
			break;
		}

		/* 5.10.1.1. 例外／割り込みハンドラ・プロシージャの保護 */
		__UNLIKELY_IF((intrtype == INTR_TYPE_SOFTINTR) && (gsd.dpl < CPU_STAT_CPL)) {
			VERBOSE(("interrupt: intrtype(softint) && DPL(%d) < CPL(%d)", gsd.dpl, CPU_STAT_CPL));
			EXCEPTION(GP_EXCEPTION, exc_errcode);
		}

		__UNLIKELY_IF(!SEG_IS_PRESENT(&gsd)) {
			VERBOSE(("interrupt: gate descriptor is not present."));
			EXCEPTION(NP_EXCEPTION, exc_errcode);
		}

		__UNLIKELY_IF((intrtype == INTR_TYPE_EXTINTR) && CPU_STAT_HLT) {
			VERBOSE(("interrupt: reset HTL in protected mode"));
			CPU_EIP++;
			CPU_STAT_HLT = 0;
		}

		switch (gsd.type) {
		case CPU_SYSDESC_TYPE_TASK:
			interrupt_task_gate(&gsd, intrtype, errorp, error_code);
			break;

		case CPU_SYSDESC_TYPE_INTR_16:
		case CPU_SYSDESC_TYPE_INTR_32:
		case CPU_SYSDESC_TYPE_TRAP_16:
		case CPU_SYSDESC_TYPE_TRAP_32:
			interrupt_intr_or_trap(&gsd, intrtype, errorp, error_code);
			break;

		default:
			EXCEPTION(GP_EXCEPTION, exc_errcode);
			break;
		}

		VERBOSE(("interrupt: ---------------------------------------------------------------- end"));
	}

	CPU_CLEAR_PREV_ESP();
}

static void CPUCALL
interrupt_task_gate(const descriptor_t *gsdp, int intrtype, int errorp, int error_code)
{
	selector_t task_sel;
	int rv;

	VERBOSE(("interrupt: TASK-GATE"));

	rv = parse_selector(&task_sel, gsdp->u.gate.selector);
	__UNLIKELY_IF(rv < 0 || task_sel.ldt || !SEG_IS_SYSTEM(&task_sel.desc)) {
		VERBOSE(("interrupt: parse_selector (selector = %04x, rv = %d, %cDT, type = %s)", gsdp->u.gate.selector, rv, task_sel.ldt ? 'L' : 'G', task_sel.desc.s ? "code/data" : "system"));
		EXCEPTION(TS_EXCEPTION, task_sel.idx);
	}

	/* check gate type */
	switch (task_sel.desc.type) {
	case CPU_SYSDESC_TYPE_TSS_16:
	case CPU_SYSDESC_TYPE_TSS_32:
		break;

	case CPU_SYSDESC_TYPE_TSS_BUSY_16:
	case CPU_SYSDESC_TYPE_TSS_BUSY_32:
		VERBOSE(("interrupt: task is busy."));
		/*FALLTHROUGH*/
	default:
		VERBOSE(("interrupt: invalid gate type (%d)", task_sel.desc.type));
		EXCEPTION(TS_EXCEPTION, task_sel.idx);
		break;
	}

	/* not present */
	__UNLIKELY_IF(selector_is_not_present(&task_sel)) {
		VERBOSE(("interrupt: selector is not present"));
		EXCEPTION(NP_EXCEPTION, task_sel.idx);
	}

	task_switch(&task_sel, TASK_SWITCH_INTR);

	CPU_SET_PREV_ESP();

	__UNLIKELY_IF(errorp) {
		VERBOSE(("interrupt: push error code (%08x)", error_code));
		if (task_sel.desc.type == CPU_SYSDESC_TYPE_TSS_32) {
			PUSH0_32(error_code);
		} else {
			PUSH0_16(error_code);
		}
	}

	/* out of range */
	__UNLIKELY_IF(CPU_EIP > CPU_STAT_CS_LIMIT) {
		VERBOSE(("interrupt: new_ip is out of range. new_ip = %08x, limit = %08x", CPU_EIP, CPU_STAT_CS_LIMIT));
		EXCEPTION(GP_EXCEPTION, 0);
	}
}

static void CPUCALL
interrupt_intr_or_trap(const descriptor_t *gsdp, int intrtype, int errorp, int error_code)
{
	selector_t cs_sel, ss_sel;
	UINT stacksize;
	UINT32 old_flags;
	UINT32 new_flags;
	UINT32 mask;
	UINT32 sp;
	UINT32 new_ip, new_sp;
	UINT32 old_ip, old_sp;
	UINT16 old_cs, old_ss, new_ss;
	BOOL is32bit;
	int exc_errcode;
	int rv; 

	new_ip = gsdp->u.gate.offset;
	old_ss = CPU_SS;
	old_cs = CPU_CS;
	old_ip = CPU_EIP;
	old_sp = CPU_ESP;
	old_flags = REAL_EFLAGREG;
	new_flags = REAL_EFLAGREG & ~(T_FLAG|RF_FLAG|NT_FLAG|VM_FLAG);
	mask = T_FLAG|RF_FLAG|NT_FLAG|VM_FLAG;

	switch (gsdp->type) {
	case CPU_SYSDESC_TYPE_INTR_16:
	case CPU_SYSDESC_TYPE_INTR_32:
		VERBOSE(("interrupt: INTERRUPT-GATE"));
		new_flags &= ~I_FLAG;
		mask |= I_FLAG;
		break;

	case CPU_SYSDESC_TYPE_TRAP_16:
	case CPU_SYSDESC_TYPE_TRAP_32:
		VERBOSE(("interrupt: TRAP-GATE"));
		break;

	default:
		ia32_panic("interrupt: gate descriptor type is invalid (type = %d)", gsdp->type);
		break;
	}

	exc_errcode = gsdp->u.gate.selector & ~3;
	if (intrtype == INTR_TYPE_EXTINTR)
		exc_errcode++;

	rv = parse_selector(&cs_sel, gsdp->u.gate.selector);
	__UNLIKELY_IF(rv < 0) {
		VERBOSE(("interrupt: parse_selector (selector = %04x, rv = %d)", gsdp->u.gate.selector, rv));
		EXCEPTION(GP_EXCEPTION, exc_errcode);
	}

	/* check segment type */
	__UNLIKELY_IF(SEG_IS_SYSTEM(&cs_sel.desc)) {
		VERBOSE(("interrupt: code segment is system segment"));
		EXCEPTION(GP_EXCEPTION, exc_errcode);
	}
	__UNLIKELY_IF(SEG_IS_DATA(&cs_sel.desc)) {
		VERBOSE(("interrupt: code segment is data segment"));
		EXCEPTION(GP_EXCEPTION, exc_errcode);
	}

	/* check privilege level */
	__UNLIKELY_IF(cs_sel.desc.dpl > CPU_STAT_CPL) {
		VERBOSE(("interrupt: DPL(%d) > CPL(%d)", cs_sel.desc.dpl, CPU_STAT_CPL));
		EXCEPTION(GP_EXCEPTION, exc_errcode);
	}

	/* not present */
	__UNLIKELY_IF(selector_is_not_present(&cs_sel)) {
		VERBOSE(("interrupt: selector is not present"));
		EXCEPTION(NP_EXCEPTION, exc_errcode);
	}

	is32bit = gsdp->type & CPU_SYSDESC_TYPE_32BIT;
	__UNLIKELY_IF(!SEG_IS_CONFORMING_CODE(&cs_sel.desc) && (cs_sel.desc.dpl < CPU_STAT_CPL)) {
		stacksize = errorp ? 12 : 10;
		if (!CPU_STAT_VM86) {
			VERBOSE(("interrupt: INTER-PRIVILEGE-LEVEL-INTERRUPT"));
		} else {
			/* VM86 */
			VERBOSE(("interrupt: INTERRUPT-FROM-VIRTUAL-8086-MODE"));
			if (cs_sel.desc.dpl != 0) {
				/* 16.3.1.1 */
				VERBOSE(("interrupt: DPL[CS](%d) != 0", cs_sel.desc.dpl));
				EXCEPTION(GP_EXCEPTION, exc_errcode);
			}
			stacksize += 8;
		}
		__LIKELY_IF(is32bit) {
			stacksize *= 2;
		}

		/* get stack pointer from TSS */
		get_stack_pointer_from_tss(cs_sel.desc.dpl, &new_ss, &new_sp);

		/* parse stack segment descriptor */
		rv = parse_selector(&ss_sel, new_ss);

		/* update exception error code */
		exc_errcode = ss_sel.idx;
		if (intrtype == INTR_TYPE_EXTINTR)
			exc_errcode++;

		__UNLIKELY_IF(rv < 0) {
			VERBOSE(("interrupt: parse_selector (selector = %04x, rv = %d)", new_ss, rv));
			EXCEPTION(TS_EXCEPTION, exc_errcode);
		}

		/* check privilege level */
		__UNLIKELY_IF(ss_sel.rpl != cs_sel.desc.dpl) {
			VERBOSE(("interrupt: selector RPL[SS](%d) != DPL[CS](%d)", ss_sel.rpl, cs_sel.desc.dpl));
			EXCEPTION(TS_EXCEPTION, exc_errcode);
		}
		__UNLIKELY_IF(ss_sel.desc.dpl != cs_sel.desc.dpl) {
			VERBOSE(("interrupt: descriptor DPL[SS](%d) != DPL[CS](%d)", ss_sel.desc.dpl, cs_sel.desc.dpl));
			EXCEPTION(TS_EXCEPTION, exc_errcode);
		}

		/* stack segment must be writable data segment. */
		__UNLIKELY_IF(SEG_IS_SYSTEM(&ss_sel.desc)) {
			VERBOSE(("interrupt: stack segment is system segment"));
			EXCEPTION(TS_EXCEPTION, exc_errcode);
		}
		__UNLIKELY_IF(SEG_IS_CODE(&ss_sel.desc)) {
			VERBOSE(("interrupt: stack segment is code segment"));
			EXCEPTION(TS_EXCEPTION, exc_errcode);
		}
		__UNLIKELY_IF(!SEG_IS_WRITABLE_DATA(&ss_sel.desc)) {
			VERBOSE(("interrupt: stack segment is read-only data segment"));
			EXCEPTION(TS_EXCEPTION, exc_errcode);
		}

		/* not present */
		__UNLIKELY_IF(selector_is_not_present(&ss_sel)) {
			VERBOSE(("interrupt: selector is not present"));
			EXCEPTION(SS_EXCEPTION, exc_errcode);
		}

		/* check stack room size */
		cpu_stack_push_check(ss_sel.idx, &ss_sel.desc, new_sp, stacksize, ss_sel.desc.d);

		/* out of range */
		__UNLIKELY_IF(new_ip > cs_sel.desc.u.seg.limit) {
			VERBOSE(("interrupt: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, cs_sel.desc.u.seg.limit));
			EXCEPTION(GP_EXCEPTION, 0);
		}

		load_ss(ss_sel.selector, &ss_sel.desc, cs_sel.desc.dpl);
		CPU_ESP = new_sp;

		load_cs(cs_sel.selector, &cs_sel.desc, cs_sel.desc.dpl);
		CPU_EIP = new_ip;

		__LIKELY_IF(is32bit) {
			__UNLIKELY_IF(CPU_STAT_VM86) {
				PUSH0_32(CPU_GS);
				PUSH0_32(CPU_FS);
				PUSH0_32(CPU_DS);
				PUSH0_32(CPU_ES);

				LOAD_SEGREG(CPU_GS_INDEX, 0);
				CPU_STAT_SREG(CPU_GS_INDEX).valid = 0;
				LOAD_SEGREG(CPU_FS_INDEX, 0);
				CPU_STAT_SREG(CPU_FS_INDEX).valid = 0;
				LOAD_SEGREG(CPU_DS_INDEX, 0);
				CPU_STAT_SREG(CPU_DS_INDEX).valid = 0;
				LOAD_SEGREG(CPU_ES_INDEX, 0);
				CPU_STAT_SREG(CPU_ES_INDEX).valid = 0;
			}
			PUSH0_32(old_ss);
			PUSH0_32(old_sp);
			PUSH0_32(old_flags);
			PUSH0_32(old_cs);
			PUSH0_32(old_ip);
			if (errorp) {
				PUSH0_32(error_code);
			}
		} else {
			__UNLIKELY_IF(CPU_STAT_VM86) {
				ia32_panic("interrupt: 16bit gate && VM86");
			}
			PUSH0_16(old_ss);
			PUSH0_16(old_sp);
			PUSH0_16(old_flags);
			PUSH0_16(old_cs);
			PUSH0_16(old_ip);
			if (errorp) {
				PUSH0_16(error_code);
			}
		}
	} else {
		__UNLIKELY_IF(CPU_STAT_VM86) {
			VERBOSE(("interrupt: VM86"));
			EXCEPTION(GP_EXCEPTION, exc_errcode);
		}
		__UNLIKELY_IF(!SEG_IS_CONFORMING_CODE(&cs_sel.desc) && (cs_sel.desc.dpl != CPU_STAT_CPL)) {
			VERBOSE(("interrupt: %sCONFORMING-CODE-SEGMENT(%d) && DPL[CS](%d) != CPL", SEG_IS_CONFORMING_CODE(&cs_sel.desc) ? "" : "NON-", cs_sel.desc.dpl, CPU_STAT_CPL));
			EXCEPTION(GP_EXCEPTION, exc_errcode);
		}

		VERBOSE(("interrupt: INTRA-PRIVILEGE-LEVEL-INTERRUPT"));

		stacksize = errorp ? 8 : 6;
		__LIKELY_IF(is32bit) {
			stacksize *= 2;
		}

		/* check stack room size */
		__LIKELY_IF(CPU_STAT_SS32) {
			sp = CPU_ESP;
		} else {
			sp = CPU_SP;
		}
		/*
		 * 17.1
		 * コールゲート、割り込みゲート、またはトラップゲートを通じて
		 * プログラムの制御を他のコード・セグメントに移行するときは、
		 * 移行中に使用されるオペランド・サイズは使用されるゲートの
		 * タイプ（16 ビットまたは32 ビット）によって決まる（移行命
		 * 令のD フラグ、プリフィックスのいずれにもよらない）。
		 */
		SS_PUSH_CHECK1(sp, stacksize, is32bit);

		/* out of range */
		__UNLIKELY_IF(new_ip > cs_sel.desc.u.seg.limit) {
			VERBOSE(("interrupt: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, cs_sel.desc.u.seg.limit));
			EXCEPTION(GP_EXCEPTION, 0);
		}

		load_cs(cs_sel.selector, &cs_sel.desc, CPU_STAT_CPL);
		CPU_EIP = new_ip;

		__LIKELY_IF(is32bit) {
			PUSH0_32(old_flags);
			PUSH0_32(old_cs);
			PUSH0_32(old_ip);
			if (errorp) {
				PUSH0_32(error_code);
			}
		} else {
			PUSH0_16(old_flags);
			PUSH0_16(old_cs);
			PUSH0_16(old_ip);
			if (errorp) {
				PUSH0_16(error_code);
			}
		}
	}
	set_eflags(new_flags, mask);

	VERBOSE(("interrupt: new EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP));
}
