Diff for /np2/i386c/ia32/ctrlxfer.c between versions 1.2 and 1.3

version 1.2, 2004/01/13 16:37:42 version 1.3, 2004/01/23 14:33:26
Line 34 Line 34
 #include "ctrlxfer.h"  #include "ctrlxfer.h"
   
   
   static void check_segreg(void);
   
   /*------------------------------------------------------------------------------
    * JMPfar_pm
    */
   static void JMPfar_pm_code_segment(selector_t *jmp_sel, DWORD new_ip);
   static void JMPfar_pm_call_gate(selector_t *jmp_sel);
   static void JMPfar_pm_task_gate(selector_t *jmp_sel);
   static void JMPfar_pm_tss(selector_t *jmp_sel);
   
 void  void
 JMPfar_pm(WORD selector, DWORD new_ip)  JMPfar_pm(WORD selector, DWORD new_ip)
 {  {
         selector_t jmp_sel;          selector_t jmp_sel;
         selector_t sel2;  
         int rv;          int rv;
   
         VERBOSE(("JMPfar_pm: EIP = 0x%08x, selector = 0x%04x, new_ip = 0x%08x", CPU_PREV_EIP, selector, new_ip));          VERBOSE(("JMPfar_pm: old EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_PREV_EIP, CPU_SS, CPU_ESP));
           VERBOSE(("JMPfar_pm: selector = 0x%04x, new_ip = 0x%08x", selector, new_ip));
         /*  
          * IF effective address in the CS, DS, ES, FS, GS, or SS segment is illegal  
          *     OR segment selector in target operand null  
          *     THEN #GP(0);  
          * FI;  
          */  
         /* XXX */  
   
         rv = parse_selector(&jmp_sel, selector);          rv = parse_selector(&jmp_sel, selector);
         if (rv < 0) {          if (rv < 0) {
Line 58  JMPfar_pm(WORD selector, DWORD new_ip) Line 60  JMPfar_pm(WORD selector, DWORD new_ip)
         }          }
   
         if (jmp_sel.desc.s) {          if (jmp_sel.desc.s) {
                 /* code segment descriptor */  
                 VERBOSE(("JMPfar_pm: code or data segment descriptor"));                  VERBOSE(("JMPfar_pm: code or data segment descriptor"));
   
                   /* check segment type */
                 if (!jmp_sel.desc.u.seg.c) {                  if (!jmp_sel.desc.u.seg.c) {
                         /* data segment */                          /* data segment */
                         VERBOSE(("JMPfar_pm: data segment"));                          VERBOSE(("JMPfar_pm: data segment"));
                         EXCEPTION(GP_EXCEPTION, jmp_sel.idx);                          EXCEPTION(GP_EXCEPTION, jmp_sel.idx);
                 }                  }
   
                 /* check privilege level */                  /* code segment descriptor */
                 if (!jmp_sel.desc.u.seg.ec) {                  JMPfar_pm_code_segment(&jmp_sel, new_ip);
                         VERBOSE(("NONCONFORMING-CODE-SEGMENT"));  
                         /* 下巻 p.119 4.8.1.1. */  
                         if ((jmp_sel.rpl > CPU_STAT_CPL)  
                          || (jmp_sel.desc.dpl != CPU_STAT_CPL)) {  
                                 VERBOSE(("JMPfar_pm: RPL(%d) > CPL(%d) or DPL(%d) != CPL(%d)", jmp_sel.rpl, CPU_STAT_CPL, jmp_sel.desc.dpl, CPU_STAT_CPL));  
                                 EXCEPTION(GP_EXCEPTION, jmp_sel.idx);  
                         }  
                 } else {  
                         VERBOSE(("CONFORMING-CODE-SEGMENT"));  
                         /* 下巻 p.120 4.8.1.2. */  
                         if (jmp_sel.desc.dpl > CPU_STAT_CPL) {  
                                 VERBOSE(("JMPfar_pm: DPL(%d) > CPL(%d)", jmp_sel.desc.dpl, CPU_STAT_CPL));  
                                 EXCEPTION(GP_EXCEPTION, jmp_sel.idx);  
                         }  
                 }  
   
                 /* not present */  
                 if (selector_is_not_present(&jmp_sel)) {  
                         VERBOSE(("JMPfar_pm: selector is not present"));  
                         EXCEPTION(NP_EXCEPTION, jmp_sel.idx);  
                 }  
   
                 if (!CPU_INST_OP32) {  
                         new_ip &= 0xffff;  
                 }  
   
                 /* out of range */  
                 if (new_ip > jmp_sel.desc.u.seg.limit) {  
                         VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, jmp_sel.desc.u.seg.limit));  
                         EXCEPTION(GP_EXCEPTION, 0);  
                 }  
   
                 VERBOSE(("JMPfar_pm: new CS = %04x, EIP = %08x", jmp_sel.selector, new_ip));  
                 load_cs(jmp_sel.selector, &jmp_sel.desc, CPU_STAT_CPL);  
                 SET_EIP(new_ip);  
         } else {          } else {
                 /* system descriptor */                  /* system descriptor */
                 VERBOSE(("JMPfar_pm: system descriptor"));                  VERBOSE(("JMPfar_pm: system descriptor"));
Line 111  JMPfar_pm(WORD selector, DWORD new_ip) Line 78  JMPfar_pm(WORD selector, DWORD new_ip)
                 switch (jmp_sel.desc.type) {                  switch (jmp_sel.desc.type) {
                 case CPU_SYSDESC_TYPE_CALL_16:                  case CPU_SYSDESC_TYPE_CALL_16:
                 case CPU_SYSDESC_TYPE_CALL_32:                  case CPU_SYSDESC_TYPE_CALL_32:
                         VERBOSE(("CALL-GATE"));                          JMPfar_pm_call_gate(&jmp_sel);
                           break;
   
                         /* check privilege level */                  case CPU_SYSDESC_TYPE_TASK:
                         if ((jmp_sel.desc.dpl < CPU_STAT_CPL)                          JMPfar_pm_task_gate(&jmp_sel);
                          || (jmp_sel.desc.dpl < jmp_sel.rpl)) {                          break;
                                 VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d) or DPL(%d) < RPL(%d)", jmp_sel.desc.dpl, CPU_STAT_CPL, jmp_sel.desc.dpl, jmp_sel.rpl));  
                                 EXCEPTION(GP_EXCEPTION, jmp_sel.idx);  
                         }  
   
                         /* not present */                  case CPU_SYSDESC_TYPE_TSS_16:
                         if (selector_is_not_present(&jmp_sel)) {                  case CPU_SYSDESC_TYPE_TSS_32:
                                 VERBOSE(("JMPfar_pm: selector is not present"));                          JMPfar_pm_tss(&jmp_sel);
                                 EXCEPTION(NP_EXCEPTION, jmp_sel.idx);                          break;
                         }  
   
                         /* parse call gate selector */                  case CPU_SYSDESC_TYPE_TSS_BUSY_16:
                         rv = parse_selector(&sel2, jmp_sel.desc.u.gate.selector);                  case CPU_SYSDESC_TYPE_TSS_BUSY_32:
                         if (rv < 0) {                          VERBOSE(("JMPfar_pm: task is busy"));
                                 VERBOSE(("JMPfar_pm: parse_selector (selector = %04x, rv = %d)", jmp_sel.desc.u.gate.selector, rv));                          /*FALLTHROUGH*/
                                 EXCEPTION(GP_EXCEPTION, sel2.idx);                  default:
                         }                          VERBOSE(("JMPfar_pm: invalid descriptor type (type = %d)", jmp_sel.desc.type));
                           EXCEPTION(GP_EXCEPTION, jmp_sel.idx);
                           break;
                   }
           }
           VERBOSE(("JMPfar_pm: new EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP));
   }
   
                         /* check code segment descriptor */  /*---
                         if (!sel2.desc.s || !sel2.desc.u.seg.c) {   * JMPfar: code segment
                                 VERBOSE(("JMPfar_pm: not code segment (%s, %s)", sel2.desc.s ? "code/data" : "system", sel2.desc.u.seg.c ? "code" : "data"));   */
                                 EXCEPTION(GP_EXCEPTION, sel2.idx);  static void
                         }  JMPfar_pm_code_segment(selector_t *code_sel, DWORD new_ip)
   {
   
                         /* check privilege level */          /* check privilege level */
                         if (!sel2.desc.u.seg.ec) {          if (!code_sel->desc.u.seg.ec) {
                                 /* 下巻 p.119 4.8.1.1. */                  VERBOSE(("JMPfar_pm: NON-CONFORMING-CODE-SEGMENT"));
                                 if ((sel2.rpl > CPU_STAT_CPL)                  /* 下巻 p.119 4.8.1.1. */
                                  || (sel2.desc.dpl != CPU_STAT_CPL)) {                  if (code_sel->rpl > CPU_STAT_CPL) {
                                         VERBOSE(("JMPfar_pm: RPL(%d) > CPL(%d) or DPL(%d) != CPL(%d)", sel2.rpl, CPU_STAT_CPL, sel2.desc.dpl, CPU_STAT_CPL));                          VERBOSE(("JMPfar_pm: RPL(%d) > CPL(%d)", code_sel->rpl, CPU_STAT_CPL));
                                         EXCEPTION(GP_EXCEPTION, sel2.idx);                          EXCEPTION(GP_EXCEPTION, code_sel->idx);
                                 }                  }
                         } else {                  if (code_sel->desc.dpl != CPU_STAT_CPL) {
                                 /* 下巻 p.120 4.8.1.2. */                          VERBOSE(("JMPfar_pm: DPL(%d) != CPL(%d)", code_sel->desc.dpl, CPU_STAT_CPL));
                                 if (sel2.desc.dpl > CPU_STAT_CPL) {                          EXCEPTION(GP_EXCEPTION, code_sel->idx);
                                         VERBOSE(("JMPfar_pm: DPL(%d) > CPL(%d)", sel2.desc.dpl, CPU_STAT_CPL));                  }
                                         EXCEPTION(GP_EXCEPTION, sel2.idx);          } else {
                                 }                  VERBOSE(("JMPfar_pm: CONFORMING-CODE-SEGMENT"));
                         }                  /* 下巻 p.120 4.8.1.2. */
                   if (code_sel->desc.dpl > CPU_STAT_CPL) {
                           VERBOSE(("JMPfar_pm: DPL(%d) > CPL(%d)", code_sel->desc.dpl, CPU_STAT_CPL));
                           EXCEPTION(GP_EXCEPTION, code_sel->idx);
                   }
           }
   
                         /* not present */          /* not present */
                         if (selector_is_not_present(&sel2)) {          if (selector_is_not_present(code_sel)) {
                                 VERBOSE(("JMPfar_pm: selector is not present"));                  VERBOSE(("JMPfar_pm: selector is not present"));
                                 EXCEPTION(NP_EXCEPTION, sel2.idx);                  EXCEPTION(NP_EXCEPTION, code_sel->idx);
                         }          }
   
                         new_ip = jmp_sel.desc.u.gate.offset;          /* out of range */
                         if (jmp_sel.desc.type == CPU_SYSDESC_TYPE_CALL_16) {          if (new_ip > code_sel->desc.u.seg.limit) {
                                 new_ip &= 0xffff;                  VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, code_sel->desc.u.seg.limit));
                         }                  EXCEPTION(GP_EXCEPTION, 0);
           }
   
                         /* out of range */          load_cs(code_sel->selector, &code_sel->desc, CPU_STAT_CPL);
                         if (new_ip > sel2.desc.u.seg.limit) {          SET_EIP(new_ip);
                                 VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, sel2.desc.u.seg.limit));  }
                                 EXCEPTION(GP_EXCEPTION, 0);  
                         }  
   
                         VERBOSE(("JMPfar_pm: new CS = %04x, EIP = %08x", sel2.selector, new_ip));  /*---
                         load_cs(sel2.selector, &sel2.desc, CPU_STAT_CPL);   * JMPfar: call gate
                         SET_EIP(new_ip);   */
                         break;  static void
   JMPfar_pm_call_gate(selector_t *callgate_sel)
   {
           selector_t cs_sel;
           int rv;
   
                 /*          VERBOSE(("JMPfar_pm: CALL-GATE"));
                  * 中巻 p.373 JMP 命令  
                  *  
                  * JMP 命令でタスク・スイッチを実行するときは EFLAGS レジスタに  
                  * ネストされたタスク・フラグ (NT) がセットされず、新しい TSS の  
                  * 以前のタスク・リンク・フィールドに前のタスクの TSS セレクタが  
                  * ロードされないので注意されたい。したがって、前のタスクへの  
                  * リターンは IRET 命令の実行では実現できない。JMP 命令で  
                  * タスク・スイッチを実行するのは、その点で CALL 命令と異なる。  
                  * すなわち、CALL 命令は NT フラグをセットし、以前の  
                  * タスク・リンク情報をセーブするので、IRET 命令でのコール元  
                  * タスクへのリターンが可能になる。  
                  */  
                 case CPU_SYSDESC_TYPE_TASK:  
                         VERBOSE(("TASK-GATE"));  
   
                         /* check privilege level */          /* check privilege level */
                         if ((jmp_sel.desc.dpl < CPU_STAT_CPL)          if (callgate_sel->desc.dpl < CPU_STAT_CPL) {
                          || (jmp_sel.desc.dpl < jmp_sel.rpl)) {                  VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d)", callgate_sel->desc.dpl, CPU_STAT_CPL));
                                 VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d) or DPL(%d) < RPL(%d)", jmp_sel.desc.dpl, CPU_STAT_CPL, jmp_sel.desc.dpl, jmp_sel.rpl));                  EXCEPTION(GP_EXCEPTION, callgate_sel->idx);
                                 EXCEPTION(GP_EXCEPTION, jmp_sel.idx);          }
                         }          if (callgate_sel->desc.dpl < callgate_sel->rpl) {
                   VERBOSE(("JMPfar_pm: DPL(%d) < RPL(%d)",  callgate_sel->desc.dpl, callgate_sel->rpl));
                   EXCEPTION(GP_EXCEPTION, callgate_sel->idx);
           }
   
                         /* not present */          /* not present */
                         if (selector_is_not_present(&jmp_sel)) {          if (selector_is_not_present(callgate_sel)) {
                                 VERBOSE(("JMPfar_pm: selector is not present"));                  VERBOSE(("JMPfar_pm: selector is not present"));
                                 EXCEPTION(NP_EXCEPTION, jmp_sel.idx);                  EXCEPTION(NP_EXCEPTION, callgate_sel->idx);
                         }          }
   
                         /* parse call tss selector */          /* parse code segment selector */
                         rv = parse_selector(&sel2, jmp_sel.desc.u.gate.selector);          rv = parse_selector(&cs_sel, callgate_sel->desc.u.gate.selector);
                         if (rv < 0 || sel2.ldt) {          if (rv < 0) {
                                 VERBOSE(("JMPfar_pm: parse_selector (selector = %04x, rv = %d, %s)", jmp_sel.desc.u.gate.selector, rv, sel2.ldt ? "LDT" : "GDT"));                  VERBOSE(("JMPfar_pm: parse_selector (selector = %04x, rv = %d)", callgate_sel->desc.u.gate.selector, rv));
                                 EXCEPTION(GP_EXCEPTION, sel2.idx);                  EXCEPTION(GP_EXCEPTION, cs_sel.idx);
                         }          }
   
                         /* check descriptor type */          /* check segment type */
                         switch (sel2.desc.type) {          if (!cs_sel.desc.s) {
                         case CPU_SYSDESC_TYPE_TSS_16:                  VERBOSE(("JMPfar_pm: code segment is system segment"));
                         case CPU_SYSDESC_TYPE_TSS_32:                  EXCEPTION(GP_EXCEPTION, cs_sel.idx);
                                 break;          }
           if (!cs_sel.desc.u.seg.c) {
                         default:                  VERBOSE(("JMPfar_pm: code segment is data segment"));
                                 VERBOSE(("JMPfar_pm: invalid descriptor type (type = %d)", sel2.desc.type));                  EXCEPTION(GP_EXCEPTION, cs_sel.idx);
                                 EXCEPTION(GP_EXCEPTION, sel2.idx);          }
                                 break;  
                         }  
   
                         /* not present */          /* check privilege level */
                         if (selector_is_not_present(&sel2)) {          if (!cs_sel.desc.u.seg.ec) {
                                 VERBOSE(("JMPfar_pm: selector is not present"));                  /* 下巻 p.119 4.8.1.1. */
                                 EXCEPTION(NP_EXCEPTION, sel2.idx);                  if (cs_sel.rpl > CPU_STAT_CPL) {
                         }                          VERBOSE(("JMPfar_pm: RPL(%d) > CPL(%d)", cs_sel.rpl, CPU_STAT_CPL));
                           EXCEPTION(GP_EXCEPTION, cs_sel.idx);
                   }
                   if (cs_sel.desc.dpl != CPU_STAT_CPL) {
                           VERBOSE(("JMPfar_pm: DPL(%d) != CPL(%d)", cs_sel.desc.dpl, CPU_STAT_CPL));
                           EXCEPTION(GP_EXCEPTION, cs_sel.idx);
                   }
           } else {
                   /* 下巻 p.120 4.8.1.2. */
                   if (cs_sel.desc.dpl > CPU_STAT_CPL) {
                           VERBOSE(("JMPfar_pm: DPL(%d) > CPL(%d)", cs_sel.desc.dpl, CPU_STAT_CPL));
                           EXCEPTION(GP_EXCEPTION, cs_sel.idx);
                   }
           }
   
                         task_switch(&sel2, TASK_SWITCH_JMP);          /* not present */
           if (selector_is_not_present(&cs_sel)) {
                   VERBOSE(("JMPfar_pm: selector is not present"));
                   EXCEPTION(NP_EXCEPTION, cs_sel.idx);
           }
   
                         /* out of range */          /* out of range */
                         if (CPU_EIP > CPU_STAT_CS_LIMIT) {          if (callgate_sel->desc.u.gate.offset > cs_sel.desc.u.seg.limit) {
                                 VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", CPU_EIP, CPU_STAT_CS_LIMIT));                  VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", callgate_sel->desc.u.gate.offset, cs_sel.desc.u.seg.limit));
                                 EXCEPTION(GP_EXCEPTION, 0);                  EXCEPTION(GP_EXCEPTION, 0);
                         }          }
                         break;  
   
                 case CPU_SYSDESC_TYPE_TSS_16:          load_cs(cs_sel.selector, &cs_sel.desc, CPU_STAT_CPL);
                 case CPU_SYSDESC_TYPE_TSS_32:          SET_EIP(callgate_sel->desc.u.gate.offset);
                         VERBOSE(("TASK-STATE-SEGMENT"));  }
   
                         /* check privilege level */  /*---
                         if ((jmp_sel.desc.dpl < CPU_STAT_CPL)   * JMPfar: task gate
                          || (jmp_sel.desc.dpl < jmp_sel.rpl)) {   */
                                 VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d) or DPL(%d) < RPL(%d)", jmp_sel.desc.dpl, CPU_STAT_CPL, jmp_sel.desc.dpl, jmp_sel.rpl));  static void
                                 EXCEPTION(TS_EXCEPTION, jmp_sel.idx);  JMPfar_pm_task_gate(selector_t *taskgate_sel)
                         }  {
           selector_t tss_sel;
           int rv;
   
                         /* not present */          VERBOSE(("JMPfar_pm: TASK-GATE"));
                         if (selector_is_not_present(&jmp_sel)) {  
                                 VERBOSE(("JMPfar_pm: selector is not present"));  
                                 EXCEPTION(NP_EXCEPTION, jmp_sel.idx);  
                         }  
   
                         task_switch(&jmp_sel, TASK_SWITCH_JMP);          /*
            * 中巻 p.373 JMP 命令
            *
            * JMP 命令でタスク・スイッチを実行するときは EFLAGS レジスタに
            * ネストされたタスク・フラグ (NT) がセットされず、新しい TSS の
            * 以前のタスク・リンク・フィールドに前のタスクの TSS セレクタが
            * ロードされないので注意されたい。したがって、前のタスクへの
            * リターンは IRET 命令の実行では実現できない。JMP 命令で
            * タスク・スイッチを実行するのは、その点で CALL 命令と異なる。
            * すなわち、CALL 命令は NT フラグをセットし、以前の
            * タスク・リンク情報をセーブするので、IRET 命令でのコール元
            * タスクへのリターンが可能になる。
            */
   
                         /* out of range */          /* check privilege level */
                         if (CPU_EIP > CPU_STAT_CS_LIMIT) {          if (taskgate_sel->desc.dpl < CPU_STAT_CPL) {
                                 VERBOSE(("JMPfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", CPU_EIP, CPU_STAT_CS_LIMIT));                  VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d)", taskgate_sel->desc.dpl, CPU_STAT_CPL));
                                 EXCEPTION(GP_EXCEPTION, 0);                  EXCEPTION(GP_EXCEPTION, taskgate_sel->idx);
                         }          }
                         break;          if (taskgate_sel->desc.dpl < taskgate_sel->rpl) {
                   VERBOSE(("JMPfar_pm: DPL(%d) < RPL(%d)", taskgate_sel->desc.dpl, taskgate_sel->rpl));
                   EXCEPTION(GP_EXCEPTION, taskgate_sel->idx);
           }
   
                 case CPU_SYSDESC_TYPE_TSS_BUSY_16:          /* not present */
                 case CPU_SYSDESC_TYPE_TSS_BUSY_32:          if (selector_is_not_present(taskgate_sel)) {
                         VERBOSE(("JMPfar_pm: task is busy"));                  VERBOSE(("JMPfar_pm: selector is not present"));
                         /*FALLTHROUGH*/                  EXCEPTION(NP_EXCEPTION, taskgate_sel->idx);
                 default:          }
                         VERBOSE(("JMPfar_pm: invalid descriptor type (type = %d)", sel2.desc.type));  
                         EXCEPTION(GP_EXCEPTION, jmp_sel.idx);          /* parse tss selector */
                         break;          rv = parse_selector(&tss_sel, taskgate_sel->desc.u.gate.selector);
                 }          if (rv < 0 || tss_sel.ldt) {
                   VERBOSE(("JMPfar_pm: parse_selector (selector = %04x, rv = %d, %cDT)", taskgate_sel->desc.u.gate.selector, rv, tss_sel.ldt ? 'L' : 'G'));
                   EXCEPTION(GP_EXCEPTION, tss_sel.idx);
           }
   
           /* check descriptor type */
           switch (tss_sel.desc.type) {
           case CPU_SYSDESC_TYPE_TSS_16:
           case CPU_SYSDESC_TYPE_TSS_32:
                   break;
   
           case CPU_SYSDESC_TYPE_TSS_BUSY_16:
           case CPU_SYSDESC_TYPE_TSS_BUSY_32:
                   VERBOSE(("JMPfar_pm: task is busy"));
                   /*FALLTHROUGH*/
           default:
                   VERBOSE(("JMPfar_pm: invalid descriptor type (type = %d)", tss_sel.desc.type));
                   EXCEPTION(GP_EXCEPTION, tss_sel.idx);
                   break;
           }
   
           /* not present */
           if (selector_is_not_present(&tss_sel)) {
                   VERBOSE(("JMPfar_pm: selector is not present"));
                   EXCEPTION(NP_EXCEPTION, tss_sel.idx);
           }
   
           task_switch(&tss_sel, TASK_SWITCH_JMP);
   }
   
   /*---
    * JMPfar: TSS
    */
   static void
   JMPfar_pm_tss(selector_t *tss_sel)
   {
   
           VERBOSE(("JMPfar_pm: TASK-STATE-SEGMENT"));
   
           /* check privilege level */
           if (tss_sel->desc.dpl < CPU_STAT_CPL) {
                   VERBOSE(("JMPfar_pm: DPL(%d) < CPL(%d)", tss_sel->desc.dpl, CPU_STAT_CPL));
                   EXCEPTION(GP_EXCEPTION, tss_sel->idx);
           }
           if (tss_sel->desc.dpl < tss_sel->rpl) {
                   VERBOSE(("JMPfar_pm: DPL(%d) < RPL(%d)", tss_sel->desc.dpl, tss_sel->rpl));
                   EXCEPTION(GP_EXCEPTION, tss_sel->idx);
         }          }
   
           /* not present */
           if (selector_is_not_present(tss_sel)) {
                   VERBOSE(("JMPfar_pm: selector is not present"));
                   EXCEPTION(NP_EXCEPTION, tss_sel->idx);
           }
   
           task_switch(tss_sel, TASK_SWITCH_JMP);
 }  }
   
   
   /*------------------------------------------------------------------------------
    * CALLfar_pm
    */
   static void CALLfar_pm_code_segment(selector_t *call_sel, DWORD new_ip);
   static void CALLfar_pm_call_gate(selector_t *call_sel);
   static void CALLfar_pm_task_gate(selector_t *call_sel);
   static void CALLfar_pm_tss(selector_t *call_sel);
   
 /*  /*
  * 4.3.6. 特権レベル間のコール操作   * 4.3.6. 特権レベル間のコール操作
  *   *
  * 1. アクセス権のチェック(特権チェック)を実行する。   * 1. アクセス権のチェック(特権チェック)を実行する。
  * 2. SS, ESP, CS, EIP の各レジスタの現在地を一時的に内部にセーブする。   * 2. SS, ESP, CS, EIP の各レジスタの現在値を一時的に内部にセーブする。
  * 3. TSS レジスタに格納されている新しいスタック(すなわち、現在コールされている   * 3. TSS レジスタに格納されている新しいスタック(すなわち、現在コールされている
  *    特権レベル用のスタック)のセグメントレジスタとスタックポインタを   *    特権レベル用のスタック)のセグメントレジスタとスタックポインタを
  *    SS レジスタと ESP レジスタにロードし、新しいスタックに切り替える。   *    SS レジスタと ESP レジスタにロードし、新しいスタックに切り替える。
  * 4. コール元プロシージャのスタックに対して一時的にセーブしておいた SS 値と   * 4. コール元プロシージャのスタックに対して一時的にセーブしておいた SS と
  *    ESP 値を、この新しいスタックにプッシュする。   *    ESP を、この新しいスタックにプッシュする。
  * 5. コール元プロシージャのスタックからパラメータをコピーする。新しいスタック   * 5. コール元プロシージャのスタックからパラメータをコピーする。新しいスタック
  *    にコピーされるパラメータの数は、コール・ゲート・ディスクリプタ内の値で   *    にコピーされるパラメータの数は、コール・ゲート・ディスクリプタ内の値で
  *    決まる。   *    決まる。
  * 6. コール元プロシージャに対して一時的にセーブしておいた CS 値と EIP 値を、   * 6. コール元プロシージャに対して一時的にセーブしておいた CS と EIP を、
  *    新しいスタックにプッシュする。   *    新しいスタックにプッシュする。
  * 7. 新しいコード・セグメントのセグメント・セレクタと新しい命令ポインタを、   * 7. 新しいコード・セグメントのセグメント・セレクタと新しい命令ポインタを、
  *    コール・ゲートから CS レジスタと EIP レジスタにそれぞれロードする。   *    コール・ゲートから CS レジスタと EIP レジスタにそれぞれロードする。
Line 301  void Line 359  void
 CALLfar_pm(WORD selector, DWORD new_ip)  CALLfar_pm(WORD selector, DWORD new_ip)
 {  {
         selector_t call_sel;          selector_t call_sel;
         selector_t sel2;  
         int rv;          int rv;
   
         VERBOSE(("CALLfar_pm: EIP = 0x%08x, selector = 0x%04x, new_ip = 0x%08x", CPU_PREV_EIP, selector, new_ip));          VERBOSE(("CALLfar_pm: old EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_PREV_EIP, CPU_SS, CPU_ESP));
           VERBOSE(("CALLfar_pm: selector = 0x%04x, new_ip = 0x%08x", selector, new_ip));
   
         rv = parse_selector(&call_sel, selector);          rv = parse_selector(&call_sel, selector);
         if (rv < 0) {          if (rv < 0) {
                   VERBOSE(("CALLfar_pm: parse_selector (selector = %04x, rv = %d)", selector, rv));
                 EXCEPTION(GP_EXCEPTION, call_sel.idx);                  EXCEPTION(GP_EXCEPTION, call_sel.idx);
         }          }
   
         if (call_sel.desc.s) {          if (call_sel.desc.s) {
                 /* code segment descriptor */                  /* code or data segment descriptor */
                   VERBOSE(("CALLfar_pm: code or data segment descriptor"));
   
                 if (!call_sel.desc.u.seg.c) {                  if (!call_sel.desc.u.seg.c) {
                         /* data segment */                          /* data segment */
                           VERBOSE(("CALLfar_pm: data segment"));
                         EXCEPTION(GP_EXCEPTION, call_sel.idx);                          EXCEPTION(GP_EXCEPTION, call_sel.idx);
                 }                  }
   
                 /* check privilege level */                  /* code segment descriptor */
                 if (!call_sel.desc.u.seg.ec) {                  CALLfar_pm_code_segment(&call_sel, new_ip);
                         VERBOSE(("NONCONFORMING-CODE-SEGMENT"));          } else {
                   /* system descriptor */
                   VERBOSE(("CALLfar_pm: system descriptor"));
   
                         /* 下巻 p.119 4.8.1.1. */                  switch (call_sel.desc.type) {
                         if ((call_sel.rpl > CPU_STAT_CPL)                  case CPU_SYSDESC_TYPE_CALL_16:
                          || (call_sel.desc.dpl != CPU_STAT_CPL)) {                  case CPU_SYSDESC_TYPE_CALL_32:
                                 EXCEPTION(GP_EXCEPTION, call_sel.idx);                          CALLfar_pm_call_gate(&call_sel);
                         }                          break;
                 } else {  
                         VERBOSE(("CONFORMING-CODE-SEGMENT"));  
   
                         /* 下巻 p.120 4.8.1.2. */                  case CPU_SYSDESC_TYPE_TASK:
                         if (call_sel.desc.dpl > CPU_STAT_CPL) {                          CALLfar_pm_task_gate(&call_sel);
                                 EXCEPTION(GP_EXCEPTION, call_sel.idx);                          break;
                         }  
                   case CPU_SYSDESC_TYPE_TSS_16:
                   case CPU_SYSDESC_TYPE_TSS_32:
                           CALLfar_pm_tss(&call_sel);
                           break;
   
                   case CPU_SYSDESC_TYPE_TSS_BUSY_16:
                   case CPU_SYSDESC_TYPE_TSS_BUSY_32:
                           VERBOSE(("CALLfar_pm: task is busy"));
                           /*FALLTHROUGH*/
                   default:
                           VERBOSE(("CALLfar_pm: invalid descriptor type (type = %d)", call_sel.desc.type));
                           EXCEPTION(GP_EXCEPTION, call_sel.idx);
                           break;
                 }                  }
           }
   
                 /* not present */          VERBOSE(("CALLfar_pm: new EIP = %04x:%08x, new ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP));
                 if (selector_is_not_present(&call_sel)) {  }
                         EXCEPTION(NP_EXCEPTION, call_sel.idx);  
   /*---
    * CALLfar_pm: code segment
    */
   static void
   CALLfar_pm_code_segment(selector_t *call_sel, DWORD new_ip)
   {
           DWORD sp;
   
           /* check privilege level */
           if (!call_sel->desc.u.seg.ec) {
                   VERBOSE(("CALLfar_pm: NON-CONFORMING-CODE-SEGMENT"));
                   /* 下巻 p.119 4.8.1.1. */
                   if (call_sel->rpl > CPU_STAT_CPL) {
                           VERBOSE(("CALLfar_pm: RPL(%d) > CPL(%d)", call_sel->rpl, CPU_STAT_CPL));
                           EXCEPTION(GP_EXCEPTION, call_sel->idx);
                   }
                   if (call_sel->desc.dpl != CPU_STAT_CPL) {
                           VERBOSE(("CALLfar_pm: DPL(%d) != CPL(%d)", call_sel->desc.dpl, CPU_STAT_CPL));
                           EXCEPTION(GP_EXCEPTION, call_sel->idx);
                   }
           } else {
                   VERBOSE(("CALLfar_pm: CONFORMING-CODE-SEGMENT"));
                   /* 下巻 p.120 4.8.1.2. */
                   if (call_sel->desc.dpl > CPU_STAT_CPL) {
                           VERBOSE(("CALLfar_pm: DPL(%d) > CPL(%d)", call_sel->desc.dpl, CPU_STAT_CPL));
                           EXCEPTION(GP_EXCEPTION, call_sel->idx);
                 }                  }
           }
   
                 if (CPU_INST_OP32) {          /* not present */
                         CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 8);          if (selector_is_not_present(call_sel)) {
                         /* out of range */                  VERBOSE(("CALLfar_pm: selector is not present"));
                         if (new_ip > call_sel.desc.u.seg.limit) {                  EXCEPTION(NP_EXCEPTION, call_sel->idx);
                                 EXCEPTION(GP_EXCEPTION, 0);          }
                         }  
   
                         PUSH0_32(CPU_CS);          if (CPU_STAT_SS32) {
                         PUSH0_32(CPU_EIP);                  sp = CPU_ESP;
                 } else {          } else {
                         CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 4);                  sp = CPU_SP;
                         /* out of range */          }
                         if (new_ip > call_sel.desc.u.seg.limit) {          if (CPU_INST_OP32) {
                                 EXCEPTION(GP_EXCEPTION, 0);                  CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 8);
                         }  
   
                         PUSH0_16(CPU_CS);                  /* out of range */
                         PUSH0_16(CPU_IP);                  if (new_ip > call_sel->desc.u.seg.limit) {
                           VERBOSE(("CALLfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, call_sel->desc.u.seg.limit));
                           EXCEPTION(GP_EXCEPTION, 0);
                 }                  }
   
                 if (!call_sel.desc.d) {                  PUSH0_32(CPU_CS);
                         new_ip &= 0xffff;                  PUSH0_32(CPU_EIP);
           } else {
                   CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 4);
   
                   /* out of range */
                   if (new_ip > call_sel->desc.u.seg.limit) {
                           VERBOSE(("CALLfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, call_sel->desc.u.seg.limit));
                           EXCEPTION(GP_EXCEPTION, 0);
                 }                  }
   
                 load_cs(call_sel.selector, &call_sel.desc, CPU_STAT_CPL);                  PUSH0_16(CPU_CS);
                 SET_EIP(new_ip);                  PUSH0_16(CPU_IP);
           }
   
           load_cs(call_sel->selector, &call_sel->desc, CPU_STAT_CPL);
           SET_EIP(new_ip);
   }
   
   /*---
    * CALLfar_pm: call gate
    */
   static void CALLfar_pm_call_gate_same_privilege(selector_t *call_sel, selector_t *cs_sel);
   static void CALLfar_pm_call_gate_more_privilege(selector_t *call_sel, selector_t *cs_sel);
   
   static void
   CALLfar_pm_call_gate(selector_t *callgate_sel)
   {
           selector_t cs_sel;
           int rv;
   
           VERBOSE(("CALLfar_pm: CALL-GATE"));
   
           /* check privilege level */
           if (callgate_sel->desc.dpl < CPU_STAT_CPL) {
                   VERBOSE(("CALLfar_pm: DPL(%d) < CPL(%d)", callgate_sel->desc.dpl, CPU_STAT_CPL));
                   EXCEPTION(GP_EXCEPTION, callgate_sel->idx);
           }
           if (callgate_sel->desc.dpl < callgate_sel->rpl) {
                   VERBOSE(("CALLfar_pm: DPL(%d) < CPL(%d)", callgate_sel->desc.dpl, callgate_sel->rpl));
                   EXCEPTION(GP_EXCEPTION, callgate_sel->idx);
           }
   
           /* not present */
           if (selector_is_not_present(callgate_sel)) {
                   VERBOSE(("CALLfar_pm: selector is not present"));
                   EXCEPTION(NP_EXCEPTION, callgate_sel->idx);
           }
   
           /* parse code segment descriptor */
           rv = parse_selector(&cs_sel, callgate_sel->desc.u.gate.selector);
           if (rv < 0) {
                   VERBOSE(("CALLfar_pm: parse_selector (selector = %04x, rv = %d)", callgate_sel->desc.u.gate.selector, rv));
                   EXCEPTION(GP_EXCEPTION, cs_sel.idx);
           }
   
           /* check segment type */
           if (!cs_sel.desc.s) {
                   VERBOSE(("CALLfar_pm: code segment is system segment"));
                   EXCEPTION(GP_EXCEPTION, cs_sel.idx);
           }
           if (!cs_sel.desc.u.seg.c) {
                   VERBOSE(("CALLfar_pm: code segment is data segment"));
                   EXCEPTION(GP_EXCEPTION, cs_sel.idx);
           }
   
           /* check privilege level */
           if (cs_sel.desc.dpl > CPU_STAT_CPL) {
                   VERBOSE(("CALLfar_pm: DPL(%d) > CPL(%d)", cs_sel.desc.dpl, CPU_STAT_CPL));
                   EXCEPTION(GP_EXCEPTION, cs_sel.idx);
           }
   
           /* not present */
           if (selector_is_not_present(&cs_sel)) {
                   VERBOSE(("CALLfar_pm: selector is not present"));
                   EXCEPTION(NP_EXCEPTION, cs_sel.idx);
           }
   
           /* out of range */
           if (callgate_sel->desc.u.gate.offset > cs_sel.desc.u.seg.limit) {
                   VERBOSE(("CALLfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", callgate_sel->desc.u.gate.offset, cs_sel.desc.u.seg.limit));
                   EXCEPTION(GP_EXCEPTION, 0);
           }
   
           if (!cs_sel.desc.u.seg.ec && (cs_sel.desc.dpl < CPU_STAT_CPL)) {
                   CALLfar_pm_call_gate_more_privilege(callgate_sel, &cs_sel);
         } else {          } else {
                 DWORD temp_eip, temp_esp;                  CALLfar_pm_call_gate_same_privilege(callgate_sel, &cs_sel);
                 WORD temp_cs, temp_ss;          }
   }
   
                 /* system descriptor */  /*---
                 switch (call_sel.desc.type) {   * CALLfar_pm: call gate (SAME-PRIVILEGE)
                 case CPU_SYSDESC_TYPE_CALL_16:   */
                 case CPU_SYSDESC_TYPE_CALL_32:  static void
                         VERBOSE(("CALL-GATE"));  CALLfar_pm_call_gate_same_privilege(selector_t *callgate_sel, selector_t *cs_sel)
   {
           DWORD sp;
           DWORD old_eip;
           WORD old_cs;
   
           VERBOSE(("CALLfar_pm: SAME-PRIVILEGE"));
   
           /* save register */
           old_cs = CPU_CS;
           old_eip = CPU_EIP;
   
                         /* check privilege level */          if (CPU_STAT_SS32) {
                         if ((call_sel.desc.dpl < CPU_STAT_CPL)                  sp = CPU_ESP;
                          || (call_sel.desc.dpl < call_sel.rpl)) {          } else {
                                 EXCEPTION(GP_EXCEPTION, call_sel.idx);                  sp = CPU_SP;
                         }          }
   
                         /* not present */          if (callgate_sel->desc.type == CPU_SYSDESC_TYPE_CALL_32) {
                         if (selector_is_not_present(&call_sel)) {                  CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 8);
                                 EXCEPTION(NP_EXCEPTION, call_sel.idx);  
                         }  
   
                         /* parse code segment descriptor */                  load_cs(cs_sel->selector, &cs_sel->desc, CPU_STAT_CPL);
                         rv = parse_selector(&sel2, call_sel.desc.u.gate.selector);                  SET_EIP(callgate_sel->desc.u.gate.offset);
                         if (rv < 0) {  
                                 EXCEPTION(GP_EXCEPTION, sel2.idx);  
                         }  
   
                         /* check code segment & privilege level */                  PUSH0_32(old_cs);
                         if (!sel2.desc.s                  PUSH0_32(old_eip);
                          || !sel2.desc.u.seg.c          } else {
                          || sel2.desc.dpl > CPU_STAT_CPL) {                  CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 4);
                                 EXCEPTION(GP_EXCEPTION, sel2.idx);  
                         }  
   
                         /* not present */                  load_cs(cs_sel->selector, &cs_sel->desc, CPU_STAT_CPL);
                         if (selector_is_not_present(&sel2)) {                  SET_EIP(callgate_sel->desc.u.gate.offset);
                                 EXCEPTION(NP_EXCEPTION, sel2.idx);  
                         }                  PUSH0_16(old_cs);
                   PUSH0_16(old_eip);
           }
   }
   
   /*---
    * CALLfar_pm: call gate (MORE-PRIVILEGE)
    */
   static void
   CALLfar_pm_call_gate_more_privilege(selector_t *callgate_sel, selector_t *cs_sel)
   {
           DWORD param[32];        /* copy param */
           selector_t ss_sel;
           DWORD sp;
           DWORD old_eip, old_esp;
           DWORD tss_esp;
           WORD old_cs, old_ss;
           WORD tss_ss;
           int param_count;
           int i;
           int rv;
   
           VERBOSE(("CALLfar_pm: MORE-PRIVILEGE"));
   
           /* save register */
           old_cs = CPU_CS;
           old_ss = CPU_SS;
           old_eip = CPU_EIP;
           old_esp = CPU_ESP;
   
           if (CPU_STAT_SS32) {
                   sp = CPU_ESP;
           } else {
                   sp = CPU_SP;
           }
   
           /* get stack pointer from TSS */
           get_stack_from_tss(cs_sel->desc.dpl, &tss_ss, &tss_esp);
   
           /* parse stack segment descriptor */
           rv = parse_selector(&ss_sel, tss_ss);
           if (rv < 0) {
                   VERBOSE(("CALLfar_pm: parse_selector (selector = %04x, rv = %d)", tss_ss, rv));
                   EXCEPTION(TS_EXCEPTION, ss_sel.idx);
           }
   
           /* check privilege level */
           if (ss_sel.rpl != cs_sel->desc.dpl) {
                   VERBOSE(("CALLfar_pm: RPL[SS](%d) != DPL[CS](%d)", ss_sel.rpl, cs_sel->desc.dpl));
                   EXCEPTION(TS_EXCEPTION, ss_sel.idx);
           }
           if (ss_sel.desc.dpl != cs_sel->desc.dpl) {
                   VERBOSE(("CALLfar_pm: DPL[SS](%d) != DPL[CS](%d)", ss_sel.desc.dpl, cs_sel->desc.dpl));
                   EXCEPTION(TS_EXCEPTION, ss_sel.idx);
           }
   
           /* stack segment must be writable data segment. */
           if (!ss_sel.desc.s) {
                   VERBOSE(("CALLfar_pm: stack segment is system segment"));
                   EXCEPTION(TS_EXCEPTION, ss_sel.idx);
           }
           if (ss_sel.desc.u.seg.c) {
                   VERBOSE(("CALLfar_pm: stack segment is code segment"));
                   EXCEPTION(TS_EXCEPTION, ss_sel.idx);
           }
           if (!ss_sel.desc.u.seg.wr) {
                   VERBOSE(("CALLfar_pm: stack segment is read-only data segment"));
                   EXCEPTION(TS_EXCEPTION, ss_sel.idx);
           }
   
           /* not present */
           if (selector_is_not_present(&ss_sel)) {
                   VERBOSE(("CALLfar_pm: stack segment selector is not present"));
                   EXCEPTION(SS_EXCEPTION, ss_sel.idx);
           }
   
           param_count = callgate_sel->desc.u.gate.count;
           VERBOSE(("CALLfar_pm: param_count = %d", param_count));
   
           if (callgate_sel->desc.type == CPU_SYSDESC_TYPE_CALL_32) {
                   CHECK_STACK_PUSH(&ss_sel.desc, tss_esp, 16 + param_count * 4);
   
                   /* dump param */
                   for (i = 0; i < param_count; i++) {
                           param[i] = cpu_vmemoryread_d(CPU_SS_INDEX, sp + i * 4);
                           VERBOSE(("CALLfar_pm: get param[%d] = %08x", i, param[i]));
                   }
   
                   load_ss(ss_sel.selector, &ss_sel.desc, ss_sel.desc.dpl);
                   CPU_ESP = tss_esp;
   
                   load_cs(cs_sel->selector, &cs_sel->desc, cs_sel->desc.dpl);
                   SET_EIP(callgate_sel->desc.u.gate.offset);
   
                   PUSH0_32(old_ss);
                   PUSH0_32(old_esp);
   
                   /* restore param */
                   for (i = param_count; i != 0; i--) {
                           PUSH0_32(param[i - 1]);
                           VERBOSE(("CALLfar_pm: set param[%d] = %08x", i - 1, param[i - 1]));
                   }
   
                   PUSH0_32(old_cs);
                   PUSH0_32(old_eip);
           } else {
                   CHECK_STACK_PUSH(&ss_sel.desc, tss_esp, 8 + param_count * 2);
   
                         /* save register */                  /* dump param */
                         temp_cs = CPU_CS;                  for (i = 0; i < param_count; i++) {
                         temp_ss = CPU_SS;                          param[i] = cpu_vmemoryread_w(CPU_SS_INDEX, sp + i * 2);
                         temp_eip = CPU_EIP;                          VERBOSE(("CALLfar_pm: get param[%d] = %04x", i, param[i]));
                         temp_esp = CPU_ESP;                  }
                         if (sel2.desc.type == CPU_SYSDESC_TYPE_CALL_16) {  
                                 temp_eip &= 0xffff;  
                                 temp_esp &= 0xffff;  
                         }  
   
                         new_ip = call_sel.desc.u.gate.offset;                  load_ss(ss_sel.selector, &ss_sel.desc, ss_sel.desc.dpl);
                   CPU_SP = tss_esp;
   
                         /* out of range */                  load_cs(cs_sel->selector, &cs_sel->desc, cs_sel->desc.dpl);
                         if (new_ip > sel2.desc.u.seg.limit) {                  SET_EIP(callgate_sel->desc.u.gate.offset);
                                 EXCEPTION(GP_EXCEPTION, 0);  
                         }  
   
                         if (!sel2.desc.u.seg.ec                  PUSH0_16(old_ss);
                          && (sel2.desc.dpl < CPU_STAT_CPL)) {                  PUSH0_16(old_esp);
                                 DWORD param[32];        // copy param  
                                 selector_t ss_sel;  
                                 DWORD tss_esp;  
                                 WORD tss_ss;  
                                 BYTE i;  
   
                                 VERBOSE(("MORE-PRIVILEGE"));  
   
                                 get_stack_from_tss(sel2.desc.dpl, &tss_ss, &tss_esp);  
   
                                 /* parse stack segment descriptor */  
                                 rv = parse_selector(&ss_sel, tss_ss);  
                                 if (rv < 0) {  
                                         EXCEPTION(TS_EXCEPTION, ss_sel.idx);  
                                 }  
   
                                 /* check privilege level */  
                                 if ((ss_sel.rpl != sel2.desc.dpl)  
                                  || (ss_sel.desc.dpl != sel2.desc.dpl)  
                                  || !ss_sel.desc.s  
                                  || !ss_sel.desc.u.seg.wr) {  
                                         EXCEPTION(TS_EXCEPTION, ss_sel.idx);  
                                 }  
   
                                 /* not present */  
                                 if (selector_is_not_present(&ss_sel)) {  
                                         EXCEPTION(NP_EXCEPTION, ss_sel.idx);  
                                 }  
   
                                 if (call_sel.desc.type == CPU_SYSDESC_TYPE_CALL_32){  
                                         CHECK_STACK_PUSH(&ss_sel.desc, tss_esp, 16 + sel2.desc.u.gate.count * 4);  
   
                                         /* dump param */  
                                         for (i = 0; i < sel2.desc.u.gate.count; i++) {  
                                                 param[i] = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP + i * 4);  
                                         }  
   
                                         load_ss(tss_ss, &ss_sel.desc, ss_sel.desc.dpl);  
                                         CPU_ESP = tss_esp;  
                                         load_cs(sel2.selector, &sel2.desc, sel2.desc.dpl);  
                                         SET_EIP(new_ip);  
   
                                         PUSH0_32(temp_ss);  
                                         PUSH0_32(temp_esp);  
   
                                         /* restore param */  
                                         for (i = sel2.desc.u.gate.count; i != 0; i--) {  
                                                 PUSH0_32(param[i - 1]);  
                                         }  
   
                                         PUSH0_32(temp_cs);  
                                         PUSH0_32(temp_eip);  
                                 } else {  
                                         CHECK_STACK_PUSH(&ss_sel.desc, tss_esp, 8 + sel2.desc.u.gate.count * 2);  
   
                                         new_ip &= 0xffff;  
   
                                         /* dump param */  
                                         for (i = 0; i < sel2.desc.u.gate.count; i++) {  
                                                 param[i] = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP + i * 2);  
                                         }  
   
                                         load_ss(tss_ss, &ss_sel.desc, ss_sel.desc.dpl);  
                                         CPU_ESP = tss_esp & 0xffff;  
                                         load_cs(sel2.selector, &sel2.desc, sel2.desc.dpl);  
                                         SET_EIP(new_ip);  
   
                                         PUSH0_16(temp_ss);  
                                         PUSH0_16(temp_esp);  
   
                                         /* restore param */  
                                         for (i = sel2.desc.u.gate.count; i != 0; i--) {  
                                                 PUSH0_16(param[i - 1]);  
                                         }  
   
                                         PUSH0_16(temp_cs);  
                                         PUSH0_16(temp_eip);  
                                 }  
                         } else {  
                                 VERBOSE(("SAME-PRIVILEGE"));  
   
                                 if (call_sel.desc.type == CPU_SYSDESC_TYPE_CALL_32){  
                                         CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 8);  
   
                                         load_cs(sel2.selector, &sel2.desc, CPU_STAT_CPL);  
                                         SET_EIP(new_ip);  
   
                                         PUSH0_32(temp_cs);  
                                         PUSH0_32(temp_eip);  
                                 } else {  
                                         CHECK_STACK_PUSH(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 4);  
   
                                         load_cs(sel2.selector, &sel2.desc, CPU_STAT_CPL);  
                                         new_ip &= 0xffff;  
                                         SET_EIP(new_ip);  
   
                                         PUSH0_16(temp_cs);  
                                         PUSH0_16(temp_eip);  
                                 }  
                         }  
                         break;  
   
                 case CPU_SYSDESC_TYPE_TASK:                  /* restore param */
                         VERBOSE(("TASK-GATE"));                  for (i = param_count; i != 0; i--) {
                           PUSH0_16(param[i - 1]);
                           VERBOSE(("CALLfar_pm: set param[%d] = %04x", i - 1, param[i - 1]));
                   }
   
                         /* check privilege level */                  PUSH0_16(old_cs);
                         if ((call_sel.desc.dpl < CPU_STAT_CPL)                  PUSH0_16(old_eip);
                          || (call_sel.desc.dpl < call_sel.rpl)) {          }
                                 EXCEPTION(GP_EXCEPTION, call_sel.idx);  }
                         }  
   
                         /* not present */  /*---
                         if (selector_is_not_present(&call_sel)) {   * CALLfar_pm: task gate
                                 EXCEPTION(NP_EXCEPTION, call_sel.idx);   */
                         }  static void
   CALLfar_pm_task_gate(selector_t *taskgate_sel)
   {
           selector_t tss_sel;
           int rv;
   
                         /* tss descriptor */          VERBOSE(("CALLfar_pm: TASK-GATE"));
                         rv = parse_selector(&sel2, call_sel.desc.u.gate.selector);  
                         if (rv < 0 || sel2.ldt) {  
                                 EXCEPTION(GP_EXCEPTION, sel2.idx);  
                         }  
   
                         /* check descriptor type */          /* check privilege level */
                         switch (sel2.desc.type) {          if (taskgate_sel->desc.dpl < CPU_STAT_CPL) {
                         case CPU_SYSDESC_TYPE_TSS_16:                  VERBOSE(("CALLfar_pm: DPL(%d) < CPL(%d)", taskgate_sel->desc.dpl, CPU_STAT_CPL));
                         case CPU_SYSDESC_TYPE_TSS_32:                  EXCEPTION(GP_EXCEPTION, taskgate_sel->idx);
                                 break;          }
           if (taskgate_sel->desc.dpl < taskgate_sel->rpl) {
                         case CPU_SYSDESC_TYPE_TSS_BUSY_16:                  VERBOSE(("CALLfar_pm: DPL(%d) < CPL(%d)", taskgate_sel->desc.dpl, taskgate_sel->rpl));
                         case CPU_SYSDESC_TYPE_TSS_BUSY_32:                  EXCEPTION(GP_EXCEPTION, taskgate_sel->idx);
                                 VERBOSE(("CALLfar_pm: task is busy"));          }
                                 /*FALLTHROUGH*/  
                         default:  
                                 EXCEPTION(GP_EXCEPTION, sel2.idx);  
                                 break;  
                         }  
   
                         /* not present */          /* not present */
                         if (selector_is_not_present(&sel2)) {          if (selector_is_not_present(taskgate_sel)) {
                                 EXCEPTION(NP_EXCEPTION, sel2.idx);                  VERBOSE(("CALLfar_pm: selector is not present"));
                         }                  EXCEPTION(NP_EXCEPTION, taskgate_sel->idx);
           }
   
                         task_switch(&sel2, TASK_SWITCH_CALL);          /* tss descriptor */
           rv = parse_selector(&tss_sel, taskgate_sel->desc.u.gate.selector);
           if (rv < 0 || tss_sel.ldt) {
                   VERBOSE(("CALLfar_pm: parse_selector (selector = %04x, rv = %d, %s)", tss_sel.selector, rv, tss_sel.ldt ? "LDT" : "GDT"));
                   EXCEPTION(GP_EXCEPTION, tss_sel.idx);
           }
   
                         /* out of range */          /* check descriptor type */
                         if (CPU_EIP > CPU_STAT_CS_LIMIT) {          switch (tss_sel.desc.type) {
                                 EXCEPTION(GP_EXCEPTION, 0);          case CPU_SYSDESC_TYPE_TSS_16:
                         }          case CPU_SYSDESC_TYPE_TSS_32:
                         break;                  break;
   
           case CPU_SYSDESC_TYPE_TSS_BUSY_16:
           case CPU_SYSDESC_TYPE_TSS_BUSY_32:
                   VERBOSE(("CALLfar_pm: task is busy"));
                   /*FALLTHROUGH*/
           default:
                   VERBOSE(("CALLfar_pm: invalid descriptor type (type = %d)", tss_sel.desc.type));
                   EXCEPTION(GP_EXCEPTION, tss_sel.idx);
                   break;
           }
   
                 case CPU_SYSDESC_TYPE_TSS_16:          /* not present */
                 case CPU_SYSDESC_TYPE_TSS_32:          if (selector_is_not_present(&tss_sel)) {
                         VERBOSE(("TASK-STATE-SEGMENT"));                  VERBOSE(("CALLfar_pm: TSS selector is not present"));
                   EXCEPTION(NP_EXCEPTION, tss_sel.idx);
           }
   
                         /* check privilege level */          task_switch(&tss_sel, TASK_SWITCH_CALL);
                         if ((call_sel.desc.dpl < CPU_STAT_CPL)  }
                          || (call_sel.desc.dpl < call_sel.rpl)) {  
                                 EXCEPTION(GP_EXCEPTION, call_sel.idx);  
                         }  
   
                         /* not present */  /*---
                         if (selector_is_not_present(&call_sel)) {   * CALLfar_pm: TSS
                                 EXCEPTION(NP_EXCEPTION, call_sel.idx);   */
                         }  static void
   CALLfar_pm_tss(selector_t *tss_sel)
   {
   
                         task_switch(&call_sel, TASK_SWITCH_CALL);          VERBOSE(("TASK-STATE-SEGMENT"));
   
                         /* out of range */          /* check privilege level */
                         if (CPU_EIP > CPU_STAT_CS_LIMIT) {          if (tss_sel->desc.dpl < CPU_STAT_CPL) {
                                 EXCEPTION(GP_EXCEPTION, 0);                  VERBOSE(("CALLfar_pm: DPL(%d) < CPL(%d)", tss_sel->desc.dpl, CPU_STAT_CPL));
                         }                  EXCEPTION(GP_EXCEPTION, tss_sel->idx);
                         break;          }
           if (tss_sel->desc.dpl < tss_sel->rpl) {
                   VERBOSE(("CALLfar_pm: DPL(%d) < CPL(%d)", tss_sel->desc.dpl, tss_sel->rpl));
                   EXCEPTION(GP_EXCEPTION, tss_sel->idx);
           }
   
                 case CPU_SYSDESC_TYPE_TSS_BUSY_16:          /* not present */
                 case CPU_SYSDESC_TYPE_TSS_BUSY_32:          if (selector_is_not_present(tss_sel)) {
                         VERBOSE(("CALLfar_pm: task is busy"));                  VERBOSE(("CALLfar_pm: TSS selector is not present"));
                         /*FALLTHROUGH*/                  EXCEPTION(NP_EXCEPTION, tss_sel->idx);
                 default:  
                         EXCEPTION(GP_EXCEPTION, call_sel.idx);  
                         break;  
                 }  
         }          }
   
           task_switch(tss_sel, TASK_SWITCH_CALL);
 }  }
   
   
   /*------------------------------------------------------------------------------
    * RETfar_pm
    */
   
 /*  /*
  * 4.3.6. 特権レベル間のリターン操作   * 4.3.6. 特権レベル間のリターン操作
  *   *
Line 632  CALLfar_pm(WORD selector, DWORD new_ip) Line 840  CALLfar_pm(WORD selector, DWORD new_ip)
 void  void
 RETfar_pm(DWORD nbytes)  RETfar_pm(DWORD nbytes)
 {  {
         selector_t ret_sel;          selector_t ret_sel, ss_sel;
           DWORD sp;
           DWORD new_ip, new_sp;
           WORD new_cs, new_ss;
         int rv;          int rv;
         DWORD new_ip;  
         WORD selector;  
   
         VERBOSE(("RETfar_pm: EIP = 0x%08x, nbytes = %d", CPU_PREV_EIP, nbytes));          VERBOSE(("RETfar_pm: old EIP = %04x:%08x, ESP = %04x:%08x, nbytes = %d", CPU_CS, CPU_PREV_EIP, CPU_SS, CPU_ESP, nbytes));
   
           if (CPU_STAT_SS32) {
                   sp = CPU_ESP;
           } else {
                   sp = CPU_SP;
           }
         if (CPU_INST_OP32) {          if (CPU_INST_OP32) {
                 CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 8 + nbytes);                  CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), sp, nbytes + 8);
                 if (CPU_STAT_SS32) {                  new_ip = cpu_vmemoryread_d(CPU_SS_INDEX, sp);
                         new_ip = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP);                  new_cs = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 4);
                         selector = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP + 4);  
                 } else {  
                         new_ip = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_SP);  
                         selector = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_SP + 4);  
                 }  
         } else {          } else {
                 CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 4 + nbytes);                  CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), sp, nbytes + 4);
                 if (CPU_STAT_SS32) {                  new_ip = cpu_vmemoryread_w(CPU_SS_INDEX, sp);
                         new_ip = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP);                  new_cs = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 2);
                         selector = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP + 2);  
                 } else {  
                         new_ip = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_SP);  
                         selector = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_SP + 2);  
                 }  
         }          }
   
         rv = parse_selector(&ret_sel, selector);          rv = parse_selector(&ret_sel, new_cs);
         if (rv < 0) {          if (rv < 0) {
                   VERBOSE(("RETfar_pm: parse_selector (selector = %04x, rv = %d, %s)", ret_sel.selector, rv));
                 EXCEPTION(GP_EXCEPTION, ret_sel.idx);                  EXCEPTION(GP_EXCEPTION, ret_sel.idx);
         }          }
   
         /* check code segment descriptor */          /* check segment type */
         if (!ret_sel.desc.s || !ret_sel.desc.u.seg.c) {          if (!ret_sel.desc.s) {
                   VERBOSE(("RETfar_pm: return to system segment"));
                   EXCEPTION(GP_EXCEPTION, ret_sel.idx);
           }
           if (!ret_sel.desc.u.seg.c) {
                   VERBOSE(("RETfar_pm: return to data segment"));
                 EXCEPTION(GP_EXCEPTION, ret_sel.idx);                  EXCEPTION(GP_EXCEPTION, ret_sel.idx);
         }          }
   
         /* check privilege level */          /* check privilege level */
         if (ret_sel.rpl < CPU_STAT_CPL          if (ret_sel.rpl < CPU_STAT_CPL) {
          || (ret_sel.desc.u.seg.ec && (ret_sel.desc.dpl > ret_sel.rpl))) {                  VERBOSE(("RETfar_pm: RPL(%d) < CPL(%d)", ret_sel.rpl, CPU_STAT_CPL));
                   EXCEPTION(GP_EXCEPTION, ret_sel.idx);
           }
           if (!ret_sel.desc.u.seg.ec && (ret_sel.desc.dpl > ret_sel.rpl)) {
                   VERBOSE(("RETfar_pm: NON-COMFORMING-CODE-SEGMENT and DPL(%d) > RPL(%d)", ret_sel.desc.dpl, ret_sel.rpl));
                 EXCEPTION(GP_EXCEPTION, ret_sel.idx);                  EXCEPTION(GP_EXCEPTION, ret_sel.idx);
         }          }
   
         /* not present */          /* not present */
         if (selector_is_not_present(&ret_sel)) {          if (selector_is_not_present(&ret_sel)) {
                   VERBOSE(("RETfar_pm: returned code segment is not present"));
                 EXCEPTION(NP_EXCEPTION, ret_sel.idx);                  EXCEPTION(NP_EXCEPTION, ret_sel.idx);
         }          }
   
         if (ret_sel.rpl > CPU_STAT_CPL) {          if (ret_sel.rpl == CPU_STAT_CPL) {
                 selector_t ss_sel;                  VERBOSE(("RETfar_pm: RETURN-TO-SAME-PRIVILEGE-LEVEL"));
                 selector_t temp_sel;  
                 DWORD new_sp;                  /* check code segment limit */
                 WORD new_ss;                  if (new_ip > ret_sel.desc.u.seg.limit) {
                 int i;                          VERBOSE(("RETfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, ret_sel.desc.u.seg.limit));
                           EXCEPTION(GP_EXCEPTION, 0);
                   }
   
                 VERBOSE(("RETURN-OUTER-PRIVILEGE-LEVEL"));                  VERBOSE(("RETfar_pm: new_ip = %08x, new_cs = %04x", new_ip, ret_sel.selector));
   
                 if (CPU_INST_OP32) {                  if (CPU_INST_OP32) {
                         CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 16 + nbytes);                          nbytes += 8;
                         if (CPU_STAT_SS32) {  
                                 new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP + 8 + nbytes);  
                                 new_ss = (WORD)cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP + 8 + 4 + nbytes);  
                         } else {  
                                 new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, (WORD)(CPU_SP + 8 + nbytes));  
                                 new_ss = (WORD)cpu_vmemoryread_d(CPU_SS_INDEX, (WORD)(CPU_SP + 8 + 4 + nbytes));  
                         }  
                 } else {                  } else {
                         CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 8 + nbytes);                          nbytes += 4;
                         if (CPU_STAT_SS32) {                  }
                                 new_sp = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP + 4 + nbytes);                  if (CPU_STAT_SS32) {
                                 new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP + 4 + 2 + nbytes);                          CPU_ESP += nbytes;
                         } else {                  } else {
                                 new_sp = cpu_vmemoryread_w(CPU_SS_INDEX, (WORD)(CPU_SP + 4 + nbytes));                          CPU_SP += nbytes;
                                 new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, (WORD)(CPU_SP + 4 + 2 + nbytes));                  }
                         }  
                   load_cs(ret_sel.selector, &ret_sel.desc, CPU_STAT_CPL);
                   SET_EIP(new_ip);
           } else {
                   VERBOSE(("RETfar_pm: RETURN-OUTER-PRIVILEGE-LEVEL"));
   
                   if (CPU_INST_OP32) {
                           CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 8 + 8 + nbytes);
                           new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 8 + nbytes);
                           new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 8 + nbytes + 4);
                   } else {
                           CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 4 + 4 + nbytes);
                           new_sp = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 4 + nbytes);
                           new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 4 + nbytes + 2);
                 }                  }
   
                 rv = parse_selector(&ss_sel, new_ss);                  rv = parse_selector(&ss_sel, new_ss);
                 if (rv < 0) {                  if (rv < 0) {
                           VERBOSE(("RETfar_pm: parse_selector (selector = %04x, rv = %d, %s)", ss_sel.selector, rv));
                         EXCEPTION(GP_EXCEPTION, ss_sel.idx);                          EXCEPTION(GP_EXCEPTION, ss_sel.idx);
                 }                  }
   
                 /* check stack segment descriptor */                  /* check stack segment descriptor */
                   if (!ss_sel.desc.s) {
                           VERBOSE(("RETfar_pm: stack segment is system segment"));
                           EXCEPTION(GP_EXCEPTION, ret_sel.idx);
                   }
                   if (ss_sel.desc.u.seg.c) {
                           VERBOSE(("RETfar_pm: stack segment is code segment"));
                           EXCEPTION(GP_EXCEPTION, ret_sel.idx);
                   }
                   if (!ss_sel.desc.u.seg.wr) {
                           VERBOSE(("RETfar_pm: stack segment is read-only data segment"));
                           EXCEPTION(GP_EXCEPTION, ret_sel.idx);
                   }
   
                 /* check privilege level */                  /* check privilege level */
                 if ((ss_sel.rpl != ret_sel.rpl)                  if (ss_sel.rpl != ret_sel.rpl) {
                  || (ss_sel.desc.dpl != ret_sel.rpl)                          VERBOSE(("RETfar_pm: RPL[SS](%d) != RPL[CS](%d)", ss_sel.rpl, ret_sel.rpl));
                  || !ss_sel.desc.s                          EXCEPTION(GP_EXCEPTION, ret_sel.idx);
                  || !ss_sel.desc.u.seg.wr) {                  }
                         EXCEPTION(GP_EXCEPTION, ss_sel.idx);                  if (ss_sel.desc.dpl != ret_sel.rpl) {
                           VERBOSE(("RETfar_pm: DPL[SS](%d) != RPL[CS](%d)", ss_sel.desc.dpl, ret_sel.rpl));
                           EXCEPTION(GP_EXCEPTION, ret_sel.idx);
                 }                  }
   
                 /* not present */                  /* not present */
                 if (selector_is_not_present(&ss_sel)) {                  if (selector_is_not_present(&ss_sel)) {
                           VERBOSE(("RETfar_pm: stack segment is not present"));
                         EXCEPTION(SS_EXCEPTION, ss_sel.idx);                          EXCEPTION(SS_EXCEPTION, ss_sel.idx);
                 }                  }
   
                 /* check code segment limit */                  /* check code segment limit */
                 if (new_ip > ret_sel.desc.u.seg.limit) {                  if (new_ip > ret_sel.desc.u.seg.limit) {
                           VERBOSE(("RETfar_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, ret_sel.desc.u.seg.limit));
                         EXCEPTION(GP_EXCEPTION, 0);                          EXCEPTION(GP_EXCEPTION, 0);
                 }                  }
   
                 /* set new register */                  VERBOSE(("RETfar_pm: new_ip = %08x, new_cs = %04x", new_ip, ret_sel.selector));
                   VERBOSE(("RETfar_pm: new_sp = %08x, new_ss = %04x", new_sp, ss_sel.selector));
   
                 load_cs(ret_sel.selector, &ret_sel.desc, ret_sel.rpl);                  load_cs(ret_sel.selector, &ret_sel.desc, ret_sel.rpl);
                 SET_EIP(new_ip);                  SET_EIP(new_ip);
   
                 load_ss(ss_sel.selector, &ss_sel.desc, ret_sel.rpl);                  load_ss(ss_sel.selector, &ss_sel.desc, ret_sel.rpl);
                 CPU_ESP = new_sp;                  if (CPU_STAT_SS32) {
                           CPU_ESP = new_sp + nbytes;
                   } else {
                           CPU_SP = new_sp + nbytes;
                   }
   
                 /* check segment register */                  /* check segment register */
                 for (i = 0; i < CPU_SEGREG_NUM; i++) {                  check_segreg();
                         if (i == CPU_CS_INDEX || i == CPU_SS_INDEX)          }
                                 continue;  
   
                         rv = parse_selector(&temp_sel, CPU_REGS_SREG(i));  
                         if (rv < 0) {  
                                 CPU_REGS_SREG(i) = 0;  
                                 CPU_STAT_SREG(i).valid = 0;  
                                 continue;  
                         }  
   
                         /* check privilege level */          VERBOSE(("RETfar_pm: new EIP = %04x:%08x, ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP));
                         if (!temp_sel.desc.s  }
                          || (temp_sel.desc.u.seg.c && temp_sel.desc.u.seg.wr)  
                          || ((!temp_sel.desc.u.seg.c || !temp_sel.desc.u.seg.ec)  
                            && ((temp_sel.desc.dpl < CPU_STAT_CPL) || (temp_sel.desc.dpl < temp_sel.rpl)))) {  /*------------------------------------------------------------------------------
                                 CPU_REGS_SREG(i) = 0;   * IRET_pm
                                 CPU_STAT_SREG(i).valid = 0;   */
                                 continue;  
                         }  #undef  RETURN_FROM_VM86
   
   static void IRET_pm_nested_task(void);
   static void IRET_pm_return_to_vm86(DWORD new_ip, DWORD new_cs, DWORD new_flags);
   #ifdef  RETURN_FROM_VM86
   static void IRET_pm_return_from_vm86(DWORD new_ip, DWORD new_cs, DWORD new_flags);
   #endif  /* RETURN_FROM_VM86 */
   
   void
   IRET_pm(void)
   {
           selector_t iret_sel, ss_sel;
           DWORD sp;
           DWORD stacksize;        /* for RETURN-TO-SAME-PRIVILEGE-LEVEL */
           DWORD mask = 0;
           DWORD new_ip, new_sp, new_flags;
           WORD new_cs, new_ss;
           int rv;
   
           VERBOSE(("IRET_pm: old EIP = %04x:%08x, old ESP = %04x:%08x", CPU_CS, CPU_PREV_EIP, CPU_SS, CPU_ESP));
   
           if (CPU_EFLAG & NT_FLAG) {
                   /* TASK-RETURN: PE=1, VM=0, NT=1 */
                   IRET_pm_nested_task();
                   VERBOSE(("IRET_pm: new EIP = %04x:%08x, new ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP));
                   return;
           }
   
           if (CPU_STAT_SS32) {
                   sp = CPU_ESP;
           } else {
                   sp = CPU_SP;
           }
           if (CPU_INST_OP32) {
                   CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 12);
                   new_ip = cpu_vmemoryread_d(CPU_SS_INDEX, sp);
                   new_cs = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 4);
                   new_flags = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 8);
           } else {
                   CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 6);
                   new_ip = cpu_vmemoryread_w(CPU_SS_INDEX, sp);
                   new_cs = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 2);
                   new_flags = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 4);
           }
           VERBOSE(("IRET_pm: new_ip = %08x, new_cs = %04x, new_eflags = %08x", new_ip, new_cs, new_flags));
   
   #ifdef  RETURN_FROM_VM86
           if (CPU_EFLAG & VM_FLAG) {
                   /* RETURN-FROM-VIRTUAL-8086-MODE */
                   IRET_pm_return_from_vm86(new_ip, new_cs, new_flags);
                   VERBOSE(("IRET_pm: new EIP = %04x:%08x, new ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP));
                   return;
           }
   #endif  /* RETURN_FROM_VM86 */
   
           if (new_flags & VM_FLAG) {
                   /* RETURN-TO-VIRTUAL-8086-MODE */
                   IRET_pm_return_to_vm86(new_ip, new_cs, new_flags);
                   VERBOSE(("IRET_pm: new EIP = %04x:%08x, new ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP));
                   return;
           }
   
           /* PROTECTED-MODE-RETURN */
           VERBOSE(("IRET_pm: PE=1, VM=0 in flags image"));
   
           rv = parse_selector(&iret_sel, new_cs);
           if (rv < 0) {
                   VERBOSE(("IRET_pm: parse_selector (selector = %04x, rv = %d)", iret_sel.selector, rv));
                   EXCEPTION(GP_EXCEPTION, iret_sel.idx);
           }
   
           /* check code segment descriptor */
           if (!iret_sel.desc.s) {
                   VERBOSE(("IRET_pm: return code segment is system segment"));
                   EXCEPTION(GP_EXCEPTION, iret_sel.idx);
           }
           if (!iret_sel.desc.u.seg.c) {
                   VERBOSE(("IRET_pm: return code segment is data segment"));
                   EXCEPTION(GP_EXCEPTION, iret_sel.idx);
           }
   
           /* check privilege level */
           if (iret_sel.rpl < CPU_STAT_CPL) {
                   VERBOSE(("IRET_pm: RPL(%d) < CPL(%d)", iret_sel.rpl, CPU_STAT_CPL));
                   EXCEPTION(GP_EXCEPTION, iret_sel.idx);
           }
           if (iret_sel.desc.u.seg.ec && (iret_sel.desc.dpl > iret_sel.rpl)) {
                   VERBOSE(("IRET_pm: CONFORMING-CODE-SEGMENT and DPL(%d) != RPL(%d)", iret_sel.desc.dpl, iret_sel.rpl));
                   EXCEPTION(GP_EXCEPTION, iret_sel.idx);
           }
   
           /* not present */
           if (selector_is_not_present(&iret_sel)) {
                   VERBOSE(("IRET_pm: code segment is not present"));
                   EXCEPTION(NP_EXCEPTION, iret_sel.idx);
           }
   
           if (iret_sel.rpl > CPU_STAT_CPL) {
                   VERBOSE(("IRET_pm: RETURN-OUTER-PRIVILEGE-LEVEL"));
   
                   if (CPU_INST_OP32) {
                           CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 20);
                           new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 12);
                           new_ss = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 16);
                   } else {
                           CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 10);
                           new_sp = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 6);
                           new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, sp + 8);
                   }
                   VERBOSE(("IRET_pm: new_sp = 0x%08x, new_ss = 0x%04x", new_sp, new_ss));
   
                   rv = parse_selector(&ss_sel, new_ss);
                   if (rv < 0) {
                           VERBOSE(("IRET_pm: parse_selector (selector = %04x, rv = %d)", ss_sel.selector, rv));
                           EXCEPTION(GP_EXCEPTION, ss_sel.idx);
                   }
   
                   /* check privilege level */
                   if (ss_sel.rpl != iret_sel.rpl) {
                           VERBOSE(("IRET_pm: RPL[SS](%d) != RPL[CS](%d)", ss_sel.rpl, iret_sel.rpl));
                           EXCEPTION(GP_EXCEPTION, ss_sel.idx);
                   }
                   if (ss_sel.desc.dpl != iret_sel.rpl) {
                           VERBOSE(("IRET_pm: DPL[SS](%d) != RPL[CS](%d)", ss_sel.desc.dpl, iret_sel.rpl));
                           EXCEPTION(GP_EXCEPTION, ss_sel.idx);
                   }
   
                   /* check stack segment descriptor */
                   if (!ss_sel.desc.s) {
                           VERBOSE(("IRET_pm: stack segment is system segment"));
                           EXCEPTION(GP_EXCEPTION, ss_sel.idx);
                   }
                   if (ss_sel.desc.u.seg.c) {
                           VERBOSE(("IRET_pm: stack segment is code segment"));
                           EXCEPTION(GP_EXCEPTION, ss_sel.idx);
                   }
                   if (!ss_sel.desc.u.seg.wr) {
                           VERBOSE(("IRET_pm: stack segment is read-only data segment"));
                           EXCEPTION(GP_EXCEPTION, ss_sel.idx);
                   }
   
                   /* not present */
                   if (selector_is_not_present(&ss_sel)) {
                           VERBOSE(("IRET_pm: stack segment is not present"));
                           EXCEPTION(SS_EXCEPTION, ss_sel.idx);
                 }                  }
                 CPU_ESP += nbytes;  
                   stacksize = 0;
         } else {          } else {
                 VERBOSE(("RETURN-TO-SAME-PRIVILEGE-LEVEL"));                  VERBOSE(("IRET_pm: RETURN-TO-SAME-PRIVILEGE-LEVEL"));
   
                 if (CPU_INST_OP32) {                  if (CPU_INST_OP32) {
                         if (new_ip > ret_sel.desc.u.seg.limit) {                          stacksize = 12;
                                 EXCEPTION(GP_EXCEPTION, 0);  
                         }  
                         POP0_32(CPU_EIP);  
                         POP0_32(CPU_CS);  
                 } else {                  } else {
                         new_ip &= 0xffff;                          stacksize = 6;
                         if (new_ip > ret_sel.desc.u.seg.limit) {                  }
                                 EXCEPTION(GP_EXCEPTION, 0);  
                         }                  /* compiler happy :-) */
                         POP0_16(CPU_EIP);                  new_sp = 0;
                         POP0_16(CPU_CS);                  new_ss = 0;
           }
   
           /* check code segment limit */
           if (new_ip > iret_sel.desc.u.seg.limit) {
                   VERBOSE(("IRET_pm: new_ip is out of range. new_ip = %08x, limit = %08x", new_ip, iret_sel.desc.u.seg.limit));
                   EXCEPTION(GP_EXCEPTION, 0);
           }
   
           mask = 0;
           if (CPU_INST_OP32)
                   mask |= RF_FLAG;
           if (CPU_STAT_CPL <= CPU_STAT_IOPL)
                   mask |= I_FLAG;
           if (CPU_STAT_CPL == 0) {
                   mask |= IOPL_FLAG;
                   if (CPU_INST_OP32) {
                           mask |= VM_FLAG|VIF_FLAG|VIP_FLAG;
                 }                  }
                 load_cs(ret_sel.selector, &ret_sel.desc, CPU_STAT_CPL);  
                 SET_EIP(new_ip);  
                 CPU_ESP += nbytes;  
         }          }
   
           /* set new register */
           load_cs(iret_sel.selector, &iret_sel.desc, iret_sel.rpl);
           SET_EIP(new_ip);
   
           set_eflags(new_flags, mask);
   
           if (iret_sel.rpl != CPU_STAT_CPL) {
                   /* RETURN-OUTER-PRIVILEGE-LEVEL */
                   load_ss(new_ss, &ss_sel.desc, iret_sel.rpl);
                   CPU_ESP = new_sp;
   
                   /* check segment register */
                   check_segreg();
           } else {
                   /* RETURN-TO-SAME-PRIVILEGE-LEVEL */
                   if (CPU_STAT_SS32) {
                           CPU_ESP += stacksize;
                   } else {
                           CPU_SP += stacksize;
                   }
           }
   
           VERBOSE(("IRET_pm: new EIP = %04x:%08x, new ESP = %04x:%08x", CPU_CS, CPU_EIP, CPU_SS, CPU_ESP));
 }  }
   
 void  /*---
 IRET_pm()   * IRET_pm: NT_FLAG
    */
   static void
   IRET_pm_nested_task(void)
 {  {
         selector_t iret_sel;          selector_t iret_sel;
         int rv;          int rv;
         DWORD new_ip, new_flags;  
         WORD new_cs;          WORD new_cs;
   
         VERBOSE(("IRET_pm: EIP = 0x%08x", CPU_PREV_EIP));          VERBOSE(("IRET_pm: TASK-RETURN: PE=1, VM=0, NT=1"));
   
         if (CPU_STAT_VM86) {          if (CPU_STAT_VM86) {
                 /* RETURN-FROM-VIRTUAL-8086-MODE */                  ia32_panic("IRET_pm: VM86");
                 VERBOSE(("Virtual-8086 mode: PE=1, VM=1"));          }
                 VERBOSE(("Processor is in virtual-8086 mode when IRET is executed and stays in virtual-8086 mode"));  
   
                 if (CPU_STAT_IOPL == CPU_IOPL3) {          new_cs = get_link_selector_from_tss();
                         VERBOSE(("Virtual mode: PE=1, VM=1, IOPL=3"));          rv = parse_selector(&iret_sel, new_cs);
                         if (CPU_INST_OP32) {          if (rv < 0 || iret_sel.ldt) {
                                 CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 12);                  VERBOSE(("IRET_pm: parse_selector (selector = %04x, rv = %d)", iret_sel.selector, rv));
                                 POP0_32(new_ip);                  EXCEPTION(GP_EXCEPTION, iret_sel.idx);
                                 POP0_32(new_cs);          }
                                 POP0_32(new_flags);  
   
                                 set_eflags(new_flags, I_FLAG|RF_FLAG);  
                         } else {  
                                 CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 6);  
                                 POP0_16(new_ip);  
                                 POP0_16(new_cs);  
                                 POP0_16(new_flags);  
   
                                 set_flags(new_flags, I_FLAG);          /* check system segment */
                         }          if (iret_sel.desc.s) {
                         CPU_SET_SEGREG(CPU_CS_INDEX, new_cs);                  VERBOSE(("IRET_pm: task segment is %d segment", iret_sel.desc.u.seg.c ? "code" : "data"));
                         SET_EIP(new_ip);                  EXCEPTION(GP_EXCEPTION, iret_sel.idx);
                         return;          }
                 }  
                 VERBOSE(("trap to virtual-8086 monitor: PE=1, VM=1, IOPL<3"));  
                 EXCEPTION(GP_EXCEPTION, 0);  
         } else if (CPU_EFLAG & NT_FLAG) {  
                 VERBOSE(("TASK-RETURN: PE=1, VM=0, NT=1"));  
   
                 new_cs = get_link_selector_from_tss();          switch (iret_sel.desc.type) {
                 rv = parse_selector(&iret_sel, new_cs);          case CPU_SYSDESC_TYPE_TSS_BUSY_16:
                 if (rv < 0 || iret_sel.ldt) {          case CPU_SYSDESC_TYPE_TSS_BUSY_32:
                         EXCEPTION(GP_EXCEPTION, iret_sel.idx);                  break;
                 }  
           case CPU_SYSDESC_TYPE_TSS_16:
           case CPU_SYSDESC_TYPE_TSS_32:
                   VERBOSE(("IRET_pm: task is not busy"));
                   /*FALLTHROUGH*/
           default:
                   VERBOSE(("IRET_pm: invalid descriptor type (type = %d)", iret_sel.desc.type));
                   EXCEPTION(GP_EXCEPTION, iret_sel.idx);
                   break;
           }
   
                 /* check system segment */          /* not present */
                 if (iret_sel.desc.s) {          if (selector_is_not_present(&iret_sel)) {
                         EXCEPTION(GP_EXCEPTION, iret_sel.idx);                  VERBOSE(("IRET_pm: tss segment is not present"));
                 }                  EXCEPTION(NP_EXCEPTION, iret_sel.idx);
                 switch (iret_sel.desc.type) {          }
                 case CPU_SYSDESC_TYPE_TSS_BUSY_16:  
                 case CPU_SYSDESC_TYPE_TSS_BUSY_32:  
                         break;  
   
                 case CPU_SYSDESC_TYPE_TSS_16:          task_switch(&iret_sel, TASK_SWITCH_IRET);
                 case CPU_SYSDESC_TYPE_TSS_32:  }
                         VERBOSE(("IRET_pm: task is not busy"));  
                         /*FALLTHROUGH*/  
                 default:  
                         EXCEPTION(GP_EXCEPTION, iret_sel.idx);  
                         break;  
                 }  
   
                 /* not present */  /*---
                 if (selector_is_not_present(&iret_sel)) {   * IRET_pm: new_flags & VM_FLAG
                         EXCEPTION(NP_EXCEPTION, iret_sel.idx);   */
                 }  static void
   IRET_pm_return_to_vm86(DWORD new_ip, DWORD new_cs, DWORD new_flags)
   {
           WORD segsel[CPU_SEGREG_NUM];
           DWORD sp;
           DWORD new_sp;
           int i;
   
                 task_switch(&iret_sel, TASK_SWITCH_IRET);          VERBOSE(("IRET_pm: Interrupt procedure was in virtual-8086 mode: PE=1, VM=1 in flags image"));
   
                 /* out of range */          if (CPU_STAT_CPL != 0) {
                 if (CPU_EIP > CPU_STAT_CS_LIMIT) {                  ia32_panic("IRET_pm: CPL != 0");
                         EXCEPTION(GP_EXCEPTION, 0);          }
                 }  
           if (CPU_STAT_SS32) {
                   sp = CPU_ESP;
         } else {          } else {
                   sp = CPU_SP;
           }
           if (!CPU_INST_OP32) {
                   ia32_panic("IRET_pm: 16bit mode");
           }
   
           CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), sp, 36);
           new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 12);
           segsel[CPU_SS_INDEX] = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 16);
           segsel[CPU_ES_INDEX] = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 20);
           segsel[CPU_DS_INDEX] = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 24);
           segsel[CPU_FS_INDEX] = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 28);
           segsel[CPU_GS_INDEX] = cpu_vmemoryread_d(CPU_SS_INDEX, sp + 32);
           segsel[CPU_CS_INDEX] = new_cs;
   
           for (i = 0; i < CPU_SEGREG_NUM; i++) {
                   CPU_REGS_SREG(i) = segsel[i];
           }
   
           set_eflags(new_flags, IOPL_FLAG|I_FLAG|VM_FLAG|RF_FLAG);
   
           CPU_ESP = new_sp;
           SET_EIP(new_ip);
   }
   
   #ifdef  RETURN_FROM_VM86
   /*---
    * IRET_pm: VM_FLAG
    */
   static void
   IRET_pm_return_from_vm86(DWORD new_ip, DWORD new_cs, DWORD new_flags)
   {
           DWORD stacksize;
   
           VERBOSE(("IRET_pm: virtual-8086 mode: VM=1"));
   
           if (CPU_STAT_IOPL == CPU_IOPL3) {
                   VERBOSE(("IRET_pm: virtual-8086 mode: IOPL=3"));
                 if (CPU_INST_OP32) {                  if (CPU_INST_OP32) {
                         CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 12);                          stacksize = 12;
                         POP0_32(new_ip);  
                         POP0_32(new_cs);  
                         POP0_32(new_flags);  
                 } else {                  } else {
                         CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 6);                          stacksize = 6;
                         POP0_16(new_ip);                  }
                         POP0_16(new_cs);                  if (CPU_STAT_SS32) {
                         POP0_16(new_flags);                          CPU_ESP += stacksize;
                 }  
   
                 if ((CPU_STAT_CPL == 0) && (new_flags & VM_FLAG)) {  
                         /* RETURN-TO-VIRTUAL-8086-MODE */  
                         descriptor_t sd;  
                         DWORD new_sp;  
                         WORD segsel[CPU_SEGREG_NUM];  
                         int i;  
   
                         VERBOSE(("Interrupt procedure was in virtual-8086 mode: PE=1, VM=1 in flags image"));  
   
                         CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 24);  
   
                         segsel[CPU_CS_INDEX] = new_cs;  
                         POP0_32(new_sp);  
                         POP0_32(segsel[CPU_SS_INDEX]);  
                         POP0_32(segsel[CPU_ES_INDEX]);  
                         POP0_32(segsel[CPU_DS_INDEX]);  
                         POP0_32(segsel[CPU_FS_INDEX]);  
                         POP0_32(segsel[CPU_GS_INDEX]);  
   
                         for (i = 0; i < CPU_SEGREG_NUM; i++) {  
                                 CPU_REGS_SREG(i) = segsel[i];  
                                 sd.u.seg.limit = 0xffff;  
                                 CPU_SET_SEGDESC_DEFAULT(&sd, i, segsel[i]);  
                                 sd.dpl = 3;  
                                 CPU_STAT_SREG(i) = sd;  
                         }  
                         CPU_ESP = new_sp;  
                         SET_EIP(new_ip & 0xffff);  
   
                         set_eflags(new_flags, IOPL_FLAG|RF_FLAG);  
                 } else {                  } else {
                         DWORD mask;                          CPU_SP += stacksize;
                   }
                         /* PROTECTED-MODE-RETURN */  
                         VERBOSE(("PE=1, VM=0 in flags image"));  
   
                         rv = parse_selector(&iret_sel, new_cs);                  set_eflags(new_flags, I_FLAG|RF_FLAG);
                         if (rv < 0) {  
                                 EXCEPTION(GP_EXCEPTION, iret_sel.idx);  
                         }  
   
                         /* check code segment descriptor */                  CPU_SET_SEGREG(CPU_CS_INDEX, new_cs);
                         if (!iret_sel.desc.s || !iret_sel.desc.u.seg.c) {                  SET_EIP(new_ip);
                                 EXCEPTION(GP_EXCEPTION, iret_sel.idx);                  return;
                         }          }
           VERBOSE(("IRET_pm: trap to virtual-8086 monitor: VM=1, IOPL<3"));
           EXCEPTION(GP_EXCEPTION, 0);
   }
   #endif  /* RETURN_FROM_VM86 */
   
                         /* check privilege level */  
                         if ((iret_sel.rpl < CPU_STAT_CPL)  
                          || (iret_sel.desc.u.seg.ec && (iret_sel.desc.dpl > iret_sel.rpl))) {  
                                 EXCEPTION(GP_EXCEPTION, iret_sel.idx);  
                         }  
   
                         /* not present */  /*-----
                         if (selector_is_not_present(&iret_sel)) {   * Misc.
                                 EXCEPTION(NP_EXCEPTION, iret_sel.idx);   */
                         }  static void
   check_segreg(void)
   {
           selector_t temp_sel;
           BOOL valid;
           int rv;
           int i;
   
                         if (iret_sel.rpl > CPU_STAT_CPL) {          /* check segment register */
                                 /* RETURN-OUTER-PRIVILEGE-LEVEL */          for (i = 0; i < CPU_SEGREG_NUM; i++) {
                                 selector_t ss_sel;                  if (i == CPU_CS_INDEX || i == CPU_SS_INDEX)
                                 WORD new_sp, new_ss;                          continue;
   
                                 if (CPU_INST_OP32) {  
                                         CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 8);  
                                         if (CPU_STAT_SS32) {  
                                                 new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP);  
                                                 new_ss = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_ESP + 4);  
                                         } else {  
                                                 new_sp = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_SP);  
                                                 new_ss = cpu_vmemoryread_d(CPU_SS_INDEX, CPU_SP + 4);  
                                         }  
                                 } else {  
                                         CHECK_STACK_POP(&CPU_STAT_SREG(CPU_SS_INDEX), CPU_ESP, 4);  
                                         if (CPU_STAT_SS32) {  
                                                 new_sp = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP);  
                                                 new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_ESP + 2);  
                                         } else {  
                                                 new_sp = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_SP);  
                                                 new_ss = cpu_vmemoryread_w(CPU_SS_INDEX, CPU_SP + 2);  
                                         }  
                                 }  
   
                                 rv = parse_selector(&ss_sel, new_ss);  
                                 if (rv < 0) {  
                                         EXCEPTION(GP_EXCEPTION, ss_sel.idx);  
                                 }  
   
                                 /* check stack segment descriptor */  
                                 /* check privilege level */  
                                 if ((ss_sel.rpl != iret_sel.rpl)  
                                  || (ss_sel.desc.dpl != iret_sel.rpl)  
                                  || !ss_sel.desc.s  
                                  || !ss_sel.desc.u.seg.wr) {  
                                         EXCEPTION(GP_EXCEPTION, ss_sel.idx);  
                                 }  
   
                                 /* not present */  
                                 if (selector_is_not_present(&ss_sel)) {  
                                         EXCEPTION(SS_EXCEPTION, ss_sel.idx);  
                                 }  
                         } else {  
                                 VERBOSE(("RETURN-TO-SAME-PRIVILEGE-LEVEL"));  
                         }  
   
                         /* check code segment limit */                  rv = parse_selector(&temp_sel, CPU_REGS_SREG(i));
                         if (new_ip > iret_sel.desc.u.seg.limit) {                  if (rv < 0) {
                                 EXCEPTION(GP_EXCEPTION, 0);                          /* segment register is invalid */
                         }                          CPU_REGS_SREG(i) = 0;
                           CPU_STAT_SREG(i).valid = 0;
                           continue;
                   }
   
                         /* set new register */                  valid = TRUE;
                         load_cs(iret_sel.selector, &iret_sel.desc, iret_sel.rpl);                  if (!temp_sel.desc.s) {
                         SET_EIP(new_ip);                          /* system segment */
                           valid = FALSE;
                         mask = 0;                  }
                         if (CPU_INST_OP32)                  if (temp_sel.desc.u.seg.c && !temp_sel.desc.u.seg.wr) {
                                 mask |= RF_FLAG;                          /* execute-only code segment */
                         if (CPU_STAT_CPL <= CPU_STAT_IOPL)                          valid = FALSE;
                                 mask |= I_FLAG;                  }
                         if (CPU_STAT_CPL == 0) {                  if (!temp_sel.desc.u.seg.c || !temp_sel.desc.u.seg.ec) {
                                 mask |= IOPL_FLAG;                          if (CPU_STAT_CPL > temp_sel.desc.dpl) {
                                 if (CPU_INST_OP32)                                  valid = FALSE;
                                         mask |= VM_FLAG|VIF_FLAG|VIP_FLAG;  
                         }                          }
                         set_eflags(new_flags, mask);                  }
   
                         if (iret_sel.rpl > CPU_STAT_CPL) {                  if (!valid) {
                                 selector_t temp_sel;                          /* segment register is invalid */
                                 int i;                          CPU_REGS_SREG(i) = 0;
                           CPU_STAT_SREG(i).valid = 0;
                                 /* RETURN-OUTER-PRIVILEGE-LEVEL */  
                                 /* check segment register */  
                                 for (i = 0; i < CPU_SEGREG_NUM; i++) {  
                                         if ((i == CPU_CS_INDEX) || (i == CPU_SS_INDEX))  
                                                 continue;  
   
                                         rv = parse_selector(&temp_sel, CPU_REGS_SREG(i));  
                                         if (rv < 0) {  
                                                 CPU_REGS_SREG(i) = 0;  
                                                 CPU_STAT_SREG(i).valid = 0;  
                                                 continue;  
                                         }  
   
                                         if ((!temp_sel.desc.u.seg.c || !temp_sel.desc.u.seg.ec)  
                                          && (CPU_STAT_CPL > temp_sel.desc.dpl)) {  
                                                 CPU_REGS_SREG(i) = 0;  
                                                 CPU_STAT_SREG(i).valid = 0;  
                                                 continue;  
                                         }  
                                 }  
                         }  
                 }                  }
         }          }
 }  }

Removed from v.1.2  
changed lines
  Added in v.1.3


RetroPC.NET-CVS <cvs@retropc.net>