diff --git a/src/CPU/x86seg.c b/src/CPU/x86seg.c index c200b3121..399b6826f 100644 --- a/src/CPU/x86seg.c +++ b/src/CPU/x86seg.c @@ -25,8 +25,6 @@ #include "x86.h" #include "386.h" #include "386_common.h" -#undef readmemb -#define readmemb(a) ((readlookup2[(a)>>12]==-1)?readmembl(a):*(uint8_t *)(readlookup2[(a) >> 12] + (a))) #include "cpu.h" /*Controls whether the accessed bit in a descriptor is set when CS is loaded.*/ @@ -736,6 +734,7 @@ void loadcsjmp(uint16_t seg, uint32_t oxpc) break; + case 0x100: /*286 Task gate*/ case 0x900: /*386 Task gate*/ cpu_state.pc=oxpc; cpl_override=1; @@ -2125,11 +2124,14 @@ void taskswitch286(uint16_t seg, uint16_t *segdat, int is32) uint16_t segdat2[4]; - base=segdat[1]|((segdat[2]&0xFF)<<16)|((segdat[3]>>8)<<24); - limit=segdat[0]|((segdat[3]&0xF)<<16); + base=segdat[1]|((segdat[2]&0xFF)<<16); + limit=segdat[0]; - if (is386) + if (is32) { + base |= (segdat[3]>>8)<<24; + limit |= (segdat[3]&0xF)<<16; + new_cr3=readmeml(base,0x1C); new_pc=readmeml(base,0x20); new_flags=readmeml(base,0x24); @@ -2215,12 +2217,12 @@ void taskswitch286(uint16_t seg, uint16_t *segdat, int is32) ldt.seg=new_ldt; templ=(ldt.seg&~7)+gdt.base; ldt.limit=readmemw(0,templ); - if (readmemb(templ+6)&0x80) + if (readmemb(0,templ+6)&0x80) { ldt.limit<<=12; ldt.limit|=0xFFF; } - ldt.base=(readmemw(0,templ+2))|(readmemb(templ+4)<<16)|(readmemb(templ+7)<<24); + ldt.base=(readmemw(0,templ+2))|(readmemb(0,templ+4)<<16)|(readmemb(0,templ+7)<<24); if (eflags&VM_FLAG) { @@ -2311,9 +2313,165 @@ void taskswitch286(uint16_t seg, uint16_t *segdat, int is32) } else { - resetx86(); - } + new_pc=readmemw(base,0x0E); + new_flags=readmemw(base,0x10); + + new_eax=readmemw(base,0x12); + new_ecx=readmemw(base,0x14); + new_edx=readmemw(base,0x16); + new_ebx=readmemw(base,0x18); + new_esp=readmemw(base,0x1A); + new_ebp=readmemw(base,0x1C); + new_esi=readmemw(base,0x1E); + new_edi=readmemw(base,0x20); + new_es=readmemw(base,0x22); + new_cs=readmemw(base,0x24); + new_ss=readmemw(base,0x26); + new_ds=readmemw(base,0x28); + new_ldt=readmemw(base,0x2A); + + if (cpu_state.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 (cpu_state.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; + + cpu_386_flags_rebuild(); + writememw(tr.base,0x0E,cpu_state.pc); + writememw(tr.base,0x10,flags); + + writememw(tr.base,0x12,AX); + writememw(tr.base,0x14,CX); + writememw(tr.base,0x16,DX); + writememw(tr.base,0x18,BX); + writememw(tr.base,0x1A,SP); + writememw(tr.base,0x1C,BP); + writememw(tr.base,0x1E,SI); + writememw(tr.base,0x20,DI); + + writememw(tr.base,0x22,ES); + writememw(tr.base,0x24,CS); + writememw(tr.base,0x26,SS); + writememw(tr.base,0x28,DS); + writememw(tr.base,0x2A,ldt.seg); + + if (optype==OPTYPE_INT) + { + writememw(base,0,tr.seg); + new_flags|=NT_FLAG; + } + if (cpu_state.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 (cpu_state.abrt) return; + tempw|=0x200; + if (tr.seg&4) writememw(ldt.base,(seg&~7)+4,tempw); + else writememw(gdt.base,(seg&~7)+4,tempw); + } + + cpu_state.pc=new_pc; + flags=new_flags; + cpu_386_flags_extract(); + + ldt.seg=new_ldt; + templ=(ldt.seg&~7)+gdt.base; + ldt.limit=readmemw(0,templ); + ldt.base=(readmemw(0,templ+2))|(readmemb(0,templ+4)<<16); + + 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; + } + + CS=new_cs; + do_seg_load(&_cs, segdat2); + if (CPL==3 && oldcpl!=3) flushmmucache_cr3(); + set_use32(0); + + AX=new_eax; + CX=new_ecx; + DX=new_edx; + BX=new_ebx; + SP=new_esp; + BP=new_ebp; + SI=new_esi; + DI=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("Resuming at %04X:%08X\n",CS,cpu_state.pc); + } tr.seg=seg; tr.base=base;