// license:BSD-3-Clause
// copyright-holders:Carl
#include "../../device.h"
#include "./i286.h"
#include "../../debugger.h"
#include "i86inline.h"


#ifndef __BIG_ENDIAN__
#define LSB_FIRST
#endif

#ifndef INLINE
#define INLINE inline
#endif
/*****************************************************************************/
/* src/emu/emucore.h */

// constants for expression endianness
enum endianness_t
{
	ENDIANNESS_LITTLE,
	ENDIANNESS_BIG
};

// declare native endianness to be one or the other
#ifdef LSB_FIRST
const endianness_t ENDIANNESS_NATIVE = ENDIANNESS_LITTLE;
#else
const endianness_t ENDIANNESS_NATIVE = ENDIANNESS_BIG;
#endif
// endian-based value: first value is if 'endian' is little-endian, second is if 'endian' is big-endian
#define ENDIAN_VALUE_LE_BE(endian,leval,beval)	(((endian) == ENDIANNESS_LITTLE) ? (leval) : (beval))
// endian-based value: first value is if native endianness is little-endian, second is if native is big-endian
#define NATIVE_ENDIAN_VALUE_LE_BE(leval,beval)	ENDIAN_VALUE_LE_BE(ENDIANNESS_NATIVE, leval, beval)
// endian-based value: first value is if 'endian' matches native, second is if 'endian' doesn't match native
#define ENDIAN_VALUE_NE_NNE(endian,leval,beval)	(((endian) == ENDIANNESS_NATIVE) ? (neval) : (nneval))

/* emumem.h */
// macros for accessing bytes and words within larger chunks

// read/write a byte to a 16-bit space
#define BYTE_XOR_BE(a)                  ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(1,0))
#define BYTE_XOR_LE(a)                  ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(0,1))

// read/write a byte to a 32-bit space
#define BYTE4_XOR_BE(a)                 ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(3,0))
#define BYTE4_XOR_LE(a)                 ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(0,3))

// read/write a word to a 32-bit space
#define WORD_XOR_BE(a)                  ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(2,0))
#define WORD_XOR_LE(a)                  ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(0,2))

// read/write a byte to a 64-bit space
#define BYTE8_XOR_BE(a)                 ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(7,0))
#define BYTE8_XOR_LE(a)                 ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(0,7))

// read/write a word to a 64-bit space
#define WORD2_XOR_BE(a)                 ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(6,0))
#define WORD2_XOR_LE(a)                 ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(0,6))

// read/write a dword to a 64-bit space
#define DWORD_XOR_BE(a)                 ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(4,0))
#define DWORD_XOR_LE(a)                 ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(0,4))


// helpers for checking address alignment
#define WORD_ALIGNED(a)                 (((a) & 1) == 0)
#define DWORD_ALIGNED(a)                (((a) & 3) == 0)
#define QWORD_ALIGNED(a)                (((a) & 7) == 0)

// I/O line states
enum line_state
{
	CLEAR_LINE = 0,				// clear (a fired or held) line
	ASSERT_LINE,				// assert an interrupt immediately
	HOLD_LINE,				// hold interrupt line until acknowledged
	PULSE_LINE				// pulse interrupt line instantaneously (only for NMI, RESET)
};

/*
 * Descriptor format
 * Data Segment
 * 48. . . . . . .40. . . . . . .32. . . . . . .24. . . . . . .16. . . . . . . 8. . . . . . . 0
 *  P|DPL|1|0|E|W|A|  BASE 23-16  |         BASE 15-0           |           LIMIT             |
 * Code Segment
 * 48. . . . . . .40. . . . . . .32. . . . . . .24. . . . . . .16. . . . . . . 8. . . . . . . 0
 *  P|DPL|1|1|C|R|A|  BASE 23-16  |         BASE 15-0           |           LIMIT             |
 * System
 * 48. . . . . . .40. . . . . . .32. . . . . . .24. . . . . . .16. . . . . . . 8. . . . . . . 0
 *  P|DPL|0| TYPE  |  BASE 23-16  |         BASE 15-0           |           LIMIT             |
 *
 *  P    == Present
 *  A    == Accessed
 *  E    == Expand Down
 *  W    == Read/Write
 *  C    == Conforming
 *  R    == Readable
 *  DPL  == Descriptor Privilege Level
 *
 *  Bits 48-64 are ignored but reserved for the 80386
 */

#define LIMIT(desc)  (desc[0]&0xffff)
#define BASE(desc)   ((desc[1]&0xffff)|((desc[2]&0xff)<<16))
#define RIGHTS(desc) ((desc[2]>>8)&0xff)

#define ACCESS(r)  (r&1)
#define SET_ACC(desc) (desc[2]|=0x100)
#define RW(r)      ((r>>1)&1)
#define READ(r)    ((r>>1)&1)
#define CONF(r)    ((r>>2)&1)
#define EXPDOWN(r) ((r>>2)&1)
#define CODE(r)    ((r>>3)&1)
#define SEGDESC(r) ((r>>4)&1)
#define DPL(r)     ((r>>5)&3)
#define PRES(r)    ((r>>7)&1)
#define GATE(r)    (r&31)
#define GATESEL(desc) (desc[1])
#define GATEOFF(desc) (desc[0])
#define GATECNT(desc) (desc[2]&31)

#define RPL(s)     (s&3)
#define IDX(s)     (s&(~7))
#define IDXTBL(s)  (s&(~3))
#define TBL(s)     (s&4)

#define TSSDESCIDLE 1
#define LDTDESC    2
#define TSSDESCBUSY 3
#define CALLGATE   4
#define TASKGATE   5
#define INTGATE    6
#define TRAPGATE   7

#define TSS_BACK 0
#define TSS_SP0  1
#define TSS_SS0  2
#define TSS_SP1  3
#define TSS_SS1  4
#define TSS_SP2  5
#define TSS_SS2  6
#define TSS_IP   7
#define TSS_FLAG 8
#define TSS_AX   9
#define TSS_CX   10
#define TSS_DX   11
#define TSS_BX   12
#define TSS_SP   13
#define TSS_BP   14
#define TSS_SI   15
#define TSS_DI   16
#define TSS_ES   17
#define TSS_CS   18
#define TSS_SS   19
#define TSS_DS   20
#define TSS_LDT  21

#define PMAX(a,b) ((a<b)?b:a)
#define CPL DPL(m_rights[SS])
#define PM (m_msw&1)

#define NT_IRET 0
#define NT_JMP  1
#define NT_CALL 2

/* these come from the 80286 timings in OPCODE.LST */
/* many of these numbers are suspect TODO: add protmode insns*/
const uint8_t i80286_cpu_device::m_i80286_timing[] =
{
	23,17,          /* exception, IRET */
		0, 2, 3, 1, /* INTs */
		2,              /* segment overrides */
		2, 2, 2,        /* flag operations */
		3, 3,16,14, /* arithmetic adjusts */
		3, 3,           /* decimal adjusts */
		2, 2,           /* sign extension */
		2, 7, 3, 3, 3, 5,   /* misc */

		7, 7,11,        /* direct JMPs */
		7,11,26,        /* indirect JMPs */
		7,13,           /* direct CALLs */
		7,11,29,        /* indirect CALLs */
	11,15,11,15,    /* returns */
		3, 7, 4, 8, /* conditional JMPs */
		4, 8, 4, 8, /* loops */

		5, 5, 5, 5, /* port reads */
		3, 3, 3, 3, /* port writes */

		2, 3, 3,        /* move, 8-bit */
		2, 3,           /* move, 8-bit immediate */
		2, 3, 3,        /* move, 16-bit */
		2, 3,           /* move, 16-bit immediate */
		5, 5, 3, 3, /* move, AL/AX memory */
		2, 5, 2, 3, /* move, segment registers */
		3, 5,           /* exchange, 8-bit */
		3, 5, 3,        /* exchange, 16-bit */

		5, 5, 3, 3, /* pushes */
		5, 5, 5, 5, /* pops */

		2, 7, 7,        /* ALU ops, 8-bit */
		3, 7, 7,        /* ALU ops, 8-bit immediate */
		2, 7, 7,        /* ALU ops, 16-bit */
		3, 7, 7,        /* ALU ops, 16-bit immediate */
		3, 7, 7,        /* ALU ops, 16-bit w/8-bit immediate */
	13,21,16,24,    /* MUL */
	13,21,16,24,    /* IMUL */
	14,22,17,25,    /* DIV */
	17,25,20,28,    /* IDIV */
		2, 2, 7, 7, /* INC/DEC */
		2, 2, 7, 7, /* NEG/NOT */

		2, 5, 1,        /* reg shift/rotate */
		7, 8, 1,        /* m8 shift/rotate */
		7, 8, 1,        /* m16 shift/rotate */

	13, 5,12,       /* CMPS 8-bit */
	13, 5,12,       /* CMPS 16-bit */
		9, 5, 8,        /* SCAS 8-bit */
		9, 5, 8,        /* SCAS 16-bit */
		5, 5, 4,        /* LODS 8-bit */
		5, 5, 4,        /* LODS 16-bit */
		4, 4, 3,        /* STOS 8-bit */
		4, 4, 3,        /* STOS 16-bit */
		5, 5, 4,        /* MOVS 8-bit */
		5, 5, 4,        /* MOVS 16-bit */

		5, 5, 4,        /* (80186) INS 8-bit */
		5, 5, 4,        /* (80186) INS 16-bit */
		5, 5, 4,        /* (80186) OUTS 8-bit */
		5, 5, 4,        /* (80186) OUTS 16-bit */
		3,17,19,        /* (80186) PUSH immediate, PUSHA/POPA */
	21,24,          /* (80186) IMUL immediate 8-bit */
	21,24,          /* (80186) IMUL immediate 16-bit */
	11,15,12, 4, 5, /* (80186) ENTER/LEAVE */
	13,             /* (80186) BOUND */
};


