単に32ビットセグメントを使いたいだけで、必要最小限の設定にとどめ、
実行後にリアルモードに復元させることもやりません。
実行後にリアルモードに復元させることもやりません。
手順としては次のような流れになります。
①セグメントディスクリプタの作成
以下のようにコード、データ、スタック用の3個だけ定義します
4Gbyteの全領域読み書き可能なフラットな空間に設定しました。
4Gbyteの全領域読み書き可能なフラットな空間に設定しました。
セレクタ ベース リミット 属性 08: 00000000 FFFFF TypeCode Execution/Read 32bitモード 10: 00000000 FFFFF TypeData Read/Write 32bitモード 18: 00000000 00000 TypeStack Read/Write,Expand Down 32bitモード
②GDTRに登録
セグメントディスクリプタのアドレスをCPU内部のレジスタGDTRにセットします
③プロテクトモードに切り替え
これは単純でCPU内部のレジスタCR0のPEビットに1を立てるだけです
④セグメントレジスタにセレクタ値をロード
セグメントディスクリプタのアドレスをCPU内部のレジスタGDTRにセットします
これは単純でCPU内部のレジスタCR0のPEビットに1を立てるだけです
⑤ユーザプログラムをコール
これらの処理をアセンブラで記述するとこんな感じです。
/******************************************************************************** * * * start up routine 80386/80486 Protected Virtual Address Mode * * * ********************************************************************************/ .globl _start .globl _end .text .code16 _start: /* エントリポイント */ movl $gdtdat,%ebx lgdt (%ebx) /* GDTのセット */ realtoproto: /* プロテクトモードへの移行 */ cli movl %cr0,%eax orl $1,%eax /* PEビットをセット */ movl %eax,%cr0 jmp flush_q1 /* パイプラインフラッシュ */ .code32 flush_q1: .byte 0xea /* セレクタ付jumpの命令 */ .word set_cs .word 0x08 /* セレクタ 0x08 */ set_cs: movw $0x10,%ax /* セレクタ0x10をds,esに */ movw %ax,%ds movw %ax,%es movw $0x18,%ax /* セレクタ0x18をssに */ movw %ax,%ss movl $0x00080000,%eax /* スタックの新しい値 */ movl %eax,%esp call _main /* 32ビットコードをcall */ hlt /************************************************************************ * * * GDT用データ * * * ************************************************************************/ gdt: .byte 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 /* セレクタ 0x00 なし */ .byte 0xff,0xff,0x00,0x00, 0x00,0x9a,0xcf,0x00 /* セレクタ 0x08 code */ .byte 0xff,0xff,0x00,0x00, 0x00,0x92,0xcf,0x00 /* セレクタ 0x10 data */ .byte 0x00,0x00,0x00,0x00, 0x00,0x96,0xc0,0x00 /* セレクタ 0x18 stack */ gdtdat: .word 0x0fff /* Limit = 4k */ .word gdt /* offset 0-15 */ .word 0x0000 /* offset 16-31 */ _end: .end
これをスタートアップルーチンstart.oとして先頭にリンクさせました。