月別アーカイブ: 2024年6月

BIOSと戯れてみる(2)

OSを書く:初歩から一歩ずつ』を一通りなめてみたのですが、実は練習問題の「キーボードから読み込む」の自分の回答が少し不満でした。今回はそのあたりのお話を。
環境はAMD64+Windows10+VMware Workstation 17 Player+Debian 12.4です。

キー押下情報を取り込む

BIOSのキーボードサービスの中からキーボード入力読み込みサービスを使いました。
ですがこれ、1文字入力なんですよね。1部のキーを除いて押した瞬間に取り込んでしまう。複数文字を得るためには複数回呼ばなければならない。あまつさえ、入力待ちの間は処理がブロックされて他に何もできない。
せめて1回で複数文字を入力したい。その文字列を編集できなくてもいいから、まとまった文字を入力したい。
とまぁそんなことを考えたわけです。

複数文字入力

バイオスの説明とかつらつらと眺めていたら、キーボードステータス取得というのがありました。説明を見たら要するに「キーボード入力の有り無しをフラグで返す」らしいです。

最初見た時、これ何の役に立つんだろうって思いました。だって読みに行けばデータの有り無しは(ブロックされるかされないかで)はっきりわかるのに、なんで必要なんだろうって。
とはいえ、考えてみたら1文字ごとに処理がブロックされるってことは、ブロックされている間、他の処理をしないわけですから、もったいないオバケが大量発生してしまいます。そう考えると入力があったら読み込み、なかったら他の作業をするってのは理にかなっているかなぁと。
こんな感じ?

文字列読み込み

説明を見る限り、このサービスを使えば読み込むデータも取れそうなんですが、注意書きでこのサービスではクリアされない、入力サービスでクリアされるとわざわざ書いてあるので、このサービスで判定して、必要な時に読みに行くのが正解なんでしょう。

何が欲しいか、何がいらないか

まず譲れないのは表示できる文字は取り込みたいですね。表示できない文字は基本的に要らないことにしましょう。ただ一つだけ例外なのはリターン。これが押されたら入力の終わりと判断して処理を抜けましょう。
そういう方針で出来たのがこんなの。

;>===========================
;>      BIOSで遊ぼっ!
;>===========================

section .data

    _c_seg          equ 0x07c0
    _c_ex_area_addr equ 0x200

section .text

boot:
    ; set segment register
    mov ax, _c_seg
    mov ds, ax

    jmp main

;>****************************
;> hlt
;>****************************
_hlt:
    hlt
    jmp _hlt

_m_buf_str: times 128 db 0x00

section .text

;****************************
; get_str_ascii
;   キーボードから文字列を取り込んでアドレスをaxに返す
;****************************
get_str_ascii:

    mov si, _m_buf_str

._loop:

    ; キーボードの状態を確認する
    mov ah, 0x11
    int 0x16
    jne ._loop

    ; キーボードから1文字取り込む
    mov ah, 0x10
    int 0x16
    mov bx, ax

    ; なんだこれ?
    cmp bl, 0x20
    jg ._skip

    ; Ctrl+Retの場合終了
    cmp bx, 0x1c0a
    je ._exit

    ; Retの場合終了
    cmp bl, 0x0d
    je ._exit

    ; 空白以下ならスキップ
    cmp bl, 0x20
    jle ._skip

    ; ~以上ならスキップ
    cmp bl, 0x7e
    jg ._skip

    mov ax, bx

    cmp si, _m_buf_str
    je ._loop

    mov [si], al
    inc si

    jmp ._loop

._skip:

    mov ax, bx

    mov ah, 0x0e
    int 0x10

    mov [si], al
    inc si

    jmp ._loop

._exit:

    mov ax, _m_buf_str

    ret

;>===========================
;> main
;>===========================

main:
    ; set segment register
    mov ax, _c_seg
    mov ds, ax

    ; 改行を入れて画面を整える
    mov ah, 0x0e
    mov al, 0x0d
    int 0x10
    mov al, 0x0a
    int 0x10

    ; キーボード入力処理
    call get_str_ascii
    mov bx, ax

    ; 返ってきた文字列を表示する
    mov ah, 0x0e
    mov al, 0x0d
    int 0x10
    mov al, 0x0a
    int 0x10

    mov si, bx

_loop:
    lodsb
    or al, al
    je _exit

    int 0x10

    jmp _loop

_exit:

    ; 処理終了
    jmp _hlt

._bun: db 0x0d, 0x0a, 0x0d, 0x0a, 0x00

times 510-($-$$) db 0
db 0x55
db 0xAA

PlayWithBIOS.2.1

もっと機能とか追加したかったのですが、これ以上だと複雑になりすぎるかなぁ。
かなり短いですけど今回はここまでです。
お疲れさまでした。
次は何しましょうか。

《2024/6/3 2:06:20》