i80286_cpu_device::i80286_cpu_device(VM* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu)
{
	memcpy(m_timing, m_i80286_timing, sizeof(m_i80286_timing));
	m_amask = 0xffffff;
	m_fetch_xor = BYTE_XOR_LE(0);
	memset(m_sregs, 0x00, sizeof(m_sregs));
	m_sregs[CS] = 0xf000;
	memset(m_base, 0x00, sizeof(m_base));
	m_base[CS] = 0xff0000;
	memset(m_limit, 0x00, sizeof(m_limit));
	m_limit[CS] = m_limit[SS] = m_limit[DS] = m_limit[ES] = 0xffff;
	memset(m_rights, 0x00, sizeof(m_rights));
	m_rights[DS] = m_rights[SS] = m_rights[ES] = 0x93;
	memset(&m_gdtr, 0x00, sizeof(m_gdtr));
	memset(&m_idtr, 0x00, sizeof(m_idtr));
	m_idtr.limit = 0x3ff;
	memset(&m_ldtr, 0x00, sizeof(m_ldtr));
	memset(&m_tr, 0x00, sizeof(m_tr));
	m_msw = 0xfff0;

	//m_test = 0;
	//m_busreq = false;
	//m_extra_cycles = 0;
	initialize_output_signals(&out_a20);
	initialize_output_signals(&out_shutdown);
}

void i80286_cpu_device::reset()
{
	i8086_common_cpu_device::reset();
	m_MF = 0;
	m_NT = 0;
	m_IOPL = 0;
	m_msw = 0xfff0;
	m_limit[CS] = m_limit[SS] = m_limit[DS] = m_limit[ES] = 0xffff;
	m_sregs[DS] = m_sregs[SS] = m_sregs[ES] = 0;
	m_base[DS] = m_base[SS] = m_base[ES] = 0;
	m_rights[DS] = m_rights[SS] = m_rights[ES] = 0x93;
	m_rights[CS] = 0x9b;
	m_valid[CS] = m_valid[SS] = m_valid[DS] = m_valid[ES] = true;
	m_idtr.base = 0;
	m_idtr.limit = 0x3ff;
	m_gdtr.base = m_ldtr.base = m_tr.base = 0;
	m_gdtr.limit = m_ldtr.limit = m_tr.limit = 0;
	m_ldtr.rights = m_tr.rights = 0;
	m_ldtr.sel = m_tr.sel = 0;
	m_sregs[CS] = 0xf000;
	m_base[CS] = 0xff0000;
	m_ip = 0xfff0;
	m_trap_level = 0;
	m_shutdown = false;
	m_extra_cycles = 0;
	//m_out_shutdown_func(false);
	write_signals(&out_shutdown, 0);
	write_signals(&out_a20, (m_amask > 0x0fffff) ? 0xffffffff : 0x00000000);
}

void i80286_cpu_device::device_start()
{
	i8086_common_cpu_device::device_start();
	// ToDo
	state_add( I286_ES, "ES", m_sregs[ES] ).formatstr("%04X");
	state_add( I286_ES_BASE, "ESBASE", m_base[ES]).formatstr("%06X");
	state_add( I286_ES_LIMIT, "ESLIMIT", m_limit[ES]).formatstr("%04X");
	state_add( I286_ES_FLAGS, "ESFLAGS", m_rights[ES]).formatstr("%02X");
	state_add( I286_CS, "CS", m_sregs[CS] ).callimport().formatstr("%04X");
	state_add( I286_CS_BASE, "CSBASE", m_base[CS]).callimport().formatstr("%06X");
	state_add( I286_CS_LIMIT, "CSLIMIT", m_limit[CS]).formatstr("%04X");
	state_add( I286_CS_FLAGS, "CSFLAGS", m_rights[CS]).formatstr("%02X");
	state_add( I286_SS, "SS", m_sregs[SS] ).formatstr("%04X");
	state_add( I286_SS_BASE, "SSBASE", m_base[SS]).formatstr("%06X");
	state_add( I286_SS_LIMIT, "SSLIMIT", m_limit[SS]).formatstr("%04X");
	state_add( I286_SS_FLAGS, "SSFLAGS", m_rights[SS]).formatstr("%02X");
	state_add( I286_DS, "DS", m_sregs[DS] ).formatstr("%04X");
	state_add( I286_DS_BASE, "DSBASE", m_base[DS]).formatstr("%06X");
	state_add( I286_DS_LIMIT, "DSLIMIT", m_limit[DS]).formatstr("%04X");
	state_add( I286_DS_FLAGS, "DSFLAGS", m_rights[DS]).formatstr("%02X");
	state_add( I286_GDTR_BASE, "GDTRBASE", m_gdtr.base).formatstr("%06X");
	state_add( I286_GDTR_LIMIT, "GDTRLIMIT", m_gdtr.limit).formatstr("%04X");
	state_add( I286_IDTR_BASE, "IDTRBASE", m_idtr.base).formatstr("%06X");
	state_add( I286_IDTR_LIMIT, "IDTRLIMIT", m_idtr.limit).formatstr("%04X");
	state_add( I286_LDTR, "LDTR", m_ldtr.sel ).formatstr("%04X");
	state_add( I286_LDTR_BASE, "LDTRBASE", m_ldtr.base).formatstr("%06X");
	state_add( I286_LDTR_LIMIT, "LDTRLIMIT", m_ldtr.limit).formatstr("%04X");
	state_add( I286_LDTR_FLAGS, "LDTRFLAGS", m_ldtr.rights).formatstr("%02X");
	state_add( I286_TR, "TR", m_tr.sel ).formatstr("%04X");
	state_add( I286_TR_BASE, "TRBASE", m_tr.base).formatstr("%06X");
	state_add( I286_TR_LIMIT, "TRLIMIT", m_tr.limit).formatstr("%04X");
	state_add( I286_TR_FLAGS, "TRFLAGS", m_tr.rights).formatstr("%02X");
	state_add( I286_MSW, "MSW", m_msw ).formatstr("%04X");
	state_add( I286_VECTOR, "V", m_int_vector).formatstr("%02X");

	state_add( I286_PC, "PC", m_pc).callimport().formatstr("%06X");
	state_add( STATE_GENPCBASE, "CURPC", m_pc ).callimport().formatstr("%06X").noshow();
	state_add( I8086_HALT, "HALT", m_halt ).mask(1);

	//m_out_shutdown_func.resolve_safe();
	write_signals(&out_shutdown, 0);
}

//-------------------------------------------------
//  state_import - import state into the device,
//  after it has been set
//-------------------------------------------------

void i80286_cpu_device::state_import(const device_state_entry &entry)
{
	switch (entry.index())
	{
	case I286_IP:
	case I286_CS_BASE:
		m_pc = m_base[CS] + m_ip;
		break;

	case I286_CS:
		// TODO: should this call data_descriptor to update the current segment?
		break;

	case STATE_GENPC:
	case STATE_GENPCBASE:
		if (m_pc - m_base[CS] > m_limit[CS])
		{
			// TODO: should this call data_descriptor instead of ignoring jumps outside the current segment?
			if (PM)
			{
				m_pc = m_base[CS] + m_ip;
			}
			else
			{
				m_sregs[CS] = m_pc >> 4;
				m_base[CS] = m_sregs[CS] << 4;
			}
		}
		m_ip = m_pc - m_base[CS];
		break;
	}
}


//-------------------------------------------------
//  state_string_export - export state as a string
//  for the debugger
//-------------------------------------------------

void i80286_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) const
{
	switch (entry.index())
	{
		case STATE_GENFLAGS:
			{
				uint16_t flags = CompressFlags();
				str = string_format("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
					flags & 0x8000 ? '0':'.',
					flags & 0x4000 ? 'N':'.',
					flags & 0x2000 ? 'I':'.',
					flags & 0x1000 ? 'I':'.',
					flags & 0x0800 ? 'O':'.',
					flags & 0x0400 ? 'D':'.',
					flags & 0x0200 ? 'I':'.',
					flags & 0x0100 ? 'T':'.',
					flags & 0x0080 ? 'S':'.',
					flags & 0x0040 ? 'Z':'.',
					flags & 0x0020 ? '0':'.',
					flags & 0x0010 ? 'A':'.',
					flags & 0x0008 ? '0':'.',
					flags & 0x0004 ? 'P':'.',
					flags & 0x0002 ? '1':'.',
					flags & 0x0001 ? 'C':'.');
			}
			break;
	}
}

bool i80286_cpu_device::memory_translate(address_spacenum spacenum, int intention, offs_t &address)
{
	if(spacenum == AS_PROGRAM)
		address &= m_amask;

	return true;
}

void i80286_cpu_device::set_irq_line(int irqline, int state)
{
	if (state != CLEAR_LINE && cpustate->halted)
	{
		cpustate->halted = 0;
	}
	try
	{
		if (irqline == INPUT_LINE_NMI)
		{
			if (state != CLEAR_LINE) {
				m_pending_irq |= NMI_IRQ;
			} else {
				//m_pending_irq &= ~NMI_IRQ;
			}
			if (m_nmi_state == (uint32_t)state) {
				return;
			}
			m_nmi_state = (uint32_t)state;

			/* on a rising edge, signal the NMI */
			if (state != CLEAR_LINE) {
				interrupt(I8086_NMI_INT_VECTOR, 2, -1);
				m_nmi_state = CLEAR_LINE;
			}
		} else {
			if(state == CLEAR_LINE)	{
				m_pending_irq &= ~INT_IRQ;
			} else {
				m_pending_irq |= INT_IRQ;
			}
			m_irq_state = state;
			/* if the IF is set, signal an interrupt */
			if (state != CLEAR_LINE && m_IF) {
				if(m_pic != NULL) {
					interrupt(m_pic->get_intr_ack(), 2, -1);
				}
				m_irq_state = CLEAR_LINE;
			}
		}
	}
	catch (UINT32 e)
	{
		trap(e);
	}
}

void i80286_cpu_device::write_signal(int id, uint32_t data, uint32_t mask)
{
	bool b_state = (data & mask);
	uint32_t state = b_state ? HOLD_LINE : CLEARLINE;
	if(id == SIG_CPU_NMI) {
		set_irq_line(INPUT_LINE_NMI, state);
	} else if(id == SIG_CPU_IRQ) 	{
		set_irq_line(INPUT_LINE_IRQ, state);
	} else if(id == SIG_I86_A20) {
		m_amask = b_state ? 0x00ffffff : 0x000fffff; // i80286_set_a20_line()
		write_signals(&out_a20, (b_state) ? 0xffffffff : 0x00000000);
	} else if(id == SIG_CPU_BUSREQ) {
		m_busreq = (data & mask);
	} else if(id == SIG_I86_TEST) {
		m_test = (data & mask);
	}
}
// when a cpu reset happens on a AT the bios checks for 9 in byte 0xf
// of the nvram.  if yes, after init, it sets the stack pointer to the value in 0040:0067
// in the bios data segment then pops es and ds off that stack, does popa then a far ret.

