「手探りでCUI OS作成に挑む」連載
この記事は「手探りでCUI OS作成に挑む」連載の一部です。
全体の目次は「手探りでCUI OS作成に挑む」連載目次を御覧下さい。
目的
コードが長くなって来たので処理の種類毎にファイルを分けます。
%include "file.asm"と書くとその場所にそのファイルの中身が展開されます。
ファイル構造
test
├── boot.asm
├── kernel.asm
├── print.asm
├── input.asm
├── command.asm
├── strings.asm
└── data.asm
boot.asm
boot.asm
org 0x7C00
start:
; セグメント初期化
xor ax, ax
mov ds, ax
mov es, ax
; スタック設定
mov ss, ax
mov sp, 0x7C00
; ディスクから第2セクタ(LBA=1)を 0x8000 に読み込む
mov ah, 0x02 ; INT 13h サービス 2: 読み込み
mov al, 2 ; 読み込むセクタ数 = 2
mov ch, 0 ; シリンダ = 0
mov cl, 2 ; セクタ番号 = 2(1始まり)
mov dh, 0 ; ヘッド = 0
mov dl, 0 ; ドライブ番号(通常フロッピーは 0x00)
mov bx, 0x7E00 ; 読み込み先アドレス
int 0x13
jc disk_error ; エラー時に無限ループ
; 読み込んだコードへジャンプ(第2セクタ)
jmp 0x0000:0x7E00
disk_error:
jmp $ ; 無限ループ(エラー時に停止)
times 510-($-$$) db 0
dw 0xAA55
kernel.asm
kernel.asm
org 0x7E00
%define MAX_INPUT 64 ; 命令の最大入力文字数
start:
; レジスタ初期化
xor ax, ax
mov ds, ax
mov es, ax
; スタック設定
mov ss, ax
mov sp, 0x7C00
; 画面クリア
mov ax, 0x0003
int 0x10
; 起動時の文言表示
mov si, welcome_msg
call print_string
; 命令を待ち受ける無限ループ
command_loop:
; c:\>を表示
mov si, prompt
call print_string
; キーボードから入力されるのを待つ(入力が終わるまで返ってこない)
call read_input
; 入力された命令によって処理をする
call parse_command
; command_loopへ戻りc:\>を表示して次の命令を待つ
jmp command_loop
%include "print.asm"
%include "input.asm"
%include "command.asm"
%include "strings.asm"
%include "data.asm"
times 1024-($-$$) db 0
print.asm
print.asm
; 文字列表示
print_string:
pusha
mov ah, 0x0E
mov bh, 0
.print_loop:
lodsb
test al, al
jz .done
int 0x10
jmp .print_loop
.done:
popa
ret
input.asm
input.asm
; キーボード入力関数
; 入力された結果はinput_bufferへ格納
read_input:
pusha
mov di, input_buffer ;入力バッファ
mov cx, 0 ; 文字数を数える
.read_char:
; 一文字読み込み(結果はASCIIコードでALへ格納される)
mov ah, 0x00
int 0x16
; エンターが押された場合の処理
cmp al, 0x0D
je .done_input
; バックスペースが押された場合の処理
cmp al, 0x08
je .backspace
; 長さが超えていないか確認
cmp cx, MAX_INPUT ; 入力文字数が上限に達していないか確認
jae .read_char ; 上限超えたら無視して次のキー入力へ
; 入力に問題が無い場合は入力バッファへ格納
stosb ; AL の文字を [ES:DI] に格納(バッファに保存)、DI++(次の位置へ)
inc cx ; 入力文字数+1
; 入力された文字を画面へ表示
mov ah, 0x0E
int 0x10
jmp .read_char
.backspace:
; バックスペース
test cx, cx
jz .read_char ; 何も入力されていない場合のバックアップは無視(消す対象が無い)
; カーソルを左に動かす
mov ah, 0x0E
mov al, 0x08
int 0x10 ; カーソルを左に移動
mov al, ' '
int 0x10 ; 空白で上書きする
mov al, 0x08
int 0x10 ; カーソルを左に移動
; 入力バッファを更新
dec di
dec cx
jmp .read_char
.done_input:
; 文字列を終了(終端文字を追加)
mov al, 0
stosb
; 改行
mov ah, 0x0E
mov al, 0x0D
int 0x10
mov al, 0x0A
int 0x10
popa
ret
command.asm
command.asm
; 命令解析関数
parse_command:
pusha
; 小文字→大文字変換(大文字小文字による差異を無くして後ろの文字列比較処理を簡単にする)
mov si, input_buffer
call to_upper
; 空でないか
cmp byte [si], 0
je .empty
; 命令毎に処理(それぞれの命令と入力を比較して一致すれば実行)
; dir実行
mov di, cmd_dir
call strcmp
je .do_dir
; help実行
mov di, cmd_help
call strcmp
je .do_help
; reboot実行
mov di, cmd_reboot
call strcmp
je .do_reboot
; 未定義命令
mov si, unknown_cmd
call print_string
jmp .done
.do_dir:
call show_dir
jmp .done
.do_help:
mov si, help_text
call print_string
jmp .done
.do_reboot:
int 0x19
.empty:
.done:
popa
ret
; dir ファイル一覧表示関数
show_dir:
pusha
mov si, dir_header
call print_string
mov si, file_list
call print_string
popa
ret
strings.asm
strings.asm
; 小文字が含まれていれば大文字へ変換
; SI=対象文字列
to_upper:
pusha
.loop:
lodsb
test al, al
jz .done
cmp al, 'a'
jb .next
cmp al, 'z'
ja .next
sub al, 0x20
mov [si-1], al
.next:
jmp .loop
.done:
popa
ret
; 文字列比較
; SI=文字列1, DI=文字列2
; 結果: ZF=1 if equal
strcmp:
pusha
.compare:
mov al, [si]
mov bl, [di]
cmp al, bl
jne .not_equal
test al, al
jz .equal
inc si
inc di
jmp .compare
.equal:
popa
cmp al, al ; ZF=1
ret
.not_equal:
popa
cmp al, bl ; ZF=0
ret
data.asm
data.asm
; 各種文字列をここに保存
welcome_msg db 'Simple CLI Shell', 0x0D, 0x0A
db 'Type HELP for commands', 0x0D, 0x0A, 0
prompt db 'C:\> ', 0
cmd_dir db 'DIR', 0
cmd_help db 'HELP', 0
cmd_reboot db 'REBOOT', 0
unknown_cmd db 'Unknown command', 0x0D, 0x0A, 0
help_text db 'Available commands:', 0x0D, 0x0A
db 'DIR - Show files', 0x0D, 0x0A
db 'HELP - This help', 0x0D, 0x0A
db 'REBOOT - Restart system', 0x0D, 0x0A, 0
dir_header db 'Directory Listing:', 0x0D, 0x0A
db '-----------------', 0x0D, 0x0A, 0
; ファイルシステムを実装していない為、一旦これらの文字列を固定で表示する
file_list db 'KERNEL.BIN', 0x0D, 0x0A
db 'COMMAND.COM', 0x0D, 0x0A
db 'CONFIG.SYS', 0x0D, 0x0A, 0
input_buffer times MAX_INPUT+1 db 0
動作確認
コードそのものは変更していないため、動作に変化は無し。
ubuntu@ubuntu:~/kaihatsu/test2$ nasm -f bin boot.asm -o boot.bin
ubuntu@ubuntu:~/kaihatsu/test2$ nasm -f bin kernel.asm -o kernel.bin
ubuntu@ubuntu:~/kaihatsu/test2$ cat boot.bin kernel.bin > os-image.bin
ubuntu@ubuntu:~/kaihatsu/test2$ qemu-system-i386 -fda os-image.bin
詰まったこと
- 2つのファイルに同じ関数を貼り付けコンパイルエラーが出た
- karnel.asm内のstartの前にincludeを配置し、qemuで立ち上げた時にエラーが出た。