DOSアプリケーションやBIOSコール、割り込みハンドラなどを実現するには
16bitの仮想86モードへの切り替えが必要になります。
では、仮想86モードとは何なのでしょう?
仮想86モードは
リアルモードに似た振る舞いをするプロテクトモードです。
リアルモードではありません。
アドレス長やseg:offの計算方法はリアルモードと同じですが、
次のような点が違います。
| 機能 | リアルモード | 仮想86モード |
| ソフトウェア割り込み(INT命令) | 割り込みを実行 | #GP(0) |
| ハードウェア割り込み・例外 | 割り込みを実行 | プロテクトモードで割り込みを実行 |
| 割り込みハンドラ | 4バイトのfarポインタ | 8バイトのdescriptor |
| IN/OUT命令 | そのまま実行 | TSSで許可されていない場合は #GP(0) |
| CLI/STI命令 | そのまま実行 | EFLAGS.IOPL != 3 のとき #GP(0) |
| PUSHF/POPF命令 | そのまま実行 | EFLAGS.IOPL != 3 のとき #GP(0) |
| IRET命令 | そのまま実行 | EFLAGS.IOPL != 3 のとき #GP(0) |
| 特権命令(HLT命令など) | そのまま実行 | #GP(0) |
struct TSS
{
unsigned short prev_TSS;
unsigned short reserved0;
unsigned int esp0;
unsigned short ss0;
unsigned short reserved1;
unsigned int esp1;
unsigned short ss1;
unsigned short reserved2;
unsigned int esp2;
unsigned short ss2;
unsigned short reserved3;
unsigned int cr3;
unsigned int eip;
unsigned int eflags;
unsigned int eax;
unsigned int ecx;
unsigned int edx;
unsigned int ebx;
unsigned int esp;
unsigned int ebp;
unsigned int esi;
unsigned int edi;
unsigned short es;
unsigned short reserved4;
unsigned short cs;
unsigned short reserved5;
unsigned short ss;
unsigned short reserved6;
unsigned short ds;
unsigned short reserved7;
unsigned short fs;
unsigned short reserved8;
unsigned short gs;
unsigned short reserved9;
unsigned short ldt;
unsigned short reserved10;
unsigned short TSStrap;
unsigned short iobase;
};
多くの情報を持っていますが、
割り込み処理では、このうち
ss0:esp0とiobaseのみが意味を持ちます。
その他はタスク切り替え用の情報です。
void v86_gpfault_handler(GPFAULT_DATA *pdata)
{
unsigned offset;
const unsigned char *pcode;
const unsigned char *pcodebegin;
unsigned char code_cur;
unsigned size_perfix = 0;
offset = ((unsigned)pintdata->cs << 4) + pintdata->eip;
pcodebegin = pcode = (const unsigned char *)offset;
read_next:;
code_cur = *pcode++;
switch (code_cur) {
case 0xcd: // INT nn
{
unsigned int_number = *pcode++;
const unsigned short *pirqhandler = (const unsigned short *)(int_number << 2);
unsigned ip_delta = pcode - pcodebegin;
v86_push(pdata,pdata->eflags);
v86_push(pdata,pdata->cs);
v86_push(pdata,pdata->eip + ip_delta);
pdata->eip = *(pirqhandler + 0);
pdata->cs = *(pirqhandler + 1);
pdata->eflags &= ~(P32_EFLAGS_IF | P32_EFLAGS_TF);
}
return;
case 0x66: size_prefix |= 1; goto read_next;
case 0x67: size_prefix |= 2; goto read_next;
(他の命令の処理...)
}
}
void v86_push(GPFAULT_DATA *pdata,unsigned short value)
{
unsigned offset;
pdata->user_sp = pdata->user_sp - 2;
offset = ((unsigned)pdata->user_ss << 4) + pdata->user_sp;
*((unsigned short *)offset) = value;
}
他の命令も同様に、
リアルモード命令と同等の処理をシミュレートします。