void i80286_cpu_device::trap(uint32_t error)
{
	int error_code = error & 0xffff;
	uint16_t number = error >> 16;
	if(error_code == 0xffff) {
		error_code = -1;
	}
	m_ip = m_prev_ip;
	try
	{
		switch(number)
		{
			case FAULT_DE:
			case FAULT_TS:
			case FAULT_NP:
			case FAULT_SS:
			case FAULT_GP:
				m_trap_level++;
				if(m_trap_level == 2)
					throw TRAP(FAULT_DF,0);
				if(m_trap_level == 3)
					break;
				interrupt_descriptor(number,1,error_code);
				break;
			case FAULT_DF:
				interrupt_descriptor(number,1,0);
				break;
			default:
				interrupt_descriptor(number,1,-1);
		}
	}
	catch(uint32_t e)
	{
		trap(e);
	}
	if(m_trap_level == 3)
	{
		m_shutdown = true;
		//m_out_shutdown_func(true);
		write_signals(&out_shutdown, 0xffffffff);
	}
	m_trap_level = 0;
}

uint32_t i80286_cpu_device::selector_address(uint16_t sel)
{
	uint32_t base;
	uint16_t limit;
	if(TBL(sel))
	{
		base = m_ldtr.base;
		limit = m_ldtr.limit;
	}
	else
	{
		base = m_gdtr.base;
		limit = m_gdtr.limit;
	}
	return ((IDX(sel) >= limit) || !IDXTBL(sel) ? -1 : base + IDX(sel));
}

int i80286_cpu_device::verify(uint16_t selector, int operation, uint8_t rights, bool valid)
{
	if(!IDXTBL(selector) && !valid)
		return FAULT_GP;
	if(!SEGDESC(rights))
		return FAULT_GP;

	switch (operation)
	{
		case I8086_READ:
			if(CODE(rights) && !READ(rights))
				return FAULT_GP;
			break;
		case I8086_WRITE:
			if(CODE(rights) || !RW(rights))
				return FAULT_GP;
			break;
		case I8086_FETCH:
			if(!CODE(rights))
				return FAULT_GP;
			break;
	}
	return 0;
}

void i80286_cpu_device::pop_seg(int reg)
{
	uint16_t sel;
	if(PM)
		check_permission(SS, m_regs.w[SP], 2, I8086_READ);
	sel = read_word(m_base[SS] + m_regs.w[SP]);
	data_descriptor(reg, sel);
	m_regs.w[SP] += 2;
}

void i80286_cpu_device::data_descriptor(int reg, uint16_t selector, int cpl, uint32_t trap, uint16_t offset, int size)
{
	if(PM)
	{
		uint16_t desc[3];
		uint8_t r;
		uint32_t addr;
		if((reg != SS) && !IDXTBL(selector))
		{
			m_sregs[reg] = 0;
			m_limit[reg] = 0;
			m_base[reg] = 0;
			m_rights[reg] = 0;
			m_valid[reg] = 0;
			return;
		}

		if((addr = selector_address(selector)) == -1)
			throw trap;

		desc[0] = read_word(addr);
		desc[1] = read_word(addr + 2);
		desc[2] = read_word(addr + 4);
		r = RIGHTS(desc);

		if(!SEGDESC(r))
			throw trap;

		if(reg == SS)
		{
			if(!IDXTBL(selector))
				throw trap;
			if(DPL(r) != cpl)
				throw trap;
			if(RPL(selector) != cpl)
				throw trap;
			if(!RW(r) || CODE(r))
				throw trap;
			if(!PRES(r))
				throw TRAP(FAULT_SS, (IDXTBL(selector) + (trap & 1)));
		}
		else
		{
			if((DPL(r) < PMAX(cpl, RPL(selector))) && (!CODE(r) || (CODE(r) && !CONF(r))))
				throw trap;
			if(CODE(r) && !READ(r))
				throw trap;
			if(!PRES(r))
				throw TRAP(FAULT_NP, (IDXTBL(selector) + (trap & 1)));
		}

		if(offset + size)
		{
			if((CODE(r) || !EXPDOWN(r)) && ((offset + size - 1) > LIMIT(desc)))
				throw (reg==SS) ? TRAP(FAULT_SS, (trap & 1)) : trap;

			if(!CODE(r) && EXPDOWN(r) && ((offset <= LIMIT(desc)) || ((offset + size - 1) > 0xffff)))
				throw (reg==SS) ? TRAP(FAULT_SS, (trap & 1)) : trap;
		}

		SET_ACC(desc);
		write_word(addr + 4, desc[2]);
		m_sregs[reg] = selector;
		m_limit[reg] = LIMIT(desc);
		m_base[reg] = BASE(desc);
		m_rights[reg] = RIGHTS(desc);
	}
	else
	{
		m_sregs[reg] = selector;
		m_base[reg] = selector << 4;
	}
	m_valid[reg] = 1;
}

void i80286_cpu_device::data_descriptor(int reg, uint16_t selector)
{
	data_descriptor(reg, selector, CPL, TRAP(FAULT_GP,IDXTBL(selector)));
}

void i80286_cpu_device::switch_task(uint16_t ntask, int type)
{
	uint16_t ndesc[3], desc[3], ntss[22], otss[22], flags;
	uint8_t r, lr;
	uint32_t naddr, oaddr, ldtaddr;
	int i;
	logerror("i286: %06x This program uses TSSs, how rare. Please report this to the developers.\n", pc());

	if(TBL(ntask))
		throw TRAP(FAULT_TS, IDXTBL(ntask));

	if((naddr = selector_address(ntask)) == -1)
		throw TRAP(FAULT_TS, IDXTBL(ntask));

	oaddr = selector_address(m_tr.sel);
	ndesc[0] = read_word(naddr);
	ndesc[1] = read_word(naddr + 2);
	ndesc[2] = read_word(naddr + 4);
	desc[2] = read_word(oaddr + 4);
	r = RIGHTS(ndesc);

	if(SEGDESC(r) || ((GATE(r) & ~2) != TSSDESCIDLE))
		throw TRAP(FAULT_GP, IDXTBL(ntask));

	if(!PRES(r))
		throw TRAP(FAULT_NP, IDXTBL(ntask));

	if(LIMIT(ndesc) < 43)
		throw TRAP(FAULT_TS, IDXTBL(ntask));

	flags = CompressFlags();

	if(type == NT_CALL)
		write_word(BASE(ndesc) + TSS_BACK * 2, m_tr.sel);

	if(type == NT_IRET)
		flags &= ~0x4000;

	otss[TSS_IP] = m_ip;
	otss[TSS_FLAG] = flags;
	otss[TSS_AX] = m_regs.w[AX];
	otss[TSS_CX] = m_regs.w[CX];
	otss[TSS_DX] = m_regs.w[DX];
	otss[TSS_BX] = m_regs.w[BX];
	otss[TSS_SP] = m_regs.w[SP];
	otss[TSS_BP] = m_regs.w[BP];
	otss[TSS_SI] = m_regs.w[SI];
	otss[TSS_DI] = m_regs.w[DI];
	otss[TSS_ES] = m_sregs[ES];
	otss[TSS_CS] = m_sregs[CS];
	otss[TSS_SS] = m_sregs[SS];
	otss[TSS_DS] = m_sregs[DS];

	for (i = 14; i < 42; i += 2)
		write_word(m_tr.base + i, otss[i / 2]);

	for (i = 0; i < 44; i += 2)
		ntss[i / 2] = read_word(BASE(ndesc) + i);

	// jmp does both
	if(type != NT_CALL)
	{
		desc[2] &= ~0x200; // mark idle
		write_word(oaddr + 4, desc[2]);
	}

	if(type != NT_IRET)
	{
		ndesc[2] |= 0x200;
		write_word(naddr + 4, ndesc[2]);
	}

	m_tr.sel = ntask;
	m_tr.limit = LIMIT(ndesc);
	m_tr.base = BASE(ndesc);
	m_tr.rights = RIGHTS(ndesc);

	load_flags(ntss[TSS_FLAG], 0);
	m_regs.w[AX] = ntss[TSS_AX];
	m_regs.w[CX] = ntss[TSS_CX];
	m_regs.w[DX] = ntss[TSS_DX];
	m_regs.w[BX] = ntss[TSS_BX];
	m_regs.w[SP] = ntss[TSS_SP];
	m_regs.w[BP] = ntss[TSS_BP];
	m_regs.w[SI] = ntss[TSS_SI];
	m_regs.w[DI] = ntss[TSS_DI];

	if(TBL(ntss[TSS_LDT]))
		throw TRAP(FAULT_TS, IDXTBL(ntss[TSS_LDT]));

	if(IDXTBL(ntss[TSS_LDT]))
	{
		if((ldtaddr = selector_address(ntss[TSS_LDT])) == -1)
			throw TRAP(FAULT_TS, IDXTBL(ntss[TSS_LDT]));

		desc[0] = read_word(ldtaddr);
		desc[1] = read_word(ldtaddr + 2);
		desc[2] = read_word(ldtaddr + 4);
		lr = RIGHTS(desc);

		if(SEGDESC(lr) || (GATE(lr) != LDTDESC))
			throw TRAP(FAULT_TS, IDXTBL(ntss[TSS_LDT]));

		if(!PRES(lr))
			throw TRAP(FAULT_TS, IDXTBL(ntss[TSS_LDT]));

		m_ldtr.sel = ntss[TSS_LDT];
		m_ldtr.limit = LIMIT(desc);
		m_ldtr.base = BASE(desc);
		m_ldtr.rights = RIGHTS(desc);
	}
	else
	{
		m_ldtr.sel = 0;
		m_ldtr.limit = 0;
		m_ldtr.base = 0;
		m_ldtr.rights = 0;
	}

	if(type == NT_CALL)
		m_NT = 1;

	m_msw |= 8;
	// Docs explicitly say SS is loaded first.  Why?  Because the DPL
	// of the TSS is compared to the DPL of SS which is CPL
	data_descriptor(SS, ntss[TSS_SS], RPL(ntss[TSS_CS]), TRAP(FAULT_TS, IDXTBL(ntss[TSS_SS])));

	try
	{
		code_descriptor(ntss[TSS_CS], ntss[TSS_IP], 0);
	}
	catch (uint32_t e)
	{
		int error_code = e & 0xffff;
		if(error_code == FAULT_GP)
			e = TRAP(FAULT_TS, (e >> 16)); // #NP fault is correct
		throw e;
	}

	data_descriptor(ES, ntss[TSS_ES], CPL, TRAP(FAULT_TS, IDXTBL(ntss[TSS_ES])));
	data_descriptor(DS, ntss[TSS_DS], CPL, TRAP(FAULT_TS, IDXTBL(ntss[TSS_DS])));
}

void i80286_cpu_device::code_descriptor(uint16_t selector, uint16_t offset, int gate)
{
	if(PM)
	{
		uint16_t desc[3];
		uint8_t r;
		uint32_t addr;
		if((addr = selector_address(selector)) == -1)
			throw TRAP(FAULT_GP, IDXTBL(selector));

		desc[0] = read_word(addr);
		desc[1] = read_word(addr + 2);
		desc[2] = read_word(addr + 4);
		r = RIGHTS(desc);

		if(SEGDESC(r))
		{
			if(!CODE(r))
				throw TRAP(FAULT_GP, IDXTBL(selector));

			if(CONF(r))
			{
				if(DPL(r) > CPL)
					throw TRAP(FAULT_GP, IDXTBL(selector));
			}
			else
				if((RPL(selector) > CPL) || (DPL(r) != CPL))
					throw TRAP(FAULT_GP, IDXTBL(selector));

			if(!PRES(r))
				throw TRAP(FAULT_NP, IDXTBL(selector));  // this order is important

			if(offset > LIMIT(desc))
				throw TRAP(FAULT_GP, 0);

			SET_ACC(desc);
			write_word(addr + 4, desc[2]);
			m_sregs[CS] = IDXTBL(selector) | CPL;
			m_limit[CS] = LIMIT(desc);
			m_base[CS] = BASE(desc);
			m_rights[CS] = RIGHTS(desc);
			m_ip = offset;
		}
		else
		{ // systemdescriptor
			uint16_t gatesel = GATESEL(desc);

			if(!gate)
				throw TRAP(FAULT_GP, IDXTBL(selector)); // tss cs must be segment
			if(DPL(r) < PMAX(CPL,RPL(selector)))
				throw TRAP(FAULT_GP, IDXTBL(selector));
			if(!PRES(r))
				throw TRAP(FAULT_NP, IDXTBL(selector));

			switch (GATE(r))
			{
				case CALLGATE:
				{
					uint16_t gatedesc[3];
					if((addr = selector_address(gatesel)) == -1)
						throw TRAP(FAULT_GP, IDXTBL(gatesel));

					gatedesc[0] = read_word(addr);
					gatedesc[1] = read_word(addr + 2);
					gatedesc[2] = read_word(addr + 4);
					r = RIGHTS(gatedesc);

					if(!CODE(r) || !SEGDESC(r))
						throw TRAP(FAULT_GP, IDXTBL(gatesel));
					if(DPL(r) > CPL)
						throw TRAP(FAULT_GP, IDXTBL(gatesel));
					if(!PRES(r))
						throw TRAP(FAULT_NP, IDXTBL(gatesel));

					if(GATEOFF(desc) > LIMIT(gatedesc))
						throw TRAP(FAULT_GP,0);

					if (!CONF(r) && (DPL(r) < CPL))
					{
						// inner call
						uint16_t tss_ss, tss_sp, oldss, oldsp;
						uint32_t oldstk;
						int i;
						if(gate == NT_JMP)
							throw TRAP(FAULT_GP, IDXTBL(gatesel)); // can't jmp to inner

						tss_ss = read_word(m_tr.base+TSS_SS0*2+(DPL(r)*4));
						tss_sp = read_word(m_tr.base+TSS_SP0*2+(DPL(r)*4));

						oldss = m_sregs[SS];
						oldsp = m_regs.w[SP];
						oldstk = m_base[SS] + oldsp;
						data_descriptor(SS, tss_ss, DPL(r), TRAP(FAULT_TS,IDXTBL(tss_ss)), tss_sp-8-(GATECNT(desc)*2), 8+(GATECNT(desc)*2));
						m_regs.w[SP] = tss_sp;
						PUSH(oldss);
						PUSH(oldsp);
						for(i = GATECNT(desc)-1; i >= 0; i--)
							PUSH(read_word(oldstk+(i*2)));
					}
					else
						check_permission(SS, m_regs.w[SP]-4, 4, I8086_READ);

					SET_ACC(gatedesc);
					write_word(addr+4, gatedesc[2]);
					m_sregs[CS]=IDXTBL(gatesel) | DPL(r);
					m_limit[CS]=LIMIT(gatedesc);
					m_base[CS]=BASE(gatedesc);
					m_rights[CS]=RIGHTS(gatedesc);
					m_ip=GATEOFF(desc);
					break;
				}

				case TASKGATE:
					selector = gatesel;
					if((addr = selector_address(selector)) == -1)
						throw TRAP(FAULT_GP, IDXTBL(selector));

					desc[2] = read_word(addr+4);
					r = RIGHTS(desc);
					if (SEGDESC(r) || (GATE(r) != TSSDESCIDLE))
						throw TRAP(FAULT_GP,IDXTBL(selector));

				case TSSDESCIDLE:
					switch_task(selector, gate);
					load_flags(CompressFlags(), CPL);
					break;

				default:
					throw TRAP(FAULT_GP,IDXTBL(selector));
			}
		}
	}
	else
	{
		m_ip = offset;
		m_sregs[CS]=selector;
		m_base[CS]=selector<<4;
		m_rights[CS]=0x9b;
		m_limit[CS]=0xffff;
	}
}

void i80286_cpu_device::interrupt_descriptor(int number, int hwint, int error)
{
	uint16_t desc[3], gatesel, flags = CompressFlags();
	uint8_t r;
	hwint = hwint ? 1 : 0;

	if(number == -1)
	{
		number = standard_irq_callback(0);

		m_irq_state = CLEAR_LINE;
		m_pending_irq &= ~INT_IRQ;
		hwint = 1;
	}

	if(!PM)
	{
		PUSH(flags & ~0xf000);
		m_TF = m_IF = 0;

		uint16_t dest_off = read_word(number * 4 + 0);
		uint16_t dest_seg = read_word(number * 4 + 2);

		PUSH(m_sregs[CS]);
		PUSH(m_ip);
		code_descriptor(dest_seg, dest_off, 0);
		return;
	}

	if((number << 3) >= m_idtr.limit)
		throw TRAP(FAULT_GP, (number * 8 + 2 + hwint));

	desc[0] = read_word(m_idtr.base + (number << 3));
	desc[1] = read_word(m_idtr.base + (number << 3) + 2);
	desc[2] = read_word(m_idtr.base + (number << 3) + 4);

	r = RIGHTS(desc);
	if(!hwint && (DPL(r) < CPL))
		throw TRAP(FAULT_GP, (number * 8 + 2 + hwint));
	if(!PRES(r))
		throw TRAP(FAULT_NP, (number * 8 + 2 + hwint));
	gatesel = GATESEL(desc);

	switch (GATE(r))
	{
		case TASKGATE:
			try
			{
				switch_task(gatesel, NT_CALL);
			}
			catch (uint32_t e)
			{
				throw e + hwint;
			}
			if((hwint == 1) && (error != -1))
				PUSH(error);
			load_flags(CompressFlags(), CPL); // new flags
			break;

		case INTGATE:
		case TRAPGATE:
		{
			uint16_t gatedesc[3];
			uint32_t addr;

			if((addr = selector_address(gatesel)) == -1)
				throw TRAP(FAULT_GP, (IDXTBL(gatesel) + hwint));

			gatedesc[0] = read_word(addr);
			gatedesc[1] = read_word(addr + 2);
			gatedesc[2] = read_word(addr + 4);
			r = RIGHTS(gatedesc);
			if(!CODE(r) || !SEGDESC(r))
				throw TRAP(FAULT_GP, (IDXTBL(gatesel) + hwint));
			if(DPL(r) > CPL)
				throw TRAP(FAULT_GP, (IDXTBL(gatesel) + hwint));
			if(!PRES(r))
				throw TRAP(FAULT_NP, (IDXTBL(gatesel) + hwint));
			if(GATEOFF(desc) > LIMIT(gatedesc))
				throw TRAP(FAULT_GP, hwint);

			if(!CONF(r) && (DPL(r) < CPL))
			{
				// inner call
				uint16_t tss_ss, tss_sp, oldss, oldsp;
				tss_ss = read_word(m_tr.base + TSS_SS0 * 2 + (DPL(r) * 4));
				tss_sp = read_word(m_tr.base + TSS_SP0 * 2 + (DPL(r) * 4));

				oldss = m_sregs[SS];
				oldsp = m_regs.w[SP];
				data_descriptor(SS, tss_ss, DPL(r), TRAP(FAULT_TS, (IDXTBL(tss_ss) + hwint)), tss_sp - ((error != -1) ? 12 : 10), (error != -1) ? 12 : 10);
				m_regs.w[SP] = tss_sp;
				PUSH(oldss);
				PUSH(oldsp);
			}
			else
				check_permission(SS, m_regs.w[SP] - ((error != -1) ? 8 : 6), (error != -1) ? 8 : 6, I8086_READ);

			SET_ACC(gatedesc);
			write_word(addr + 4, gatedesc[2]);
			PUSH(flags);
			PUSH(m_sregs[CS]);
			PUSH(m_ip);
			if((hwint == 1) && (error != -1))
				PUSH(error);
			m_sregs[CS] = IDXTBL(gatesel) | DPL(r);
			m_limit[CS] = LIMIT(gatedesc);
			m_base[CS] = BASE(gatedesc);
			m_rights[CS] = RIGHTS(gatedesc);
			m_ip = GATEOFF(desc);
			m_TF = 0;
			m_NT = 0;
			if(GATE(RIGHTS(desc)) == INTGATE)
				m_IF = 0;
			break;
		}
		default:
			throw TRAP(FAULT_GP, (number * 8 + 2 + hwint));
	}
}

