summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/cpu/i86/instr286.c
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2011-05-17 13:22:58 +0000
committer Miodrag Milanovic <mmicko@gmail.com>2011-05-17 13:22:58 +0000
commit949d84671402e776b762b6cb60513a6422d3d9a5 (patch)
tree91ae5519c6adebff0a2163821ee94cd97feaed6f /src/emu/cpu/i86/instr286.c
parent9cf16652c7308de7bf872ddde80480503e241ddd (diff)
i286 - Added 286 LOADALL and triple fault resets. [Carl]
i286 - Added support for 16-bit protected mode far calls and segment privilege checks. [Carl] i86 - Fixed shift instructions, helps detecting proper CPU [Miodrag Milanovic] i86 - Fixed handling of invalid instructions in 0x60-0x6f section [Miodrag Milanovic]
Diffstat (limited to 'src/emu/cpu/i86/instr286.c')
-rw-r--r--src/emu/cpu/i86/instr286.c700
1 files changed, 570 insertions, 130 deletions
diff --git a/src/emu/cpu/i86/instr286.c b/src/emu/cpu/i86/instr286.c
index 6765995d15c..dba92252483 100644
--- a/src/emu/cpu/i86/instr286.c
+++ b/src/emu/cpu/i86/instr286.c
@@ -21,9 +21,98 @@
#define IS_WRITEABLE(a) ( ( (a) & 0xa ) == 2 )
#define IS_READABLE(a) ( ( ( (a) & 0xa ) == 0xa ) || ( ( (a) & 8 ) == 0 ) )
-static void i80286_trap2(i80286_state *cpustate,int number)
+#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 2
+#define TSS_SS0 4
+#define TSS_SP1 6
+#define TSS_SS1 8
+#define TSS_SP2 10
+#define TSS_SS2 12
+#define TSS_IP 14
+#define TSS_FLAG 16
+#define TSS_AX 18
+#define TSS_CX 20
+#define TSS_DX 22
+#define TSS_BX 24
+#define TSS_SP 26
+#define TSS_BP 28
+#define TSS_SI 30
+#define TSS_DI 32
+#define TSS_ES 34
+#define TSS_CS 36
+#define TSS_SS 38
+#define TSS_DS 40
+#define TSS_LDT 42
+
+#define PMAX(a,b) ((a<b)?b:a)
+
+// 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.
+
+static void i80286_trap2(i80286_state *cpustate,UINT32 error)
{
- i80286_interrupt(cpustate,number);
+ int error_code = error & 0xffff;
+ UINT16 number = error >> 16;
+ if(error_code == 0xffff) error_code = -1;
+ cpustate->pc = cpustate->prevpc;
+ try {
+ switch(number) {
+ case DIVIDE_BY_ZERO:
+ case INVALID_TSS:
+ case SEG_NOT_PRESENT:
+ case STACK_FAULT:
+ case GENERAL_PROTECTION_FAULT:
+ cpustate->trap_level++;
+ if(cpustate->trap_level == 2) throw TRAP(DOUBLE_FAULT,0);
+ if(cpustate->trap_level == 3) break;
+ i80286_interrupt_descriptor(cpustate,number,1,error_code);
+ break;
+ case DOUBLE_FAULT:
+ i80286_interrupt_descriptor(cpustate,number,1,0);
+ break;
+ default:
+ i80286_interrupt_descriptor(cpustate,number,1,-1);
+ }
+ } catch(UINT32 e) { i80286_trap2(cpustate, e); }
+ if(cpustate->trap_level == 3)
+ // this is supposed to triggered by support hardware
+ // create a shutdown output line that causes a reset
+ // NMI can wake processor without reset
+ device_set_input_line(cpustate->device, INPUT_LINE_RESET, PULSE_LINE);
+ cpustate->trap_level = 0;
}
static int i80286_selector_okay(i80286_state *cpustate,UINT16 selector)
@@ -44,120 +133,275 @@ static offs_t i80286_selector_to_address(i80286_state *cpustate,UINT16 selector)
}
}
-static void i80286_data_descriptor(i80286_state *cpustate,int reg, UINT16 selector)
+static void i80286_pop_seg(i80286_state *cpustate, int reg) {
+ UINT16 sel;
+ sel = ReadWord(cpustate->base[SS]+cpustate->regs.w[SP]);
+ i80286_data_descriptor(cpustate, reg, sel, CPL);
+ cpustate->regs.w[SP] += 2;
+}
+
+static void i80286_data_descriptor(i80286_state *cpustate,int reg, UINT16 selector, int cpl)
{
if (PM) {
- UINT16 help;
+ UINT16 desc[3];
+ UINT8 r;
+ int addr;
/* selector format
15..3 number/address in descriptor table
2: 0 global, 1 local descriptor table
1,0: requested privileg level
must be higher or same as current privileg level in code selector */
- if (selector&4) { /* local descriptor table */
- if (selector>cpustate->ldtr.limit) i80286_trap2(cpustate,GENERAL_PROTECTION_FAULT);
- cpustate->sregs[reg]=selector;
- cpustate->limit[reg]=ReadWord(cpustate->ldtr.base+(selector&~7));
- cpustate->base[reg]=ReadWord(cpustate->ldtr.base+(selector&~7)+2)
- |(ReadWord(cpustate->ldtr.base+(selector&~7)+4)<<16);
- cpustate->rights[reg]=cpustate->base[reg]>>24;
- cpustate->base[reg]&=0xffffff;
+ if ((reg != SS) && !IDXTBL(selector)) {
+ cpustate->sregs[reg]=0;
+ cpustate->limit[reg]=0;
+ cpustate->base[reg]=0;
+ cpustate->rights[reg]=0;
+ return;
+ }
+
+ if (TBL(selector)) { /* local descriptor table */
+ if (selector>cpustate->ldtr.limit) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector));
+ addr = cpustate->ldtr.base+IDX(selector);
} else { /* global descriptor table */
- if (!(selector&~7)||(selector>cpustate->gdtr.limit)) i80286_trap2(cpustate,GENERAL_PROTECTION_FAULT);
- cpustate->sregs[reg]=selector;
- cpustate->limit[reg]=ReadWord(cpustate->gdtr.base+(selector&~7));
- cpustate->base[reg]=ReadWord(cpustate->gdtr.base+(selector&~7)+2);
- help=ReadWord(cpustate->gdtr.base+(selector&~7)+4);
- cpustate->rights[reg]=help>>8;
- cpustate->base[reg]|=(help&0xff)<<16;
+ if (selector>cpustate->gdtr.limit) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector));
+ addr = cpustate->gdtr.base+IDX(selector);
}
+
+ desc[0] = ReadWord(addr);
+ desc[1] = ReadWord(addr+2);
+ desc[2] = ReadWord(addr+4);
+ r = RIGHTS(desc);
+ if (!SEGDESC(r)) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector));
+ if (reg == SS) {
+ if (!IDXTBL(selector)) throw TRAP(GENERAL_PROTECTION_FAULT,0);
+ if (DPL(r)!=cpl) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector));
+ if (RPL(selector)!=cpl) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector));
+ if (!RW(r) || CODE(r)) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector));
+ if (!PRES(r)) throw TRAP(STACK_FAULT,IDXTBL(selector));
+ } else {
+ if (DPL(r) < PMAX(cpl,RPL(selector))) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector));
+ if (CODE(r) && !READ(r)) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector));
+ }
+
+ SET_ACC(desc);
+ WriteWord(addr+4, desc[2]);
+ cpustate->sregs[reg]=selector;
+ cpustate->limit[reg]=LIMIT(desc);
+ cpustate->base[reg]=BASE(desc);
+ cpustate->rights[reg]=RIGHTS(desc);
} else {
cpustate->sregs[reg]=selector;
cpustate->base[reg]=selector<<4;
}
}
-static void i80286_code_descriptor(i80286_state *cpustate,UINT16 selector, UINT16 offset)
+static void i80286_code_descriptor(i80286_state *cpustate,UINT16 selector, UINT16 offset, int gate)
{
- UINT16 word1, word2, word3;
if (PM) {
+ UINT16 desc[3];
+ UINT8 r;
+ int addr;
/* selector format
15..3 number/address in descriptor table
2: 0 global, 1 local descriptor table
1,0: requested privileg level
must be higher or same as current privileg level in code selector */
- if (selector&4) { /* local descriptor table */
- if (selector>cpustate->ldtr.limit) i80286_trap2(cpustate,GENERAL_PROTECTION_FAULT);
- word1=ReadWord(cpustate->ldtr.base+(selector&~7));
- word2=ReadWord(cpustate->ldtr.base+(selector&~7)+2);
- word3=ReadWord(cpustate->ldtr.base+(selector&~7)+4);
+ if (TBL(selector)) { /* local descriptor table */
+ if (selector>cpustate->ldtr.limit) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector));
+ addr = cpustate->ldtr.base+IDX(selector);
} else { /* global descriptor table */
- if (!(selector&~7)||(selector>cpustate->gdtr.limit)) i80286_trap2(cpustate,GENERAL_PROTECTION_FAULT);
- word1=ReadWord(cpustate->gdtr.base+(selector&~7));
- word2=ReadWord(cpustate->gdtr.base+(selector&~7)+2);
- word3=ReadWord(cpustate->gdtr.base+(selector&~7)+4);
+ if (!IDX(selector)||(selector>cpustate->gdtr.limit)) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector));
+ addr = cpustate->gdtr.base+IDX(selector);
}
- if (word3&0x1000) {
- cpustate->sregs[CS]=selector;
- cpustate->limit[CS]=word1;
- cpustate->base[CS]=word2|((word3&0xff)<<16);
- cpustate->rights[CS]=word3>>8;
+ desc[0] = ReadWord(addr);
+ desc[1] = ReadWord(addr+2);
+ desc[2] = ReadWord(addr+4);
+ r = RIGHTS(desc);
+
+ if (SEGDESC(r)) {
+ if (!CODE(r)) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector));
+ if (CONF(r)) { if(DPL(r)>CPL) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector)); }
+ else if ((RPL(selector)>CPL) || (DPL(r)!=CPL)) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector));
+
+ SET_ACC(desc);
+ WriteWord(addr+4, desc[2]);
+ if (!PRES(r)) throw TRAP(SEG_NOT_PRESENT,IDXTBL(selector)); // this order is important
+ if (offset > LIMIT(desc)) throw TRAP(GENERAL_PROTECTION_FAULT, 0);
+ cpustate->sregs[CS]=IDXTBL(selector) | CPL;
+ cpustate->limit[CS]=LIMIT(desc);
+ cpustate->base[CS]=BASE(desc);
+ cpustate->rights[CS]=RIGHTS(desc);
cpustate->pc=cpustate->base[CS]+offset;
} else { // systemdescriptor
- switch (word3&0xf00) {
- case 0x400: // call gate
- // word3&0x1f words to be copied from stack to stack
- i80286_data_descriptor(cpustate, CS, word2);
- cpustate->pc=cpustate->base[CS]+word1;
- break;
- case 0x500: // task gate
- i80286_data_descriptor(cpustate, CS, word2);
- cpustate->pc=cpustate->base[CS]+word1;
- break;
- case 0x600: // interrupt gate
- cpustate->TF = cpustate->IF = 0;
- i80286_data_descriptor(cpustate, CS, word2);
- cpustate->pc=cpustate->base[CS]+word1;
+ UINT16 gatedesc[3];
+ UINT16 gatesel = GATESEL(desc);
+ if (!gate) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector)); // can't ret through gate
+ if (DPL(r) < PMAX(CPL,RPL(selector))) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector));
+ if (!PRES(r)) throw TRAP(SEG_NOT_PRESENT, IDXTBL(selector));
+
+ switch (GATE(r)) {
+ case CALLGATE:
+ if (TBL(gatesel)) { /* local descriptor table */
+ if (gatesel>cpustate->ldtr.limit) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(gatesel));
+ addr = cpustate->ldtr.base+IDX(gatesel);
+ } else { /* global descriptor table */
+ if (!IDX(gatesel)||(gatesel>cpustate->gdtr.limit)) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(gatesel));
+ addr = cpustate->gdtr.base+IDX(gatesel);
+ }
+ addr = cpustate->gdtr.base+IDX(gatesel);
+ gatedesc[0] = ReadWord(addr);
+ gatedesc[1] = ReadWord(addr+2);
+ gatedesc[2] = ReadWord(addr+4);
+ r = RIGHTS(gatedesc);
+ if (!CODE(r)) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(gatesel));
+ if (DPL(r)>CPL) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(gatesel));
+ if (!PRES(r)) throw TRAP(SEG_NOT_PRESENT,IDXTBL(gatesel));
+ if (GATEOFF(desc) > LIMIT(gatedesc)) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(gatesel));
+
+ if (!CONF(r)&&(DPL(r)<CPL)) { // inner call
+ UINT16 ssdesc[3];
+ UINT16 tss_ss,tss_sp, oldss, oldsp;
+ int oldsp_phy, ssr, i;
+ if(gate == 1) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(gatesel)); // can't jmp to inner
+ tss_ss = ReadWord(cpustate->tr.base+TSS_SS0+(DPL(r)*4));
+ tss_sp = ReadWord(cpustate->tr.base+TSS_SP0+(DPL(r)*4));
+
+ if (!IDXTBL(tss_ss)) throw TRAP(INVALID_TSS, 0);
+ if (TBL(tss_ss)) { /* local descriptor table */
+ if (tss_ss>cpustate->ldtr.limit) throw TRAP(INVALID_TSS,IDXTBL(tss_ss));
+ addr = cpustate->ldtr.base+IDX(tss_ss);
+ } else { /* global descriptor table */
+ if (tss_ss>cpustate->gdtr.limit) throw TRAP(INVALID_TSS,IDXTBL(tss_ss));
+ addr = cpustate->gdtr.base+IDX(tss_ss);
+ }
+ ssdesc[0] = ReadWord(addr);
+ ssdesc[1] = ReadWord(addr+2);
+ ssdesc[2] = ReadWord(addr+4);
+ ssr = RIGHTS(ssdesc);
+ if ((RPL(tss_ss) != DPL(r)) || DPL(ssr) != DPL(r)) TRAP(INVALID_TSS,IDXTBL(tss_ss));
+ if (!RW(ssr) || !PRES(ssr)) throw TRAP(INVALID_TSS,IDXTBL(tss_ss));
+ oldss = cpustate->sregs[SS];
+ oldsp = cpustate->regs.w[SP];
+ oldsp_phy = cpustate->base[SS]+oldsp+(GATECNT(desc)*2);
+ cpustate->sregs[SS]=tss_ss;
+ cpustate->limit[SS]=LIMIT(ssdesc);
+ cpustate->base[SS]=BASE(ssdesc);
+ cpustate->rights[SS]=RIGHTS(ssdesc);
+ PUSH(oldss);
+ PUSH(oldsp);
+ for (i = 0; i < GATECNT(desc); i++)
+ PUSH(ReadWord(oldsp_phy-(i*2)));
+ }
+ cpustate->sregs[CS]=IDXTBL(gatesel) | DPL(r);
+ cpustate->limit[CS]=LIMIT(gatedesc);
+ cpustate->base[CS]=BASE(gatedesc);
+ cpustate->rights[CS]=RIGHTS(gatedesc);
+ cpustate->pc=(cpustate->base[CS]+GATEOFF(desc))&AMASK;
break;
- case 0x700: // trap gate
- i80286_data_descriptor(cpustate, CS, word2);
- cpustate->pc=cpustate->base[CS]+word1;
+ case TASKGATE:
+ i80286_data_descriptor(cpustate, CS, desc[1], CPL);
+ cpustate->pc=cpustate->base[CS]+desc[0];
break;
+ default:
+ throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(selector));
}
}
} else {
cpustate->sregs[CS]=selector;
cpustate->base[CS]=selector<<4;
- cpustate->pc=cpustate->base[CS]+offset;
+ cpustate->rights[CS]=0x9a;
+ cpustate->limit[CS]=0xffff;
+ cpustate->pc=(cpustate->base[CS]+offset)&AMASK;
}
}
-static void i80286_interrupt_descriptor(i80286_state *cpustate,UINT16 number)
+static void i80286_interrupt_descriptor(i80286_state *cpustate,UINT16 number, int hwint, int error)
{
- UINT16 word1,word2,word3;
- if ((number<<3)>=cpustate->idtr.limit) {
- ;// go into shutdown mode
- return;
- }
- PREFIX(_pushf(cpustate));
- PUSH(cpustate->sregs[CS]);
- PUSH(cpustate->pc - cpustate->base[CS]);
- word1=ReadWord(cpustate->idtr.base+(number<<3));
- word2=ReadWord(cpustate->idtr.base+(number<<3)+2);
- word3=ReadWord(cpustate->idtr.base+(number<<3)+4);
- switch (word3&0xf00) {
- case 0x500: // task gate
- i80286_data_descriptor(cpustate, CS, word2);
- cpustate->pc=cpustate->base[CS]+word1;
- break;
- case 0x600: // interrupt gate
- cpustate->TF = cpustate->IF = 0;
- i80286_data_descriptor(cpustate, CS, word2);
- cpustate->pc=cpustate->base[CS]+word1;
+ UINT16 desc[3], gatedesc[3], gatesel;
+ int r, addr;
+
+ if(!PM) return PREFIX86(_interrupt)(cpustate, number);
+
+ if ((number<<3)>=cpustate->idtr.limit)
+ throw TRAP(GENERAL_PROTECTION_FAULT,(number*8+2+(hwint&&1)));
+
+ desc[0] = ReadWord(cpustate->idtr.base+(number<<3));
+ desc[1] = ReadWord(cpustate->idtr.base+(number<<3)+2);
+ desc[2] = ReadWord(cpustate->idtr.base+(number<<3)+4);
+ r = RIGHTS(desc);
+ if (!hwint && (DPL(r)<CPL)) throw TRAP(GENERAL_PROTECTION_FAULT,(number*8+2+(hwint&&1)));
+ if (!PRES(r)) throw TRAP(SEG_NOT_PRESENT,(number*8+2+(hwint&&1)));
+
+ switch (GATE(r)) {
+ case TASKGATE:
+ i80286_data_descriptor(cpustate, CS, desc[1], CPL);
+ cpustate->pc=cpustate->base[CS]+desc[0];
break;
- case 0x700: // trap gate
- i80286_data_descriptor(cpustate, CS, word2);
- cpustate->pc=cpustate->base[CS]+word1;
+ case INTGATE:
+ case TRAPGATE:
+ gatesel = GATESEL(desc);
+ if (TBL(gatesel)) { /* local descriptor table */
+ if (gatesel>cpustate->ldtr.limit) throw TRAP(GENERAL_PROTECTION_FAULT,(IDXTBL(gatesel)+(hwint&&1)));
+ addr = cpustate->ldtr.base+IDX(gatesel);
+ } else { /* global descriptor table */
+ if (!IDX(gatesel)||(gatesel>cpustate->gdtr.limit)) throw TRAP(GENERAL_PROTECTION_FAULT,(IDXTBL(gatesel)+(hwint&&1)));
+ addr = cpustate->gdtr.base+IDX(gatesel);
+ }
+ gatedesc[0] = ReadWord(addr);
+ gatedesc[1] = ReadWord(addr+2);
+ gatedesc[2] = ReadWord(addr+4);
+ r = RIGHTS(gatedesc);
+ if (!CODE(r)) throw TRAP(GENERAL_PROTECTION_FAULT,(IDXTBL(gatesel)+(hwint&&1)));
+ if (DPL(r)>CPL) throw TRAP(GENERAL_PROTECTION_FAULT,(IDXTBL(gatesel)+(hwint&&1)));
+ if (!PRES(r)) throw TRAP(SEG_NOT_PRESENT,(IDXTBL(gatesel)+(hwint&&1)));
+ if (GATEOFF(desc) > LIMIT(gatedesc)) throw TRAP(GENERAL_PROTECTION_FAULT,(IDXTBL(gatesel)+(hwint&&1)));
+
+ if (!CONF(r)&&(DPL(r)<CPL)) { // inner call
+ UINT16 ssdesc[3];
+ UINT16 tss_ss,tss_sp, oldss, oldsp;
+ int ssr;
+ tss_ss = ReadWord(cpustate->tr.base+TSS_SS0+(DPL(r)*4));
+ tss_sp = ReadWord(cpustate->tr.base+TSS_SP0+(DPL(r)*4));
+
+ if (!IDXTBL(tss_ss)) throw TRAP(INVALID_TSS, (hwint&&1));
+ if (TBL(tss_ss)) { /* local descriptor table */
+ if (tss_ss>cpustate->ldtr.limit) throw TRAP(INVALID_TSS,(IDXTBL(tss_ss)+(hwint&&1)));
+ addr = cpustate->ldtr.base+IDX(tss_ss);
+ } else { /* global descriptor table */
+ if (tss_ss>cpustate->gdtr.limit) throw TRAP(INVALID_TSS,(IDXTBL(tss_ss)+(hwint&&1)));
+ addr = cpustate->gdtr.base+IDX(tss_ss);
+ }
+ ssdesc[0] = ReadWord(addr);
+ ssdesc[1] = ReadWord(addr+2);
+ ssdesc[2] = ReadWord(addr+4);
+ ssr = RIGHTS(ssdesc);
+ if ((RPL(tss_ss) != DPL(r)) || (DPL(ssr) != DPL(r))) TRAP(INVALID_TSS,(IDXTBL(tss_ss)+(hwint&&1)));
+ if (!SEGDESC(ssr) || CODE(ssr) || !RW(ssr)) throw TRAP(INVALID_TSS,(IDXTBL(tss_ss)+(hwint&&1)));
+ if (!PRES(ssr)) throw TRAP(STACK_FAULT,(IDXTBL(tss_ss)+(hwint&&1)));
+ oldss = cpustate->sregs[SS];
+ oldsp = cpustate->regs.w[SP];
+ cpustate->sregs[SS]=tss_ss;
+ cpustate->limit[SS]=LIMIT(ssdesc);
+ cpustate->base[SS]=BASE(ssdesc);
+ cpustate->rights[SS]=RIGHTS(ssdesc);
+ PUSH(oldss);
+ PUSH(oldsp);
+ }
+ PREFIX(_pushf(cpustate));
+ PUSH(cpustate->sregs[CS]);
+ PUSH(cpustate->pc-cpustate->base[CS]);
+ if((hwint == 1) && (error != -1)) PUSH(error);
+ cpustate->sregs[CS]=IDXTBL(gatesel) | DPL(r);
+ cpustate->limit[CS]=LIMIT(gatedesc);
+ cpustate->base[CS]=BASE(gatedesc);
+ cpustate->rights[CS]=RIGHTS(gatedesc);
+ cpustate->pc=(cpustate->base[CS]+GATEOFF(desc))&AMASK;
+ cpustate->TF = 0;
+ if (GATE(RIGHTS(desc)) == INTGATE) cpustate->IF = 0;
+ CHANGE_PC(cpustate->pc);
break;
+ default:
+ throw TRAP(GENERAL_PROTECTION_FAULT,(number*8+2+(hwint&&1)));
}
}
@@ -167,65 +411,100 @@ static void PREFIX286(_0fpre)(i8086_state *cpustate)
UINT16 ModRM;
UINT16 tmp;
offs_t addr;
+ int msw, sel, addr2, r;
+ UINT16 desc[2];
switch (next) {
case 0:
+ if (!PM) throw TRAP(ILLEGAL_INSTRUCTION,-1);
ModRM=FETCHOP;
switch (ModRM&0x38) {
case 0: /* sldt */
- if (!PM) i80286_trap2(cpustate,ILLEGAL_INSTRUCTION);
PutRMWord(ModRM, cpustate->ldtr.sel);
break;
case 8: /* str */
- if (!PM) i80286_trap2(cpustate,ILLEGAL_INSTRUCTION);
PutRMWord(ModRM, cpustate->tr.sel);
break;
case 0x10: /* lldt */
- if (!PM) i80286_trap2(cpustate,ILLEGAL_INSTRUCTION);
- if (PM&&(CPL!=0)) i80286_trap2(cpustate,GENERAL_PROTECTION_FAULT);
- cpustate->ldtr.sel=GetRMWord(ModRM);
- if ((cpustate->ldtr.sel&~7)>=cpustate->gdtr.limit) i80286_trap2(cpustate,GENERAL_PROTECTION_FAULT);
- cpustate->ldtr.limit=ReadWord(cpustate->gdtr.base+(cpustate->ldtr.sel&~7));
- cpustate->ldtr.base=ReadWord(cpustate->gdtr.base+(cpustate->ldtr.sel&~7)+2)
- |(ReadWord(cpustate->gdtr.base+(cpustate->ldtr.sel&~7)+4)<<16);
- cpustate->ldtr.rights=cpustate->ldtr.base>>24;
- cpustate->ldtr.base&=0xffffff;
+ if (CPL!=0) throw TRAP(GENERAL_PROTECTION_FAULT,0);
+ sel=GetRMWord(ModRM);
+ if (IDXTBL(sel)) {
+ if (IDX(sel)>=cpustate->gdtr.limit) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(sel));
+ addr2 = cpustate->gdtr.base + IDX(sel);
+ desc[0] = ReadWord(addr2);
+ desc[1] = ReadWord(addr2+2);
+ desc[2] = ReadWord(addr2+4);
+ r = RIGHTS(desc);
+ if (GATE(r) != LDTDESC) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(sel));
+ if (!PRES(r)) TRAP(SEG_NOT_PRESENT,IDXTBL(sel));
+ } else {
+ desc[0] = 0;
+ desc[1] = 0;
+ desc[2] = 0;
+ }
+ cpustate->ldtr.sel=sel;
+ cpustate->ldtr.limit=LIMIT(desc);
+ cpustate->ldtr.base=BASE(desc);
+ cpustate->ldtr.rights=RIGHTS(desc);
break;
case 0x18: /* ltr */
- if (!PM) i80286_trap2(cpustate,ILLEGAL_INSTRUCTION);
- if (CPL!=0) i80286_trap2(cpustate,GENERAL_PROTECTION_FAULT);
- cpustate->tr.sel=GetRMWord(ModRM);
- if ((cpustate->tr.sel&~7)>=cpustate->gdtr.limit) i80286_trap2(cpustate,GENERAL_PROTECTION_FAULT);
- cpustate->tr.limit=ReadWord(cpustate->gdtr.base+(cpustate->tr.sel&~7));
- cpustate->tr.base=ReadWord(cpustate->gdtr.base+(cpustate->tr.sel&~7)+2)
- |(ReadWord(cpustate->gdtr.base+(cpustate->tr.sel&~7)+4)<<16);
- cpustate->tr.rights=cpustate->tr.base>>24;
- cpustate->tr.base&=0xffffff;
+ if (CPL!=0) throw TRAP(GENERAL_PROTECTION_FAULT,0);
+ sel=GetRMWord(ModRM);
+ if (IDX(sel)>=cpustate->gdtr.limit) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(sel));
+ addr2 = cpustate->gdtr.base + IDX(sel);
+ desc[0] = ReadWord(addr2);
+ desc[1] = ReadWord(addr2+2);
+ desc[2] = ReadWord(addr2+4);
+ r = RIGHTS(desc);
+ if (GATE(r) != TSSDESCIDLE) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(sel));
+ if (!PRES(r)) TRAP(SEG_NOT_PRESENT,IDXTBL(sel));
+ cpustate->tr.sel=sel;
+ cpustate->tr.limit=LIMIT(desc);
+ cpustate->tr.base=BASE(desc);
+ cpustate->tr.rights=RIGHTS(desc);
break;
case 0x20: /* verr */
- if (!PM) i80286_trap2(cpustate,ILLEGAL_INSTRUCTION);
tmp=GetRMWord(ModRM);
- if (tmp&4) {
- cpustate->ZeroVal=! ( ((tmp&~7)<cpustate->ldtr.limit)
- && IS_READABLE( ReadByte(cpustate->ldtr.base+(tmp&~7)+5)) );
- } else {
- cpustate->ZeroVal=! ( ((tmp&~7)<cpustate->gdtr.limit)
- && IS_READABLE( ReadByte(cpustate->gdtr.base+(tmp&~7)+5)) );
+ if (!tmp)
+ cpustate->ZeroVal=0;
+ else if (tmp>((TBL(tmp)?cpustate->ldtr.limit:cpustate->gdtr.limit)))
+ cpustate->ZeroVal=0;
+ else {
+ addr2 = (TBL(tmp)?cpustate->ldtr.base:cpustate->gdtr.base);
+ desc[0] = ReadWord(addr2);
+ desc[1] = ReadWord(addr2+2);
+ desc[2] = ReadWord(addr2+4);
+ r = RIGHTS(desc);
+ if (!SEGDESC(r))
+ cpustate->ZeroVal=0;
+ else if (CODE(r) && CONF(r))
+ cpustate->ZeroVal=1;
+ else if ((DPL(r)<CPL) || (DPL(r)<RPL(tmp)))
+ cpustate->ZeroVal=0;
+ else cpustate->ZeroVal=1;
}
break;
case 0x28: /* verw */
- if (!PM) i80286_trap2(cpustate,ILLEGAL_INSTRUCTION);
tmp=GetRMWord(ModRM);
- if (tmp&4) {
- cpustate->ZeroVal=! ( ((tmp&~7)<cpustate->ldtr.limit)
- && IS_WRITEABLE( ReadByte(cpustate->ldtr.base+(tmp&~7)+5)) );
- } else {
- cpustate->ZeroVal=! ( ((tmp&~7)<cpustate->gdtr.limit)
- && IS_WRITEABLE( ReadByte(cpustate->gdtr.base+(tmp&~7)+5)) );
+ if (!tmp)
+ cpustate->ZeroVal=0;
+ else if (tmp>((TBL(tmp)?cpustate->ldtr.limit:cpustate->gdtr.limit)))
+ cpustate->ZeroVal=0;
+ else {
+ addr2 = (TBL(tmp)?cpustate->ldtr.base:cpustate->gdtr.base);
+ desc[0] = ReadWord(addr2);
+ desc[1] = ReadWord(addr2+2);
+ desc[2] = ReadWord(addr2+4);
+ r = RIGHTS(desc);
+ if (!SEGDESC(r) || CODE(r))
+ cpustate->ZeroVal=0;
+ else if ((DPL(r)<CPL) || (DPL(r)<RPL(tmp)))
+ cpustate->ZeroVal=0;
+ else cpustate->ZeroVal=1;
}
break;
default:
- i80286_trap2(cpustate,ILLEGAL_INSTRUCTION);
+ throw TRAP(ILLEGAL_INSTRUCTION,-1);
break;
}
break;
@@ -237,20 +516,20 @@ static void PREFIX286(_0fpre)(i8086_state *cpustate)
case 0: /* sgdt */
PutRMWord(ModRM,cpustate->gdtr.limit);
PutRMWordOffset(2,cpustate->gdtr.base&0xffff);
- PutRMByteOffset(4,cpustate->gdtr.base>>16);
+ PutRMWordOffset(4,0xff00|cpustate->gdtr.base>>16);
break;
case 8: /* sidt */
PutRMWord(ModRM,cpustate->idtr.limit);
PutRMWordOffset(2,cpustate->idtr.base&0xffff);
- PutRMByteOffset(4,cpustate->idtr.base>>16);
+ PutRMWordOffset(4,0xff00|cpustate->idtr.base>>16);
break;
case 0x10: /* lgdt */
- if (PM&&(CPL!=0)) i80286_trap2(cpustate,GENERAL_PROTECTION_FAULT);
+ if (PM&&(CPL!=0)) throw TRAP(GENERAL_PROTECTION_FAULT,0);
cpustate->gdtr.limit=GetRMWord(ModRM);
cpustate->gdtr.base=GetRMWordOffset(2)|(GetRMByteOffset(4)<<16);
break;
case 0x18: /* lidt */
- if (PM&&(CPL!=0)) i80286_trap2(cpustate,GENERAL_PROTECTION_FAULT);
+ if (PM&&(CPL!=0)) throw TRAP(GENERAL_PROTECTION_FAULT,0);
cpustate->idtr.limit=GetRMWord(ModRM);
cpustate->idtr.base=GetRMWordOffset(2)|(GetRMByteOffset(4)<<16);
break;
@@ -258,11 +537,13 @@ static void PREFIX286(_0fpre)(i8086_state *cpustate)
PutRMWord(ModRM, cpustate->msw);
break;
case 0x30: /* lmsw */
- if (PM&&(CPL!=0)) i80286_trap2(cpustate,GENERAL_PROTECTION_FAULT);
- cpustate->msw=(cpustate->msw&1)|GetRMWord(ModRM);
+ if (PM&&(CPL!=0)) throw TRAP(GENERAL_PROTECTION_FAULT,0);
+ msw = GetRMWord(ModRM);
+ if (!PM&(msw&1)) cpustate->sregs[CS] = IDX(cpustate->sregs[CS]); // cheat and set cpl to 0
+ cpustate->msw=(cpustate->msw&1)|msw;
break;
default:
- i80286_trap2(cpustate,ILLEGAL_INSTRUCTION);
+ throw TRAP(ILLEGAL_INSTRUCTION,-1);
break;
}
break;
@@ -280,7 +561,7 @@ static void PREFIX286(_0fpre)(i8086_state *cpustate)
}
break;
case 3: /* LSL */
- if (!PM) i80286_trap2(cpustate,ILLEGAL_INSTRUCTION);
+ if (!PM) throw TRAP(ILLEGAL_INSTRUCTION,-1);
ModRM = FETCHOP;
tmp=GetRMWord(ModRM);
if ( i80286_selector_okay(cpustate,tmp) )
@@ -294,12 +575,54 @@ static void PREFIX286(_0fpre)(i8086_state *cpustate)
cpustate->ZeroVal = 1;
}
break;
+ case 5: /* loadall */
+ if (PM&&(CPL!=0)) throw TRAP(GENERAL_PROTECTION_FAULT,0);
+ cpustate->msw = (cpustate->msw&1)|ReadWord(0x806);
+ cpustate->tr.sel = ReadWord(0x816);
+ cpustate->flags = ReadWord(0x818);
+ ExpandFlags(cpustate->flags);
+ cpustate->pc = ReadWord(0x81a);
+ cpustate->ldtr.sel = ReadWord(0x81c);
+ cpustate->sregs[DS] = ReadWord(0x81e);
+ cpustate->sregs[SS] = ReadWord(0x820);
+ cpustate->sregs[CS] = ReadWord(0x822);
+ cpustate->sregs[ES] = ReadWord(0x824);
+ cpustate->regs.w[DI] = ReadWord(0x826);
+ cpustate->regs.w[SI] = ReadWord(0x828);
+ cpustate->regs.w[BP] = ReadWord(0x82a);
+ cpustate->regs.w[SP] = ReadWord(0x82c);
+ cpustate->regs.w[BX] = ReadWord(0x82e);
+ cpustate->regs.w[DX] = ReadWord(0x830);
+ cpustate->regs.w[CX] = ReadWord(0x832);
+ cpustate->regs.w[AX] = ReadWord(0x834);
+ UINT16 desc[3];
+// loadall uses base-rights-limit order
+#define LOADDESC(addr, sreg) { desc[1] = ReadWord(addr); desc[2] = ReadWord(addr+2); desc[0] = ReadWord(addr+4); \
+ cpustate->base[sreg] = BASE(desc); cpustate->rights[sreg] = RIGHTS(desc); \
+ cpustate->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] = ReadWord(addr); desc[2] = ReadWord(addr+2); desc[0] = ReadWord(addr+4); \
+ cpustate->reg.base = BASE(desc); (void)(r); cpustate->reg.limit = LIMIT(desc); }
+ LOADDESC(0x84e, gdtr, 1);
+ LOADDESC(0x854, ldtr, cpustate->ldtr.rights = RIGHTS(desc));
+ LOADDESC(0x85a, idtr, 1);
+ LOADDESC(0x860, tr, cpustate->tr.rights = RIGHTS(desc));
+#undef LOADDESC
+ cpustate->pc = (cpustate->pc + cpustate->base[CS]) & AMASK;
+ CHANGE_PC(cpustate->pc);
+ break;
+
case 6: /* clts */
- if (PM&&(CPL!=0)) i80286_trap2(cpustate,GENERAL_PROTECTION_FAULT);
+ if (PM&&(CPL!=0)) throw TRAP(GENERAL_PROTECTION_FAULT,0);
cpustate->msw=~8;
break;
default:
- i80286_trap2(cpustate,ILLEGAL_INSTRUCTION);
+ throw TRAP(ILLEGAL_INSTRUCTION,-1);
break;
}
}
@@ -322,36 +645,153 @@ static void PREFIX286(_arpl)(i8086_state *cpustate) /* 0x63 */
}
else
{
- i80286_trap2(cpustate,ILLEGAL_INSTRUCTION);
+ throw TRAP(ILLEGAL_INSTRUCTION,-1);
+ }
+}
+
+static void i80286_load_flags(i8086_state *cpustate, int flags, int cpl)
+{
+ if(PM && cpl) {
+ int mask = 0xf000;
+ if(cpl>IOPL) mask |= 0x200;
+ flags &= ~mask;
+ flags |= (cpustate->flags & mask);
}
+ else if(!PM) (flags &= ~0xf000);
+ ExpandFlags(flags);
+ cpustate->flags = flags;
+ cpustate->flags = CompressFlags();
+
+ if (cpustate->TF) PREFIX(_trap)(cpustate);
+ /* if the IF is set, and an interrupt is pending, signal an interrupt */
+ if (cpustate->IF && cpustate->irq_state)
+ i80286_interrupt_descriptor(cpustate, (*cpustate->irq_callback)(cpustate->device, 0), 2, -1);
+}
+
+static void PREFIX286(_popf)(i8086_state *cpustate)
+{
+ UINT16 flags;
+ POP(flags);
+ ICOUNT -= timing.popf;
+ i80286_load_flags(cpustate, flags, CPL);
+}
+
+static UINT16 i80286_far_return(i8086_state *cpustate, int iret, int bytes)
+{
+ UINT16 sel, off, flags = 0;
+ int spaddr;
+ // must be restartable
+ spaddr = (cpustate->base[SS] + cpustate->regs.w[SP]) & AMASK;
+ off = ReadWord(spaddr);
+ sel = ReadWord(spaddr+2);
+ if(iret) flags = ReadWord(spaddr+4);
+
+ if(PM) {
+ UINT16 desc[3], newsp, newss;
+ int addr, r;
+
+ if (TBL(sel)) { /* local descriptor table */
+ if (sel>cpustate->ldtr.limit) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(sel));
+ addr = cpustate->ldtr.base+IDX(sel);
+ } else { /* global descriptor table */
+ if (!IDX(sel)||(sel>cpustate->gdtr.limit)) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(sel));
+ addr = cpustate->gdtr.base+IDX(sel);
+ }
+ if (RPL(sel)<CPL) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(sel));
+ desc[0] = ReadWord(addr);
+ desc[1] = ReadWord(addr+2);
+ desc[2] = ReadWord(addr+4);
+ r = RIGHTS(desc);
+ if (!CODE(r) || !SEGDESC(r)) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(sel));
+ if (CONF(r)) { if(DPL(r)>RPL(sel)) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(sel)); }
+ else if (DPL(r)!=RPL(sel)) throw TRAP(GENERAL_PROTECTION_FAULT,IDXTBL(sel));
+
+ SET_ACC(desc);
+ WriteWord(addr+4, desc[2]);
+ if (!PRES(r)) throw TRAP(SEG_NOT_PRESENT,IDXTBL(sel));
+ if (off > LIMIT(desc)) throw TRAP(GENERAL_PROTECTION_FAULT, IDXTBL(sel));
+ if (CPL<RPL(sel)) {
+ newsp = ReadWord(spaddr+((iret?6:4)+bytes));
+ newss = ReadWord(spaddr+((iret?8:6)+bytes));
+ i80286_data_descriptor(cpustate, SS, newss, RPL(sel));
+ cpustate->regs.w[SP] = newsp + bytes;
+ } else cpustate->regs.w[SP] += (iret?6:4) + bytes;
+ cpustate->sregs[CS]=sel;
+ cpustate->limit[CS]=LIMIT(desc);
+ cpustate->base[CS]=BASE(desc);
+ cpustate->rights[CS]=RIGHTS(desc);
+ cpustate->pc=(cpustate->base[CS]+off)&AMASK ;
+
+ // docs say check rpl but windows doesn't like it
+ if (((CODE(cpustate->rights[DS])&&!CONF(cpustate->rights[DS])) || !CODE(cpustate->rights[DS]))
+ && (DPL(cpustate->rights[DS]) < CPL))
+ i80286_data_descriptor(cpustate, DS, 0, CPL);
+
+ if (((CODE(cpustate->rights[ES])&&!CONF(cpustate->rights[ES])) || !CODE(cpustate->rights[ES]))
+ && (DPL(cpustate->rights[ES]) < CPL))
+ i80286_data_descriptor(cpustate, ES, 0, CPL);
+ } else {
+ cpustate->regs.w[SP] += (iret?6:4) + bytes;
+ cpustate->sregs[CS]=sel;
+ cpustate->base[CS]=sel<<4;
+ cpustate->rights[CS]=0x93;
+ cpustate->limit[CS]=0xffff;
+ cpustate->pc=(cpustate->base[CS]+off)&AMASK;
+ }
+ CHANGE_PC(cpustate->pc);
+
+ return flags;
+}
+
+static void PREFIX286(_iret)(i8086_state *cpustate)
+{
+ int oldcpl = (PM)?CPL:0;
+ UINT16 flags = i80286_far_return(cpustate, 1, 0);
+ ICOUNT -= timing.iret;
+ i80286_load_flags(cpustate, flags, oldcpl);
+}
+
+static void PREFIX286(_retf_d16)(i8086_state *cpustate)
+{
+ unsigned count = FETCH;
+ count += FETCH << 8;
+ i80286_far_return(cpustate, 0, count);
+ ICOUNT -= timing.ret_far_imm;
+}
+
+static void PREFIX286(_retf)(i8086_state *cpustate)
+{
+ i80286_far_return(cpustate, 0, 0);
+ ICOUNT -= timing.ret_far;
}
static void i80286_check_permission(i8086_state *cpustate, UINT8 check_seg, UINT16 offset, i80286_size size, i80286_operation operation)
{
if (PM)
{
+ // is the selector null?
+ if (!IDXTBL(cpustate->sregs[check_seg])) throw TRAP(GENERAL_PROTECTION_FAULT, 0);
+
/* Is the segment physically present? */
- if ( ! IS_PRESENT( cpustate->rights[check_seg] ) )
- throw GENERAL_PROTECTION_FAULT;
+ if (!PRES(cpustate->rights[check_seg]))
+ throw TRAP((check_seg==SS)?STACK_FAULT:SEG_NOT_PRESENT, IDXTBL(cpustate->sregs[check_seg]));
/* Would we go past the segment boundary? */
if ( (offset + (size-1)) > cpustate->limit[check_seg] )
- {
- throw GENERAL_PROTECTION_FAULT;
- }
+ throw TRAP((check_seg==SS)?STACK_FAULT:GENERAL_PROTECTION_FAULT, 0);
switch(operation)
{
case I80286_READ:
/* Is the segment readable? */
if ( ! IS_READABLE( cpustate->rights[check_seg] ) )
- throw GENERAL_PROTECTION_FAULT;
+ throw TRAP(GENERAL_PROTECTION_FAULT, IDXTBL(cpustate->sregs[check_seg]));
break;
case I80286_WRITE:
/* Is the segment writeable? */
if ( ! IS_WRITEABLE( cpustate->rights[check_seg] ) )
- throw GENERAL_PROTECTION_FAULT;
+ throw TRAP(GENERAL_PROTECTION_FAULT, IDXTBL(cpustate->sregs[check_seg]));
break;
case I80286_EXECUTE: