PCem
view src/x86seg.c @ 142:bd46c39a78e8
Implemented selector limits on some instructions - fixes LBA2.
GPFs in real mode now work.
Selectors correctly zeroed on far return to lower privilege.
| author | TomW |
|---|---|
| date | Wed Aug 13 20:33:56 2014 +0100 |
| parents | 30de9361da9a |
| children |
line source
1 //#if 0
2 #include <stdio.h>
3 #include <stdarg.h>
4 #include <stdlib.h>
5 #include "ibm.h"
6 #include "mem.h"
7 #include "x86.h"
8 #include "386.h"
10 /*Controls whether the accessed bit in a descriptor is set when CS is loaded. The
11 386 PRM is ambiguous on this subject, but BOCHS doesn't set it and Windows 98
12 setup crashes if it is.
13 The accessed bit is always set for data and stack selectors though.*/
14 //#define CS_ACCESSED
16 /*Controls whether the accessed bit in a descriptor is set when a data or stack
17 selector is loaded. This SHOULD be set, however Windows 98 setup crashes if it
18 is.*/
19 //#define SEL_ACCESSED
20 int stimes = 0;
21 int dtimes = 0;
22 int btimes = 0;
23 int is486=1;
25 uint32_t abrt_error;
26 int cgate16,cgate32;
28 #define breaknullsegs 0
30 int intgatesize;
32 void taskswitch286(uint16_t seg, uint16_t *segdat, int is32);
33 void taskswitch386(uint16_t seg, uint16_t *segdat);
35 int output;
36 void pmodeint(int num, int soft);
37 /*NOT PRESENT is INT 0B
38 GPF is INT 0D*/
40 FILE *pclogf;
41 void x86abort(const char *format, ...)
42 {
43 char buf[256];
44 // return;
45 if (!pclogf)
46 pclogf=fopen("pclog.txt","wt");
47 //return;
48 va_list ap;
49 va_start(ap, format);
50 vsprintf(buf, format, ap);
51 va_end(ap);
52 fputs(buf,pclogf);
53 fflush(pclogf);
54 dumpregs();
55 exit(-1);
56 }
58 uint8_t opcode2;
60 static void seg_reset(x86seg *s)
61 {
62 s->access = 0 << 5;
63 s->limit = 0xFFFF;
64 s->limit_low = 0;
65 s->limit_high = 0xffff;
66 }
68 void x86seg_reset()
69 {
70 seg_reset(&_cs);
71 seg_reset(&_ds);
72 seg_reset(&_es);
73 seg_reset(&_fs);
74 seg_reset(&_gs);
75 seg_reset(&_ss);
76 }
78 void x86_doabrt(int x86_abrt)
79 {
80 // ingpf = 1;
81 CS = oldcs;
82 pc = oldpc;
83 _cs.access = oldcpl << 5;
84 // pclog("x86_doabrt - %02X %08X %04X:%08X %i\n", x86_abrt, abrt_error, CS, pc, ins);
86 /* if (CS == 0x3433 && pc == 0x000006B0)
87 {
88 pclog("Quit it\n");
89 dumpregs();
90 exit(-1);
91 }*/
92 // pclog("GPF! - error %04X %04X(%08X):%08X %02X %02X %i %04X %i %i\n",error,CS,cs,pc,opcode,opcode2,ins,flags&I_FLAG,IOPL, dtimes);
94 if (msw & 1)
95 pmodeint(x86_abrt, 0);
96 else
97 {
98 uint32_t addr = (x86_abrt << 2) + idt.base;
99 if (stack32)
100 {
101 writememw(ss,ESP-2,flags);
102 writememw(ss,ESP-4,CS);
103 writememw(ss,ESP-6,pc);
104 ESP-=6;
105 }
106 else
107 {
108 writememw(ss,((SP-2)&0xFFFF),flags);
109 writememw(ss,((SP-4)&0xFFFF),CS);
110 writememw(ss,((SP-6)&0xFFFF),pc);
111 SP-=6;
112 }
114 flags&=~I_FLAG;
115 flags&=~T_FLAG;
116 oxpc=pc;
117 pc=readmemw(0,addr);
118 loadcs(readmemw(0,addr+2));
119 return;
120 }
122 if (abrt) return;
124 if (intgatesize == 16)
125 {
126 if (stack32)
127 {
128 writememw(ss, ESP-2, abrt_error);
129 ESP-=2;
130 }
131 else
132 {
133 writememw(ss, ((SP-2)&0xFFFF), abrt_error);
134 SP-=2;
135 }
136 }
137 else
138 {
139 if (stack32)
140 {
141 writememl(ss, ESP-4, abrt_error);
142 ESP-=4;
143 }
144 else
145 {
146 writememl(ss, ((SP-4)&0xFFFF), abrt_error);
147 SP-=4;
148 }
149 }
150 // ingpf = 0;
151 // abrt = gpf = 1;
152 }
153 void x86gpf(char *s, uint16_t error)
154 {
155 // pclog("GPF %04X\n", error);
156 abrt = ABRT_GPF;
157 abrt_error = error;
158 }
159 void x86ss(char *s, uint16_t error)
160 {
161 // pclog("SS %04X\n", error);
162 abrt = ABRT_SS;
163 abrt_error = error;
164 }
165 void x86ts(char *s, uint16_t error)
166 {
167 // pclog("TS %04X\n", error);
168 abrt = ABRT_TS;
169 abrt_error = error;
170 }
171 void x86np(char *s, uint16_t error)
172 {
173 // pclog("NP %04X : %s\n", error, s);
174 abrt = ABRT_NP;
175 abrt_error = error;
176 }
179 static void do_seg_load(x86seg *s, uint16_t *segdat)
180 {
181 s->limit = segdat[0] | ((segdat[3] & 0xF) << 16);
182 if (segdat[3] & 0x80)
183 s->limit = (s->limit << 12) | 0xFFF;
184 s->base = segdat[1] | ((segdat[2] & 0xFF) << 16);
185 if (is386)
186 s->base |= ((segdat[3] >> 8) << 24);
187 s->access = segdat[2] >> 8;
189 if ((segdat[2] & 0x1800) != 0x1000 || !(segdat[2] & (1 << 10))) /*expand-down*/
190 {
191 s->limit_high = s->limit;
192 s->limit_low = 0;
193 }
194 else
195 {
196 s->limit_high = (segdat[3] & 0x40) ? 0xffffffff : 0xffff;
197 s->limit_low = s->limit + 1;
198 }
199 // if (output) pclog("SEG : base=%08x limit=%08x low=%08x high=%08x\n", s->base, s->limit, s->limit_low, s->limit_high);
200 }
202 static void do_seg_v86_init(x86seg *s)
203 {
204 s->access = 3 << 5;
205 s->limit = 0xffff;
206 s->limit_low = 0;
207 s->limit_high = 0xffff;
208 }
210 static void check_seg_valid(x86seg *s)
211 {
212 int dpl = (s->access >> 5) & 3;
213 int valid = 1;
215 if (s->seg & 4)
216 {
217 if ((s->seg & ~7) >= ldt.limit)
218 {
219 // pclog("Bigger than LDT limit %04X %04X %02X %02X %02X\n", s->seg, ldt.limit, opcode, opcode2, rmdat);
220 valid = 0;
221 }
222 }
223 else
224 {
225 if ((s->seg & ~7) >= gdt.limit)
226 {
227 // pclog("Bigger than GDT limit %04X %04X\n", s->seg, gdt.limit);
228 valid = 0;
229 }
230 }
232 switch (s->access & 0x1f)
233 {
234 case 0x10: case 0x11: case 0x12: case 0x13: /*Data segments*/
235 case 0x14: case 0x15: case 0x16: case 0x17:
236 case 0x1A: case 0x1B: /*Readable non-conforming code*/
237 if ((s->seg & 3) > dpl || (CPL) > dpl)
238 {
239 // pclog("Data seg fail - %04X:%08X %04X %i\n", CS, pc, s->seg, dpl);
240 valid = 0;
241 break;
242 }
243 break;
245 case 0x1E: case 0x1F: /*Readable conforming code*/
246 break;
248 default:
249 valid = 0;
250 break;
251 }
253 if (!valid)
254 loadseg(0, s);
255 }
257 void loadseg(uint16_t seg, x86seg *s)
258 {
259 uint16_t segdat[4];
260 uint32_t addr;
261 int dpl;
263 if (msw&1 && !(eflags&VM_FLAG))
264 {
265 // intcount++;
266 if (!(seg&~3))
267 {
268 if (s==&_ss)
269 {
270 pclog("SS selector = NULL!\n");
271 x86ss(NULL,0);
272 return;
273 // dumpregs();
274 // exit(-1);
275 }
276 // if (s->base!=-1) pclog("NEW! ");
277 s->seg=0;
278 s->access = 0;
279 s->base=-1;
280 // 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,pc);
281 return;
282 }
283 // if (s==&_ss) pclog("Load SS %04X\n",seg);
284 // pclog("Protected mode seg load!\n");
285 addr=seg&~7;
286 if (seg&4)
287 {
288 if (addr>=ldt.limit)
289 {
290 pclog("Bigger than LDT limit %04X %04X %02X %02X %02X\n",seg,ldt.limit, opcode, opcode2, rmdat);
291 // dumppic();
292 // dumpregs();
293 // exit(-1);
294 x86gpf(NULL,seg&~3);
295 return;
296 }
297 addr+=ldt.base;
298 }
299 else
300 {
301 if (addr>=gdt.limit)
302 {
303 pclog("Bigger than GDT limit %04X %04X 1\n",seg,gdt.limit);
304 // dumpregs();
305 // exit(-1);
306 x86gpf(NULL,seg&~3);
307 return;
308 }
309 addr+=gdt.base;
310 }
311 cpl_override=1;
312 segdat[0]=readmemw(0,addr);
313 segdat[1]=readmemw(0,addr+2);
314 segdat[2]=readmemw(0,addr+4);
315 segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return;
316 dpl=(segdat[2]>>13)&3;
317 if (s==&_ss)
318 {
319 if (!(seg&~3))
320 {
321 pclog("Load SS null selector\n");
322 x86gpf(NULL,seg&~3);
323 return;
324 }
325 if ((seg&3)!=CPL || dpl!=CPL)
326 {
327 pclog("Invalid SS permiss\n");
328 x86gpf(NULL,seg&~3);
329 // x86abort("Invalid SS permiss for %04X!\n",seg&0xFFFC);
330 return;
331 }
332 switch ((segdat[2]>>8)&0x1F)
333 {
334 case 0x12: case 0x13: case 0x16: case 0x17: /*r/w*/
335 break;
336 default:
337 pclog("Invalid SS type\n");
338 x86gpf(NULL,seg&~3);
339 // x86abort("Invalid SS segment type for %04X!\n",seg&0xFFFC);
340 return;
341 }
342 if (!(segdat[2]&0x8000))
343 {
344 pclog("Load SS not present!\n");
345 x86ss(NULL,seg&~3);
346 return;
347 }
348 if (segdat[3]&0x40) stack32=1;
349 else stack32=0;
350 // pclog("Load SS %04x %04x %04x %04x\n", segdat[0], segdat[1], segdat[2], segdat[3]);
351 }
352 else if (s!=&_cs)
353 {
354 if (output) pclog("Seg data %04X %04X %04X %04X\n", segdat[0], segdat[1], segdat[2], segdat[3]);
355 if (output) pclog("Seg type %03X\n",segdat[2]&0x1F00);
356 switch ((segdat[2]>>8)&0x1F)
357 {
358 case 0x10: case 0x11: case 0x12: case 0x13: /*Data segments*/
359 case 0x14: case 0x15: case 0x16: case 0x17:
360 case 0x1A: case 0x1B: /*Readable non-conforming code*/
361 // pclog("Load seg %04X %i %i %04X:%08X\n",seg,dpl,CS&3,CS,pc);
362 if ((seg&3)>dpl || (CPL)>dpl)
363 {
364 pclog("Data seg fail - %04X:%08X %04X %i %04X\n",CS,pc,seg,dpl,segdat[2]);
365 x86gpf(NULL,seg&~3);
366 // x86abort("Data segment load - level too low!\n",seg&0xFFFC);
367 return;
368 }
369 break;
370 case 0x1E: case 0x1F: /*Readable conforming code*/
371 break;
372 default:
373 pclog("Invalid segment type for %04X! %04X\n",seg&0xFFFC,segdat[2]);
374 x86gpf(NULL,seg&~3);
375 return;
376 }
377 }
379 if (!(segdat[2] & 0x8000))
380 {
381 x86np("Load data seg not present", seg & 0xfffc);
382 return;
383 }
384 s->seg = seg;
385 do_seg_load(s, segdat);
387 #ifndef CS_ACCESSED
388 if (s != &_cs)
389 {
390 #endif
391 #ifdef SEL_ACCESSED
392 cpl_override = 1;
393 writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/
394 cpl_override = 0;
395 #endif
396 #ifndef CS_ACCESSED
397 }
398 #endif
399 }
400 else
401 {
402 s->base = seg << 4;
403 s->seg = seg;
404 if (s == &_ss)
405 stack32 = 0;
406 }
407 }
409 #define DPL ((segdat[2]>>13)&3)
410 #define DPL2 ((segdat2[2]>>13)&3)
411 #define DPL3 ((segdat3[2]>>13)&3)
413 void loadcs(uint16_t seg)
414 {
415 uint16_t segdat[4];
416 uint32_t addr;
417 if (output) pclog("Load CS %04X\n",seg);
418 if (msw&1 && !(eflags&VM_FLAG))
419 {
420 // intcount++;
421 // flushmmucache();
422 // pclog("Load CS %04X\n",seg);
423 if (!(seg&~3))
424 {
425 pclog("Trying to load CS with NULL selector! lcs\n");
426 // dumpregs();
427 // exit(-1);
428 x86gpf(NULL,0);
429 return;
430 }
431 // pclog("Protected mode CS load! %04X\n",seg);
432 addr=seg&~7;
433 if (seg&4)
434 {
435 if (addr>=ldt.limit)
436 {
437 pclog("Bigger than LDT limit %04X %04X CS\n",seg,ldt.limit);
438 x86gpf(NULL,seg&~3);
439 return;
440 }
441 addr+=ldt.base;
442 }
443 else
444 {
445 if (addr>=gdt.limit)
446 {
447 pclog("Bigger than GDT limit %04X %04X CS\n",seg,gdt.limit);
448 x86gpf(NULL,seg&~3);
449 return;
450 }
451 addr+=gdt.base;
452 }
453 cpl_override=1;
454 segdat[0]=readmemw(0,addr);
455 segdat[1]=readmemw(0,addr+2);
456 segdat[2]=readmemw(0,addr+4);
457 segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return;
458 if (optype==JMP) pclog("Code seg - %04X - %04X %04X %04X %04X\n",seg,segdat[0],segdat[1],segdat[2],segdat[3]);
459 // if (!(segdat[2]&0x8000)) x86abort("Code segment not present!\n");
460 // if (output) pclog("Segdat2 %04X\n",segdat[2]);
461 if (segdat[2]&0x1000) /*Normal code segment*/
462 {
463 if (!(segdat[2]&0x400)) /*Not conforming*/
464 {
465 if ((seg&3)>CPL)
466 {
467 x86gpf(NULL,seg&~3);
468 pclog("loadcs RPL > CPL %04X %04X %i %02X\n",segdat[2],seg,CPL,opcode);
469 return;
470 }
471 if (CPL != DPL)
472 {
473 x86gpf(NULL,seg&~3);
474 return;
475 }
476 }
477 if (CPL < DPL)
478 {
479 x86gpf(NULL,seg&~3);
480 return;
481 }
482 if (!(segdat[2]&0x8000))
483 {
484 x86np("Load CS not present", seg & 0xfffc);
485 return;
486 }
487 if (segdat[3]&0x40) use32=0x300;
488 else use32=0;
489 CS=(seg&~3)|CPL;
490 do_seg_load(&_cs, segdat);
491 use32=(segdat[3]&0x40)?0x300:0;
492 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
494 #ifdef CS_ACCESSED
495 cpl_override = 1;
496 writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/
497 cpl_override = 0;
498 #endif
499 // if (output) pclog("Load CS %08X\n",_cs.base);
500 // CS=(CS&0xFFFC)|((_cs.access>>5)&3);
501 }
502 else /*System segment*/
503 {
504 if (!(segdat[2]&0x8000))
505 {
506 x86np("Load CS system seg not present\n", seg & 0xfffc);
507 return;
508 }
509 switch (segdat[2]&0xF00)
510 {
511 default:
512 pclog("Bad CS %02X %02X %i special descriptor %03X %04X\n",opcode,rmdat,optype,segdat[2]&0xF00,seg);
513 x86gpf(NULL,seg&~3);
514 return;
515 }
516 }
517 // pclog("CS = %04X base=%06X limit=%04X access=%02X %04X\n",CS,cs,_cs.limit,_cs.access,addr);
518 // dumpregs();
519 // exit(-1);
520 }
521 else
522 {
523 _cs.base=seg<<4;
524 _cs.limit=0xFFFF;
525 _cs.limit_low = 0;
526 _cs.limit_high = 0xffff;
527 CS=seg;
528 if (eflags&VM_FLAG) _cs.access=3<<5;
529 else _cs.access=0<<5;
530 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
531 }
532 }
534 void loadcsjmp(uint16_t seg, uint32_t oxpc)
535 {
536 uint16_t segdat[4];
537 uint32_t addr;
538 int count;
539 uint16_t type,seg2;
540 uint32_t newpc;
541 // pclog("Load CS JMP %04X\n",seg);
542 if (msw&1 && !(eflags&VM_FLAG))
543 {
544 if (!(seg&~3))
545 {
546 pclog("Trying to load CS with NULL selector! lcsjmp\n");
547 x86gpf(NULL,0);
548 return;
549 // dumpregs();
550 // exit(-1);
551 }
552 addr=seg&~7;
553 if (seg&4)
554 {
555 if (addr>=ldt.limit)
556 {
557 pclog("Bigger than LDT limit %04X %04X CS\n",seg,ldt.limit);
558 x86gpf(NULL,seg&~3);
559 return;
560 }
561 addr+=ldt.base;
562 }
563 else
564 {
565 if (addr>=gdt.limit)
566 {
567 pclog("Bigger than GDT limit %04X %04X CS\n",seg,gdt.limit);
568 x86gpf(NULL,seg&~3);
569 return;
570 }
571 addr+=gdt.base;
572 }
573 cpl_override=1;
574 segdat[0]=readmemw(0,addr);
575 segdat[1]=readmemw(0,addr+2);
576 segdat[2]=readmemw(0,addr+4);
577 segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return;
578 if (output) pclog("%04X %04X %04X %04X\n",segdat[0],segdat[1],segdat[2],segdat[3]);
579 if (segdat[2]&0x1000) /*Normal code segment*/
580 {
581 // pclog("Normal CS\n");
582 if (!(segdat[2]&0x400)) /*Not conforming*/
583 {
584 if ((seg&3)>CPL)
585 {
586 x86gpf(NULL,seg&~3);
587 return;
588 }
589 if (CPL != DPL)
590 {
591 x86gpf(NULL,seg&~3);
592 return;
593 }
594 }
595 if (CPL < DPL)
596 {
597 x86gpf(NULL,seg&~3);
598 return;
599 }
600 if (!(segdat[2]&0x8000))
601 {
602 x86np("Load CS JMP not present\n", seg & 0xfffc);
603 return;
604 }
605 if (segdat[3]&0x40) use32=0x300;
606 else use32=0;
608 #ifdef CS_ACCESSED
609 cpl_override = 1;
610 writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/
611 cpl_override = 0;
612 #endif
614 CS = (seg & ~3) | CPL;
615 segdat[2] = (segdat[2] & ~(3 << (5+8))) | (CPL << (5+8));
617 do_seg_load(&_cs, segdat);
618 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
619 use32=(segdat[3]&0x40)?0x300:0;
620 }
621 else /*System segment*/
622 {
623 // pclog("System CS\n");
624 if (!(segdat[2]&0x8000))
625 {
626 x86np("Load CS JMP system selector not present\n", seg & 0xfffc);
627 return;
628 }
629 type=segdat[2]&0xF00;
630 if (type==0x400) newpc=segdat[0];
631 else newpc=segdat[0]|(segdat[3]<<16);
632 switch (type)
633 {
634 case 0x400: /*Call gate*/
635 case 0xC00:
636 // pclog("Call gate\n");
637 cgate32=(type&0x800);
638 cgate16=!cgate32;
639 oldcs=CS;
640 oldpc=pc;
641 count=segdat[2]&31;
642 if ((DPL < CPL) || (DPL < (seg&3)))
643 {
644 x86gpf(NULL,seg&~3);
645 return;
646 }
647 if (!(segdat[2]&0x8000))
648 {
649 x86np("Load CS JMP call gate not present\n", seg & 0xfffc);
650 return;
651 }
652 seg2=segdat[1];
654 if (!(seg2&~3))
655 {
656 pclog("Trying to load CS with NULL selector! lcsjmpcg\n");
657 x86gpf(NULL,0);
658 return;
659 // dumpregs();
660 // exit(-1);
661 }
662 addr=seg2&~7;
663 if (seg2&4)
664 {
665 if (addr>=ldt.limit)
666 {
667 pclog("Bigger than LDT limit %04X %04X CSJ\n",seg2,gdt.limit);
668 x86gpf(NULL,seg2&~3);
669 return;
670 }
671 addr+=ldt.base;
672 }
673 else
674 {
675 if (addr>=gdt.limit)
676 {
677 pclog("Bigger than GDT limit %04X %04X CSJ\n",seg2,gdt.limit);
678 x86gpf(NULL,seg2&~3);
679 return;
680 }
681 addr+=gdt.base;
682 }
683 cpl_override=1;
684 segdat[0]=readmemw(0,addr);
685 segdat[1]=readmemw(0,addr+2);
686 segdat[2]=readmemw(0,addr+4);
687 segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return;
689 if (DPL > CPL)
690 {
691 x86gpf(NULL,seg2&~3);
692 return;
693 }
694 if (!(segdat[2]&0x8000))
695 {
696 x86np("Load CS JMP from call gate not present\n", seg & 0xfffc);
697 return;
698 }
701 switch (segdat[2]&0x1F00)
702 {
703 case 0x1800: case 0x1900: case 0x1A00: case 0x1B00: /*Non-conforming code*/
704 if (DPL > CPL)
705 {
706 pclog("Call gate DPL > CPL");
707 x86gpf(NULL,seg2&~3);
708 return;
709 }
710 case 0x1C00: case 0x1D00: case 0x1E00: case 0x1F00: /*Conforming*/
711 CS=seg2;
712 do_seg_load(&_cs, segdat);
713 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
714 use32=(segdat[3]&0x40)?0x300:0;
715 pc=newpc;
717 #ifdef CS_ACCESSED
718 cpl_override = 1;
719 writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/
720 cpl_override = 0;
721 #endif
722 break;
724 default:
725 pclog("JMP Call gate bad segment type\n");
726 x86gpf(NULL,seg2&~3);
727 return;
728 }
729 break;
732 case 0x900: /*386 Task gate*/
733 // pclog("Task gate\n");
734 pc=oxpc;
735 cpl_override=1;
736 taskswitch286(seg,segdat,segdat[2]&0x800);
737 cpl_override=0;
738 // case 0xB00: /*386 Busy task gate*/
739 // if (optype==JMP) pclog("Task switch!\n");
740 // taskswitch386(seg,segdat);
741 return;
743 default:
744 pclog("Bad JMP CS %02X %02X %i special descriptor %03X %04X\n",opcode,rmdat,optype,segdat[2]&0xF00,seg);
745 x86gpf(NULL,0);
746 return;
747 // dumpregs();
748 // exit(-1);
749 }
750 }
751 // pclog("CS = %04X base=%06X limit=%04X access=%02X %04X\n",CS,cs,_cs.limit,_cs.access,addr);
752 // dumpregs();
753 // exit(-1);
754 }
755 else
756 {
757 _cs.base=seg<<4;
758 _cs.limit=0xFFFF;
759 _cs.limit_low = 0;
760 _cs.limit_high = 0xffff;
761 CS=seg;
762 if (eflags&VM_FLAG) _cs.access=3<<5;
763 else _cs.access=0<<5;
764 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
765 }
766 }
768 void PUSHW(uint16_t v)
769 {
770 // if (output==3) pclog("PUSHW %04X to %08X\n",v,ESP-4);
771 if (stack32)
772 {
773 writememw(ss,ESP-2,v);
774 if (abrt) return;
775 ESP-=2;
776 }
777 else
778 {
779 // pclog("Write %04X to %08X\n", v, ss+((SP-2)&0xFFFF));
780 writememw(ss,((SP-2)&0xFFFF),v);
781 if (abrt) return;
782 SP-=2;
783 }
784 }
785 void PUSHL(uint32_t v)
786 {
787 // if (output==3) pclog("PUSHL %08X to %08X\n",v,ESP-4);
788 if (stack32)
789 {
790 writememl(ss,ESP-4,v);
791 if (abrt) return;
792 ESP-=4;
793 }
794 else
795 {
796 writememl(ss,((SP-4)&0xFFFF),v);
797 if (abrt) return;
798 SP-=4;
799 }
800 }
801 uint16_t POPW()
802 {
803 uint16_t tempw;
804 if (stack32)
805 {
806 tempw=readmemw(ss,ESP);
807 if (abrt) return 0;
808 ESP+=2;
809 }
810 else
811 {
812 tempw=readmemw(ss,SP);
813 if (abrt) return 0;
814 SP+=2;
815 }
816 return tempw;
817 }
818 uint32_t POPL()
819 {
820 uint32_t templ;
821 if (stack32)
822 {
823 templ=readmeml(ss,ESP);
824 if (abrt) return 0;
825 ESP+=4;
826 }
827 else
828 {
829 templ=readmeml(ss,SP);
830 if (abrt) return 0;
831 SP+=4;
832 }
833 return templ;
834 }
836 void loadcscall(uint16_t seg)
837 {
838 uint16_t seg2;
839 uint16_t segdat[4],segdat2[4],newss;
840 uint32_t addr,oldssbase=ss, oaddr;
841 uint32_t newpc;
842 int count;
843 uint16_t oldcs=CPL;
844 uint32_t oldss,oldsp,newsp,oldpc, oldsp2;
845 int type;
846 uint16_t tempw;
848 int csout = output;
850 if (msw&1 && !(eflags&VM_FLAG))
851 {
852 //flushmmucache();
853 if (csout) pclog("Protected mode CS load! %04X\n",seg);
854 if (!(seg&~3))
855 {
856 pclog("Trying to load CS with NULL selector! lcscall\n");
857 x86gpf(NULL,0);
858 return;
859 // dumpregs();
860 // exit(-1);
861 }
862 addr=seg&~7;
863 if (seg&4)
864 {
865 if (addr>=ldt.limit)
866 {
867 pclog("Bigger than LDT limit %04X %04X CSC\n",seg,gdt.limit);
868 x86gpf(NULL,seg&~3);
869 return;
870 }
871 addr+=ldt.base;
872 }
873 else
874 {
875 if (addr>=gdt.limit)
876 {
877 pclog("Bigger than GDT limit %04X %04X CSC\n",seg,gdt.limit);
878 x86gpf(NULL,seg&~3);
879 return;
880 }
881 addr+=gdt.base;
882 }
883 cpl_override=1;
884 segdat[0]=readmemw(0,addr);
885 segdat[1]=readmemw(0,addr+2);
886 segdat[2]=readmemw(0,addr+4);
887 segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return;
888 type=segdat[2]&0xF00;
889 if (type==0x400) newpc=segdat[0];
890 else newpc=segdat[0]|(segdat[3]<<16);
892 if (csout) pclog("Code seg call - %04X - %04X %04X %04X\n",seg,segdat[0],segdat[1],segdat[2]);
893 if (segdat[2]&0x1000)
894 {
895 if (!(segdat[2]&0x400)) /*Not conforming*/
896 {
897 if ((seg&3)>CPL)
898 {
899 if (csout) pclog("Not conforming, RPL > CPL\n");
900 x86gpf(NULL,seg&~3);
901 return;
902 }
903 if (CPL != DPL)
904 {
905 if (csout) pclog("Not conforming, CPL != DPL (%i %i)\n",CPL,DPL);
906 x86gpf(NULL,seg&~3);
907 return;
908 }
909 }
910 if (CPL < DPL)
911 {
912 if (csout) pclog("CPL < DPL\n");
913 x86gpf(NULL,seg&~3);
914 return;
915 }
916 if (!(segdat[2]&0x8000))
917 {
918 if (csout) pclog("Not present\n");
919 x86np("Load CS call not present", seg & 0xfffc);
920 return;
921 }
922 if (segdat[3]&0x40) use32=0x300;
923 else use32=0;
925 #ifdef CS_ACCESSED
926 cpl_override = 1;
927 writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/
928 cpl_override = 0;
929 #endif
931 /*Conforming segments don't change CPL, so preserve existing CPL*/
932 if (segdat[2]&0x400)
933 {
934 seg = (seg & ~3) | CPL;
935 segdat[2] = (segdat[2] & ~(3 << (5+8))) | (CPL << (5+8));
936 }
937 else /*On non-conforming segments, set RPL = CPL*/
938 seg = (seg & ~3) | CPL;
939 CS=seg;
940 do_seg_load(&_cs, segdat);
941 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
942 use32=(segdat[3]&0x40)?0x300:0;
943 if (csout) pclog("Complete\n");
944 }
945 else
946 {
947 type=segdat[2]&0xF00;
948 if (csout) pclog("Type %03X\n",type);
949 switch (type)
950 {
951 case 0x400: /*Call gate*/
952 case 0xC00: /*386 Call gate*/
953 if (output) pclog("Callgate %08X\n", pc);
954 cgate32=(type&0x800);
955 cgate16=!cgate32;
956 oldcs=CS;
957 oldpc=pc;
958 count=segdat[2]&31;
959 if ((DPL < CPL) || (DPL < (seg&3)))
960 {
961 x86gpf(NULL,seg&~3);
962 return;
963 }
964 if (!(segdat[2]&0x8000))
965 {
966 if (output) pclog("Call gate not present %04X\n",seg);
967 x86np("Call gate not present\n", seg & 0xfffc);
968 return;
969 }
970 seg2=segdat[1];
972 if (output) pclog("New address : %04X:%08X\n", seg2, newpc);
974 if (!(seg2&~3))
975 {
976 pclog("Trying to load CS with NULL selector! lcscallcg\n");
977 x86gpf(NULL,0);
978 return;
979 // dumpregs();
980 // exit(-1);
981 }
982 addr=seg2&~7;
983 if (seg2&4)
984 {
985 if (addr>=ldt.limit)
986 {
987 pclog("Bigger than LDT limit %04X %04X CSC\n",seg2,gdt.limit);
988 x86gpf(NULL,seg2&~3);
989 return;
990 }
991 addr+=ldt.base;
992 }
993 else
994 {
995 if (addr>=gdt.limit)
996 {
997 pclog("Bigger than GDT limit %04X %04X CSC\n",seg2,gdt.limit);
998 x86gpf(NULL,seg2&~3);
999 return;
1000 }
1001 addr+=gdt.base;
1002 }
1003 cpl_override=1;
1004 segdat[0]=readmemw(0,addr);
1005 segdat[1]=readmemw(0,addr+2);
1006 segdat[2]=readmemw(0,addr+4);
1007 segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return;
1009 if (output) pclog("Code seg2 call - %04X - %04X %04X %04X\n",seg2,segdat[0],segdat[1],segdat[2]);
1011 if (DPL > CPL)
1012 {
1013 x86gpf(NULL,seg2&~3);
1014 return;
1015 }
1016 if (!(segdat[2]&0x8000))
1017 {
1018 if (output) pclog("Call gate CS not present %04X\n",seg2);
1019 x86np("Call gate CS not present", seg2 & 0xfffc);
1020 return;
1021 }
1024 switch (segdat[2]&0x1F00)
1025 {
1026 case 0x1800: case 0x1900: case 0x1A00: case 0x1B00: /*Non-conforming code*/
1027 if (DPL < CPL)
1028 {
1029 oaddr = addr;
1030 /*Load new stack*/
1031 oldss=SS;
1032 oldsp=oldsp2=ESP;
1033 cpl_override=1;
1034 if (tr.access&8)
1035 {
1036 addr = 4 + tr.base + (DPL * 8);
1037 newss=readmemw(0,addr+4);
1038 newsp=readmeml(0,addr);
1039 }
1040 else
1041 {
1042 addr = 2 + tr.base + (DPL * 4);
1043 newss=readmemw(0,addr+2);
1044 newsp=readmemw(0,addr);
1045 }
1046 cpl_override=0;
1047 if (abrt) return;
1048 if (output) pclog("New stack %04X:%08X\n",newss,newsp);
1049 if (!(newss&~3))
1050 {
1051 pclog("Call gate loading null SS\n");
1052 x86ts(NULL,newss&~3);
1053 return;
1054 }
1055 addr=newss&~7;
1056 if (newss&4)
1057 {
1058 if (addr>=ldt.limit)
1059 {
1060 x86abort("Bigger than LDT limit %04X %08X %04X CSC SS\n",newss,addr,ldt.limit);
1061 x86ts(NULL,newss&~3);
1062 return;
1063 }
1064 addr+=ldt.base;
1065 }
1066 else
1067 {
1068 if (addr>=gdt.limit)
1069 {
1070 x86abort("Bigger than GDT limit %04X %04X CSC\n",newss,gdt.limit);
1071 x86ts(NULL,newss&~3);
1072 return;
1073 }
1074 addr+=gdt.base;
1075 }
1076 cpl_override=1;
1077 if (output) pclog("Read stack seg\n");
1078 segdat2[0]=readmemw(0,addr);
1079 segdat2[1]=readmemw(0,addr+2);
1080 segdat2[2]=readmemw(0,addr+4);
1081 segdat2[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return;
1082 if (output) pclog("Read stack seg done!\n");
1083 if (((newss & 3) != DPL) || (DPL2 != DPL))
1084 {
1085 pclog("Call gate loading SS with wrong permissions %04X %04X %i %i %04X %04X\n", newss, seg2, DPL, DPL2, segdat[2], segdat2[2]);
1086 // dumpregs();
1087 // exit(-1);
1088 x86ts(NULL,newss&~3);
1089 return;
1090 }
1091 if ((segdat2[2]&0x1A00)!=0x1200)
1092 {
1093 pclog("Call gate loading SS wrong type\n");
1094 x86ts(NULL,newss&~3);
1095 return;
1096 }
1097 if (!(segdat2[2]&0x8000))
1098 {
1099 pclog("Call gate loading SS not present\n");
1100 x86np("Call gate loading SS not present\n", newss & 0xfffc);
1101 return;
1102 }
1103 if (!stack32) oldsp &= 0xFFFF;
1104 SS=newss;
1105 stack32=segdat2[3]&0x40;
1106 if (stack32) ESP=newsp;
1107 else SP=newsp;
1109 do_seg_load(&_ss, segdat2);
1111 if (output) pclog("Set access 1\n");
1113 #ifdef SEL_ACCESSED
1114 cpl_override = 1;
1115 writememw(0, addr+4, segdat2[2] | 0x100); /*Set accessed bit*/
1116 cpl_override = 0;
1117 #endif
1119 CS=seg2;
1120 do_seg_load(&_cs, segdat);
1121 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
1122 use32=(segdat[3]&0x40)?0x300:0;
1123 pc=newpc;
1125 if (output) pclog("Set access 2\n");
1127 #ifdef CS_ACCESSED
1128 cpl_override = 1;
1129 writememw(0, oaddr+4, segdat[2] | 0x100); /*Set accessed bit*/
1130 cpl_override = 0;
1131 #endif
1133 if (output) pclog("Type %04X\n",type);
1134 if (type==0xC00)
1135 {
1136 PUSHL(oldss);
1137 PUSHL(oldsp2);
1138 if (abrt)
1139 {
1140 pclog("ABRT PUSHL\n");
1141 SS = oldss;
1142 ESP = oldsp2;
1143 return;
1144 }
1145 // if (output) pclog("Stack now %04X:%08X\n",SS,ESP);
1146 if (count)
1147 {
1148 while (count)
1149 {
1150 count--;
1151 PUSHL(readmeml(oldssbase,oldsp+(count*4)));
1152 if (abrt)
1153 {
1154 pclog("ABRT COPYL\n");
1155 SS = oldss;
1156 ESP = oldsp2;
1157 return;
1158 }
1159 }
1160 }
1161 // x86abort("Call gate with count %i\n",count);
1162 // PUSHL(oldcs);
1163 // PUSHL(oldpc); if (abrt) return;
1164 }
1165 else
1166 {
1167 if (output) pclog("Stack %04X\n",SP);
1168 PUSHW(oldss);
1169 if (output) pclog("Write SS to %04X:%04X\n",SS,SP);
1170 PUSHW(oldsp2);
1171 if (abrt)
1172 {
1173 pclog("ABRT PUSHW\n");
1174 SS = oldss;
1175 ESP = oldsp2;
1176 return;
1177 }
1178 if (output) pclog("Write SP to %04X:%04X\n",SS,SP);
1179 // if (output) pclog("Stack %04X %i %04X:%04X\n",SP,count,oldssbase,oldsp);
1180 // if (output) pclog("PUSH %04X %04X %i %i now %04X:%08X\n",oldss,oldsp,count,stack32,SS,ESP);
1181 if (count)
1182 {
1183 while (count)
1184 {
1185 count--;
1186 tempw=readmemw(oldssbase,(oldsp&0xFFFF)+(count*2));
1187 if (output) pclog("PUSH %04X\n",tempw);
1188 PUSHW(tempw);
1189 if (abrt)
1190 {
1191 pclog("ABRT COPYW\n");
1192 SS = oldss;
1193 ESP = oldsp2;
1194 return;
1195 }
1196 }
1197 }
1198 // if (output) pclog("Stack %04X\n",SP);
1199 // if (count) x86abort("Call gate with count\n");
1200 // PUSHW(oldcs);
1201 // PUSHW(oldpc); if (abrt) return;
1202 }
1203 break;
1204 }
1205 else if (DPL > CPL)
1206 {
1207 pclog("Call gate DPL > CPL");
1208 x86gpf(NULL,seg2&~3);
1209 return;
1210 }
1211 case 0x1C00: case 0x1D00: case 0x1E00: case 0x1F00: /*Conforming*/
1212 /* if (type==0xC00)
1213 {
1214 PUSHL(oldcs);
1215 PUSHL(oldpc); if (abrt) return;
1216 }
1217 else
1218 {
1219 PUSHW(oldcs);
1220 PUSHW(oldpc); if (abrt) return;
1221 }*/
1222 CS=seg2;
1223 do_seg_load(&_cs, segdat);
1224 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
1225 use32=(segdat[3]&0x40)?0x300:0;
1226 pc=newpc;
1228 #ifdef CS_ACCESSED
1229 cpl_override = 1;
1230 writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/
1231 cpl_override = 0;
1232 #endif
1233 break;
1235 default:
1236 pclog("Call gate bad segment type\n");
1237 x86gpf(NULL,seg2&~3);
1238 return;
1239 }
1240 break;
1242 // case 0x900: /*386 Task gate*/
1243 // case 0xB00: /*386 Busy task gate*/
1244 // if (optype==JMP) pclog("Task switch!\n");
1245 // taskswitch386(seg,segdat);
1246 // return;
1249 default:
1250 pclog("Bad CALL special descriptor %03X\n",segdat[2]&0xF00);
1251 x86gpf(NULL,seg&~3);
1252 return;
1253 // dumpregs();
1254 // exit(-1);
1255 }
1256 }
1257 // pclog("CS = %04X base=%06X limit=%04X access=%02X %04X\n",CS,cs,_cs.limit,_cs.access,addr);
1258 // dumpregs();
1259 // exit(-1);
1260 }
1261 else
1262 {
1263 _cs.base=seg<<4;
1264 _cs.limit=0xFFFF;
1265 _cs.limit_low = 0;
1266 _cs.limit_high = 0xffff;
1267 CS=seg;
1268 if (eflags&VM_FLAG) _cs.access=3<<5;
1269 else _cs.access=0<<5;
1270 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
1271 }
1272 }
1274 void pmoderetf(int is32, uint16_t off)
1275 {
1276 uint32_t newpc;
1277 uint32_t newsp;
1278 uint32_t addr, oaddr;
1279 uint16_t segdat[4],segdat2[4],seg,newss;
1280 uint32_t oldsp=ESP;
1281 if (output) pclog("RETF %i %04X:%04X %08X %04X\n",is32,CS,pc,cr0,eflags);
1282 if (is32)
1283 {
1284 newpc=POPL();
1285 seg=POPL(); if (abrt) return;
1286 }
1287 else
1288 {
1289 if (output) pclog("PC read from %04X:%04X\n",SS,SP);
1290 newpc=POPW();
1291 if (output) pclog("CS read from %04X:%04X\n",SS,SP);
1292 seg=POPW(); if (abrt) return;
1293 }
1294 if (output) pclog("Return to %04X:%08X\n",seg,newpc);
1295 if ((seg&3)<CPL)
1296 {
1297 pclog("RETF RPL<CPL %04X %i %i %04X:%08X\n",seg,CPL,ins,CS,pc);
1298 // output=3;
1299 // timetolive=100;
1300 // dumpregs();
1301 // exit(-1);
1302 ESP=oldsp;
1303 x86gpf(NULL,seg&~3);
1304 return;
1305 }
1306 if (!(seg&~3))
1307 {
1308 pclog("Trying to load CS with NULL selector! retf\n");
1309 // dumpregs();
1310 // exit(-1);
1311 x86gpf(NULL,0);
1312 return;
1313 }
1314 addr=seg&~7;
1315 if (seg&4)
1316 {
1317 if (addr>=ldt.limit)
1318 {
1319 pclog("Bigger than LDT limit %04X %04X RETF\n",seg,ldt.limit);
1320 x86gpf(NULL,seg&~3);
1321 return;
1322 }
1323 addr+=ldt.base;
1324 }
1325 else
1326 {
1327 if (addr>=gdt.limit)
1328 {
1329 pclog("Bigger than GDT limit %04X %04X RETF\n",seg,gdt.limit);
1330 x86gpf(NULL,seg&~3);
1331 // dumpregs();
1332 // exit(-1);
1333 return;
1334 }
1335 addr+=gdt.base;
1336 }
1337 cpl_override=1;
1338 segdat[0]=readmemw(0,addr);
1339 segdat[1]=readmemw(0,addr+2);
1340 segdat[2]=readmemw(0,addr+4);
1341 segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) { ESP=oldsp; return; }
1342 oaddr = addr;
1344 if (output) pclog("CPL %i RPL %i %i\n",CPL,seg&3,is32);
1346 if (stack32) ESP+=off;
1347 else SP+=off;
1349 if (CPL==(seg&3))
1350 {
1351 if (output) pclog("RETF CPL = RPL %04X\n", segdat[2]);
1352 switch (segdat[2]&0x1F00)
1353 {
1354 case 0x1800: case 0x1900: case 0x1A00: case 0x1B00: /*Non-conforming*/
1355 if (CPL != DPL)
1356 {
1357 pclog("RETF non-conforming CPL != DPL\n");
1358 ESP=oldsp;
1359 x86gpf(NULL,seg&~3);
1360 return;
1361 }
1362 break;
1363 case 0x1C00: case 0x1D00: case 0x1E00: case 0x1F00: /*Conforming*/
1364 if (CPL < DPL)
1365 {
1366 pclog("RETF non-conforming CPL < DPL\n");
1367 ESP=oldsp;
1368 x86gpf(NULL,seg&~3);
1369 return;
1370 }
1371 break;
1372 default:
1373 pclog("RETF CS not code segment\n");
1374 x86gpf(NULL,seg&~3);
1375 return;
1376 }
1377 if (!(segdat[2]&0x8000))
1378 {
1379 pclog("RETF CS not present %i %04X %04X %04X\n",ins, segdat[0], segdat[1], segdat[2]);
1380 ESP=oldsp;
1381 x86np("RETF CS not present\n", seg & 0xfffc);
1382 return;
1383 }
1385 #ifdef CS_ACCESSED
1386 cpl_override = 1;
1387 writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/
1388 cpl_override = 0;
1389 #endif
1391 pc=newpc;
1392 if (segdat[2] & 0x400)
1393 segdat[2] = (segdat[2] & ~(3 << (5+8))) | ((seg & 3) << (5+8));
1394 CS = seg;
1395 do_seg_load(&_cs, segdat);
1396 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
1397 use32=(segdat[3]&0x40)?0x300:0;
1399 // pclog("CPL=RPL return to %04X:%08X\n",CS,pc);
1400 }
1401 else
1402 {
1403 switch (segdat[2]&0x1F00)
1404 {
1405 case 0x1800: case 0x1900: case 0x1A00: case 0x1B00: /*Non-conforming*/
1406 if ((seg&3) != DPL)
1407 {
1408 pclog("RETF non-conforming RPL != DPL\n");
1409 ESP=oldsp;
1410 x86gpf(NULL,seg&~3);
1411 return;
1412 }
1413 if (output) pclog("RETF non-conforming, %i %i\n",seg&3, DPL);
1414 break;
1415 case 0x1C00: case 0x1D00: case 0x1E00: case 0x1F00: /*Conforming*/
1416 if ((seg&3) < DPL)
1417 {
1418 pclog("RETF non-conforming RPL < DPL\n");
1419 ESP=oldsp;
1420 x86gpf(NULL,seg&~3);
1421 return;
1422 }
1423 if (output) pclog("RETF conforming, %i %i\n",seg&3, DPL);
1424 break;
1425 default:
1426 pclog("RETF CS not code segment\n");
1427 ESP=oldsp;
1428 x86gpf(NULL,seg&~3);
1429 return;
1430 }
1431 if (!(segdat[2]&0x8000))
1432 {
1433 pclog("RETF CS not present! %i %04X %04X %04X\n",ins, segdat[0], segdat[1], segdat[2]);
1435 ESP=oldsp;
1436 x86np("RETF CS not present\n", seg & 0xfffc);
1437 return;
1438 }
1439 if (is32)
1440 {
1441 newsp=POPL();
1442 newss=POPL(); if (abrt) return;
1443 // pclog("is32 new stack %04X:%04X\n",newss,newsp);
1444 }
1445 else
1446 {
1447 if (output) pclog("SP read from %04X:%04X\n",SS,SP);
1448 newsp=POPW();
1449 if (output) pclog("SS read from %04X:%04X\n",SS,SP);
1450 newss=POPW(); if (abrt) return;
1451 // pclog("!is32 new stack %04X:%04X\n",newss,newsp);
1452 }
1453 if (output) pclog("Read new stack : %04X:%04X (%08X)\n", newss, newsp, ldt.base);
1454 if (!(newss&~3))
1455 {
1456 pclog("RETF loading null SS\n");
1457 ESP=oldsp;
1458 x86gpf(NULL,newss&~3);
1459 return;
1460 }
1461 addr=newss&~7;
1462 if (newss&4)
1463 {
1464 if (addr>=ldt.limit)
1465 {
1466 pclog("Bigger than LDT limit %04X %04X RETF SS\n",newss,gdt.limit);
1467 ESP=oldsp;
1468 x86gpf(NULL,newss&~3);
1469 return;
1470 }
1471 addr+=ldt.base;
1472 }
1473 else
1474 {
1475 if (addr>=gdt.limit)
1476 {
1477 pclog("Bigger than GDT limit %04X %04X RETF SS\n",newss,gdt.limit);
1478 ESP=oldsp;
1479 x86gpf(NULL,newss&~3);
1480 return;
1481 }
1482 addr+=gdt.base;
1483 }
1484 cpl_override=1;
1485 segdat2[0]=readmemw(0,addr);
1486 segdat2[1]=readmemw(0,addr+2);
1487 segdat2[2]=readmemw(0,addr+4);
1488 segdat2[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) { ESP=oldsp; return; }
1489 if (output) pclog("Segment data %04X %04X %04X %04X\n", segdat2[0], segdat2[1], segdat2[2], segdat2[3]);
1490 // if (((newss & 3) != DPL) || (DPL2 != DPL))
1491 if ((newss & 3) != (seg & 3))
1492 {
1493 pclog("RETF loading SS with wrong permissions %i %i %04X %04X\n", newss & 3, seg & 3, newss, seg);
1494 ESP=oldsp;
1495 // output = 3;
1496 // dumpregs();
1497 // exit(-1);
1498 x86gpf(NULL,newss&~3);
1499 return;
1500 }
1501 if ((segdat2[2]&0x1A00)!=0x1200)
1502 {
1503 pclog("RETF loading SS wrong type\n");
1504 ESP=oldsp;
1505 // dumpregs();
1506 // exit(-1);
1507 x86gpf(NULL,newss&~3);
1508 return;
1509 }
1510 if (!(segdat2[2]&0x8000))
1511 {
1512 pclog("RETF loading SS not present\n");
1513 ESP=oldsp;
1514 x86np("RETF loading SS not present\n", newss & 0xfffc);
1515 return;
1516 }
1517 if (DPL2 != (seg & 3))
1518 {
1519 pclog("RETF loading SS with wrong permissions2 %i %i %04X %04X\n", DPL2, seg & 3, newss, seg);
1520 ESP=oldsp;
1521 x86gpf(NULL,newss&~3);
1522 return;
1523 }
1524 SS=newss;
1525 stack32=segdat2[3]&0x40;
1526 if (stack32) ESP=newsp;
1527 else SP=newsp;
1528 do_seg_load(&_ss, segdat2);
1530 #ifdef SEL_ACCESSED
1531 cpl_override = 1;
1532 writememw(0, addr+4, segdat2[2] | 0x100); /*Set accessed bit*/
1534 #ifdef CS_ACCESSED
1535 writememw(0, oaddr+4, segdat[2] | 0x100); /*Set accessed bit*/
1536 #endif
1537 cpl_override = 0;
1538 #endif
1539 /*Conforming segments don't change CPL, so CPL = RPL*/
1540 if (segdat[2]&0x400)
1541 segdat[2] = (segdat[2] & ~(3 << (5+8))) | ((seg & 3) << (5+8));
1543 pc=newpc;
1544 CS=seg;
1545 do_seg_load(&_cs, segdat);
1546 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
1547 use32=(segdat[3]&0x40)?0x300:0;
1549 if (stack32) ESP+=off;
1550 else SP+=off;
1552 check_seg_valid(&_ds);
1553 check_seg_valid(&_es);
1554 check_seg_valid(&_fs);
1555 check_seg_valid(&_gs);
1556 // pclog("CPL<RPL return to %04X:%08X %04X:%08X\n",CS,pc,SS,ESP);
1557 }
1558 }
1560 void restore_stack()
1561 {
1562 ss=oldss; _ss.limit=oldsslimit;
1563 }
1565 void pmodeint(int num, int soft)
1566 {
1567 uint16_t segdat[4],segdat2[4],segdat3[4];
1568 uint32_t addr, oaddr;
1569 uint16_t newss;
1570 uint32_t oldss,oldsp;
1571 int type;
1572 uint32_t newsp;
1573 uint16_t seg;
1574 int stack_changed=0;
1576 // if (!num) pclog("Pmode int 0 at %04X(%06X):%08X\n",CS,cs,pc);
1577 // pclog("Pmode int %02X %i %04X:%08X %04X:%08X %i\n",num,soft,CS,pc, SS, ESP, abrt);
1578 if (eflags&VM_FLAG && IOPL!=3 && soft)
1579 {
1580 if (output) pclog("V86 banned int\n");
1581 pclog("V86 banned int!\n");
1582 x86gpf(NULL,0);
1583 return;
1584 // dumpregs();
1585 // exit(-1);
1586 }
1587 addr=(num<<3);
1588 if (addr>=idt.limit)
1589 {
1590 if (num==8)
1591 {
1592 /*Triple fault - reset!*/
1593 pclog("Triple fault!\n");
1594 // output=1;
1595 softresetx86();
1596 }
1597 else if (num==0xD)
1598 {
1599 pclog("Double fault!\n");
1600 pmodeint(8,0);
1601 }
1602 else
1603 {
1604 pclog("INT out of range\n");
1605 x86gpf(NULL,(num*8)+2+(soft)?0:1);
1606 }
1607 if (output) pclog("addr >= IDT.limit\n");
1608 return;
1609 }
1610 addr+=idt.base;
1611 cpl_override=1;
1612 segdat[0]=readmemw(0,addr);
1613 segdat[1]=readmemw(2,addr);
1614 segdat[2]=readmemw(4,addr);
1615 segdat[3]=readmemw(6,addr); cpl_override=0; if (abrt) { pclog("Abrt reading from %08X\n",addr); return; }
1616 oaddr = addr;
1618 if (output) pclog("Addr %08X seg %04X %04X %04X %04X\n",addr,segdat[0],segdat[1],segdat[2],segdat[3]);
1619 if (!(segdat[2]&0x1F00))
1620 {
1621 //pclog("No seg\n");
1622 x86gpf(NULL,(num*8)+2);
1623 return;
1624 }
1625 if (DPL<CPL && soft)
1626 {
1627 //pclog("INT : DPL<CPL %04X:%08X %i %i %04X\n",CS,pc,DPL,CPL,segdat[2]);
1628 x86gpf(NULL,(num*8)+2);
1629 return;
1630 }
1631 type=segdat[2]&0x1F00;
1632 // if (output) pclog("Gate type %04X\n",type);
1633 switch (type)
1634 {
1635 case 0x600: case 0x700: case 0xE00: case 0xF00: /*Interrupt and trap gates*/
1636 intgatesize=(type>=0x800)?32:16;
1637 // if (output) pclog("Int gate %04X %i oldpc %04X pc %04X\n",type,intgatesize,oldpc,pc);
1638 if (!(segdat[2]&0x8000))
1639 {
1640 pclog("Int gate not present\n");
1641 x86np("Int gate not present\n", (num << 3) | 2);
1642 return;
1643 }
1644 seg=segdat[1];
1645 // pclog("Interrupt gate : %04X:%04X%04X\n",seg,segdat[3],segdat[0]);
1647 addr=seg&~7;
1648 if (seg&4)
1649 {
1650 if (addr>=ldt.limit)
1651 {
1652 pclog("Bigger than LDT limit %04X %04X INT\n",seg,gdt.limit);
1653 x86gpf(NULL,seg&~3);
1654 return;
1655 }
1656 addr+=ldt.base;
1657 }
1658 else
1659 {
1660 if (addr>=gdt.limit)
1661 {
1662 pclog("Bigger than GDT limit %04X %04X INT %i\n",seg,gdt.limit,ins);
1663 x86gpf(NULL,seg&~3);
1664 return;
1665 }
1666 addr+=gdt.base;
1667 }
1668 /* if ((seg&3) < CPL)
1669 {
1670 pclog("INT to higher level\n");
1671 x86gpf(NULL,seg&~3);
1672 return;
1673 }*/
1674 cpl_override=1;
1675 segdat2[0]=readmemw(0,addr);
1676 segdat2[1]=readmemw(0,addr+2);
1677 segdat2[2]=readmemw(0,addr+4);
1678 segdat2[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return;
1679 oaddr = addr;
1681 if (DPL2 > CPL)
1682 {
1683 pclog("INT to higher level 2\n");
1684 x86gpf(NULL,seg&~3);
1685 return;
1686 }
1687 //pclog("Type %04X\n",segdat2[2]);
1688 switch (segdat2[2]&0x1F00)
1689 {
1690 case 0x1800: case 0x1900: case 0x1A00: case 0x1B00: /*Non-conforming*/
1691 if (DPL2<CPL)
1692 {
1693 stack_changed=1;
1694 if (!(segdat2[2]&0x8000))
1695 {
1696 pclog("Int gate CS not present\n");
1697 x86np("Int gate CS not present\n", segdat[1] & 0xfffc);
1698 return;
1699 }
1700 if ((eflags&VM_FLAG) && DPL2)
1701 {
1702 pclog("V86 calling int gate, DPL != 0\n");
1703 x86gpf(NULL,segdat[1]&0xFFFC);
1704 return;
1705 }
1706 /*Load new stack*/
1707 oldss=SS;
1708 oldsp=ESP;
1709 cpl_override=1;
1710 if (tr.access&8)
1711 {
1712 addr = 4 + tr.base + (DPL2 * 8);
1713 newss=readmemw(0,addr+4);
1714 newsp=readmeml(0,addr);
1715 }
1716 else
1717 {
1718 addr = 2 + tr.base + (DPL2 * 8);
1719 newss=readmemw(0,addr+2);
1720 newsp=readmemw(0,addr);
1721 }
1722 cpl_override=0;
1723 if (!(newss&~3))
1724 {
1725 pclog("Int gate loading null SS\n");
1726 x86ss(NULL,newss&~3);
1727 return;
1728 }
1729 addr=newss&~7;
1730 if (newss&4)
1731 {
1732 if (addr>=ldt.limit)
1733 {
1734 pclog("Bigger than LDT limit %04X %04X PMODEINT SS\n",newss,gdt.limit);
1735 x86ss(NULL,newss&~3);
1736 return;
1737 }
1738 addr+=ldt.base;
1739 }
1740 else
1741 {
1742 if (addr>=gdt.limit)
1743 {
1744 pclog("Bigger than GDT limit %04X %04X CSC\n",newss,gdt.limit);
1745 x86ss(NULL,newss&~3);
1746 return;
1747 }
1748 addr+=gdt.base;
1749 }
1750 cpl_override=1;
1751 segdat3[0]=readmemw(0,addr);
1752 segdat3[1]=readmemw(0,addr+2);
1753 segdat3[2]=readmemw(0,addr+4);
1754 segdat3[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) return;
1755 if (((newss & 3) != DPL2) || (DPL3 != DPL2))
1756 {
1757 pclog("Int gate loading SS with wrong permissions\n");
1758 x86ss(NULL,newss&~3);
1759 return;
1760 }
1761 if ((segdat3[2]&0x1A00)!=0x1200)
1762 {
1763 pclog("Int gate loading SS wrong type\n");
1764 x86ss(NULL,newss&~3);
1765 return;
1766 }
1767 if (!(segdat3[2]&0x8000))
1768 {
1769 pclog("Int gate loading SS not present\n");
1770 x86np("Int gate loading SS not present\n", newss & 0xfffc);
1771 return;
1772 }
1773 SS=newss;
1774 stack32=segdat3[3]&0x40;
1775 if (stack32) ESP=newsp;
1776 else SP=newsp;
1777 do_seg_load(&_ss, segdat3);
1779 #ifdef CS_ACCESSED
1780 cpl_override = 1;
1781 writememw(0, addr+4, segdat3[2] | 0x100); /*Set accessed bit*/
1782 cpl_override = 0;
1783 #endif
1785 if (output) pclog("New stack %04X:%08X\n",SS,ESP);
1786 cpl_override=1;
1787 if (type>=0x800)
1788 {
1789 // if (output) pclog("Push 32 %i\n",eflags&VM_FLAG);
1790 if (eflags & VM_FLAG)
1791 {
1792 PUSHL(GS);
1793 PUSHL(FS);
1794 PUSHL(DS);
1795 PUSHL(ES); if (abrt) return;
1796 loadseg(0,&_ds);
1797 loadseg(0,&_es);
1798 loadseg(0,&_fs);
1799 loadseg(0,&_gs);
1800 }
1801 PUSHL(oldss);
1802 PUSHL(oldsp);
1803 PUSHL(flags|(eflags<<16));
1804 // if (soft) pclog("Pushl CS %08X\n", CS);
1805 PUSHL(CS);
1806 // if (soft) pclog("Pushl PC %08X\n", pc);
1807 PUSHL(pc); if (abrt) return;
1808 // if (output) pclog("32Stack %04X:%08X\n",SS,ESP);
1809 }
1810 else
1811 {
1812 // if (output) pclog("Push 16\n");
1813 PUSHW(oldss);
1814 PUSHW(oldsp);
1815 PUSHW(flags);
1816 // if (soft) pclog("Pushw CS %04X\n", CS);
1817 PUSHW(CS);
1818 // if (soft) pclog("Pushw pc %04X\n", pc);
1819 PUSHW(pc); if (abrt) return;
1820 // if (output) pclog("16Stack %04X:%08X\n",SS,ESP);
1821 }
1822 cpl_override=0;
1823 _cs.access=0;
1824 // pclog("Non-confirming int gate, CS = %04X\n");
1825 break;
1826 }
1827 else if (DPL2!=CPL)
1828 {
1829 pclog("Non-conforming int gate DPL != CPL\n");
1830 x86gpf(NULL,seg&~3);
1831 return;
1832 }
1833 case 0x1C00: case 0x1D00: case 0x1E00: case 0x1F00: /*Conforming*/
1834 if (!(segdat2[2]&0x8000))
1835 {
1836 pclog("Int gate CS not present\n");
1837 x86np("Int gate CS not present\n", segdat[1] & 0xfffc);
1838 return;
1839 }
1840 if ((eflags & VM_FLAG) && DPL2<CPL)
1841 {
1842 pclog("Int gate V86 mode DPL2<CPL\n");
1843 x86gpf(NULL,seg&~3);
1844 return;
1845 }
1846 // if (!stack_changed && ssegs) restore_stack();
1847 if (type>0x800)
1848 {
1849 PUSHL(flags|(eflags<<16));
1850 // if (soft) pclog("Pushlc CS %08X\n", CS);
1851 PUSHL(CS);
1852 // if (soft) pclog("Pushlc PC %08X\n", pc);
1853 PUSHL(pc); if (abrt) return;
1854 }
1855 else
1856 {
1857 PUSHW(flags);
1858 // if (soft) pclog("Pushwc CS %04X\n", CS);
1859 PUSHW(CS);
1860 // if (soft) pclog("Pushwc PC %04X\n", pc);
1861 PUSHW(pc); if (abrt) return;
1862 }
1863 break;
1864 default:
1865 pclog("Int gate CS not code segment - %04X %04X %04X %04X\n",segdat2[0],segdat2[1],segdat2[2],segdat2[3]);
1866 x86gpf(NULL,seg&~3);
1867 return;
1868 }
1869 CS=(seg&~3)|CPL;
1870 // pclog("New CS = %04X\n",CS);
1871 do_seg_load(&_cs, segdat2);
1872 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
1873 if (type>0x800) pc=segdat[0]|(segdat[3]<<16);
1874 else pc=segdat[0];
1875 use32=(segdat2[3]&0x40)?0x300:0;
1876 // pclog("Int gate done!\n");
1878 #ifdef CS_ACCESSED
1879 cpl_override = 1;
1880 writememw(0, oaddr+4, segdat2[2] | 0x100); /*Set accessed bit*/
1881 cpl_override = 0;
1882 #endif
1884 eflags&=~VM_FLAG;
1885 if (!(type&0x100))
1886 {
1887 flags&=~I_FLAG;
1888 // pclog("INT %02X disabling interrupts %i\n",num,soft);
1889 }
1890 flags&=~(T_FLAG|NT_FLAG);
1891 // if (output) pclog("Final Stack %04X:%08X\n",SS,ESP);
1892 break;
1894 case 0x500: /*Task gate*/
1895 // pclog("Task gate\n");
1896 seg=segdat[1];
1897 addr=seg&~7;
1898 if (seg&4)
1899 {
1900 if (addr>=ldt.limit)
1901 {
1902 pclog("Bigger than LDT limit %04X %04X INT\n",seg,gdt.limit);
1903 x86gpf(NULL,seg&~3);
1904 return;
1905 }
1906 addr+=ldt.base;
1907 }
1908 else
1909 {
1910 if (addr>=gdt.limit)
1911 {
1912 pclog("Bigger than GDT limit %04X %04X INT %i\n",seg,gdt.limit,ins);
1913 x86gpf(NULL,seg&~3);
1914 return;
1915 }
1916 addr+=gdt.base;
1917 }
1918 cpl_override=1;
1919 segdat2[0]=readmemw(0,addr);
1920 segdat2[1]=readmemw(0,addr+2);
1921 segdat2[2]=readmemw(0,addr+4);
1922 segdat2[3]=readmemw(0,addr+6);
1923 cpl_override=0; if (abrt) return;
1924 if (!(segdat2[2]&0x8000))
1925 {
1926 pclog("Int task gate not present\n");
1927 x86np("Int task gate not present\n", segdat[1] & 0xfffc);
1928 return;
1929 }
1930 optype=INT;
1931 cpl_override=1;
1932 taskswitch286(seg,segdat2,segdat2[2]&0x800);
1933 cpl_override=0;
1934 break;
1936 default:
1937 pclog("Bad int gate type %04X %04X %04X %04X %04X\n",segdat[2]&0x1F00,segdat[0],segdat[1],segdat[2],segdat[3]);
1938 x86gpf(NULL,seg&~3);
1939 return;
1940 }
1941 }
1943 void pmodeiret(int is32)
1944 {
1945 uint32_t newsp;
1946 uint16_t newss;
1947 uint32_t tempflags,flagmask;
1948 uint32_t newpc;
1949 uint16_t segdat[4],segdat2[4];
1950 uint16_t segs[4];
1951 uint16_t seg;
1952 uint32_t addr, oaddr;
1953 uint32_t oldsp=ESP;
1954 if (is386 && (eflags&VM_FLAG))
1955 {
1956 // if (output) pclog("V86 IRET\n");
1957 if (IOPL!=3)
1958 {
1959 pclog("V86 IRET! IOPL!=3\n");
1960 x86gpf(NULL,0);
1961 return;
1962 }
1963 oxpc=pc;
1964 if (is32)
1965 {
1966 newpc=POPL();
1967 seg=POPL();
1968 tempflags=POPL(); if (abrt) return;
1969 }
1970 else
1971 {
1972 newpc=POPW();
1973 seg=POPW();
1974 tempflags=POPW(); if (abrt) return;
1975 }
1976 pc=newpc;
1977 _cs.base=seg<<4;
1978 _cs.limit=0xFFFF;
1979 _cs.limit_low = 0;
1980 _cs.limit_high = 0xffff;
1981 CS=seg;
1982 flags=(flags&0x3000)|(tempflags&0xCFD5)|2;
1983 return;
1984 }
1986 // pclog("IRET %i\n",is32);
1987 //flushmmucache();
1988 // if (output) pclog("Pmode IRET %04X:%04X ",CS,pc);
1990 if (flags&NT_FLAG)
1991 {
1992 // pclog("NT IRET\n");
1993 seg=readmemw(tr.base,0);
1994 addr=seg&~7;
1995 if (seg&4)
1996 {
1997 if (addr>=ldt.limit)
1998 {
1999 pclog("TS Bigger than LDT limit %04X %04X IRET\n",seg,gdt.limit);
2000 x86gpf(NULL,seg&~3);
2001 return;
2002 }
2003 addr+=ldt.base;
2004 }
2005 else
2006 {
2007 if (addr>=gdt.limit)
2008 {
2009 pclog("TS Bigger than GDT limit %04X %04X IRET\n",seg,gdt.limit);
2010 x86gpf(NULL,seg&~3);
2011 return;
2012 }
2013 addr+=gdt.base;
2014 }
2015 cpl_override=1;
2016 segdat[0]=readmemw(0,addr);
2017 segdat[1]=readmemw(0,addr+2);
2018 segdat[2]=readmemw(0,addr+4);
2019 segdat[3]=readmemw(0,addr+6);
2020 taskswitch286(seg,segdat,0);
2021 cpl_override=0;
2022 return;
2023 }
2024 oxpc=pc;
2025 flagmask=0xFFFF;
2026 if (CPL) flagmask&=~0x3000;
2027 if (IOPL<CPL) flagmask&=~0x200;
2028 // if (output) pclog("IRET %i %i %04X %i\n",CPL,IOPL,flagmask,is32);
2029 if (is32)
2030 {
2031 // pclog("POP\n");
2032 newpc=POPL();
2033 seg=POPL();
2034 tempflags=POPL(); if (abrt) { ESP = oldsp; return; }
2035 // if (output) pclog("IRETD pop %08X %08X %08X\n",newpc,seg,tempflags);
2036 if (is386 && ((tempflags>>16)&VM_FLAG))
2037 {
2038 // pclog("IRETD to V86\n");
2040 newsp=POPL();
2041 newss=POPL();
2042 segs[0]=POPL();
2043 segs[1]=POPL();
2044 segs[2]=POPL();
2045 segs[3]=POPL(); if (abrt) { ESP = oldsp; return; }
2046 // pclog("Pop stack %04X:%04X\n",newss,newsp);
2047 eflags=tempflags>>16;
2048 loadseg(segs[0],&_es);
2049 do_seg_v86_init(&_es);
2050 loadseg(segs[1],&_ds);
2051 do_seg_v86_init(&_ds);
2052 loadseg(segs[2],&_fs);
2053 do_seg_v86_init(&_fs);
2054 loadseg(segs[3],&_gs);
2055 do_seg_v86_init(&_gs);
2057 // pclog("V86 IRET %04X:%08X\n",SS,ESP);
2058 // output=3;
2060 pc=newpc;
2061 _cs.base=seg<<4;
2062 _cs.limit=0xFFFF;
2063 _cs.limit_low = 0;
2064 _cs.limit_high = 0xffff;
2065 CS=seg;
2066 _cs.access=3<<5;
2067 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
2069 ESP=newsp;
2070 loadseg(newss,&_ss);
2071 do_seg_v86_init(&_ss);
2072 use32=0;
2073 flags=(tempflags&0xFFD5)|2;
2075 // pclog("V86 IRET to %04X:%04X %04X:%04X %04X %04X %04X %04X %i\n",CS,pc,SS,SP,DS,ES,FS,GS,abrt);
2076 // if (CS==0xFFFF && pc==0xFFFFFFFF) timetolive=12;
2077 /* {
2078 dumpregs();
2079 exit(-1);
2080 }*/
2081 return;
2082 }
2083 }
2084 else
2085 {
2086 newpc=POPW();
2087 seg=POPW();
2088 tempflags=POPW(); if (abrt) { ESP = oldsp; return; }
2089 }
2090 // if (!is386) tempflags&=0xFFF;
2091 // pclog("Returned to %04X:%08X %04X %04X %i\n",seg,newpc,flags,tempflags, ins);
2092 if (!(seg&~3))
2093 {
2094 pclog("IRET CS=0\n");
2095 ESP = oldsp;
2096 // dumpregs();
2097 // exit(-1);
2098 x86gpf(NULL,0);
2099 return;
2100 }
2102 // if (output) pclog("IRET %04X:%08X\n",seg,newpc);
2103 addr=seg&~7;
2104 if (seg&4)
2105 {
2106 if (addr>=ldt.limit)
2107 {
2108 pclog("Bigger than LDT limit %04X %04X IRET\n",seg,gdt.limit);
2109 ESP = oldsp;
2110 x86gpf(NULL,seg&~3);
2111 return;
2112 }
2113 addr+=ldt.base;
2114 }
2115 else
2116 {
2117 if (addr>=gdt.limit)
2118 {
2119 pclog("Bigger than GDT limit %04X %04X IRET\n",seg,gdt.limit);
2120 ESP = oldsp;
2121 x86gpf(NULL,seg&~3);
2122 return;
2123 }
2124 addr+=gdt.base;
2125 }
2126 if ((seg&3) < CPL)
2127 {
2128 pclog("IRET to lower level\n");
2129 ESP = oldsp;
2130 x86gpf(NULL,seg&~3);
2131 return;
2132 }
2133 cpl_override=1;
2134 segdat[0]=readmemw(0,addr);
2135 segdat[1]=readmemw(0,addr+2);
2136 segdat[2]=readmemw(0,addr+4);
2137 segdat[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) { ESP = oldsp; return; }
2138 // pclog("Seg type %04X %04X\n",segdat[2]&0x1F00,segdat[2]);
2140 switch (segdat[2]&0x1F00)
2141 {
2142 case 0x1800: case 0x1900: case 0x1A00: case 0x1B00: /*Non-conforming code*/
2143 if ((seg&3) != DPL)
2144 {
2145 pclog("IRET NC DPL %04X %04X %04X %04X %04X\n", seg, segdat[0], segdat[1], segdat[2], segdat[3]);
2146 ESP = oldsp;
2147 // dumpregs();
2148 // exit(-1);
2149 x86gpf(NULL,seg&~3);
2150 return;
2151 }
2152 break;
2153 case 0x1C00: case 0x1D00: case 0x1E00: case 0x1F00: /*Conforming code*/
2154 if ((seg&3) < DPL)
2155 {
2156 pclog("IRET C DPL\n");
2157 ESP = oldsp;
2158 x86gpf(NULL,seg&~3);
2159 return;
2160 }
2161 break;
2162 default:
2163 pclog("IRET CS != code seg\n");
2164 ESP = oldsp;
2165 x86gpf(NULL,seg&~3);
2166 // dumpregs();
2167 // exit(-1);
2168 return;
2169 }
2170 if (!(segdat[2]&0x8000))
2171 {
2172 pclog("IRET CS not present %i %04X %04X %04X\n",ins, segdat[0], segdat[1], segdat[2]);
2173 ESP = oldsp;
2174 x86np("IRET CS not present\n", seg & 0xfffc);
2175 return;
2176 }
2177 // pclog("Seg %04X CPL %04X\n",seg,CPL);
2178 if ((seg&3) == CPL)
2179 {
2180 // pclog("Same level\n");
2181 CS=seg;
2182 do_seg_load(&_cs, segdat);
2183 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
2184 use32=(segdat[3]&0x40)?0x300:0;
2186 #ifdef CS_ACCESSED
2187 cpl_override = 1;
2188 writememw(0, addr+4, segdat[2] | 0x100); /*Set accessed bit*/
2189 cpl_override = 0;
2190 #endif
2191 }
2192 else /*Return to outer level*/
2193 {
2194 oaddr = addr;
2195 if (output) pclog("Outer level\n");
2196 if (is32)
2197 {
2198 newsp=POPL();
2199 newss=POPL(); if (abrt) { ESP = oldsp; return; }
2200 }
2201 else
2202 {
2203 newsp=POPW();
2204 newss=POPW(); if (abrt) { ESP = oldsp; return; }
2205 }
2207 if (output) pclog("IRET load stack %04X:%04X\n",newss,newsp);
2209 if (!(newss&~3))
2210 {
2211 pclog("IRET loading null SS\n");
2212 ESP = oldsp;
2213 x86gpf(NULL,newss&~3);
2214 return;
2215 }
2216 addr=newss&~7;
2217 if (newss&4)
2218 {
2219 if (addr>=ldt.limit)
2220 {
2221 pclog("Bigger than LDT limit %04X %04X PMODEIRET SS\n",newss,gdt.limit);
2222 ESP = oldsp;
2223 x86gpf(NULL,newss&~3);
2224 return;
2225 }
2226 addr+=ldt.base;
2227 }
2228 else
2229 {
2230 if (addr>=gdt.limit)
2231 {
2232 pclog("Bigger than GDT limit %04X %04X PMODEIRET\n",newss,gdt.limit);
2233 ESP = oldsp;
2234 x86gpf(NULL,newss&~3);
2235 return;
2236 }
2237 addr+=gdt.base;
2238 }
2239 cpl_override=1;
2240 segdat2[0]=readmemw(0,addr);
2241 segdat2[1]=readmemw(0,addr+2);
2242 segdat2[2]=readmemw(0,addr+4);
2243 segdat2[3]=readmemw(0,addr+6); cpl_override=0; if (abrt) { ESP = oldsp; return; }
2244 // pclog("IRET SS sd2 %04X\n",segdat2[2]);
2245 // if (((newss & 3) != DPL) || (DPL2 != DPL))
2246 if ((newss & 3) != (seg & 3))
2247 {
2248 pclog("IRET loading SS with wrong permissions %04X %04X\n", newss, seg);
2249 ESP = oldsp;
2250 // dumpregs();
2251 // exit(-1);
2252 x86gpf(NULL,newss&~3);
2253 return;
2254 }
2255 if ((segdat2[2]&0x1A00)!=0x1200)
2256 {
2257 pclog("IRET loading SS wrong type\n");
2258 ESP = oldsp;
2259 x86gpf(NULL,newss&~3);
2260 return;
2261 }
2262 if (DPL2 != (seg & 3))
2263 {
2264 pclog("IRET loading SS with wrong permissions2 %i %i %04X %04X\n", DPL2, seg & 3, newss, seg);
2265 ESP = oldsp;
2266 x86gpf(NULL,newss&~3);
2267 return;
2268 }
2269 if (!(segdat2[2]&0x8000))
2270 {
2271 pclog("IRET loading SS not present\n");
2272 ESP = oldsp;
2273 x86np("IRET loading SS not present\n", newss & 0xfffc);
2274 return;
2275 }
2276 SS=newss;
2277 stack32=segdat2[3]&0x40;
2278 if (stack32) ESP=newsp;
2279 else SP=newsp;
2280 do_seg_load(&_ss, segdat2);
2282 #ifdef SEL_ACCESSED
2283 cpl_override = 1;
2284 writememw(0, addr+4, segdat2[2] | 0x100); /*Set accessed bit*/
2286 #ifdef CS_ACCESSED
2287 writememw(0, oaddr+4, segdat[2] | 0x100); /*Set accessed bit*/
2288 #endif
2289 cpl_override = 0;
2290 #endif
2291 /*Conforming segments don't change CPL, so CPL = RPL*/
2292 if (segdat[2]&0x400)
2293 segdat[2] = (segdat[2] & ~(3 << (5+8))) | ((seg & 3) << (5+8));
2295 CS=seg;
2296 do_seg_load(&_cs, segdat);
2297 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
2298 use32=(segdat[3]&0x40)?0x300:0;
2300 check_seg_valid(&_ds);
2301 check_seg_valid(&_es);
2302 check_seg_valid(&_fs);
2303 check_seg_valid(&_gs);
2304 }
2305 pc=newpc;
2306 flags=(flags&~flagmask)|(tempflags&flagmask&0xFFD5)|2;
2307 if (is32) eflags=tempflags>>16;
2308 // pclog("done\n");
2309 }
2311 void taskswitch286(uint16_t seg, uint16_t *segdat, int is32)
2312 {
2313 uint32_t base;
2314 uint32_t limit;
2315 uint32_t templ;
2316 uint16_t tempw;
2318 uint32_t new_cr3=0;
2319 uint16_t new_es,new_cs,new_ss,new_ds,new_fs,new_gs;
2320 uint16_t new_ldt;
2322 uint32_t new_eax,new_ebx,new_ecx,new_edx,new_esp,new_ebp,new_esi,new_edi,new_pc,new_flags;
2324 uint32_t addr;
2326 uint16_t segdat2[4];
2328 //output=3;
2329 base=segdat[1]|((segdat[2]&0xFF)<<16)|((segdat[3]>>8)<<24);
2330 limit=segdat[0]|((segdat[3]&0xF)<<16);
2331 // pclog("286 Task switch! %04X:%04X\n",CS,pc);
2332 /// pclog("TSS %04X base %08X limit %04X old TSS %04X %08X %i\n",seg,base,limit,tr.seg,tr.base,ins);
2333 // / pclog("%04X %04X %04X %04X\n",segdat[0],segdat[1],segdat[2],segdat[3]);
2335 if (is386)
2336 {
2337 // if (output) pclog("32-bit TSS\n");
2339 new_cr3=readmeml(base,0x1C);
2340 new_pc=readmeml(base,0x20);
2341 new_flags=readmeml(base,0x24);
2343 new_eax=readmeml(base,0x28);
2344 new_ecx=readmeml(base,0x2C);
2345 new_edx=readmeml(base,0x30);
2346 new_ebx=readmeml(base,0x34);
2347 new_esp=readmeml(base,0x38);
2348 new_ebp=readmeml(base,0x3C);
2349 new_esi=readmeml(base,0x40);
2350 new_edi=readmeml(base,0x44);
2352 new_es=readmemw(base,0x48);
2353 // if (output) pclog("Read CS from %08X\n",base+0x4C);
2354 new_cs=readmemw(base,0x4C);
2355 new_ss=readmemw(base,0x50);
2356 new_ds=readmemw(base,0x54);
2357 new_fs=readmemw(base,0x58);
2358 new_gs=readmemw(base,0x5C);
2359 new_ldt=readmemw(base,0x60);
2361 if (abrt) return;
2362 if (optype==JMP || optype==INT)
2363 {
2364 if (tr.seg&4) tempw=readmemw(ldt.base,(tr.seg&~7)+4);
2365 else tempw=readmemw(gdt.base,(tr.seg&~7)+4);
2366 if (abrt) return;
2367 tempw&=~0x200;
2368 if (tr.seg&4) writememw(ldt.base,(tr.seg&~7)+4,tempw);
2369 else writememw(gdt.base,(tr.seg&~7)+4,tempw);
2370 }
2372 if (optype==IRET) flags&=~NT_FLAG;
2374 // if (output) pclog("Write PC %08X %08X\n",tr.base,pc);
2375 cpu_386_flags_rebuild();
2376 writememl(tr.base,0x1C,cr3);
2377 writememl(tr.base,0x20,pc);
2378 writememl(tr.base,0x24,flags|(eflags<<16));
2380 writememl(tr.base,0x28,EAX);
2381 writememl(tr.base,0x2C,ECX);
2382 writememl(tr.base,0x30,EDX);
2383 writememl(tr.base,0x34,EBX);
2384 writememl(tr.base,0x38,ESP);
2385 writememl(tr.base,0x3C,EBP);
2386 writememl(tr.base,0x40,ESI);
2387 writememl(tr.base,0x44,EDI);
2389 writememl(tr.base,0x48,ES);
2390 // if (output) pclog("Write CS %04X to %08X\n",CS,tr.base+0x4C);
2391 writememl(tr.base,0x4C,CS);
2392 writememl(tr.base,0x50,SS);
2393 writememl(tr.base,0x54,DS);
2394 writememl(tr.base,0x58,FS);
2395 writememl(tr.base,0x5C,GS);
2396 writememl(tr.base,0x60,ldt.seg);
2398 if (optype==INT)
2399 {
2400 writememl(base,0,tr.seg);
2401 new_flags|=NT_FLAG;
2402 }
2403 if (abrt) return;
2404 if (optype==JMP || optype==INT)
2405 {
2406 if (tr.seg&4) tempw=readmemw(ldt.base,(seg&~7)+4);
2407 else tempw=readmemw(gdt.base,(seg&~7)+4);
2408 if (abrt) return;
2409 tempw|=0x200;
2410 if (tr.seg&4) writememw(ldt.base,(seg&~7)+4,tempw);
2411 else writememw(gdt.base,(seg&~7)+4,tempw);
2412 }
2416 cr3=new_cr3;
2417 // pclog("TS New CR3 %08X\n",cr3);
2418 flushmmucache();
2422 pc=new_pc;
2423 // if (output) pclog("New pc %08X\n",new_pc);
2424 flags=new_flags;
2425 eflags=new_flags>>16;
2426 cpu_386_flags_extract();
2428 // if (output) pclog("Load LDT %04X\n",new_ldt);
2429 ldt.seg=new_ldt;
2430 templ=(ldt.seg&~7)+gdt.base;
2431 // if (output) pclog("Load from %08X %08X\n",templ,gdt.base);
2432 ldt.limit=readmemw(0,templ);
2433 if (readmemb(templ+6)&0x80)
2434 {
2435 ldt.limit<<=12;
2436 ldt.limit|=0xFFF;
2437 }
2438 ldt.base=(readmemw(0,templ+2))|(readmemb(templ+4)<<16)|(readmemb(templ+7)<<24);
2439 // if (output) pclog("Limit %04X Base %08X\n",ldt.limit,ldt.base);
2442 if (eflags&VM_FLAG)
2443 {
2444 pclog("Task switch V86!\n");
2445 x86gpf(NULL,0);
2446 return;
2447 }
2449 if (!(new_cs&~3))
2450 {
2451 pclog("TS loading null CS\n");
2452 x86gpf(NULL,0);
2453 return;
2454 }
2455 addr=new_cs&~7;
2456 if (new_cs&4)
2457 {
2458 if (addr>=ldt.limit)
2459 {
2460 pclog("Bigger than LDT limit %04X %04X %04X TS\n",new_cs,ldt.limit,addr);
2461 x86gpf(NULL,0);
2462 return;
2463 }
2464 addr+=ldt.base;
2465 }
2466 else
2467 {
2468 if (addr>=gdt.limit)
2469 {
2470 pclog("Bigger than GDT limit %04X %04X TS\n",new_cs,gdt.limit);
2471 x86gpf(NULL,0);
2472 return;
2473 }
2474 addr+=gdt.base;
2475 }
2476 segdat2[0]=readmemw(0,addr);
2477 segdat2[1]=readmemw(0,addr+2);
2478 segdat2[2]=readmemw(0,addr+4);
2479 segdat2[3]=readmemw(0,addr+6);
2480 if (!(segdat2[2]&0x8000))
2481 {
2482 pclog("TS loading CS not present\n");
2483 x86np("TS loading CS not present\n", new_cs & 0xfffc);
2484 return;
2485 }
2486 switch (segdat2[2]&0x1F00)
2487 {
2488 case 0x1800: case 0x1900: case 0x1A00: case 0x1B00: /*Non-conforming*/
2489 if ((new_cs&3) != DPL2)
2490 {
2491 pclog("TS load CS non-conforming RPL != DPL");
2492 x86gpf(NULL,new_cs&~3);
2493 return;
2494 }
2495 break;
2496 case 0x1C00: case 0x1D00: case 0x1E00: case 0x1F00: /*Conforming*/
2497 if ((new_cs&3) < DPL2)
2498 {
2499 pclog("TS load CS non-conforming RPL < DPL");
2500 x86gpf(NULL,new_cs&~3);
2501 return;
2502 }
2503 break;
2504 default:
2505 pclog("TS load CS not code segment\n");
2506 x86gpf(NULL,new_cs&~3);
2507 return;
2508 }
2510 // if (output) pclog("new_cs %04X\n",new_cs);
2511 CS=new_cs;
2512 do_seg_load(&_cs, segdat2);
2513 if (CPL==3 && oldcpl!=3) flushmmucache_cr3();
2514 use32=(segdat2[3]&0x40)?0x300:0;
2516 EAX=new_eax;
2517 ECX=new_ecx;
2518 EDX=new_edx;
2519 EBX=new_ebx;
2520 ESP=new_esp;
2521 EBP=new_ebp;
2522 ESI=new_esi;
2523 EDI=new_edi;
2525 if (output) pclog("Load ES %04X\n",new_es);
2526 loadseg(new_es,&_es);
2527 if (output) pclog("Load SS %04X\n",new_ss);
2528 loadseg(new_ss,&_ss);
2529 if (output) pclog("Load DS %04X\n",new_ds);
2530 loadseg(new_ds,&_ds);
2531 if (output) pclog("Load FS %04X\n",new_fs);
2532 loadseg(new_fs,&_fs);
2533 if (output) pclog("Load GS %04X\n",new_gs);
2534 loadseg(new_gs,&_gs);
2536 if (output) pclog("Resuming at %04X:%08X\n",CS,pc);
2537 }
2538 else
2539 {
2540 pclog("16-bit TSS\n");
2541 resetx86();
2542 //exit(-1);
2543 }
2546 tr.seg=seg;
2547 tr.base=base;
2548 tr.limit=limit;
2549 tr.access=segdat[2]>>8;
2550 }