uint8_t i80286_cpu_device::read_port_byte(uint16_t port)
{
	if(PM && (CPL > m_IOPL))
		throw TRAP(FAULT_GP, 0);
	if(m_io != NULL) {
		return m_io->read_io8((uint32_t)port);
	}
	return 0xff;
	//return m_io->read_byte(port);
}

uint16_t i80286_cpu_device::read_port_word(uint16_t port)
{
	if(PM && (CPL > m_IOPL))
		throw TRAP(FAULT_GP, 0);
	if(m_io != NULL) {
		return m_io->read_io16((uint32_t)port);
	}
	//return m_io->read_byte_unaligned(port);
	return 0xff;
}

void i80286_cpu_device::write_port_byte(uint16_t port, uint8_t data)
{
	if(PM && (CPL > m_IOPL))
		throw TRAP(FAULT_GP, 0);
	if(m_io != NULL) {
		m_io->write_io8((uint32_t)port, (uint32_t)data);
	}
}

void i80286_cpu_device::write_port_word(uint16_t port, uint16_t data)
{
	if(PM && (CPL > m_IOPL))
		throw TRAP(FAULT_GP, 0);
	//m_io->write_word_unaligned(port, data);
	if(m_io != NULL) {
		m_io->write_io16((uint32_t)port, (uint32_t)data);
	}
}

uint8_t i80286_cpu_device::fetch_op()
{
	uint8_t data = 0xff;
	if(m_ip > m_limit[CS])
		throw TRAP(FAULT_GP, 0);
   
	//data = m_direct_opcodes->read_byte( pc() & m_amask, m_fetch_xor );
	if(m_program != NULL) data = m_program->read_data8(pc() & m_amask);
	m_ip++;
	return data;
}

uint8_t i80286_cpu_device::fetch()
{
	uint8_t data = 0xff;
	if(m_ip > m_limit[CS])
		throw TRAP(FAULT_GP, 0);

	//data = m_direct_opcodes->read_byte( pc() & m_amask, m_fetch_xor );
	if(m_program != NULL) data = m_program->read_data8(pc() & m_amask);
	m_ip++;
	return data;
}

uint32_t i80286_cpu_device::calc_addr(int seg, uint16_t offset, int size, int op, bool override)
{
	seg = (m_seg_prefix && (seg==DS || seg==SS) && override) ? m_prefix_seg : seg;
	if(op != I8086_NONE)
		check_permission(seg, offset, size, op);

	return (m_base[seg] + offset) & (op != I8086_NONE ? m_amask : 0xffffff);
}

void i80286_cpu_device::set_extra_clock(int icount)
{
	m_extra_cycles += icount;
}

int i80286_cpu_device::get_extra_clock()
{
	return m_extra_cycles;
}

uint32_t i80286_cpu_device::get_pc()
{
	return prev_pc();
}

uint32_t i80286_cpu_device::get_next_pc()
{
	return pc();
}

void i80286_cpu_device::debugger_hook()
{
	if(d_debugger != NULL) {
		bool now_debugging = d_debugger->now_debugging;
		if(now_debugging) {
			d_debugger->check_break_points(pc);
			if(d_debugger->now_suspended) {
				emu->mute_sound();
				cpustate->debugger->now_waiting = true;
				while(d_debugger->now_debugging && d_debugger->now_suspended) {
					emu->sleep(10);
				}
				cpustate->debugger->now_waiting = false;
			}
			if(d_debugger->now_debugging) {
				d_program = d_io = d_debugger;
			} else {
				now_debugging = false;
			}
			try
			{
				if (PM && ((m_pc - base[CS]) > limit[CS]))
					throw TRAP(FAULT_GP, sregs[CS] & ~3);
				m_prev_ip = m_ip;
				execute_opcode(); // call instruction
			}
			catch (UINT32 e)
			{
				trap(e);
			}
			
			if(now_debugging) {
				if(!d_debugger->now_going) {
					d_debugger->now_suspended = true;
				}
				d_program = d_program_stored;
				d_io = d_io_stored;
			}
		} else {
			exec_opcode();
		}
	} else {
		exec_opcode();
	}
}

