//#if 0 #include #include #include #include "ibm.h" #include "mem.h" #include "x86.h" #include "386.h" #include "cpu.h" /*Controls whether the accessed bit in a descriptor is set when CS is loaded.*/ #define CS_ACCESSED /*Controls whether the accessed bit in a descriptor is set when a data or stack selector is loaded.*/ #define SEL_ACCESSED int stimes = 0; int dtimes = 0; int btimes = 0; int is486=1; uint32_t abrt_error; int cgate16,cgate32; #define breaknullsegs 0 int intgatesize; void taskswitch286(uint16_t seg, uint16_t *segdat, int is32); void taskswitch386(uint16_t seg, uint16_t *segdat); int output; void pmodeint(int num, int soft); /*NOT PRESENT is INT 0B GPF is INT 0D*/ FILE *pclogf; void x86abort(const char *format, ...) { va_list ap; va_start(ap, format); vprintf(format, ap); va_end(ap); fflush(stdout); savenvr(); dumpregs(); fflush(stdout); exit(-1); } uint8_t opcode2; static void seg_reset(x86seg *s) { s->access = (0 << 5) | 2 | 0x80; s->limit = 0xFFFF; s->limit_low = 0; s->limit_high = 0xffff; } void x86seg_reset() { seg_reset(&_cs); seg_reset(&_ds); seg_reset(&_es); seg_reset(&_fs); seg_reset(&_gs); seg_reset(&_ss); } void x86_doabrt(int x86_abrt) { // ingpf = 1; CS = oldcs; cpu_state.pc = oldpc; _cs.access = (oldcpl << 5) | 0x80; // pclog("x86_doabrt - %02X %08X %04X:%08X %i\n", x86_abrt, abrt_error, CS, pc, ins); /* if (CS == 0x3433 && cpu_state.pc == 0x000006B0) { pclog("Quit it\n"); dumpregs(); exit(-1); }*/ // pclog("GPF! - error %04X %04X(%08X):%08X %02X %02X %i %04X %i %i\n",error,CS,cs,cpu_state.pc,opcode,opcode2,ins,flags&I_FLAG,IOPL, dtimes); if (msw & 1) pmodeint(x86_abrt, 0); else { uint32_t addr = (x86_abrt << 2) + idt.base; if (stack32) { writememw(ss,ESP-2,flags); writememw(ss,ESP-4,CS); writememw(ss,ESP-6,cpu_state.pc); ESP-=6; } else { writememw(ss,((SP-2)&0xFFFF),flags); writememw(ss,((SP-4)&0xFFFF),CS); writememw(ss,((SP-6)&0xFFFF),cpu_state.pc); SP-=6; } flags&=~I_FLAG; flags&=~T_FLAG; oxpc=cpu_state.pc; cpu_state.pc=readmemw(0,addr); loadcs(readmemw(0,addr+2)); return; } if (abrt) return; if (intgatesize == 16) { if (stack32) { writememw(ss, ESP-2, abrt_error); ESP-=2; } else { writememw(ss, ((SP-2)&0xFFFF), abrt_error); SP-=2; } } else { if (stack32) { writememl(ss, ESP-4, abrt_error); ESP-=4; } else { writememl(ss, ((SP-4)&0xFFFF), abrt_error); SP-=4; } } // ingpf = 0; // abrt = gpf = 1; } void x86gpf(char *s, uint16_t error) { pclog("GPF %04X : %s\n", error, s); abrt = ABRT_GPF; abrt_error = error; } void x86ss(char *s, uint16_t error) { pclog("SS %04X\n", error); abrt = ABRT_SS; abrt_error = error; } void x86ts(char *s, uint16_t error) { pclog("TS %04X\n", error); abrt = ABRT_TS; abrt_error = error; } void x86np(char *s, uint16_t error) { pclog("NP %04X : %s\n", error, s); abrt = ABRT_NP; abrt_error = error; } void do_seg_load(x86seg *s, uint16_t *segdat) { s->limit = segdat[0] | ((segdat[3] & 0xF) << 16); if (segdat[3] & 0x80) s->limit = (s->limit << 12) | 0xFFF; s->base = segdat[1] | ((segdat[2] & 0xFF) << 16); if (is386) s->base |= ((segdat[3] >> 8) << 24); s->access = segdat[2] >> 8; if ((segdat[2] & 0x1800) != 0x1000 || !(segdat[2] & (1 << 10))) /*expand-down*/ { s->limit_high = s->limit; s->limit_low = 0; } else { s->limit_high = (segdat[3] & 0x40) ? 0xffffffff : 0xffff; s->limit_low = s->limit + 1; } // if (output) pclog("SEG : base=%08x limit=%08x low=%08x high=%08x\n", s->base, s->limit, s->limit_low, s->limit_high); } static void do_seg_v86_init(x86seg *s) { s->access = (3 << 5) | 2 | 0x80; s->limit = 0xffff; s->limit_low = 0; s->limit_high = 0xffff; } static void check_seg_valid(x86seg *s) { int dpl = (s->access >> 5) & 3; int valid = 1; if (s->seg & 4) { if ((s->seg & ~7) >= ldt.limit) { // pclog("Bigger than LDT limit %04X %04X %02X %02X %02X\n", s->seg, ldt.limit, opcode, opcode2, rmdat); valid = 0; } } else { if ((s->seg & ~7) >= gdt.limit) { // pclog("Bigger than GDT limit %04X %04X\n", s->seg, gdt.limit); valid = 0; } } switch (s->access & 0x1f) { case 0x10: case 0x11: case 0x12: case 0x13: /*Data segments*/ case 0x14: case 0x15: case 0x16: case 0x17: case 0x1A: case 0x1B: /*Readable non-conforming code*/ if ((s->seg & 3) > dpl || (CPL) > dpl) { // pclog("Data seg fail - %04X:%08X %04X %i\n", CS, cpu_state.pc, s->seg, dpl); valid = 0; break; } break; case 0x1E: case 0x1F: /*Readable conforming code*/ break; default: valid = 0; break; } if (!valid) loadseg(0, s); } void loadseg(uint16_t seg, x86seg *s) { uint16_t segdat[4]; uint32_t addr; int dpl; if (msw&1 && !(eflags&VM_FLAG)) { // intcount++; if (!(seg&~3)) { if (s==&_ss) { pclog("SS selector = NULL!\n"); x86ss(NULL,0); return; // dumpregs(); // exit(-1); } // if (s->base!=-1) pclog("NEW! "); s->seg=0; // s->access = 0; s->access = 0x80; s->base=-1; // pclog("NULL selector %s%s%s%s %04X(%06X):%06X\n",(s==&_ds)?"DS":"",(s==&_es)?"ES":"",(s==&_fs)?"FS":"",(s==&_gs)?"GS":"",CS,cs,cpu_state.pc); return; } // if (s==&_ss) pclog("Load SS %04X\n",seg); // pclog("Protected mode seg load!\n"); addr=seg&~7; if (seg&4) { if (addr>=ldt.limit) { pclog("Bigger than LDT limit %04X %04X %02X %02X %02X\n",seg,ldt.limit, opcode, opcode2, rmdat); // dumppic(); // dumpregs(); // exit(-1); x86gpf("loadseg(): Bigger than LDT limit",seg&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("Bigger than GDT limit %04X %04X 1\n",seg,gdt.limit); // dumpregs(); // exit(-1); x86gpf("loadseg(): Bigger than GDT limit",seg&~3); return; } addr+=gdt.base; } cpl_override=1; segdat[0]=readmemw(0,addr); segdat[1]=readmemw(0,addr+2); segdat[2]=readmemw(0,addr+4); segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return; dpl=(segdat[2]>>13)&3; if (s==&_ss) { if (!(seg&~3)) { pclog("Load SS null selector\n"); x86gpf(NULL,seg&~3); return; } if ((seg&3)!=CPL || dpl!=CPL) { pclog("Invalid SS permiss\n"); x86gpf(NULL,seg&~3); // x86abort("Invalid SS permiss for %04X!\n",seg&0xFFFC); return; } switch ((segdat[2]>>8)&0x1F) { case 0x12: case 0x13: case 0x16: case 0x17: /*r/w*/ break; default: pclog("Invalid SS type\n"); x86gpf(NULL,seg&~3); // x86abort("Invalid SS segment type for %04X!\n",seg&0xFFFC); return; } if (!(segdat[2]&0x8000)) { pclog("Load SS not present!\n"); x86ss(NULL,seg&~3); return; } stack32 = (segdat[3] & 0x40) ? 1 : 0; // pclog("Load SS %04x %04x %04x %04x\n", segdat[0], segdat[1], segdat[2], segdat[3]); } else if (s!=&_cs) { if (output) pclog("Seg data %04X %04X %04X %04X\n", segdat[0], segdat[1], segdat[2], segdat[3]); if (output) pclog("Seg type %03X\n",segdat[2]&0x1F00); switch ((segdat[2]>>8)&0x1F) { case 0x10: case 0x11: case 0x12: case 0x13: /*Data segments*/ case 0x14: case 0x15: case 0x16: case 0x17: case 0x1A: case 0x1B: /*Readable non-conforming code*/ // pclog("Load seg %04X %i %i %04X:%08X\n",seg,dpl,CS&3,CS,cpu_state.pc); if ((seg&3)>dpl || (CPL)>dpl) { pclog("Data seg fail - %04X:%08X %04X %i %04X\n",CS,cpu_state.pc,seg,dpl,segdat[2]); x86gpf(NULL,seg&~3); // x86abort("Data segment load - level too low!\n",seg&0xFFFC); return; } break; case 0x1E: case 0x1F: /*Readable conforming code*/ break; default: pclog("Invalid segment type for %04X! %04X\n",seg&0xFFFC,segdat[2]); x86gpf(NULL,seg&~3); return; } } if (!(segdat[2] & 0x8000)) { x86np("Load data seg not present", seg & 0xfffc); return; } s->seg = seg; do_seg_load(s, segdat); #ifndef CS_ACCESSED if (s != &_cs) { #endif #ifdef SEL_ACCESSED cpl_override = 1; writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/ cpl_override = 0; #endif #ifndef CS_ACCESSED } #endif s->checked = 0; } else { s->access = (3 << 5) | 2 | 0x80; s->base = seg << 4; s->seg = seg; if (s == &_ss) stack32 = 0; s->checked = 1; } } #define DPL ((segdat[2]>>13)&3) #define DPL2 ((segdat2[2]>>13)&3) #define DPL3 ((segdat3[2]>>13)&3) void loadcs(uint16_t seg) { uint16_t segdat[4]; uint32_t addr; if (output) pclog("Load CS %04X\n",seg); if (msw&1 && !(eflags&VM_FLAG)) { // intcount++; // flushmmucache(); // pclog("Load CS %04X\n",seg); if (!(seg&~3)) { pclog("Trying to load CS with NULL selector! lcs\n"); // dumpregs(); // exit(-1); x86gpf(NULL,0); return; } // pclog("Protected mode CS load! %04X\n",seg); addr=seg&~7; if (seg&4) { if (addr>=ldt.limit) { pclog("Bigger than LDT limit %04X %04X CS\n",seg,ldt.limit); x86gpf(NULL,seg&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("Bigger than GDT limit %04X %04X CS\n",seg,gdt.limit); x86gpf(NULL,seg&~3); return; } addr+=gdt.base; } cpl_override=1; segdat[0]=readmemw(0,addr); segdat[1]=readmemw(0,addr+2); segdat[2]=readmemw(0,addr+4); segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return; if (optype==JMP) pclog("Code seg - %04X - %04X %04X %04X %04X\n",seg,segdat[0],segdat[1],segdat[2],segdat[3]); // if (!(segdat[2]&0x8000)) x86abort("Code segment not present!\n"); // if (output) pclog("Segdat2 %04X\n",segdat[2]); if (segdat[2]&0x1000) /*Normal code segment*/ { if (!(segdat[2]&0x400)) /*Not conforming*/ { if ((seg&3)>CPL) { x86gpf(NULL,seg&~3); pclog("loadcs RPL > CPL %04X %04X %i %02X\n",segdat[2],seg,CPL,opcode); return; } if (CPL != DPL) { x86gpf("loadcs(): CPL != DPL",seg&~3); return; } } if (CPL < DPL) { x86gpf("loadcs(): CPL < DPL",seg&~3); return; } if (!(segdat[2]&0x8000)) { x86np("Load CS not present", seg & 0xfffc); return; } if (segdat[3]&0x40) use32=0x300; else use32=0; CS=(seg&~3)|CPL; do_seg_load(&_cs, segdat); use32=(segdat[3]&0x40)?0x300:0; if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); #ifdef CS_ACCESSED cpl_override = 1; writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/ cpl_override = 0; #endif // if (output) pclog("Load CS %08X\n",_cs.base); // CS=(CS&0xFFFC)|((_cs.access>>5)&3); } else /*System segment*/ { if (!(segdat[2]&0x8000)) { x86np("Load CS system seg not present\n", seg & 0xfffc); return; } switch (segdat[2]&0xF00) { default: pclog("Bad CS %02X %02X %i special descriptor %03X %04X\n",opcode,rmdat,optype,segdat[2]&0xF00,seg); x86gpf(NULL,seg&~3); return; } } // pclog("CS = %04X base=%06X limit=%04X access=%02X %04X\n",CS,cs,_cs.limit,_cs.access,addr); // dumpregs(); // exit(-1); } else { _cs.base=seg<<4; _cs.limit=0xFFFF; _cs.limit_low = 0; _cs.limit_high = 0xffff; CS=seg; if (eflags&VM_FLAG) _cs.access=(3<<5) | 2 | 0x80; else _cs.access=(0<<5) | 2 | 0x80; if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); } } void loadcsjmp(uint16_t seg, uint32_t oxpc) { uint16_t segdat[4]; uint32_t addr; int count; uint16_t type,seg2; uint32_t newpc; // pclog("Load CS JMP %04X\n",seg); if (msw&1 && !(eflags&VM_FLAG)) { if (!(seg&~3)) { pclog("Trying to load CS with NULL selector! lcsjmp\n"); x86gpf(NULL,0); return; // dumpregs(); // exit(-1); } addr=seg&~7; if (seg&4) { if (addr>=ldt.limit) { pclog("Bigger than LDT limit %04X %04X CS\n",seg,ldt.limit); x86gpf(NULL,seg&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("Bigger than GDT limit %04X %04X CS\n",seg,gdt.limit); x86gpf(NULL,seg&~3); return; } addr+=gdt.base; } cpl_override=1; segdat[0]=readmemw(0,addr); segdat[1]=readmemw(0,addr+2); segdat[2]=readmemw(0,addr+4); segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return; if (output) pclog("%04X %04X %04X %04X\n",segdat[0],segdat[1],segdat[2],segdat[3]); if (segdat[2]&0x1000) /*Normal code segment*/ { // pclog("Normal CS\n"); if (!(segdat[2]&0x400)) /*Not conforming*/ { if ((seg&3)>CPL) { x86gpf("loadcsjmp(): segment PL > CPL",seg&~3); return; } if (CPL != DPL) { x86gpf("loadcsjmp(): CPL != DPL",seg&~3); return; } } if (CPL < DPL) { x86gpf("loadcsjmp(): CPL < DPL",seg&~3); return; } if (!(segdat[2]&0x8000)) { x86np("Load CS JMP not present\n", seg & 0xfffc); return; } if (segdat[3]&0x40) use32=0x300; else use32=0; #ifdef CS_ACCESSED cpl_override = 1; writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/ cpl_override = 0; #endif CS = (seg & ~3) | CPL; segdat[2] = (segdat[2] & ~(3 << (5+8))) | (CPL << (5+8)); do_seg_load(&_cs, segdat); if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); use32=(segdat[3]&0x40)?0x300:0; cycles -= timing_jmp_pm; } else /*System segment*/ { // pclog("System CS\n"); if (!(segdat[2]&0x8000)) { x86np("Load CS JMP system selector not present\n", seg & 0xfffc); return; } type=segdat[2]&0xF00; if (type==0x400) newpc=segdat[0]; else newpc=segdat[0]|(segdat[3]<<16); switch (type) { case 0x400: /*Call gate*/ case 0xC00: // pclog("Call gate\n"); cgate32=(type&0x800); cgate16=!cgate32; oldcs=CS; oldpc=cpu_state.pc; count=segdat[2]&31; #if 0 if ((DPL < CPL) || (DPL < (seg&3))) { x86gpf(NULL,seg&~3); return; } #endif if (DPL < CPL) { x86gpf("loadcsjmp(): ex DPL < CPL",seg&~3); return; } if ((DPL < (seg&3))) { x86gpf("loadcsjmp(): ex (DPL < (seg&3))",seg&~3); return; } if (!(segdat[2]&0x8000)) { x86np("Load CS JMP call gate not present\n", seg & 0xfffc); return; } seg2=segdat[1]; if (!(seg2&~3)) { pclog("Trying to load CS with NULL selector! lcsjmpcg\n"); x86gpf(NULL,0); return; // dumpregs(); // exit(-1); } addr=seg2&~7; if (seg2&4) { if (addr>=ldt.limit) { pclog("Bigger than LDT limit %04X %04X CSJ\n",seg2,gdt.limit); x86gpf(NULL,seg2&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("Bigger than GDT limit %04X %04X CSJ\n",seg2,gdt.limit); x86gpf(NULL,seg2&~3); return; } addr+=gdt.base; } cpl_override=1; segdat[0]=readmemw(0,addr); segdat[1]=readmemw(0,addr+2); segdat[2]=readmemw(0,addr+4); segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return; if (DPL > CPL) { x86gpf("loadcsjmp(): ex DPL > CPL",seg2&~3); return; } if (!(segdat[2]&0x8000)) { x86np("Load CS JMP from call gate not present\n", seg2 & 0xfffc); return; } switch (segdat[2]&0x1F00) { case 0x1800: case 0x1900: case 0x1A00: case 0x1B00: /*Non-conforming code*/ if (DPL > CPL) { pclog("Call gate DPL > CPL"); x86gpf(NULL,seg2&~3); return; } case 0x1C00: case 0x1D00: case 0x1E00: case 0x1F00: /*Conforming*/ CS=seg2; do_seg_load(&_cs, segdat); if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); use32=(segdat[3]&0x40)?0x300:0; cpu_state.pc=newpc; #ifdef CS_ACCESSED cpl_override = 1; writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/ cpl_override = 0; #endif break; default: pclog("JMP Call gate bad segment type\n"); x86gpf(NULL,seg2&~3); return; } cycles -= timing_jmp_pm_gate; break; case 0x900: /*386 Task gate*/ // pclog("Task gate\n"); cpu_state.pc=oxpc; cpl_override=1; taskswitch286(seg,segdat,segdat[2]&0x800); flags &= ~NT_FLAG; cpl_override=0; // case 0xB00: /*386 Busy task gate*/ // if (optype==JMP) pclog("Task switch!\n"); // taskswitch386(seg,segdat); return; default: pclog("Bad JMP CS %02X %02X %i special descriptor %03X %04X\n",opcode,rmdat,optype,segdat[2]&0xF00,seg); x86gpf(NULL,0); return; // dumpregs(); // exit(-1); } } // pclog("CS = %04X base=%06X limit=%04X access=%02X %04X\n",CS,cs,_cs.limit,_cs.access,addr); // dumpregs(); // exit(-1); } else { _cs.base=seg<<4; _cs.limit=0xFFFF; _cs.limit_low = 0; _cs.limit_high = 0xffff; CS=seg; if (eflags&VM_FLAG) _cs.access=(3<<5) | 2 | 0x80; else _cs.access=(0<<5) | 2 | 0x80; if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); cycles -= timing_jmp_rm; } } void PUSHW(uint16_t v) { // if (output==3) pclog("PUSHW %04X to %08X\n",v,ESP-4); if (stack32) { writememw(ss,ESP-2,v); if (abrt) return; ESP-=2; } else { // pclog("Write %04X to %08X\n", v, ss+((SP-2)&0xFFFF)); writememw(ss,((SP-2)&0xFFFF),v); if (abrt) return; SP-=2; } } void PUSHL(uint32_t v) { // if (output==3) pclog("PUSHL %08X to %08X\n",v,ESP-4); if (stack32) { writememl(ss,ESP-4,v); if (abrt) return; ESP-=4; } else { writememl(ss,((SP-4)&0xFFFF),v); if (abrt) return; SP-=4; } } uint16_t POPW() { uint16_t tempw; if (stack32) { tempw=readmemw(ss,ESP); if (abrt) return 0; ESP+=2; } else { tempw=readmemw(ss,SP); if (abrt) return 0; SP+=2; } return tempw; } uint32_t POPL() { uint32_t templ; if (stack32) { templ=readmeml(ss,ESP); if (abrt) return 0; ESP+=4; } else { templ=readmeml(ss,SP); if (abrt) return 0; SP+=4; } return templ; } void loadcscall(uint16_t seg) { uint16_t seg2; uint16_t segdat[4],segdat2[4],newss; uint32_t addr,oldssbase=ss, oaddr; uint32_t newpc; int count; uint16_t oldcs=CPL; uint32_t oldss,oldsp,newsp,oldpc, oldsp2; int type; uint16_t tempw; int csout = output; if (msw&1 && !(eflags&VM_FLAG)) { //flushmmucache(); if (csout) pclog("Protected mode CS load! %04X\n",seg); if (!(seg&~3)) { pclog("Trying to load CS with NULL selector! lcscall\n"); x86gpf(NULL,0); return; // dumpregs(); // exit(-1); } addr=seg&~7; if (seg&4) { if (addr>=ldt.limit) { pclog("Bigger than LDT limit %04X %04X CSC\n",seg,gdt.limit); x86gpf(NULL,seg&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("Bigger than GDT limit %04X %04X CSC\n",seg,gdt.limit); x86gpf(NULL,seg&~3); return; } addr+=gdt.base; } cpl_override=1; segdat[0]=readmemw(0,addr); segdat[1]=readmemw(0,addr+2); segdat[2]=readmemw(0,addr+4); segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return; type=segdat[2]&0xF00; if (type==0x400) newpc=segdat[0]; else newpc=segdat[0]|(segdat[3]<<16); if (csout) pclog("Code seg call - %04X - %04X %04X %04X\n",seg,segdat[0],segdat[1],segdat[2]); if (segdat[2]&0x1000) { if (!(segdat[2]&0x400)) /*Not conforming*/ { if ((seg&3)>CPL) { /* if (csout) */ pclog("Not conforming, RPL > CPL\n"); x86gpf("loadcscall(): segment > CPL",seg&~3); return; } if (CPL != DPL) { /* if (csout) */ pclog("Not conforming, CPL != DPL (%i %i)\n",CPL,DPL); x86gpf(NULL,seg&~3); return; } } if (CPL < DPL) { /* if (csout) */ pclog("CPL < DPL\n"); x86gpf(NULL,seg&~3); return; } if (!(segdat[2]&0x8000)) { /* if (csout) */ pclog("Not present\n"); x86np("Load CS call not present", seg & 0xfffc); return; } if (segdat[3]&0x40) use32=0x300; else use32=0; #ifdef CS_ACCESSED cpl_override = 1; writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/ cpl_override = 0; #endif /*Conforming segments don't change CPL, so preserve existing CPL*/ if (segdat[2]&0x400) { seg = (seg & ~3) | CPL; segdat[2] = (segdat[2] & ~(3 << (5+8))) | (CPL << (5+8)); } else /*On non-conforming segments, set RPL = CPL*/ seg = (seg & ~3) | CPL; CS=seg; do_seg_load(&_cs, segdat); if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); use32=(segdat[3]&0x40)?0x300:0; if (csout) pclog("Complete\n"); cycles -= timing_call_pm; } else { type=segdat[2]&0xF00; if (csout) pclog("Type %03X\n",type); switch (type) { case 0x400: /*Call gate*/ case 0xC00: /*386 Call gate*/ if (output) pclog("Callgate %08X\n", cpu_state.pc); cgate32=(type&0x800); cgate16=!cgate32; oldcs=CS; oldpc=cpu_state.pc; count=segdat[2]&31; #if 0 if ((DPL < CPL) || (DPL < (seg&3))) { x86gpf("",seg&~3); return; } #endif if ((DPL < CPL)) { x86gpf("loadcscall(): ex DPL < CPL",seg&~3); return; } if ((DPL < (seg&3))) { x86gpf("loadcscall(): ex (DPL < (seg&3))",seg&~3); return; } if (!(segdat[2]&0x8000)) { if (output) pclog("Call gate not present %04X\n",seg); x86np("Call gate not present\n", seg & 0xfffc); return; } seg2=segdat[1]; if (output) pclog("New address : %04X:%08X\n", seg2, newpc); if (!(seg2&~3)) { pclog("Trying to load CS with NULL selector! lcscallcg\n"); x86gpf(NULL,0); return; // dumpregs(); // exit(-1); } addr=seg2&~7; if (seg2&4) { if (addr>=ldt.limit) { pclog("Bigger than LDT limit %04X %04X CSC\n",seg2,gdt.limit); x86gpf(NULL,seg2&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("Bigger than GDT limit %04X %04X CSC\n",seg2,gdt.limit); x86gpf(NULL,seg2&~3); return; } addr+=gdt.base; } cpl_override=1; segdat[0]=readmemw(0,addr); segdat[1]=readmemw(0,addr+2); segdat[2]=readmemw(0,addr+4); segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return; if (output) pclog("Code seg2 call - %04X - %04X %04X %04X\n",seg2,segdat[0],segdat[1],segdat[2]); if (DPL > CPL) { x86gpf("loadcscall(): ex DPL > CPL",seg2&~3); return; } if (!(segdat[2]&0x8000)) { if (output) pclog("Call gate CS not present %04X\n",seg2); x86np("Call gate CS not present", seg2 & 0xfffc); return; } switch (segdat[2]&0x1F00) { case 0x1800: case 0x1900: case 0x1A00: case 0x1B00: /*Non-conforming code*/ if (DPL < CPL) { oaddr = addr; /*Load new stack*/ oldss=SS; oldsp=oldsp2=ESP; cpl_override=1; if (tr.access&8) { addr = 4 + tr.base + (DPL * 8); newss=readmemw(0,addr+4); newsp=readmeml(0,addr); } else { addr = 2 + tr.base + (DPL * 4); newss=readmemw(0,addr+2); newsp=readmemw(0,addr); } cpl_override=0; if (abrt) return; if (output) pclog("New stack %04X:%08X\n",newss,newsp); if (!(newss&~3)) { pclog("Call gate loading null SS\n"); x86ts(NULL,newss&~3); return; } addr=newss&~7; if (newss&4) { if (addr>=ldt.limit) { x86abort("Bigger than LDT limit %04X %08X %04X CSC SS\n",newss,addr,ldt.limit); x86ts(NULL,newss&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { x86abort("Bigger than GDT limit %04X %04X CSC\n",newss,gdt.limit); x86ts(NULL,newss&~3); return; } addr+=gdt.base; } cpl_override=1; if (output) pclog("Read stack seg\n"); segdat2[0]=readmemw(0,addr); segdat2[1]=readmemw(0,addr+2); segdat2[2]=readmemw(0,addr+4); segdat2[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return; if (output) pclog("Read stack seg done!\n"); if (((newss & 3) != DPL) || (DPL2 != DPL)) { pclog("Call gate loading SS with wrong permissions %04X %04X %i %i %04X %04X\n", newss, seg2, DPL, DPL2, segdat[2], segdat2[2]); // dumpregs(); // exit(-1); x86ts(NULL,newss&~3); return; } if ((segdat2[2]&0x1A00)!=0x1200) { pclog("Call gate loading SS wrong type\n"); x86ts(NULL,newss&~3); return; } if (!(segdat2[2]&0x8000)) { pclog("Call gate loading SS not present\n"); x86np("Call gate loading SS not present\n", newss & 0xfffc); return; } if (!stack32) oldsp &= 0xFFFF; SS=newss; stack32 = (segdat2[3] & 0x40) ? 1 : 0; if (stack32) ESP=newsp; else SP=newsp; do_seg_load(&_ss, segdat2); if (output) pclog("Set access 1\n"); #ifdef SEL_ACCESSED cpl_override = 1; writememw(0, addr+4, segdat2[2] | 0x100); /*Set accessed bit*/ cpl_override = 0; #endif CS=seg2; do_seg_load(&_cs, segdat); if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); use32=(segdat[3]&0x40)?0x300:0; cpu_state.pc=newpc; if (output) pclog("Set access 2\n"); #ifdef CS_ACCESSED cpl_override = 1; writememw(0, oaddr+4, segdat[2] | 0x100); /*Set accessed bit*/ cpl_override = 0; #endif if (output) pclog("Type %04X\n",type); if (type==0xC00) { PUSHL(oldss); PUSHL(oldsp2); if (abrt) { pclog("ABRT PUSHL\n"); SS = oldss; ESP = oldsp2; return; } // if (output) pclog("Stack now %04X:%08X\n",SS,ESP); if (count) { while (count) { count--; PUSHL(readmeml(oldssbase,oldsp+(count*4))); if (abrt) { pclog("ABRT COPYL\n"); SS = oldss; ESP = oldsp2; return; } } } // x86abort("Call gate with count %i\n",count); // PUSHL(oldcs); // PUSHL(oldpc); if (abrt) return; } else { if (output) pclog("Stack %04X\n",SP); PUSHW(oldss); if (output) pclog("Write SS to %04X:%04X\n",SS,SP); PUSHW(oldsp2); if (abrt) { pclog("ABRT PUSHW\n"); SS = oldss; ESP = oldsp2; return; } if (output) pclog("Write SP to %04X:%04X\n",SS,SP); // if (output) pclog("Stack %04X %i %04X:%04X\n",SP,count,oldssbase,oldsp); // if (output) pclog("PUSH %04X %04X %i %i now %04X:%08X\n",oldss,oldsp,count,stack32,SS,ESP); if (count) { while (count) { count--; tempw=readmemw(oldssbase,(oldsp&0xFFFF)+(count*2)); if (output) pclog("PUSH %04X\n",tempw); PUSHW(tempw); if (abrt) { pclog("ABRT COPYW\n"); SS = oldss; ESP = oldsp2; return; } } } // if (output) pclog("Stack %04X\n",SP); // if (count) x86abort("Call gate with count\n"); // PUSHW(oldcs); // PUSHW(oldpc); if (abrt) return; } cycles -= timing_call_pm_gate_inner; break; } else if (DPL > CPL) { pclog("Call gate DPL > CPL"); x86gpf(NULL,seg2&~3); return; } case 0x1C00: case 0x1D00: case 0x1E00: case 0x1F00: /*Conforming*/ /* if (type==0xC00) { PUSHL(oldcs); PUSHL(oldpc); if (abrt) return; } else { PUSHW(oldcs); PUSHW(oldpc); if (abrt) return; }*/ CS=seg2; do_seg_load(&_cs, segdat); if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); use32=(segdat[3]&0x40)?0x300:0; cpu_state.pc=newpc; #ifdef CS_ACCESSED cpl_override = 1; writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/ cpl_override = 0; #endif cycles -= timing_call_pm_gate; break; default: pclog("Call gate bad segment type\n"); x86gpf(NULL,seg2&~3); return; } break; // case 0x900: /*386 Task gate*/ // case 0xB00: /*386 Busy task gate*/ // if (optype==JMP) pclog("Task switch!\n"); // taskswitch386(seg,segdat); // return; default: pclog("Bad CALL special descriptor %03X\n",segdat[2]&0xF00); x86gpf(NULL,seg&~3); return; // dumpregs(); // exit(-1); } } // pclog("CS = %04X base=%06X limit=%04X access=%02X %04X\n",CS,cs,_cs.limit,_cs.access,addr); // dumpregs(); // exit(-1); } else { _cs.base=seg<<4; _cs.limit=0xFFFF; _cs.limit_low = 0; _cs.limit_high = 0xffff; CS=seg; if (eflags&VM_FLAG) _cs.access=(3<<5) | 2 | 0x80; else _cs.access=(0<<5) | 2 | 0x80; if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); } } void pmoderetf(int is32, uint16_t off) { uint32_t newpc; uint32_t newsp; uint32_t addr, oaddr; uint16_t segdat[4],segdat2[4],seg,newss; uint32_t oldsp=ESP; if (output) pclog("RETF %i %04X:%04X %08X %04X\n",is32,CS,cpu_state.pc,cr0,eflags); if (is32) { newpc=POPL(); seg=POPL(); if (abrt) return; } else { if (output) pclog("PC read from %04X:%04X\n",SS,SP); newpc=POPW(); if (output) pclog("CS read from %04X:%04X\n",SS,SP); seg=POPW(); if (abrt) return; } if (output) pclog("Return to %04X:%08X\n",seg,newpc); if ((seg&3)=ldt.limit) { pclog("Bigger than LDT limit %04X %04X RETF\n",seg,ldt.limit); x86gpf(NULL,seg&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("Bigger than GDT limit %04X %04X RETF\n",seg,gdt.limit); x86gpf(NULL,seg&~3); // dumpregs(); // exit(-1); return; } addr+=gdt.base; } cpl_override=1; segdat[0]=readmemw(0,addr); segdat[1]=readmemw(0,addr+2); segdat[2]=readmemw(0,addr+4); segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) { ESP=oldsp; return; } oaddr = addr; if (output) pclog("CPL %i RPL %i %i\n",CPL,seg&3,is32); if (stack32) ESP+=off; else SP+=off; if (CPL==(seg&3)) { if (output) pclog("RETF CPL = RPL %04X\n", segdat[2]); switch (segdat[2]&0x1F00) { case 0x1800: case 0x1900: case 0x1A00: case 0x1B00: /*Non-conforming*/ if (CPL != DPL) { pclog("RETF non-conforming CPL != DPL\n"); ESP=oldsp; x86gpf(NULL,seg&~3); return; } break; case 0x1C00: case 0x1D00: case 0x1E00: case 0x1F00: /*Conforming*/ if (CPL < DPL) { pclog("RETF non-conforming CPL < DPL\n"); ESP=oldsp; x86gpf(NULL,seg&~3); return; } break; default: pclog("RETF CS not code segment\n"); x86gpf(NULL,seg&~3); return; } if (!(segdat[2]&0x8000)) { pclog("RETF CS not present %i %04X %04X %04X\n",ins, segdat[0], segdat[1], segdat[2]); ESP=oldsp; x86np("RETF CS not present\n", seg & 0xfffc); return; } #ifdef CS_ACCESSED cpl_override = 1; writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/ cpl_override = 0; #endif cpu_state.pc=newpc; if (segdat[2] & 0x400) segdat[2] = (segdat[2] & ~(3 << (5+8))) | ((seg & 3) << (5+8)); CS = seg; do_seg_load(&_cs, segdat); _cs.access = (_cs.access & ~(3 << 5)) | ((CS & 3) << 5); if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); use32=(segdat[3]&0x40)?0x300:0; // pclog("CPL=RPL return to %04X:%08X\n",CS,cpu_state.pc); cycles -= timing_retf_pm; } else { switch (segdat[2]&0x1F00) { case 0x1800: case 0x1900: case 0x1A00: case 0x1B00: /*Non-conforming*/ if ((seg&3) != DPL) { pclog("RETF non-conforming RPL != DPL\n"); ESP=oldsp; x86gpf(NULL,seg&~3); return; } if (output) pclog("RETF non-conforming, %i %i\n",seg&3, DPL); break; case 0x1C00: case 0x1D00: case 0x1E00: case 0x1F00: /*Conforming*/ if ((seg&3) < DPL) { pclog("RETF non-conforming RPL < DPL\n"); ESP=oldsp; x86gpf(NULL,seg&~3); return; } if (output) pclog("RETF conforming, %i %i\n",seg&3, DPL); break; default: pclog("RETF CS not code segment\n"); ESP=oldsp; x86gpf(NULL,seg&~3); return; } if (!(segdat[2]&0x8000)) { pclog("RETF CS not present! %i %04X %04X %04X\n",ins, segdat[0], segdat[1], segdat[2]); ESP=oldsp; x86np("RETF CS not present\n", seg & 0xfffc); return; } if (is32) { newsp=POPL(); newss=POPL(); if (abrt) return; // pclog("is32 new stack %04X:%04X\n",newss,newsp); } else { if (output) pclog("SP read from %04X:%04X\n",SS,SP); newsp=POPW(); if (output) pclog("SS read from %04X:%04X\n",SS,SP); newss=POPW(); if (abrt) return; // pclog("!is32 new stack %04X:%04X\n",newss,newsp); } if (output) pclog("Read new stack : %04X:%04X (%08X)\n", newss, newsp, ldt.base); if (!(newss&~3)) { pclog("RETF loading null SS\n"); ESP=oldsp; x86gpf(NULL,newss&~3); return; } addr=newss&~7; if (newss&4) { if (addr>=ldt.limit) { pclog("Bigger than LDT limit %04X %04X RETF SS\n",newss,gdt.limit); ESP=oldsp; x86gpf(NULL,newss&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("Bigger than GDT limit %04X %04X RETF SS\n",newss,gdt.limit); ESP=oldsp; x86gpf(NULL,newss&~3); return; } addr+=gdt.base; } cpl_override=1; segdat2[0]=readmemw(0,addr); segdat2[1]=readmemw(0,addr+2); segdat2[2]=readmemw(0,addr+4); segdat2[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) { ESP=oldsp; return; } if (output) pclog("Segment data %04X %04X %04X %04X\n", segdat2[0], segdat2[1], segdat2[2], segdat2[3]); // if (((newss & 3) != DPL) || (DPL2 != DPL)) if ((newss & 3) != (seg & 3)) { pclog("RETF loading SS with wrong permissions %i %i %04X %04X\n", newss & 3, seg & 3, newss, seg); ESP=oldsp; // output = 3; // dumpregs(); // exit(-1); x86gpf(NULL,newss&~3); return; } if ((segdat2[2]&0x1A00)!=0x1200) { pclog("RETF loading SS wrong type\n"); ESP=oldsp; // dumpregs(); // exit(-1); x86gpf(NULL,newss&~3); return; } if (!(segdat2[2]&0x8000)) { pclog("RETF loading SS not present\n"); ESP=oldsp; x86np("RETF loading SS not present\n", newss & 0xfffc); return; } if (DPL2 != (seg & 3)) { pclog("RETF loading SS with wrong permissions2 %i %i %04X %04X\n", DPL2, seg & 3, newss, seg); ESP=oldsp; x86gpf(NULL,newss&~3); return; } SS=newss; stack32 = (segdat2[3] & 0x40) ? 1 : 0; if (stack32) ESP=newsp; else SP=newsp; do_seg_load(&_ss, segdat2); #ifdef SEL_ACCESSED cpl_override = 1; writememw(0, addr+4, segdat2[2] | 0x100); /*Set accessed bit*/ #ifdef CS_ACCESSED writememw(0, oaddr+4, segdat[2] | 0x100); /*Set accessed bit*/ #endif cpl_override = 0; #endif /*Conforming segments don't change CPL, so CPL = RPL*/ if (segdat[2]&0x400) segdat[2] = (segdat[2] & ~(3 << (5+8))) | ((seg & 3) << (5+8)); cpu_state.pc=newpc; CS=seg; do_seg_load(&_cs, segdat); if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); use32=(segdat[3]&0x40)?0x300:0; if (stack32) ESP+=off; else SP+=off; check_seg_valid(&_ds); check_seg_valid(&_es); check_seg_valid(&_fs); check_seg_valid(&_gs); // pclog("CPL=idt.limit) { if (num==8) { /*Triple fault - reset!*/ pclog("Triple fault!\n"); // output=1; softresetx86(); } else if (num==0xD) { pclog("Double fault!\n"); pmodeint(8,0); } else { pclog("INT out of range\n"); x86gpf(NULL,(num*8)+2+(soft)?0:1); } if (output) pclog("addr >= IDT.limit\n"); return; } addr+=idt.base; cpl_override=1; segdat[0]=readmemw(0,addr); segdat[1]=readmemw(2,addr); segdat[2]=readmemw(4,addr); segdat[3]=readmemw(6,addr); cpl_override=0; if (abrt) { pclog("Abrt reading from %08X\n",addr); return; } oaddr = addr; if (output) pclog("Addr %08X seg %04X %04X %04X %04X\n",addr,segdat[0],segdat[1],segdat[2],segdat[3]); if (!(segdat[2]&0x1F00)) { // pclog("No seg\n"); x86gpf(NULL,(num*8)+2); return; } if (DPL=0x800)?32:16; // if (output) pclog("Int gate %04X %i oldpc %04X pc %04X\n",type,intgatesize,oldpc,cpu_state.pc); if (!(segdat[2]&0x8000)) { pclog("Int gate not present\n"); x86np("Int gate not present\n", (num << 3) | 2); return; } seg=segdat[1]; new_cpl = seg & 3; // pclog("Interrupt gate : %04X:%04X%04X\n",seg,segdat[3],segdat[0]); addr=seg&~7; if (seg&4) { if (addr>=ldt.limit) { pclog("Bigger than LDT limit %04X %04X INT\n",seg,gdt.limit); x86gpf(NULL,seg&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("Bigger than GDT limit %04X %04X INT %i\n",seg,gdt.limit,ins); x86gpf(NULL,seg&~3); return; } addr+=gdt.base; } /* if ((seg&3) < CPL) { pclog("INT to higher level\n"); x86gpf(NULL,seg&~3); return; }*/ cpl_override=1; segdat2[0]=readmemw(0,addr); segdat2[1]=readmemw(0,addr+2); segdat2[2]=readmemw(0,addr+4); segdat2[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return; oaddr = addr; if (DPL2 > CPL) { pclog("INT to higher level 2\n"); x86gpf(NULL,seg&~3); return; } //pclog("Type %04X\n",segdat2[2]); switch (segdat2[2]&0x1F00) { case 0x1800: case 0x1900: case 0x1A00: case 0x1B00: /*Non-conforming*/ if (DPL2=ldt.limit) { pclog("Bigger than LDT limit %04X %04X PMODEINT SS\n",newss,gdt.limit); x86ss(NULL,newss&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("Bigger than GDT limit %04X %04X CSC\n",newss,gdt.limit); x86ss(NULL,newss&~3); return; } addr+=gdt.base; } cpl_override=1; segdat3[0]=readmemw(0,addr); segdat3[1]=readmemw(0,addr+2); segdat3[2]=readmemw(0,addr+4); segdat3[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return; if (((newss & 3) != DPL2) || (DPL3 != DPL2)) { pclog("Int gate loading SS with wrong permissions\n"); x86ss(NULL,newss&~3); return; } if ((segdat3[2]&0x1A00)!=0x1200) { pclog("Int gate loading SS wrong type\n"); x86ss(NULL,newss&~3); return; } if (!(segdat3[2]&0x8000)) { pclog("Int gate loading SS not present\n"); x86np("Int gate loading SS not present\n", newss & 0xfffc); return; } SS=newss; stack32 = (segdat3[3] & 0x40) ? 1 : 0; if (stack32) ESP=newsp; else SP=newsp; do_seg_load(&_ss, segdat3); #ifdef CS_ACCESSED cpl_override = 1; writememw(0, addr+4, segdat3[2] | 0x100); /*Set accessed bit*/ cpl_override = 0; #endif if (output) pclog("New stack %04X:%08X\n",SS,ESP); cpl_override=1; if (type>=0x800) { // if (output) pclog("Push 32 %i\n",eflags&VM_FLAG); if (eflags & VM_FLAG) { PUSHL(GS); PUSHL(FS); PUSHL(DS); PUSHL(ES); if (abrt) return; loadseg(0,&_ds); loadseg(0,&_es); loadseg(0,&_fs); loadseg(0,&_gs); } PUSHL(oldss); PUSHL(oldsp); PUSHL(flags|(eflags<<16)); // if (soft) pclog("Pushl CS %08X\n", CS); PUSHL(CS); // if (soft) pclog("Pushl PC %08X\n", cpu_state.pc); PUSHL(cpu_state.pc); if (abrt) return; // if (output) pclog("32Stack %04X:%08X\n",SS,ESP); } else { // if (output) pclog("Push 16\n"); PUSHW(oldss); PUSHW(oldsp); PUSHW(flags); // if (soft) pclog("Pushw CS %04X\n", CS); PUSHW(CS); // if (soft) pclog("Pushw pc %04X\n", cpu_state.pc); PUSHW(cpu_state.pc); if (abrt) return; // if (output) pclog("16Stack %04X:%08X\n",SS,ESP); } cpl_override=0; _cs.access=0 | 0x80; cycles -= timing_int_pm_outer - timing_int_pm; // pclog("Non-confirming int gate, CS = %04X\n"); break; } else if (DPL2!=CPL) { pclog("Non-conforming int gate DPL != CPL\n"); x86gpf(NULL,seg&~3); return; } case 0x1C00: case 0x1D00: case 0x1E00: case 0x1F00: /*Conforming*/ if (!(segdat2[2]&0x8000)) { pclog("Int gate CS not present\n"); x86np("Int gate CS not present\n", segdat[1] & 0xfffc); return; } if ((eflags & VM_FLAG) && DPL20x800) { PUSHL(flags|(eflags<<16)); // if (soft) pclog("Pushlc CS %08X\n", CS); PUSHL(CS); // if (soft) pclog("Pushlc PC %08X\n", cpu_state.pc); PUSHL(cpu_state.pc); if (abrt) return; } else { PUSHW(flags); // if (soft) pclog("Pushwc CS %04X\n", CS); PUSHW(CS); // if (soft) pclog("Pushwc PC %04X\n", cpu_state.pc); PUSHW(cpu_state.pc); if (abrt) return; } new_cpl = CS & 3; break; default: pclog("Int gate CS not code segment - %04X %04X %04X %04X\n",segdat2[0],segdat2[1],segdat2[2],segdat2[3]); x86gpf(NULL,seg&~3); return; } do_seg_load(&_cs, segdat2); CS = (seg & ~3) | new_cpl; _cs.access = (_cs.access & ~(3 << 5)) | (new_cpl << 5); // pclog("New CS = %04X\n",CS); if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); if (type>0x800) cpu_state.pc=segdat[0]|(segdat[3]<<16); else cpu_state.pc=segdat[0]; use32=(segdat2[3]&0x40)?0x300:0; // pclog("Int gate done!\n"); #ifdef CS_ACCESSED cpl_override = 1; writememw(0, oaddr+4, segdat2[2] | 0x100); /*Set accessed bit*/ cpl_override = 0; #endif eflags&=~VM_FLAG; if (!(type&0x100)) { flags&=~I_FLAG; // pclog("INT %02X disabling interrupts %i\n",num,soft); } flags&=~(T_FLAG|NT_FLAG); // if (output) pclog("Final Stack %04X:%08X\n",SS,ESP); cycles -= timing_int_pm; break; case 0x500: /*Task gate*/ // pclog("Task gate\n"); seg=segdat[1]; addr=seg&~7; if (seg&4) { if (addr>=ldt.limit) { pclog("Bigger than LDT limit %04X %04X INT\n",seg,gdt.limit); x86gpf(NULL,seg&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("Bigger than GDT limit %04X %04X INT %i\n",seg,gdt.limit,ins); x86gpf(NULL,seg&~3); return; } addr+=gdt.base; } cpl_override=1; segdat2[0]=readmemw(0,addr); segdat2[1]=readmemw(0,addr+2); segdat2[2]=readmemw(0,addr+4); segdat2[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return; if (!(segdat2[2]&0x8000)) { pclog("Int task gate not present\n"); x86np("Int task gate not present\n", segdat[1] & 0xfffc); return; } optype=OPTYPE_INT; cpl_override=1; taskswitch286(seg,segdat2,segdat2[2]&0x800); cpl_override=0; break; default: pclog("Bad int gate type %04X %04X %04X %04X %04X\n",segdat[2]&0x1F00,segdat[0],segdat[1],segdat[2],segdat[3]); x86gpf(NULL,seg&~3); return; } } void pmodeiret(int is32) { uint32_t newsp; uint16_t newss; uint32_t tempflags,flagmask; uint32_t newpc; uint16_t segdat[4],segdat2[4]; uint16_t segs[4]; uint16_t seg; uint32_t addr, oaddr; uint32_t oldsp=ESP; if (is386 && (eflags&VM_FLAG)) { // if (output) pclog("V86 IRET\n"); if (IOPL!=3) { pclog("V86 IRET! IOPL!=3\n"); x86gpf(NULL,0); return; } oxpc=cpu_state.pc; if (is32) { newpc=POPL(); seg=POPL(); tempflags=POPL(); if (abrt) return; } else { newpc=POPW(); seg=POPW(); tempflags=POPW(); if (abrt) return; } cpu_state.pc=newpc; _cs.base=seg<<4; _cs.limit=0xFFFF; _cs.limit_low = 0; _cs.limit_high = 0xffff; _cs.access |= 0x80; CS=seg; flags=(flags&0x3000)|(tempflags&0xCFD5)|2; cycles -= timing_iret_rm; return; } // pclog("IRET %i\n",is32); //flushmmucache(); // if (output) pclog("Pmode IRET %04X:%04X ",CS,cpu_state.pc); if (flags&NT_FLAG) { // pclog("NT IRET\n"); seg=readmemw(tr.base,0); addr=seg&~7; if (seg&4) { if (addr>=ldt.limit) { pclog("TS Bigger than LDT limit %04X %04X IRET\n",seg,gdt.limit); x86gpf(NULL,seg&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("TS Bigger than GDT limit %04X %04X IRET\n",seg,gdt.limit); x86gpf(NULL,seg&~3); return; } addr+=gdt.base; } cpl_override=1; segdat[0]=readmemw(0,addr); segdat[1]=readmemw(0,addr+2); segdat[2]=readmemw(0,addr+4); segdat[3]=readmemw(0,addr+6); taskswitch286(seg,segdat,0); cpl_override=0; return; } oxpc=cpu_state.pc; flagmask=0xFFFF; if (CPL) flagmask&=~0x3000; if (IOPL>16)&VM_FLAG)) { // pclog("IRETD to V86\n"); newsp=POPL(); newss=POPL(); segs[0]=POPL(); segs[1]=POPL(); segs[2]=POPL(); segs[3]=POPL(); if (abrt) { ESP = oldsp; return; } // pclog("Pop stack %04X:%04X\n",newss,newsp); eflags=tempflags>>16; loadseg(segs[0],&_es); do_seg_v86_init(&_es); loadseg(segs[1],&_ds); do_seg_v86_init(&_ds); loadseg(segs[2],&_fs); do_seg_v86_init(&_fs); loadseg(segs[3],&_gs); do_seg_v86_init(&_gs); // pclog("V86 IRET %04X:%08X\n",SS,ESP); // output=3; cpu_state.pc=newpc; _cs.base=seg<<4; _cs.limit=0xFFFF; _cs.limit_low = 0; _cs.limit_high = 0xffff; CS=seg; _cs.access=(3<<5) | 2 | 0x80; if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); ESP=newsp; loadseg(newss,&_ss); do_seg_v86_init(&_ss); use32=0; flags=(tempflags&0xFFD5)|2; cycles -= timing_iret_v86; // pclog("V86 IRET to %04X:%04X %04X:%04X %04X %04X %04X %04X %i\n",CS,cpu_state.pc,SS,SP,DS,ES,FS,GS,abrt); // if (CS==0xFFFF && pc==0xFFFFFFFF) timetolive=12; /* { dumpregs(); exit(-1); }*/ return; } } else { newpc=POPW(); seg=POPW(); tempflags=POPW(); if (abrt) { ESP = oldsp; return; } } // if (!is386) tempflags&=0xFFF; // pclog("Returned to %04X:%08X %04X %04X %i\n",seg,newpc,flags,tempflags, ins); if (!(seg&~3)) { pclog("IRET CS=0\n"); ESP = oldsp; // dumpregs(); // exit(-1); x86gpf(NULL,0); return; } // if (output) pclog("IRET %04X:%08X\n",seg,newpc); addr=seg&~7; if (seg&4) { if (addr>=ldt.limit) { pclog("Bigger than LDT limit %04X %04X IRET\n",seg,gdt.limit); ESP = oldsp; x86gpf(NULL,seg&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("Bigger than GDT limit %04X %04X IRET\n",seg,gdt.limit); ESP = oldsp; x86gpf(NULL,seg&~3); return; } addr+=gdt.base; } if ((seg&3) < CPL) { pclog("IRET to lower level\n"); ESP = oldsp; x86gpf(NULL,seg&~3); return; } cpl_override=1; segdat[0]=readmemw(0,addr); segdat[1]=readmemw(0,addr+2); segdat[2]=readmemw(0,addr+4); segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) { ESP = oldsp; return; } // pclog("Seg type %04X %04X\n",segdat[2]&0x1F00,segdat[2]); switch (segdat[2]&0x1F00) { case 0x1800: case 0x1900: case 0x1A00: case 0x1B00: /*Non-conforming code*/ if ((seg&3) != DPL) { pclog("IRET NC DPL %04X %04X %04X %04X %04X\n", seg, segdat[0], segdat[1], segdat[2], segdat[3]); ESP = oldsp; // dumpregs(); // exit(-1); x86gpf(NULL,seg&~3); return; } break; case 0x1C00: case 0x1D00: case 0x1E00: case 0x1F00: /*Conforming code*/ if ((seg&3) < DPL) { pclog("IRET C DPL\n"); ESP = oldsp; x86gpf(NULL,seg&~3); return; } break; default: pclog("IRET CS != code seg\n"); ESP = oldsp; x86gpf(NULL,seg&~3); // dumpregs(); // exit(-1); return; } if (!(segdat[2]&0x8000)) { pclog("IRET CS not present %i %04X %04X %04X\n",ins, segdat[0], segdat[1], segdat[2]); ESP = oldsp; x86np("IRET CS not present\n", seg & 0xfffc); return; } // pclog("Seg %04X CPL %04X\n",seg,CPL); if ((seg&3) == CPL) { // pclog("Same level\n"); CS=seg; do_seg_load(&_cs, segdat); _cs.access = (_cs.access & ~(3 << 5)) | ((CS & 3) << 5); if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); use32=(segdat[3]&0x40)?0x300:0; #ifdef CS_ACCESSED cpl_override = 1; writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/ cpl_override = 0; #endif cycles -= timing_iret_pm; } else /*Return to outer level*/ { oaddr = addr; if (output) pclog("Outer level\n"); if (is32) { newsp=POPL(); newss=POPL(); if (abrt) { ESP = oldsp; return; } } else { newsp=POPW(); newss=POPW(); if (abrt) { ESP = oldsp; return; } } if (output) pclog("IRET load stack %04X:%04X\n",newss,newsp); if (!(newss&~3)) { pclog("IRET loading null SS\n"); ESP = oldsp; x86gpf(NULL,newss&~3); return; } addr=newss&~7; if (newss&4) { if (addr>=ldt.limit) { pclog("Bigger than LDT limit %04X %04X PMODEIRET SS\n",newss,gdt.limit); ESP = oldsp; x86gpf(NULL,newss&~3); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("Bigger than GDT limit %04X %04X PMODEIRET\n",newss,gdt.limit); ESP = oldsp; x86gpf(NULL,newss&~3); return; } addr+=gdt.base; } cpl_override=1; segdat2[0]=readmemw(0,addr); segdat2[1]=readmemw(0,addr+2); segdat2[2]=readmemw(0,addr+4); segdat2[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) { ESP = oldsp; return; } // pclog("IRET SS sd2 %04X\n",segdat2[2]); // if (((newss & 3) != DPL) || (DPL2 != DPL)) if ((newss & 3) != (seg & 3)) { pclog("IRET loading SS with wrong permissions %04X %04X\n", newss, seg); ESP = oldsp; // dumpregs(); // exit(-1); x86gpf(NULL,newss&~3); return; } if ((segdat2[2]&0x1A00)!=0x1200) { pclog("IRET loading SS wrong type\n"); ESP = oldsp; x86gpf(NULL,newss&~3); return; } if (DPL2 != (seg & 3)) { pclog("IRET loading SS with wrong permissions2 %i %i %04X %04X\n", DPL2, seg & 3, newss, seg); ESP = oldsp; x86gpf(NULL,newss&~3); return; } if (!(segdat2[2]&0x8000)) { pclog("IRET loading SS not present\n"); ESP = oldsp; x86np("IRET loading SS not present\n", newss & 0xfffc); return; } SS=newss; stack32 = (segdat2[3] & 0x40) ? 1 : 0; if (stack32) ESP=newsp; else SP=newsp; do_seg_load(&_ss, segdat2); #ifdef SEL_ACCESSED cpl_override = 1; writememw(0, addr+4, segdat2[2] | 0x100); /*Set accessed bit*/ #ifdef CS_ACCESSED writememw(0, oaddr+4, segdat[2] | 0x100); /*Set accessed bit*/ #endif cpl_override = 0; #endif /*Conforming segments don't change CPL, so CPL = RPL*/ if (segdat[2]&0x400) segdat[2] = (segdat[2] & ~(3 << (5+8))) | ((seg & 3) << (5+8)); CS=seg; do_seg_load(&_cs, segdat); _cs.access = (_cs.access & ~(3 << 5)) | ((CS & 3) << 5); if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); use32=(segdat[3]&0x40)?0x300:0; check_seg_valid(&_ds); check_seg_valid(&_es); check_seg_valid(&_fs); check_seg_valid(&_gs); cycles -= timing_iret_pm_outer; } cpu_state.pc=newpc; flags=(flags&~flagmask)|(tempflags&flagmask&0xFFD5)|2; if (is32) eflags=tempflags>>16; // pclog("done\n"); } void taskswitch286(uint16_t seg, uint16_t *segdat, int is32) { uint32_t base; uint32_t limit; uint32_t templ; uint16_t tempw; uint32_t new_cr3=0; uint16_t new_es,new_cs,new_ss,new_ds,new_fs,new_gs; uint16_t new_ldt; uint32_t new_eax,new_ebx,new_ecx,new_edx,new_esp,new_ebp,new_esi,new_edi,new_pc,new_flags; uint32_t addr; uint16_t segdat2[4]; //output=3; base=segdat[1]|((segdat[2]&0xFF)<<16)|((segdat[3]>>8)<<24); limit=segdat[0]|((segdat[3]&0xF)<<16); // pclog("286 Task switch! %04X:%04X\n",CS,cpu_state.pc); /// pclog("TSS %04X base %08X limit %04X old TSS %04X %08X %i\n",seg,base,limit,tr.seg,tr.base,ins); // / pclog("%04X %04X %04X %04X\n",segdat[0],segdat[1],segdat[2],segdat[3]); if (is386) { // if (output) pclog("32-bit TSS\n"); new_cr3=readmeml(base,0x1C); new_pc=readmeml(base,0x20); new_flags=readmeml(base,0x24); new_eax=readmeml(base,0x28); new_ecx=readmeml(base,0x2C); new_edx=readmeml(base,0x30); new_ebx=readmeml(base,0x34); new_esp=readmeml(base,0x38); new_ebp=readmeml(base,0x3C); new_esi=readmeml(base,0x40); new_edi=readmeml(base,0x44); new_es=readmemw(base,0x48); // if (output) pclog("Read CS from %08X\n",base+0x4C); new_cs=readmemw(base,0x4C); new_ss=readmemw(base,0x50); new_ds=readmemw(base,0x54); new_fs=readmemw(base,0x58); new_gs=readmemw(base,0x5C); new_ldt=readmemw(base,0x60); if (abrt) return; if (optype==JMP || optype==OPTYPE_INT) { if (tr.seg&4) tempw=readmemw(ldt.base,(tr.seg&~7)+4); else tempw=readmemw(gdt.base,(tr.seg&~7)+4); if (abrt) return; tempw&=~0x200; if (tr.seg&4) writememw(ldt.base,(tr.seg&~7)+4,tempw); else writememw(gdt.base,(tr.seg&~7)+4,tempw); } if (optype==IRET) flags&=~NT_FLAG; // if (output) pclog("Write PC %08X %08X\n",tr.base,cpu_state.pc); cpu_386_flags_rebuild(); writememl(tr.base,0x1C,cr3); writememl(tr.base,0x20,cpu_state.pc); writememl(tr.base,0x24,flags|(eflags<<16)); writememl(tr.base,0x28,EAX); writememl(tr.base,0x2C,ECX); writememl(tr.base,0x30,EDX); writememl(tr.base,0x34,EBX); writememl(tr.base,0x38,ESP); writememl(tr.base,0x3C,EBP); writememl(tr.base,0x40,ESI); writememl(tr.base,0x44,EDI); writememl(tr.base,0x48,ES); // if (output) pclog("Write CS %04X to %08X\n",CS,tr.base+0x4C); writememl(tr.base,0x4C,CS); writememl(tr.base,0x50,SS); writememl(tr.base,0x54,DS); writememl(tr.base,0x58,FS); writememl(tr.base,0x5C,GS); writememl(tr.base,0x60,ldt.seg); if (optype==OPTYPE_INT) { writememl(base,0,tr.seg); new_flags|=NT_FLAG; } if (abrt) return; if (optype==JMP || optype==OPTYPE_INT) { if (tr.seg&4) tempw=readmemw(ldt.base,(seg&~7)+4); else tempw=readmemw(gdt.base,(seg&~7)+4); if (abrt) return; tempw|=0x200; if (tr.seg&4) writememw(ldt.base,(seg&~7)+4,tempw); else writememw(gdt.base,(seg&~7)+4,tempw); } cr3=new_cr3; // pclog("TS New CR3 %08X\n",cr3); flushmmucache(); cpu_state.pc=new_pc; // if (output) pclog("New pc %08X\n",new_pc); flags=new_flags; eflags=new_flags>>16; cpu_386_flags_extract(); // if (output) pclog("Load LDT %04X\n",new_ldt); ldt.seg=new_ldt; templ=(ldt.seg&~7)+gdt.base; // if (output) pclog("Load from %08X %08X\n",templ,gdt.base); ldt.limit=readmemw(0,templ); if (readmemb(templ+6)&0x80) { ldt.limit<<=12; ldt.limit|=0xFFF; } ldt.base=(readmemw(0,templ+2))|(readmemb(templ+4)<<16)|(readmemb(templ+7)<<24); // if (output) pclog("Limit %04X Base %08X\n",ldt.limit,ldt.base); if (eflags&VM_FLAG) { pclog("Task switch V86!\n"); x86gpf(NULL,0); return; } if (!(new_cs&~3)) { pclog("TS loading null CS\n"); x86gpf(NULL,0); return; } addr=new_cs&~7; if (new_cs&4) { if (addr>=ldt.limit) { pclog("Bigger than LDT limit %04X %04X %04X TS\n",new_cs,ldt.limit,addr); x86gpf(NULL,0); return; } addr+=ldt.base; } else { if (addr>=gdt.limit) { pclog("Bigger than GDT limit %04X %04X TS\n",new_cs,gdt.limit); x86gpf(NULL,0); return; } addr+=gdt.base; } segdat2[0]=readmemw(0,addr); segdat2[1]=readmemw(0,addr+2); segdat2[2]=readmemw(0,addr+4); segdat2[3]=readmemw(0,addr+6); if (!(segdat2[2]&0x8000)) { pclog("TS loading CS not present\n"); x86np("TS loading CS not present\n", new_cs & 0xfffc); return; } switch (segdat2[2]&0x1F00) { case 0x1800: case 0x1900: case 0x1A00: case 0x1B00: /*Non-conforming*/ if ((new_cs&3) != DPL2) { pclog("TS load CS non-conforming RPL != DPL"); x86gpf(NULL,new_cs&~3); return; } break; case 0x1C00: case 0x1D00: case 0x1E00: case 0x1F00: /*Conforming*/ if ((new_cs&3) < DPL2) { pclog("TS load CS non-conforming RPL < DPL"); x86gpf(NULL,new_cs&~3); return; } break; default: pclog("TS load CS not code segment\n"); x86gpf(NULL,new_cs&~3); return; } // if (output) pclog("new_cs %04X\n",new_cs); CS=new_cs; do_seg_load(&_cs, segdat2); if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); use32=(segdat2[3]&0x40)?0x300:0; EAX=new_eax; ECX=new_ecx; EDX=new_edx; EBX=new_ebx; ESP=new_esp; EBP=new_ebp; ESI=new_esi; EDI=new_edi; if (output) pclog("Load ES %04X\n",new_es); loadseg(new_es,&_es); if (output) pclog("Load SS %04X\n",new_ss); loadseg(new_ss,&_ss); if (output) pclog("Load DS %04X\n",new_ds); loadseg(new_ds,&_ds); if (output) pclog("Load FS %04X\n",new_fs); loadseg(new_fs,&_fs); if (output) pclog("Load GS %04X\n",new_gs); loadseg(new_gs,&_gs); if (output) pclog("Resuming at %04X:%08X\n",CS,cpu_state.pc); } else { pclog("16-bit TSS\n"); resetx86(); //exit(-1); } tr.seg=seg; tr.base=base; tr.limit=limit; tr.access=segdat[2]>>8; }