『OSを書く:初歩から一歩ずつ』を一通りなめてみたのですが、実は練習問題の「キーボードから読み込む」の自分の回答が少し不満でした。今回はそのあたりのお話を。
環境はAMD64+Windows10+VMware Workstation 17 Player+Debian 12.4です。
キー押下情報を取り込む
BIOSのキーボードサービスの中からキーボード入力読み込みサービスを使いました。
ですがこれ、1文字入力なんですよね。1部のキーを除いて押した瞬間に取り込んでしまう。複数文字を得るためには複数回呼ばなければならない。あまつさえ、入力待ちの間は処理がブロックされて他に何もできない。
せめて1回で複数文字を入力したい。その文字列を編集できなくてもいいから、まとまった文字を入力したい。
とまぁそんなことを考えたわけです。
複数文字入力
バイオスの説明とかつらつらと眺めていたら、キーボードステータス取得というのがありました。説明を見たら要するに「キーボード入力の有り無しをフラグで返す」らしいです。
AH : 0x02 : キーボードステータス読み込みファンクションを指定EFLAGSのZFフラグ : 0 : データが存在しない
1 : データが存在する(入力ファンクション実行でクリアされる)
AH : スキャンコード
AL : アスキーコード
最初見た時、これ何の役に立つんだろうって思いました。だって読みに行けばデータの有り無しは(ブロックされるかされないかで)はっきりわかるのに、なんで必要なんだろうって。
とはいえ、考えてみたら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》