void i80286_cpu_device:: exec_opcode()
{
	uint8_t op = fetch_op();
	try {
	switch(op)
	{
	case 0x07: // i_pop_es
		pop_seg(ES);
		CLK(POP_SEG);
		break;
		
	case 0x0f:
		{
			unsigned next = fetch_op();
			uint16_t desc[3], tmp, msw, sel;
			uint8_t r;
			uint32_t addr;
			
			switch (next)
			{
			case 0:
				if(!PM)
					throw TRAP(FAULT_UD, (uint16_t)-1);
				m_modrm = fetch();
				switch (m_modrm & 0x38)
				{
				case 0: /* sldt */
					PutRMWord(m_ldtr.sel);
					break;
					
				case 8: /* str */
					PutRMWord(m_tr.sel);
					break;
									
				case 0x10: /* lldt */
					if(CPL != 0)
						throw TRAP(FAULT_GP, 0);
					sel = GetRMWord();
					if(TBL(sel))
						throw TRAP(FAULT_GP, IDXTBL(sel));
					if(IDXTBL(sel))
					{
						if(IDX(sel) >= m_gdtr.limit)
							throw TRAP(FAULT_GP, IDXTBL(sel));
						addr = m_gdtr.base + IDX(sel);
						desc[0] = read_word(addr);
						desc[1] = read_word(addr + 2);
						desc[2] = read_word(addr + 4);
						r = RIGHTS(desc);
						if(SEGDESC(r) || (GATE(r) != LDTDESC))
							throw TRAP(FAULT_GP, IDXTBL(sel));
						if(!PRES(r))
							throw TRAP(FAULT_NP, IDXTBL(sel));
					}
					else
					{
						desc[0] = 0;
						desc[1] = 0;
						desc[2] = 0;
					}
					m_ldtr.sel = sel;
					m_ldtr.limit = LIMIT(desc);
					m_ldtr.base = BASE(desc);
					m_ldtr.rights = RIGHTS(desc);
					break;
					
				case 0x18: /* ltr */
					if(CPL != 0)
						throw TRAP(FAULT_GP, 0);
					sel = GetRMWord();
					if((addr = selector_address(sel)) == -1)
						throw TRAP(FAULT_GP, IDXTBL(sel));
					desc[0] = read_word(addr);
					desc[1] = read_word(addr + 2);
					desc[2] = read_word(addr + 4);
					r = RIGHTS(desc);
					if(SEGDESC(r) || (GATE(r) != TSSDESCIDLE))
						throw TRAP(FAULT_GP, IDXTBL(sel));
					if(!PRES(r))
						throw TRAP(FAULT_NP, IDXTBL(sel));
					desc[2] |= 0x200; // mark busy
					write_word(addr + 4, desc[2]);
					m_tr.sel = sel;
					m_tr.limit = LIMIT(desc);
					m_tr.base = BASE(desc);
					m_tr.rights = RIGHTS(desc);
					break;

				case 0x20: /* verr */
					tmp = GetRMWord();
					if((addr = selector_address(tmp)) == -1)
						m_ZeroVal = 1;
					else
					{
						desc[2] = read_word(addr + 4);
						r = RIGHTS(desc);
						m_ZeroVal = verify(tmp, I8086_READ, RIGHTS(desc), 0);
						m_ZeroVal = m_ZeroVal || (CODE(r) && CONF(r) ? 0 : (DPL(r) < PMAX(RPL(tmp),CPL)));
					}
					break;

				case 0x28: /* verw */
					tmp = GetRMWord();
					if((addr = selector_address(tmp)) == -1)
						m_ZeroVal = 1;
					else
					{
						desc[2] = read_word(addr + 4);
						r = RIGHTS(desc);
						m_ZeroVal = verify(tmp, I8086_WRITE, RIGHTS(desc), 0);
						m_ZeroVal = m_ZeroVal || (DPL(r) < PMAX(RPL(tmp),CPL));
					}
					break;

				default:
					throw TRAP(FAULT_UD, (uint16_t)-1);
				}
				break;
			case 1:
				{
					uint32_t ea;
					m_modrm = fetch();
					if((m_modrm >= 0xc0) && (m_modrm < 0xe0))
						throw TRAP(FAULT_UD, (uint16_t)-1);
					switch (m_modrm & 0x38)
					{
					case 0: /* sgdt */
						ea = get_ea(6, I8086_WRITE);
						write_word(ea, m_gdtr.limit);
						write_word(ea + 2, m_gdtr.base & 0xffff);
						write_word(ea + 4, 0xff00 | m_gdtr.base >> 16);
						break;
					case 8: /* sidt */
						ea = get_ea(6, I8086_WRITE);
						write_word(ea, m_idtr.limit);
						write_word(ea + 2, m_idtr.base & 0xffff);
						write_word(ea + 4, 0xff00 | m_idtr.base >> 16);
						break;
					case 0x10: /* lgdt */
						if(PM && (CPL != 0))
							throw TRAP(FAULT_GP, 0);
						ea = get_ea(6, I8086_READ);
						m_gdtr.limit = read_word(ea);
						m_gdtr.base = read_word(ea + 2) | (read_byte(ea + 4) << 16);
						break;
					case 0x18: /* lidt */
						if(PM && (CPL != 0))
							throw TRAP(FAULT_GP, 0);
						ea = get_ea(6, I8086_READ);
						m_idtr.limit = read_word(ea);
						m_idtr.base = read_word(ea + 2) | (read_byte(ea + 4) << 16);
						break;
					case 0x20: /* smsw */
						PutRMWord(m_msw);
						break;
					case 0x30: /* lmsw */
						if(PM && (CPL != 0))
							throw TRAP(FAULT_GP, 0);
						msw = GetRMWord();
						m_msw = (m_msw & 1) | msw;
						break;
					default:
						throw TRAP(FAULT_UD, (uint16_t)-1);
					}
					break;
				}
			case 2: /* LAR */
				if(!PM)
					throw TRAP(FAULT_UD, (uint16_t)-1);
				m_modrm = fetch_op();
				tmp = GetRMWord();
				if((addr = selector_address(tmp)) == -1)
					m_ZeroVal = 1;
				else
				{
					desc[2] = read_word(addr + 4);
					r = RIGHTS(desc);
					if(!SEGDESC(r) && ((GATE(r) > TRAPGATE) || !GATE(r)))
						m_ZeroVal = 1;
					else if(DPL(r) >= PMAX(RPL(tmp),CPL) || (SEGDESC(r) && CODE(r) && CONF(r)))
					{
						m_ZeroVal = 0;
						// rights are expected to be in upper byte
						RegWord(r << 8);
					}
					else
						m_ZeroVal = 1;
				}
				break;
			case 3: /* LSL */
				if(!PM)
					throw TRAP(FAULT_UD, (uint16_t)-1);
				m_modrm = fetch_op();
				tmp = GetRMWord();
				if((addr = selector_address(tmp)) == -1)
					m_ZeroVal = 1;
				else
				{
					desc[2] = read_word(addr + 4);
					r = RIGHTS(desc);
					if(!SEGDESC(r) && ((GATE(r) >= CALLGATE) || !GATE(r)))
						m_ZeroVal = 1; // not valid for gates
					else if(DPL(r) >= PMAX(RPL(tmp),CPL) || (SEGDESC(r) && CODE(r) && CONF(r)))
					{
						m_ZeroVal = 0;
						RegWord(read_word(addr));
					}
					else
						m_ZeroVal = 1;
				}
				break;
			case 5: /* loadall */
				if(PM && (CPL != 0))
					throw TRAP(FAULT_GP, 0);
				m_msw = (m_msw & 1) | read_word(0x806);
				m_tr.sel = read_word(0x816);
				ExpandFlags(read_word(0x818));
				m_ip = read_word(0x81a);
				m_ldtr.sel = read_word(0x81c);
				m_sregs[DS] = read_word(0x81e);
				m_sregs[SS] = read_word(0x820);
				m_sregs[CS] = read_word(0x822);
				m_sregs[ES] = read_word(0x824);
				m_regs.w[DI] = read_word(0x826);
				m_regs.w[SI] = read_word(0x828);
				m_regs.w[BP] = read_word(0x82a);
				m_regs.w[SP] = read_word(0x82c);
				m_regs.w[BX] = read_word(0x82e);
				m_regs.w[DX] = read_word(0x830);
				m_regs.w[CX] = read_word(0x832);
				m_regs.w[AX] = read_word(0x834);
				// loadall uses base-rights-limit order
#define LOADDESC(addr, sreg) {  desc[1] = read_word(addr); desc[2] = read_word(addr+2); desc[0] = read_word(addr+4); \
					m_base[sreg] = BASE(desc); m_rights[sreg] = RIGHTS(desc); \
					m_limit[sreg] = LIMIT(desc); }
				LOADDESC(0x836, ES);
				LOADDESC(0x83C, CS);
				LOADDESC(0x842, SS);
				LOADDESC(0x848, DS);
#undef LOADDESC
				// void cast supresses warning
#define LOADDESC(addr, reg, r) { desc[1] = read_word(addr); desc[2] = read_word(addr + 2); desc[0] = read_word(addr + 4); \
					reg.base = BASE(desc); (void)(r); reg.limit = LIMIT(desc); }
				LOADDESC(0x84e, m_gdtr, 1);
				LOADDESC(0x854, m_ldtr, m_ldtr.rights = RIGHTS(desc));
				LOADDESC(0x85a, m_idtr, 1);
				LOADDESC(0x860, m_tr, m_tr.rights = RIGHTS(desc));
#undef LOADDESC
				break;

			case 6: /* clts */
				if(PM && (CPL != 0))
					throw TRAP(FAULT_GP, 0);
				m_msw &= ~8;
				break;
			default:
				throw TRAP(FAULT_UD, (uint16_t)-1);
			}
			break;
		}

	case 0x17: // i_pop_ss
		pop_seg(SS);
		CLK(POP_SEG);
		m_no_interrupt = 1;
		break;

	case 0x1f: // i_pop_ds
		pop_seg(DS);
		CLK(POP_SEG);
		break;

	case 0x54: // i_push_sp
		PUSH(m_regs.w[SP]);
		CLK(PUSH_R16);
		break;

	case 0x60: // i_pusha
		{
			check_permission(SS, m_regs.w[SP]-16, 16, I8086_WRITE);
			uint32_t tmp = m_regs.w[SP];
			PUSH(m_regs.w[AX]);
			PUSH(m_regs.w[CX]);
			PUSH(m_regs.w[DX]);
			PUSH(m_regs.w[BX]);
			PUSH(tmp);
			PUSH(m_regs.w[BP]);
			PUSH(m_regs.w[SI]);
			PUSH(m_regs.w[DI]);
			CLK(PUSHA);
		}
		break;

	case 0x61: // i_popa
		check_permission(SS, m_regs.w[SP], 16, I8086_READ);
		m_regs.w[DI] = POP();
		m_regs.w[SI] = POP();
		m_regs.w[BP] = POP();
		POP();
		m_regs.w[BX] = POP();
		m_regs.w[DX] = POP();
		m_regs.w[CX] = POP();
		m_regs.w[AX] = POP();
		CLK(POPA);
		break;

	case 0x62: // i_bound
		{
			uint32_t low,high,tmp;
			m_modrm = fetch();
			low = GetRMWord();
			high = GetnextRMWord();
			tmp = RegWord();
			if (tmp<low || tmp>high)
				interrupt(5);
			CLK(BOUND);
			logerror("%06x: bound %04x high %04x low %04x tmp\n", pc(), high, low, tmp);
		}
		break;

	case 0x63: // arpl
		{
			uint16_t tmp, source;
			if (!PM) throw TRAP(FAULT_UD,(uint16_t)-1);

			m_modrm=fetch_op();
			tmp=GetRMWord();
			source=RegWord();

			if (RPL(tmp)<RPL(source))
			{
				m_ZeroVal = 0;
				PutbackRMWord(IDXTBL(tmp)|RPL(source));
			}
			else
				m_ZeroVal = 1;
			break;
		}

	case 0x68: // i_push_d16
		PUSH( fetch_word() );
		CLK(PUSH_IMM);
		break;

	case 0x69: // i_imul_d16
		{
			uint32_t tmp;
			DEF_r16w();
			tmp = fetch_word();
			m_dst = (int32_t)((int16_t)m_src)*(int32_t)((int16_t)tmp);
			m_CarryVal = m_OverVal = (((int32_t)m_dst) >> 15 != 0) && (((int32_t)m_dst) >> 15 != -1);
			RegWord(m_dst);
			CLKM(IMUL_RRI16, IMUL_RMI16);
		}
		break;

	case 0x6a: // i_push_d8
		PUSH( (uint16_t)((int16_t)((int8_t)fetch())) );
		CLK(PUSH_IMM);
		break;

	case 0x6b: // i_imul_d8
		{
			uint32_t src2;
			DEF_r16w();
			src2= (uint16_t)((int16_t)((int8_t)fetch()));
			m_dst = (int32_t)((int16_t)m_src)*(int32_t)((int16_t)src2);
			m_CarryVal = m_OverVal = (((int32_t)m_dst) >> 15 != 0) && (((int32_t)m_dst) >> 15 != -1);
			RegWord(m_dst);
			CLKM(IMUL_RRI8, IMUL_RMI8);
		}
		break;

	case 0x6c: // i_insb
		i_insb();
		break;

	case 0x6d: // i_insw
		i_insw();
		break;

	case 0x6e: // i_outsb
		i_outsb();
		break;

	case 0x6f: // i_outsw
		i_outsw();
		break;

	case 0x8c: // i_mov_wsreg
		m_modrm = fetch();
		if((m_modrm & 0x38) > 0x18)
		{
			logerror("%06x: Mov Sreg - Invalid register\n", pc());
			throw TRAP(FAULT_UD, (uint16_t)-1);
		}
		PutRMWord(m_sregs[(m_modrm & 0x38) >> 3]);
		CLKM(MOV_RS,MOV_MS);
		break;

	case 0x8e: // i_mov_sregw
		m_modrm = fetch();
		m_src = GetRMWord();
		CLKM(MOV_SR,MOV_SM);
		switch (m_modrm & 0x38)
		{
		case 0x00:  /* mov es,ew */
			data_descriptor(ES, m_src);
			break;
		case 0x10:  /* mov ss,ew */
			data_descriptor(SS, m_src);
			m_no_interrupt = 1;
			break;
		case 0x18:  /* mov ds,ew */
			data_descriptor(DS, m_src);
			break;
		default:
			logerror("%06x: Mov Sreg - Invalid register\n", pc());
			throw TRAP(FAULT_UD, (uint16_t)-1);
		}
		break;

	case 0x8f: // i_popw
		{
			m_modrm = fetch();
			uint16_t tmp = read_word(calc_addr(SS, m_regs.w[SP], 2, I8086_READ, false));
			PutRMWord( tmp );
			m_regs.w[SP] += 2;
			CLKM(POP_R16,POP_M16);
			break;
		}

	case 0x9a: // i_call_far
		{
			uint16_t cs = m_sregs[CS];
			uint16_t tmp = fetch_word();
			uint16_t tmp2 = fetch_word();
			uint16_t ip = m_ip;
			code_descriptor(tmp2, tmp, NT_CALL);
			if(call_pseudo_bios(((tmp2 << 4) + tmp) & m_amask)) {
				CLK(CALL_FAR);
				return;
			}
			PUSH(cs);
			PUSH(ip);
			CLK(CALL_FAR);
		}
		break;

	case 0x9b: // i_wait
		if((m_msw & 0x0a) == 0x0a)
			throw TRAP(FAULT_NM, (uint16_t)-1);
		if (m_test_state != 0) {
			m_icount = 0;
			m_ip--; // pc?
		} else {
			CLK(WAIT);
		}
		break;

	case 0x9c: // pushf
		{
			uint16_t flags = CompressFlags();
			if(!PM)
				flags &= ~0xf000;
			PUSH(flags);
			CLK(PUSHF);
			break;
		}

	case 0x9d: // popf
		{
			uint16_t flags;
			flags = POP();
			CLK(POPF);
			load_flags(flags, CPL);
			break;
		}

	case 0xc0: // i_rotshft_bd8
		{
			uint8_t c;
			m_modrm = fetch();
			m_src = GetRMByte();
			m_dst = m_src;
			c = fetch() & 0x1f;
			CLKM(ROT_REG_BASE,ROT_M8_BASE);
			m_icount -= m_timing[ROT_REG_BIT] * c;
			if (c)
			{
				switch ( m_modrm & 0x38 )
				{
				case 0x00: do { ROL_BYTE();  c--; } while (c>0); PutbackRMByte(m_dst); break;
				case 0x08: do { ROR_BYTE();  c--; } while (c>0); PutbackRMByte(m_dst); break;
				case 0x10: do { ROLC_BYTE(); c--; } while (c>0); PutbackRMByte(m_dst); break;
				case 0x18: do { RORC_BYTE(); c--; } while (c>0); PutbackRMByte(m_dst); break;
				case 0x30:
				case 0x20: SHL_BYTE(c); break;
				case 0x28: SHR_BYTE(c); break;
				case 0x38: SHRA_BYTE(c); break;
				}
			}
		}
		break;

	case 0xc1: // i_rotshft_wd8
		{
			uint8_t c;
			m_modrm = fetch();
			m_src = GetRMWord();
			m_dst = m_src;
			c = fetch() & 0x1f;
			CLKM(ROT_REG_BASE,ROT_M16_BASE);
			m_icount -= m_timing[ROT_REG_BIT] * c;
			if (c)
			{
				switch ( m_modrm & 0x38 )
				{
				case 0x00: do { ROL_WORD();  c--; } while (c>0); PutbackRMWord(m_dst); break;
				case 0x08: do { ROR_WORD();  c--; } while (c>0); PutbackRMWord(m_dst); break;
				case 0x10: do { ROLC_WORD(); c--; } while (c>0); PutbackRMWord(m_dst); break;
				case 0x18: do { RORC_WORD(); c--; } while (c>0); PutbackRMWord(m_dst); break;
				case 0x30:
				case 0x20: SHL_WORD(c); break;
				case 0x28: SHR_WORD(c); break;
				case 0x38: SHRA_WORD(c); break;
				}
			}
		}
		break;

	case 0xc4: // i_les_dw
		{
			m_modrm = fetch();
			if(m_modrm >= 0xc0)
				throw TRAP(FAULT_UD, (uint16_t)-1);
			uint16_t tmp = GetRMWord();
			data_descriptor(ES, GetnextRMWord());
			RegWord(tmp);
			CLK(LOAD_PTR);
			break;
		}

	case 0xc5: // i_lds_dw
		{
			m_modrm = fetch();
			if(m_modrm >= 0xc0)
				throw TRAP(FAULT_UD, (uint16_t)-1);
			uint16_t tmp = GetRMWord();
			data_descriptor(DS, GetnextRMWord());
			RegWord(tmp);
			CLK(LOAD_PTR);
			break;
		}

	case 0xc8: // i_enter
		{
			uint16_t nb = fetch();
			uint32_t level;

			nb |= fetch() << 8;
			level = fetch();
			CLK(!level ? ENTER0 : (level == 1) ? ENTER1 : ENTER_BASE);
			if(level > 1)
				m_icount -= level * m_timing[ENTER_COUNT];
			PUSH(m_regs.w[BP]);
			m_regs.w[BP] = m_regs.w[SP];
			m_regs.w[SP] -= nb;
			for (int i=1; i<level; i++)
			{
				PUSH( GetMemW(SS,m_regs.w[BP] - i*2) );
			}
			if (level)
			{
				PUSH(m_regs.w[BP]);
			}
		}
		break;

	case 0xc9: // i_leave
		m_regs.w[SP] = m_regs.w[BP];
		m_regs.w[BP] = POP();
		CLK(LEAVE);
		break;

	case 0xca: // ret far imm
		{
			unsigned count = fetch_word();
			far_return(0, count);
			CLK(RET_FAR_IMM);
			break;
		}
	case 0xcb: // ret far
		far_return(0, 0);
		CLK(RET_FAR);
		break;

	case 0xcf: // iret
		{
			int oldcpl = (PM) ? CPL : 0;
			uint16_t flags = far_return(1, 0);
			CLK(IRET);
			load_flags(flags, oldcpl);
			break;
		}

	case 0xd2: // i_rotshft_bcl
		{
			uint8_t c;

			m_modrm = fetch();
			m_src = GetRMByte();
			m_dst = m_src;
			c = m_regs.b[CL] & 0x1f;
			CLKM(ROT_REG_BASE,ROT_M16_BASE);
			m_icount -= m_timing[ROT_REG_BIT] * c;
			if (c)
			{
				switch ( m_modrm & 0x38 )
				{
				case 0x00: do { ROL_BYTE();  c--; } while (c>0); PutbackRMByte(m_dst); break;
				case 0x08: do { ROR_BYTE();  c--; } while (c>0); PutbackRMByte(m_dst); break;
				case 0x10: do { ROLC_BYTE(); c--; } while (c>0); PutbackRMByte(m_dst); break;
				case 0x18: do { RORC_BYTE(); c--; } while (c>0); PutbackRMByte(m_dst); break;
				case 0x30:
				case 0x20: SHL_BYTE(c); break;
				case 0x28: SHR_BYTE(c); break;
				case 0x38: SHRA_BYTE(c); break;
				}
			}
		}
		break;

	case 0xd3: // i_rotshft_wcl
		{
			uint8_t c;

			m_modrm = fetch();
			m_src = GetRMWord();
			m_dst = m_src;
			c = m_regs.b[CL] & 0x1f;
			CLKM(ROT_REG_BASE,ROT_M16_BASE);
			m_icount -= m_timing[ROT_REG_BIT] * c;
			if (c)
			{
				switch ( m_modrm & 0x38 )
				{
				case 0x00: do { ROL_WORD();  c--; } while (c>0); PutbackRMWord(m_dst); break;
				case 0x08: do { ROR_WORD();  c--; } while (c>0); PutbackRMWord(m_dst); break;
				case 0x10: do { ROLC_WORD(); c--; } while (c>0); PutbackRMWord(m_dst); break;
				case 0x18: do { RORC_WORD(); c--; } while (c>0); PutbackRMWord(m_dst); break;
				case 0x30:
				case 0x20: SHL_WORD(c); break;
				case 0x28: SHR_WORD(c); break;
				case 0x38: SHRA_WORD(c); break;
				}
			}
		}
		break;

	case 0xd8: // i_esc
	case 0xd9:
	case 0xda:
	case 0xdb:
	case 0xdc:
	case 0xdd:
	case 0xde:
	case 0xdf:
		if((m_msw & 8) || (m_msw & 4))
			throw TRAP(FAULT_NM, (uint16_t)-1);
		m_modrm = fetch();
		GetRMByte();
		CLK(NOP);
		if((m_modrm == 0xe0) && (op == 0xdf))
			m_regs.w[AX] = 0xffff;  // FPU not present
		break;

	case 0xea: // i_jmp_far
		{
			uint16_t tmp = fetch_word();
			uint16_t tmp1 = fetch_word();
			code_descriptor(tmp1, tmp, NT_JMP);
			CLK(JMP_FAR);
			break;
		}

	case 0xf0: // i_lock
		if(PM && (CPL > m_IOPL))
			throw TRAP(FAULT_GP, 0);
		logerror("%06x: Warning - BUSLOCK\n", pc());
		m_no_interrupt = 1;
		CLK(NOP);
		break;

	case 0xf4: // i_hlt
		if(PM && CPL)
			throw TRAP(FAULT_GP, 0);
		m_icount = 0;
		m_halt = true;
		break;

	case 0xfa: // i_cli
		if(PM && (CPL > m_IOPL))
			throw TRAP(FAULT_GP, 0);
		m_IF = 0;
		CLK(FLAG_OPS);
		break;

	case 0xfb: // i_sti
		if(PM && (CPL > m_IOPL))
			throw TRAP(FAULT_GP, 0);
		m_IF = 1;
		CLK(FLAG_OPS);
		break;

	case 0xff: // i_ffpre
		{
			uint32_t tmp, tmp1;
			m_modrm = fetch();
			tmp = GetRMWord();
			switch ( m_modrm & 0x38 )
			{
			case 0x00:  /* INC */
				tmp1 = tmp+1;
				m_OverVal = (tmp==0x7fff);
				set_AF(tmp1,tmp,1);
				set_SZPF_Word(tmp1);
				PutbackRMWord(tmp1);
				CLKM(INCDEC_R16,INCDEC_M16);
				break;
			case 0x08:  /* DEC */
				tmp1 = tmp-1;
				m_OverVal = (tmp==0x8000);
				set_AF(tmp1,tmp,1);
				set_SZPF_Word(tmp1);
				PutbackRMWord(tmp1);
				CLKM(INCDEC_R16,INCDEC_M16);
				break;
			case 0x10:  /* CALL */
				if(call_pseudo_bios((m_base[m_CS] + (uint16_t)tmp) & m_amask)) {
					CLK(CALL_R16,CALL_M16);
					//m_ip = m_ip + tmp;
					return;
				}
				PUSH(m_ip);
				m_ip = tmp;
				CLKM(CALL_R16,CALL_M16);
				break;
			case 0x18:  /* CALL FAR */
				{
					uint16_t ip = m_ip;
					tmp1 = m_sregs[CS];
					code_descriptor(GetnextRMWord(), tmp, NT_CALL);
					if(call_pseudo_bios(((m_sregs[CS] << 4) + tmp1) & m_amask) { // OK?
						CLK(CALL_M32);
						//m_ip = m_ip + tmp;
						return;
					}
					PUSH(tmp1);
					PUSH(ip);
					CLK(CALL_M32);
					break;
				}
			case 0x20:  /* JMP */
				m_ip = tmp;
				CLKM(JMP_R16,JMP_M16);
				break;
			case 0x28:  /* JMP FAR */
				code_descriptor(GetnextRMWord(), tmp, NT_JMP);
				CLK(JMP_M32);
				break;
			case 0x30:
				PUSH(tmp);
				CLKM(PUSH_R16,PUSH_M16);
				break;
			default:
				logerror("%06x: FF Pre with unimplemented mod\n", pc());
				throw TRAP(FAULT_UD,(uint16_t)-1);
			}
		}
		break;

	case 0xf2: // i_repne
	case 0xf3:
		{
			bool pass = false;
			uint8_t next = repx_op();
			uint16_t c = m_regs.w[CX];

			switch (next)
			{
			case 0x6c:  CLK(OVERRIDE); if (c) do { i_insb();  c--; } while (c>0 && m_icount>0);          m_regs.w[CX]=c; m_seg_prefix = false; m_seg_prefix_next = false; break;
			case 0x6d:  CLK(OVERRIDE); if (c) do { i_insw();  c--; } while (c>0 && m_icount>0);          m_regs.w[CX]=c; m_seg_prefix = false; m_seg_prefix_next = false; break;
			case 0x6e:  CLK(OVERRIDE); if (c) do { i_outsb(); c--; } while (c>0 && m_icount>0);          m_regs.w[CX]=c; m_seg_prefix = false; m_seg_prefix_next = false; break;
			case 0x6f:  CLK(OVERRIDE); if (c) do { i_outsw(); c--; } while (c>0 && m_icount>0);          m_regs.w[CX]=c; m_seg_prefix = false; m_seg_prefix_next = false; break;
			default:
				// Decrement IP and pass on
				m_ip -= 1 + (m_seg_prefix_next ? 1 : 0);
				pass = true;
			}
			if(!pass)
			{
				if(c)
					m_ip = m_prev_ip;
				break;
			}
		}

	default:
		if(!common_op(op))
		{
			m_icount -= 10; // UD fault timing?
			logerror("%06x: Invalid Opcode %02x\n", pc(), op);
			m_ip = m_prev_ip;
			throw TRAP(FAULT_UD, (uint16_t)-1);
		}
		break;
	}
	catch(uint32_t e)
	{
		trap(e);
	}
}

