home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

Assembler/ForFun(x86_32)/04, 16bit BIOS with NASM (v1)

Assembler/ForFun(x86_32)/04, 16bit BIOS with NASM (v1)

Assembler / ForFun(x86_32) / 04, 16bit BIOS with NASM (v1)
id: 785 所有者: msakamoto-sf    作成日: 2010-09-17 13:15:40
カテゴリ: Assembler 

x86エミュレータであるBochsを使うことで、BIOSレベルでのプログラミングを楽しむことが出来ます。
今回はNASMを使って"Hello, World!"を出力させてみましょう。

なお、本記事ではMaster Boot Record (MBR)を使ったアセンブラプログラミングの詳細は解説しません。すでに入門レベルは経験済みの読者を想定しています。



Bochsの準備

最初にBochsが使う仮想マシン設定ファイルを用意します。拡張子は一般的に".bxrc"とすることが多いようです。
Bochs付属のサンプルを使っても良いですが、最低限度の機能があれば良いので、次の5行だけでもOKです。
hello.bxrc:

romimage: file=$BXSHARE/BIOS-bochs-latest 
vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest
megs: 16
floppya: 1_44=hello1.img, status=inserted
boot: floppy

フロッピーからのBOOTにのみ対応し、16MBのメインメモリ、フロッピーイメージは"hello1.img"で挿入された状態で起動します。

では、これから"hello1.img"を作ってみましょう。

"Hello, World!" : INT 10h - AH=13h 版

BIOSのINT 10hがビデオ関連の割り込みになっています。
まず最初に、改行コードなども扱ってくれる INT 10h - AH=13h 割り込みを使って表示させてみます。
INT 10h - AH=13h 割り込みでは画面のページ番号やカーソル位置などを設定しておく必要があります。
ページ番号は INT 10h - AH=0Fh , カーソル位置は INT 10h - 03h で取得出来ますので、事前に呼んでおきます。

ソースコードは次のようになります。
hello1.asm:

org 0H
bits 16

    JMP 0x07C0:start ; far jmp, update CS to 0x07C0

start:
    ; copy CS(0x07C0) to DS, ES
    MOV AX, CS
    MOV DS, AX
    MOV ES, AX

    ; get current video mode
    XOR AX, AX
    XOR BX, BX
    MOV AH, 0FH
    INT 10H
    ; active page num => BH

    ; get cursor position and size
    MOV AH, 03H
    INT 10H
    ; (row, column) => (DH, DL)

    ; write string
    MOV AH, 13H
    MOV AL, 1 ; bit0 = 1 => update cursor
    ; BH = page num # already set by INT10H/AH=0FH
    MOV BL, 11111001B ; character attribute, blinking, bg=light gray, fg=light blue
    MOV CX, [hellomsg_len] ; string length
    ; DH, DL (row, column) already set by INT10H/AH=03H
    PUSH BP
    MOV BP, hellomsg
    INT 10H
    POP BP

    JMP $

hellomsg_0:
hellomsg: db `Hello, \r\nBIOS World!\r\n`
hellomsg_len: dw $ - hellomsg_0

times 510-($-$$) db 0
    dw 0xAA55

MBRは0x7C00以降に読み込まれます。念のため、far jmpを使ってCSを更新しておきます。
MBRですので、510バイト0埋め + "0xAA55"が必要です。NASMですと以下の記述で実現出来ます。

times 510-($-$$) db 0
    dw 0xAA55

"$"はNASMではアセンブラの現在位置を示します。"$$"は現在のセクションの先頭位置を示します。よって

$ - $$

で現在位置がセクション先頭からどれだけ離れているかが分かります。

"times"プレフィクスは、その後ろに続くアセンブラを指定回数分繰り返す機能があります。

これらの組み合わせで、

times 510-($-$$) db 0

これが「現在位置から510バイトまで0埋め」と解釈されます。

コンパイル:

> nasm -f bin -o hello1.img hello1.asm

Bochsで実行してみると、"Booting from Floppy ..."の次の行に、改行処理されて"Hello, BIOS World!"文字列が点滅しながら表示されます。色も指定した通りの背景色・文字色で表示されています。


"Hello, World!" : ビデオメモリに直接書き込む版

IBM PCの伝統として、セグメント0xB800がカラーテキストモードのデフォルト画面にマッピングされています。
0xB800:0000 以降に、直接カラーデータやASCIIコードを書き込むことで文字列を表示することが可能です。
1文字は1ワードで表現され、ASCIIデータが低位に、カラーデータが高位に格納されています。
画面サイズは、Bochs起動直後は80桁x25行になっています。

では、画面背景をLight Grayで塗りつぶし、左上から"Hello, BIOS World!"を黒字で表示させてみましょう。
hello2.asm:

org 0H
bits 16

    JMP 0x07C0:start ; far jmp, update CS to 0x07C0

videoseg equ 0xB800
cols equ 80
rows equ 25
bgcolor equ 01110000B ; no blink, bg = light gray, fg = black
bgtext equ 0x20 ; space char

start:
    ; copy CS(0x07C0) to DS, ES
    MOV AX, CS
    MOV DS, AX

    MOV AX, videoseg
    MOV ES, AX
    MOV DI, 0

    ; clear background color and texts
    MOV AL, bgtext
    MOV AH, bgcolor
    ; for (i = 0; i < rows; i++) {
    MOV CX, rows
.bg_fill_rows:
    PUSH CX
    ;     for (j = 0; j < cols; j++) {
    MOV CX, cols
.bg_fill_cols:
    MOV [ES:DI], AX
    ADD DI, 2
    LOOP .bg_fill_cols
    ;     }
    POP CX
    LOOP .bg_fill_rows
    ; }

    XOR AX, AX
    XOR DI, DI
    MOV SI, hellomsg
.print:
    MOV BYTE AL, [DS:SI]
    CMP AX, 0
    JZ .print_end
    MOV BYTE [ES:DI], AL
    INC SI
    ADD DI, 2
    JMP .print
.print_end:

    JMP $


hellomsg: db "Hello, BIOS World!", 0

times 510-($-$$) db 0
    dw 0xAA55

コンパイル:

> nasm -f bin -o hello2.img hello2.asm

hello.bxrc でhello2.imgを指定します。

floppya: 1_44=hello1.img, status=inserted
->
floppya: 1_44=hello2.img, status=inserted

Bochsで実行してみます。上手く表示出来ました。


まとめと参考記事

NASMとBIOS, VGAの提供する INT 10h 割り込みを使って "Hello, World!" アセンブラプログラミングを体験しました。

なおデバッグ機能を有効にしたBochsで試す時は、起動後に

> b 0x7C00

としてMBRの先頭の物理アドレス指定でブレークポイントを設定し、

> c

で実行すると、MBRの先頭で止まります。"vb"で"0x07C0:0000"形式で設定してもブレークしないので、必ず物理アドレスでブレークポイントを設定して下さい。

今回はテキストモードしか取りあげませんでしたが、グラフィックモードなどを使うことでビデオゲームを作ることも出来ます。
VGAプログラミングの詳細は、x86アセンブラプログラミングではなく、ゲームプログラミングとして検索した方が良いでしょう。



プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2010-09-19 20:59:21
md5:ebc3298dbf19bc8e3fee961b77f8ea41
sha1:06cf0e119a06681f02417c453181c6ea4a7cde26
コメント
コメントを投稿するにはログインして下さい。