Skip to content
hikalium edited this page Jun 19, 2016 · 2 revisions

BIOSをつかってメッセージを表示しよう

  • 簡単な復習
  • BIOSによって読み込まれるブートセクタの大きさは512Bytes
  • ブートセクタの読み込まれるアドレスは0000:7c00
  • 最初はリアルモードで起動する
  • CLI命令を実行するまで、CPUは割り込みを受け続けることを忘れないように

(全てではないが)多くのBIOS割り込みは、DSレジスタにリアルモードのセグメントセレクタが代入されていることを期待しています。これが、BIOS割り込みが保護モードで動作しない理由です。そのため、もしもINT 10h / AH=0x0eを使用して画面に出力したいなら、出力したい文字を指すアドレスseg:offsetが正しいものかどうか確認しなければなりません。

リアルモードでは、アドレスはsegment * 16 + offsetとして計算されます。offsetが16よりも大きくなることがあるために、同じアドレスを指すseg:offsetの組は複数存在します。たとえば、ブートローダーが読み込まれるアドレスは0000:7C00とも、07C0:0000とも言えます。これらは、実際に全く同じアドレス0x7C00を指します。(16 * 0x0000 + 0x7C00 = 16 * 0x07C0 + 0x0000 = 0x7C00)

0000:7C00と07C0:0000のどちらを使ってもよいのですが、ORG疑似命令を使用する際に、何が起きるのか気をつけなければなりません。デフォルトでは、生バイナリの開始オフセットは0であるとされています。しかし、必要であればそれを変更することができます。

たとえば以下のコードは、セグメントを0x7C0としたときに変数msgにアクセスする例です。

; boot.asm
   mov ax, 0x07c0
   mov ds, ax
 
   mov si, msg
ch_loop:lodsb
   or al, al ; zero=end or str
   jz hang   ; get out
   mov ah, 0x0E
   int 0x10
   jmp ch_loop
 
hang:
   jmp hang
 
msg   db 'Hello World', 13, 10, 0
   times 510-($-$$) db 0
   db 0x55
   db 0xAA

もう一つの例は、ORGを使用したバージョンです。このとき、msgはセグメントを0とした状態でアクセスされます。ここで注意しなければならないのは、この場合においても、DSは好きな値に設定可能であるということです。

[ORG 0x7c00]
 
   xor ax, ax ; make it zero
   mov ds, ax
 
   mov si, msg
ch_loop:lodsb
   or al, al  ; zero=end of string
   jz hang    ; get out
   mov ah, 0x0E
   int 0x10
   jmp ch_loop
 
hang:
   jmp hang
 
msg   db 'Hello World', 13, 10, 0
 
   times 510-($-$$) db 0
   db 0x55
   db 0xAA

サブルーチン

典型的な「サブルーチン」は、以下のコードのように、たいていCALLとRETを使って、元々のコードの流れから切り離されます。

[ORG 0x7c00]
   xor ax, ax  ;make it zero
   mov ds, ax
 
   mov si, msg
   call bios_print
 
hang:
   jmp hang
 
msg   db 'Hello World', 13, 10, 0
 
bios_print:
   lodsb
   or al, al  ;zero=end of str
   jz done    ;get out
   mov ah, 0x0E
   int 0x10
   jmp bios_print
done:
   ret
 
   times 510-($-$$) db 0
   db 0x55
   db 0xAA

いくつかの理解しがたい理由のために、SIに代入してそしてサブルーチンに飛ぶと、私の環境ではうまくいきませんでした。NASMのマクロを利用することで、パラメーターを渡すふりができます。(マクロ定義は呼び出しより前になければいけません。)

%macro BiosPrint 1
                mov si, word %1
ch_loop:lodsb
   or al, al
   jz done
   mov ah, 0x0E
   int 0x10
   jmp ch_loop
done:
%endmacro
 
[ORG 0x7c00]
   xor ax, ax
   mov ds, ax
 
   BiosPrint msg
 
hang:
   jmp hang
 
msg   db 'Hello World', 13, 10, 0
 
   times 510-($-$$) db 0
   db 0x55
   db 0xAA

もしもソースコードが長くなってきて、読みにくくなってきたら、別ファイルに分けることも可能です。別ファイルに分けたら、以下のようにメインのコードの最初で読み込んでください。

jmp main
 
%include "othercode.inc"
 
main:
   ; 残りのコードが続く…

最初のjmp命令を忘れないようにしてください。忘れてしまうと、othercode.incの最初の関数が実行されてしまうでしょう。

Clone this wiki locally