void i80286_cpu_device::execute_run(int icount)
{
	if (m_halt || m_busreq)
	{
//#ifdef SINGLE_MODE_DMA
		if (d_dma != NULL) {
			d_dma->do_dma();
		}
//#endif
		if (icount == -1) {
			int passed_icount = max(1, m_extra_cycles);
			// this is main cpu, cpustate->icount is not used
			/*cpustate->icount = */m_extra_cycles = 0;
			return passed_icount;
		} else {
			cpustate->icount += icount;
			int base_icount = m_icount;

			/* adjust for any interrupts that came in */
			m_icount -= m_extra_cycles;
			m_extra_cycles = 0;

			/* if busreq is raised, spin cpu while remained clock */
			if (m_icount > 0) {
				m_icount = 0;
			}
			return base_icount - m_icount;
		}
	}

	if (icount == -1) {
		m_icount = 1;
	} else {
		m_icount += icount;
	}
	int base_icount = m_icount;

	/* adjust for any interrupts that came in */
	m_icount -= m_extra_cycles;
	m_extra_cycles = 0;

	while(m_icount > 0 && !m_busreq)
	{
//		try
		{
			if ( m_seg_prefix_next )
			{
				m_seg_prefix = true;
				m_seg_prefix_next = false;
			}
			else
			{
				m_prev_ip = m_ip;
				m_seg_prefix = false;

				/* Dispatch IRQ */
				if ( m_pending_irq && m_no_interrupt == 0 )
				{
					if ( m_pending_irq & NMI_IRQ )
					{
						interrupt_descriptor(NMI, 1, -1);
						m_pending_irq &= ~NMI_IRQ;
						m_halt = false;
						m_shutdown = false;
						//m_out_shutdown_func(false);
						write_signals(&out_shutdown, 0);
					}
					else if ( m_IF )
					{
						interrupt_descriptor(-1, 1, -1);
						m_halt = false;
					}
				}

				if(m_halt || m_shutdown)
				{
					m_icount = 0;
					return;
				}

				/* No interrupt allowed between last instruction and this one */
				if ( m_no_interrupt )
				{
					m_no_interrupt--;
				}

				/* trap should allow one instruction to be executed */
				if ( m_fire_trap )
				{
					if ( m_fire_trap >= 2 )
					{
						interrupt(1);
						m_fire_trap = 0;
					}
					else
					{
						m_fire_trap++;
					}
				}
			}
			
			/* debugger_instruction_hook( this, pc() & m_amask ); */
			debugger_hook();
			//exec_opcode();
		}
//		catch(uint32_t e)
//		{
//			trap(e);
//		}
//#ifdef SINGLE_MODE_DMA
		if (d_dma != NULL) {
			d_dma->do_dma();
		}
//#endif
	}
	/* if busreq is raised, spin cpu while remained clock */
	if (m_icount > 0 && m_busreq) {
		m_icount = 0;
	}
	return base_icount - m_icount;
}


