INT 16H風のキーボード割り込みを自作

## 「手探りでCUI OS作成に挑む」連載

この記事は「手探りでCUI OS作成に挑む」連載の一部です。
全体の目次は[「手探りでCUI OS作成に挑む」連載目次](https://qiita.com/earthen94/items/b5d77f98f0cf85a13346)を御覧下さい。

## 目的
BIOSを開発する前準備として割り込みの処理を少しずつ実装しています。
今日は文字入力を実装しました。

## 実装コード
```
org 0x7C00
bits 16

jmp start

; データ定義
msg_prompt db "Press a-j: ", 0
msg_newline db 13, 10, 0  ; エンターで改行  0x0D,0x0A,0に等しい

start:

    ; 设置栈
    mov ax, 0x9000
    mov ss, ax
    mov sp, 0xFFFE

    mov ax, cs 
    mov ds, ax
    mov es, ax

    mov si, 0x2F * 4      ; IVTの0x2F番地に登録する

    ; IVT[0x2F] にキーボード処理のアドレスを設定
    lea dx, get_key        ; DXへアドレスを格納
    mov word [si], dx      ; オフセットを設定（低位）
    mov word [si + 2], cs  ; セグメントを設定（高位）
    
    ; Press a-jと画面表示
    mov si, msg_prompt
    call print_string
    
main_loop:
    ; 自分で定義した文字入力の処理を呼び出す
    int 0x2F
    cmp al, 0         ; a-j以外は
    je main_loop      ; 表示しない
    
    ; 入力された文字を画面表示
    call print_char
    
    ; 改行
    mov si, msg_newline
    call print_string
    
    jmp main_loop     ; 次の文字の入力へ

; 文字表示
print_char:
    pusha
    mov ah, 0x0E
    int 0x10
    popa
    ret

; 文字列表示
print_string:
    pusha
    mov ah, 0x0E
.print_loop:
    lodsb
    or al, al
    jz .done
    int 0x10
    jmp .print_loop
.done:
    popa
    ret

; キーボードからの入力を受け取り対応するASCIIコードをAL経由で返す
get_key:
    in al, 0x64       ; 0x64ポート経由で入力されているかを確認
    test al, 1        ; 0ビット目　1：有り　0：無し
    jz get_key        ; 押されていなければget_keyへ戻って初めから
    
    in al, 0x60       ; 押されたキーの番号を取得

    ; キーの番号最高位が　０：押された　１：離された
    test al, 0x80
    jnz get_key       ; 離された場合は入力と見做さない
    
    ; キーの番号からASCIIコードへ変換していく(今回は検証が目的な為、少数の実装に留める)
    cmp al, 0x1E      ; 'a'
    je .a
    cmp al, 0x30      ; 'b'
    je .b
    cmp al, 0x2E      ; 'c'
    je .c
    cmp al, 0x20      ; 'd'
    je .d
    cmp al, 0x12      ; 'e'
    je .e
    cmp al, 0x21      ; 'f'
    je .f
    cmp al, 0x22      ; 'g'
    je .g
    cmp al, 0x23      ; 'h'
    je .h
    cmp al, 0x17      ; 'i'
    je .i
    cmp al, 0x24      ; 'j'
    je .j
    
    xor al, al        ; a-j以外は0を返す
    ret
    
.a:
    mov al, 'a'
    ret
.b:
    mov al, 'b'
    ret
.c:
    mov al, 'c'
    ret
.d:
    mov al, 'd'
    ret
.e:
    mov al, 'e'
    ret
.f:
    mov al, 'f'
    ret
.g:
    mov al, 'g'
    ret
.h:
    mov al, 'h'
    ret
.i:
    mov al, 'i'
    ret
.j:
    mov al, 'j'
    ret
    
times 510-($-$$) db 0
dw 0xAA55
```

## 動作確認

### QEMU
```
nasm -f bin boot.asm -o boot.bin
qemu-system-i386 -fda boot.bin
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/51509276-35d4-48f9-be00-833c688d517e.png)

### 8086実機
コンパクトフラッシュへ書き込む
```
sudo dd if=boot.bin of=/dev/sdc bs=512 count=1 conv=notrunc
```
起動はしたのですが、何を押しても反応がなく、適当なキーを何回か押していると、いきなり最後に押したキーが大量に表示されてしまいました。
QEMU上ではうまく動作しましたが、実機では意図通りに動かすにはもう少し研究が必要なようです。

実機のBIOSは変更できないため、これから作ろうとしているBIOSコードはQEMUのようなエミュレータ上で動作させることしかできません。
そういう意味では、実機で動かす意味は本質的には無いのですが、「なぜ動かないのか」には非常に興味があるため、今後の課題とします。

![微信图片_2025-06-05_212102_837.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/f0735c31-9137-4d21-a92a-618150dab99d.png)
![微信图片_2025-06-05_212108_299.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/181865d2-bc97-4162-8387-71420933ab14.png)


## 今日の作業風景
![微信图片_2025-06-05_210210_627.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/6b00079d-d21e-4640-ba87-483c6993489f.png)