『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》