仮想86モードの準備が整いましたので、
いよいよ32bitコードからのソフトウェア割り込みを作ることにします。
すでに16bitコードとして INT 29h を作ってありますので、
これが完成すれば文字出力 API の INT 29h を呼び出せるようになり、
今後の開発が便利になります。
ソフトウェア割り込みに伴うレジスタの受け渡し方法はいろいろありますが、
将来 DPMI を実装した場合のことを考え、
今回は DPMI のレジスタ構造体 DPMI_REGS を使うことにしました。
DPMI_REGSは次の構造体です。
フラグが16bitしかありませんが、仮想86の呼び出しで問題になることはないでしょう。
struct DPMI_REGS
{
union {
unsigned edi;
unsigned short di;
}; // +00h
union {
unsigned esi;
unsigned short si;
}; // +04h
union {
unsigned ebp;
unsigned short bp;
}; // +08h
unsigned dpmi_esp; // +0Ch:reserved for DPMI
union {
unsigned ebx;
unsigned short bx;
}; // +10h
union {
unsigned edx;
unsigned short dx;
}; // +14h
union {
unsigned ecx;
unsigned short cx;
}; // +18h
union {
unsigned eax;
unsigned short ax;
}; // +1Ch
unsigned short flags; // +20h
unsigned short es; // +22h
unsigned short ds; // +24h
unsigned short fs; // +26h
unsigned short gs; // +28h
unsigned short ip; // +2Ah
unsigned short cs; // +2Ch
unsigned short sp; // +2Eh
unsigned short ss; // +30h
};
仮想86に切り替えて 16 bit の割り込み(INT XXh)を
シミュレートするコードは次のとおりです。
extern "C" void asm_call_vm86_handler(DPMI_REGS *pregs,unsigned *ptss_esp0);
void call_vm86_interrupt(DPMI_REGS *pr,unsigned int_number)
{
const unsigned short *pirqhandler = (const unsigned short *)(void*)(int_number << 2);
dpmi_vm86_push(pr,pr->flags);
dpmi_vm86_push(pr,g_thunk16ret_seg);
dpmi_vm86_push(pr,g_thunk16ret_off);
pr->ip = *(pirqhandler + 0);
pr->cs = *(pirqhandler + 1);
pr->flags &= ~(EFLAGS_IF | EFLAGS_TF);
asm_call_vm86_handler( pr, &g_tss.esp0 );
}
asm_call_vm86_handler() は下請けコードです。__asm__( ".text\n" ".globl asm_call_vm86_handler\n" ".globl _asm_call_vm86_handler\n" "asm_call_vm86_handler:\n" "_asm_call_vm86_handler:\n" "mov 4(%esp),%eax\n" // pregs "mov 8(%esp),%ecx\n" // ptss_esp0 "pushl %gs\n" "pushl %fs\n" "pushl %ebx\n" "pushl %ebp\n" "pushl %esi\n" "pushl %edi\n" "pushl (%ecx)\n" // save old tss_esp0 "pushl %eax\n" // push pregs "mov %esp,(%ecx)\n" // set new tss_esp0 "xor %ecx,%ecx\n" "mov 40(%eax),%cx\n" // GS "push %ecx\n" "mov 38(%eax),%cx\n" // FS "push %ecx\n" "mov 36(%eax),%cx\n" // DS "push %ecx\n" "mov 34(%eax),%cx\n" // ES "push %ecx\n" "mov 48(%eax),%cx\n" // SS "push %ecx\n" "mov 46(%eax),%cx\n" // SP "push %ecx\n" "mov 32(%eax),%cx\n" // FLAGS "or $0x23002,%ecx\n" // VM,IOPL3,x86 "push %ecx\n" "xor %ecx,%ecx\n" "mov 44(%eax),%cx\n" // CS "push %ecx\n" "mov 42(%eax),%cx\n" // IP "push %ecx\n" "mov 0(%eax),%edi\n" "mov 4(%eax),%esi\n" "mov 8(%eax),%ebp\n" "mov 16(%eax),%ebx\n" "mov 20(%eax),%edx\n" "mov 24(%eax),%ecx\n" "mov 28(%eax),%eax\n" "iret\n" );dpmi_vm86_push と dpmi_vm86_pop は push と pop をシミュレートするコードです。 これらは次のとおりです。
void dpmi_vm86_push(DPMI_REGS *pr,unsigned short value)
{
unsigned offset;
pr->sp = pr->sp - 2;
offset = ((unsigned)pr->ss << 4) + pr->sp;
*((unsigned short *)(void*)offset) = value;
}
unsigned short dpmi_vm86_pop(DPMI_REGS *pr)
{
unsigned short ret;
unsigned offset;
offset = ((unsigned)pr->ss << 4) + pr->sp;
ret = *((unsigned short *)(void*)offset);
pr->sp = pr->sp + 2;
return ret;
}
32bit コードで IRET を実行すると、
仮想86モードに切り替わります。
これにより 16bit の割り込みがシミュレートされ、
割り込み処理後は
g_thunk16ret_seg:g_thunk16ret_off に制御が返されます。
extern "C" P32_DPMI_REGS *asm_get_vm86_callregs(unsigned tss_esp0);
extern "C" void asm_return_from_vm86_handler(unsigned *ptss_esp0);
void vm86_returntopm32( GPFAULT_DATA *pintdata )
{
P32_DPMI_REGS *pr;
pr = asm_get_vm86_callregs( g_tss.esp0 );
pr->eax = pintdata->eax;
pr->ecx = pintdata->ecx;
pr->edx = pintdata->edx;
pr->ebx = pintdata->ebx;
pr->ebp = pintdata->ebp;
pr->esi = pintdata->esi;
pr->edi = pintdata->edi;
pr->flags = pintdata->eflags;
pr->es = pintdata->v86_es;
pr->ds = pintdata->v86_ds;
asm_return_from_vm86_handler( &g_tss.esp0 );
}
vm86_returntopm32() は下請けに2つのアセンブラ関数を呼び出しています。__asm__( ".text\n" ".globl asm_get_vm86_callregs\n" ".globl _asm_get_vm86_callregs\n" "asm_get_vm86_callregs:\n" "_asm_get_vm86_callregs:\n" "mov 4(%esp),%eax\n" // tss_esp0 "mov (%eax),%eax\n" // *tss_esp0 "ret\n" ); __asm__( ".text\n" ".globl asm_return_from_vm86_handler\n" ".globl _asm_return_from_vm86_handler\n" "asm_return_from_vm86_handler:\n" "_asm_return_from_vm86_handler:\n" "mov 4(%esp),%ecx\n" // ptss_esp0 "mov (%ecx),%esp\n" // get new tss_esp0 "popl %eax\n" "popl (%ecx)\n" // restore old tss_esp0 "popl %edi\n" "popl %esi\n" "popl %ebp\n" "popl %ebx\n" "popl %fs\n" "popl %gs\n" "ret\n" );