assembly
x86
OS
nasm

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

More than 1 year has 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