BitVisorからBIOSを呼び出す方法を紹介します。
callrealmode
core/callrealmode.h
で定義されている関数群がBIOSを呼び出すためのものです。ここに使いたいものがすでにあれば、簡単です。
なお、仮想マシンを開始した後(ブートセクターを実行後)はBIOSを呼び出してはいけません。
ない場合は追加する必要があります。多少手間ではありますが、他の呼び出しを真似て書けばそれほど難しいものではありません。例として以下のふたつのビデオBIOSの呼び出しを追加します:
- ボーダーカラーの設定
- ピクセルの書き込み
core/callrealmode.h
core/callrealmode.h
に呼び出したいインターフェイスを追加します。今回グラフィックスモードを使いたいので、モードの定義も増やしておきます:
--- a/core/callrealmode.h
+++ b/core/callrealmode.h
@@ -47,6 +47,7 @@
#define GETSHIFTFLAGS_INSERT_BIT 0x80
#define VIDEOMODE_80x25TEXT_16COLORS 0x03
+#define VIDEOMODE_320x200GRAPHICS_16COLORS 0x0D
struct sysmemmap {
u64 base;
@@ -92,5 +93,7 @@ void callrealmode_setcursorpos (u8 page_
void callrealmode_startkernel32 (u32 paramsaddr, u32 startaddr);
void callrealmode_tcgbios (u32 al, struct tcgbios_args *args);
void callrealmode_getfontinfo (u8 bh, u16 *es, u16 *bp, u16 *cx, u8 *dl);
+void callrealmode_setbordercolor (int color);
+void callrealmode_writepixel (int page, int color, int x, int y);
#endif
core/callrealmode.c
core/callrealmode.c
ではcore/callrealmode.h
で定義した関数を実装します。定数や構造体の具体的な定義は後回しにして、とりあえず必要そうなものを書いてみます:
--- a/core/callrealmode.c
+++ b/core/callrealmode.c
@@ -392,5 +392,21 @@ callrealmode_getfontinfo (u8 bh, u16 *es
*dl = d.u.getfontinfo.dl_ret;
}
+void callrealmode_setbordercolor (int color) {
+ struct callrealmode_data d;
+ d.func = CALLREALMODE_FUNC_SETBORDERCOLOR;
+ d.u.setbordercolor.color = color;
+ callrealmode_call (&d);
+}
+void callrealmode_writepixel (int page, int color, int x, int y) {
+ struct callrealmode_data d;
+ d.func = CALLREALMODE_FUNC_WRITEPIXEL;
+ d.u.writepixel.page = page;
+ d.u.writepixel.color = color;
+ d.u.writepixel.x = x;
+ d.u.writepixel.y = y;
+ callrealmode_call (&d);
+}
+
INITFUNC ("global0", callrealmode_init_global);
INITFUNC ("panic0", callrealmode_panic);
core/callrealmode_asm.h
core/callrealmode_asm.h
にはcore/callrealmode.c
から使用する定数や構造体の定義を追加します:
--- a/core/callrealmode_asm.h
+++ b/core/callrealmode_asm.h
@@ -49,6 +49,8 @@ enum callrealmode_func {
CALLREALMODE_FUNC_STARTKERNEL32 = 0x9,
CALLREALMODE_FUNC_TCGBIOS = 0xA,
CALLREALMODE_FUNC_GETFONTINFO = 0xB,
+ CALLREALMODE_FUNC_SETBORDERCOLOR = 0xC,
+ CALLREALMODE_FUNC_WRITEPIXEL = 0xD,
};
struct callrealmode_printmsg {
@@ -112,6 +114,15 @@ struct callrealmode_getfontinfo {
u8 dl_ret, bh;
} __attribute__ ((packed));
+struct callrealmode_setbordercolor {
+ u8 color;
+};
+
+struct callrealmode_writepixel {
+ u8 page, color;
+ u16 x, y;
+} __attribute__ ((packed));
+
struct callrealmode_data {
enum callrealmode_func func : 32;
union {
@@ -126,6 +137,8 @@ struct callrealmode_data {
struct callrealmode_startkernel32 startkernel32;
struct callrealmode_tcgbios tcgbios;
struct callrealmode_getfontinfo getfontinfo;
+ struct callrealmode_setbordercolor setbordercolor;
+ struct callrealmode_writepixel writepixel;
} u;
} __attribute__ ((packed));
enum callrealmode_funcに追加した値は次のcore/callrealmode_asm.s
に追加するものと一致するようにします。
core/callrealmode_asm.s
core/callrealmode_asm.s
はBIOSを呼び出すルーチンです。アセンブリ言語で記述します。Real-address modeへの移行などの処理はすでに書かれているので、新たな部分だけ追加すればOKです。core/callrealmode_asm.h
に追加した値とCALLREALMODE_FUNC_*
の値が一致していることと、core/callrealmode_asm.h
に追加した構造体のメンバーのオフセット+0x30とOFF_*
の値が一致していることと、構造体のメンバーにアクセスする際のサイズが一致していることを確認します。
--- a/core/callrealmode_asm.s
+++ b/core/callrealmode_asm.s
@@ -40,6 +40,8 @@
CALLREALMODE_FUNC_STARTKERNEL32 = 0x9
CALLREALMODE_FUNC_TCGBIOS = 0xA
CALLREALMODE_FUNC_GETFONTINFO = 0xB
+ CALLREALMODE_FUNC_SETBORDERCOLOR = 0xC
+ CALLREALMODE_FUNC_WRITEPIXEL = 0xD
SEG_SEL_CODE_REAL = 0x0000
SEG_SEL_DATA_REAL = 0x0000
@@ -178,6 +180,11 @@ callrealmode_switch:
OFF_GETFONTINFO_CX_RET = 0x34
OFF_GETFONTINFO_DL_RET = 0x36
OFF_GETFONTINFO_BH = 0x37
+ OFF_SETBORDERCOLOR_COLOR = 0x30
+ OFF_WRITEPIXEL_PAGE = 0x30
+ OFF_WRITEPIXEL_COLOR = 0x31
+ OFF_WRITEPIXEL_X = 0x32
+ OFF_WRITEPIXEL_Y = 0x34
# Which function?
mov OFF_FUNC(%bp),%ax
@@ -205,6 +212,10 @@ callrealmode_switch:
je tcgbios
cmp $CALLREALMODE_FUNC_GETFONTINFO,%ax
je getfontinfo
+ cmp $CALLREALMODE_FUNC_SETBORDERCOLOR,%ax
+ je setbordercolor
+ cmp $CALLREALMODE_FUNC_WRITEPIXEL,%ax
+ je writepixel
# Error!
cld
mov $(errormsg_data-1-callrealmode_start+CALLREALMODE_OFFSET),%di
@@ -447,6 +458,25 @@ getfontinfo:
mov %dl,OFF_GETFONTINFO_DL_RET(%bp)
ret
+# setbordercolor
+#
+setbordercolor:
+ mov OFF_SETBORDERCOLOR_COLOR(%bp),%bh
+ mov $0x1001,%ax
+ int $0x10
+ ret
+
+# writepixel
+#
+writepixel:
+ mov OFF_WRITEPIXEL_PAGE(%bp),%bh
+ mov OFF_WRITEPIXEL_COLOR(%bp),%al
+ mov OFF_WRITEPIXEL_X(%bp),%cx
+ mov OFF_WRITEPIXEL_Y(%bp),%dx
+ mov $0x0C,%ah
+ int $0x10
+ ret
+
# Subroutines
#
paging_and_protection_off:
機能テスト
上で追加した機能をテストしてみます。今回はcore/main.c
から呼び出してみることにします。
diff --git a/core/main.c b/core/main.c
--- a/core/main.c
+++ b/core/main.c
@@ -322,6 +322,29 @@ process_cpu_type_ext (void)
}
}
+static void drawtest (void) {
+ int x,y,x2,y2,xp2,x12,d;
+ callrealmode_setvideomode (VIDEOMODE_320x200GRAPHICS_16COLORS);
+ callrealmode_setbordercolor (3);
+ for(y=0;y<200;y+=2)for(x=y%4;x<320;x+=4)callrealmode_writepixel(0,7,x,y);
+ for(y=50;y<150;y++)for(x=85;x<235;x++)callrealmode_writepixel(0,15,x,y);
+ x=30,y=0,x2=900,y2=0;
+ while(x>=y){
+ for(int i=0;i<x;i++)callrealmode_writepixel(0,4,160+i,100+y),
+ callrealmode_writepixel(0,4,160-i,100+y),
+ callrealmode_writepixel(0,4,160+i,100-y),
+ callrealmode_writepixel(0,4,160-i,100-y),
+ callrealmode_writepixel(0,4,160+y,100+i),
+ callrealmode_writepixel(0,4,160-y,100+i),
+ callrealmode_writepixel(0,4,160+y,100-i),
+ callrealmode_writepixel(0,4,160-y,100-i);
+ y2+=y+y+1,y++,xp2=900-y2,x12=x2-x-x+1;
+ if(xp2-x12<x2-xp2)x--,x2=x12;
+ }
+ if((d=msgopen("ttyin"))>=0)msgsendint(d,0),msgclose(d);
+ callrealmode_setvideomode (VIDEOMODE_80x25TEXT_16COLORS);
+}
+
static void
bsp_init_thread (void *args)
{
@@ -356,6 +379,7 @@ bsp_init_thread (void *args)
} else if (!uefi_booted) {
load_drivers ();
get_tmpbuf (&tmpbufaddr, &tmpbufsize);
+ drawtest ();
load_bootsector (bios_boot_drive, tmpbufaddr, tmpbufsize);
sync_cursor_pos ();
}
ブートセクターの読み込みの前に、グラフィックスモードに切り替え、ボーダーカラーを設定し、背景に点を並べ、白い長方形と赤い円を描き、キー入力を待つだけの簡単なプログラムです。実行すると以下のようになります:
完璧だ...!