void i80286_cpu_device::load_flags(uint16_t flags, int cpl)
{
	uint16_t oldflags = CompressFlags();
	flags &= ~0x8000;
	if(PM && cpl)
	{
		uint16_t mask = 0x3000;
		if(cpl > m_IOPL)
			mask |= 0x200;
		flags &= ~mask;
		flags |= (oldflags & mask);
	}
	else if(!PM)
		(flags &= ~0xf000);
	ExpandFlags(flags);

	if(m_TF)
		m_fire_trap = 1;
}

uint16_t i80286_cpu_device::far_return(int iret, int bytes)
{
	uint16_t sel, off, flags = 0;
	int spaddr;

	if(PM && m_NT && iret)
	{
		switch_task(read_word(m_tr.base + TSS_BACK * 2), NT_IRET);
		return CompressFlags();
	}

	// must be restartable
	if(PM)
		check_permission(SS, m_regs.w[SP], (iret ? 6 : 4), I8086_READ);

	spaddr = (m_base[SS] + m_regs.w[SP]) & m_amask;
	off = read_word(spaddr);
	sel = read_word(spaddr + 2);
	if(iret)
		flags = read_word(spaddr + 4);

	if(PM)
	{
		uint16_t desc[3], newsp, newss;
		int addr, r;

		if((addr = selector_address(sel)) == -1)
			throw TRAP(FAULT_GP, IDXTBL(sel));

		if(RPL(sel) < CPL)
			throw TRAP(FAULT_GP, IDXTBL(sel));
		desc[0] = read_word(addr);
		desc[1] = read_word(addr + 2);
		desc[2] = read_word(addr + 4);
		r = RIGHTS(desc);

		if(!CODE(r) || !SEGDESC(r))
			throw TRAP(FAULT_GP, IDXTBL(sel));
		if(CONF(r))
		{
			if(DPL(r) > RPL(sel))
				throw TRAP(FAULT_GP, IDXTBL(sel));
		}
		else if(DPL(r) != RPL(sel))
			throw TRAP(FAULT_GP, IDXTBL(sel));

		if(!PRES(r))
			throw TRAP(FAULT_NP, IDXTBL(sel));
		if(off > LIMIT(desc))
			throw TRAP(FAULT_GP, 0);
		if(CPL < RPL(sel))
		{
			check_permission(SS, m_regs.w[SP] + (iret ? 6 : 4) + bytes, 4, I8086_READ);
			newsp = read_word(spaddr + ((iret ? 6 : 4) + bytes));
			newss = read_word(spaddr + ((iret ? 8 : 6) + bytes));
			data_descriptor(SS, newss, RPL(sel), TRAP(FAULT_GP, IDXTBL(newss)));
			m_regs.w[SP] = newsp + bytes;
		}
		else
			m_regs.w[SP] += (iret ? 6 : 4) + bytes;
		SET_ACC(desc);
		write_word(addr + 4, desc[2]);
		m_sregs[CS] = sel;
		m_limit[CS] = LIMIT(desc);
		m_base[CS] = BASE(desc);
		m_rights[CS] = RIGHTS(desc);
		m_ip = off;

		// docs say check rpl but windows doesn't like it
		r = m_rights[DS];
		if(verify(m_sregs[DS], I8086_READ, r, 0) || (CODE(r) && CONF(r) ? 0 : (DPL(r) < CPL)))
			data_descriptor(DS, 0);
		r = m_rights[ES];
		if(verify(m_sregs[ES], I8086_READ, r, 0) || (CODE(r) && CONF(r) ? 0 : (DPL(r) < CPL)))
			data_descriptor(ES, 0);
	}
	else
	{
		m_regs.w[SP] += (iret ? 6 : 4) + bytes;
		m_sregs[CS] = sel;
		m_base[CS] = sel << 4;
		m_rights[CS] = 0x9b;
		m_limit[CS] = 0xffff;
		m_ip = off;
	}

	return flags;
}

void i80286_cpu_device::check_permission(uint8_t check_seg, uint32_t offset, uint16_t size, int operation)
{
	int trap;
	uint8_t rights;
	if(PM)
	{
		rights = m_rights[check_seg];
		trap = verify(m_sregs[check_seg], operation, rights, m_valid[check_seg]);
		if((CODE(rights) || !EXPDOWN(rights)) && ((offset+size-1) > m_limit[check_seg])) {
			trap = FAULT_GP;
		}
		if(!CODE(rights) && EXPDOWN(rights) && ((offset <= m_limit[check_seg]) || ((offset + size - 1) > 0xffff))) {
			trap = FAULT_GP;
		}
		if((trap == FAULT_GP) && (check_seg == SS)) {
			trap = FAULT_SS;
		}
		if(trap) {
			throw TRAP(trap, 0);
		}
	}
}

int i80286_cpu_device::debug_dasm(uint32_t pc, _TCHAR *buffer, size_t buffer_len)
{

	std::ostringstream ostbuf;
	extern int i386_dasm_one(std::ostream &stream, uint64_t eip, const uint8_t *oprom, int mode);
	
	cpu_state *cpustate = (cpu_state *)opaque;
	UINT64 eip = pc - base[CS];
	UINT8 ops[16];
	for(int i = 0; i < 16; i++) {
		int wait;
		ops[i] = d_mem->read_data8w(pc + i, &wait);
	}
	const uint8_t *oprom = ops;
	i386_dasm_one(ostbuf, eip, oprom, 1);
	ostbuf.str().copy((char *)buffer, buffer_len);
}

bool i80286_cpu_device::call_pseudo_bios(uint32_t pc)
{
	// blank
	return false; // Not hit
}
