Posted at

BIOSを使用しないでテキストを表示させる

More than 3 years have passed since last update.


VRAMに直書きする

自分の前回の投稿(自作OS(1): ブートローダ)では、BIOSの割り込み0x10のテレタイプモードでテキストを表示したが今回はBIOSを使用せずにVRAMに直接書き込むことでテキストを表示させる

※とりあえず今回はリアルモードでの話

NASMを使用します


VRAMの領域は0xb800:0x0000

2byteが色を含めた1文字分に相当する

具体的には、


  • 1byte目: キャラクタコード

  • 2byte目: 0-3bit 文字色, 4-8bit 背景色

といった具合

また、80文字x25行なので、1行は160byteとなる

基本的に知っておくことはこれだけで、適切なオフセットを計算して2byteを書き込むということを繰り返す操作となる


コード


各種初期化

まず、stosw命令はアキュムレータレジスタ(AX)をエクストラセグメント(ES)のディスティネーションインデックス(DI)に書き込むので、ESにはVRAMのセグメントアドレス0xb800を入れておく

ソースインデックス(SI)には目的のテキストのアドレスを入れておく

[bits 16]

[org 0x7c00]

start:
;; initialize segment registers
xor ax, ax
mov ds, ax
mov ss, ax
;; initialize stack
mov sp, 0x7c00

mov ax, 0xb800 ; VRAM segment
mov es, ax ; segment reg for stosw
mov si, msg

jmp println


メインの処理

汎用レジスタの使い方は以下のような感じ


  • アキュムレータレジスタ(AX): ALは文字、AHは色情報

  • カウンターレジスタ(CX): 行内オフセット

  • ベースレジスタ(BX): 行オフセット (160 byte/line), 25行あるので2byte使いました

大まかな処理の流れ


  1. SIレジスタからlodsbでALに次の文字を格納

  2. 終端なら1行分インクリメントして、とりあえずhang

  3. AHに文字色と背景色の情報を格納

  4. BXにオフセット値を乗せる

  5. オフセットアドレス DI = CX + BX

  6. ES:DIにAXの内容を書き込む

  7. CXを1文字分(2byte)インクリメントして次の文字へ

println:

xor cx, cx ; cx = 0
print_char:
lodsb ; load a char to AL
or al, al ; if \0

jnz print
add word [line], 160 ; 1 line = 80 chars x 2 bytes
jmp hang
print:
mov ah, 0x0f ; char:white, back:black

;; set offset
mov bx, word [line]
xor di, di ; di = 0 (beginning of VRAM)
add di, cx ; x-offset
add di, bx ; y-offset
;; write
stosw ; stor ax to ES:DI, and di -= 2

add cx, 2 ; char/attr = 2byte
jmp print_char

hang:
hlt
jmp hang

line dw 0 ; = line num x ( 80 x 2 )
msg db "Hello World", 0
times 510-($-$$) db 0
dw 0xaa55


参考

OSDev

OS Wiki