30日OS
Ubuntuで10日目までやる方法は 【Ubuntu/NASMで】『30日でできる!OS自作入門』を10日目まで進めたのでポイントをまとめてみた に載ってる
1日目
必要なやつを入れる
- ghex: バイナリエディタ
- QEMU: OS用の?エミュレータ
- nasm: アセンブラ
sudo apt install qemu ghex nasm
helloos.img
を以下のコマンドでバイナリエディタで開き本の通りに打ち込む
touch helloos.img
ghex helloos.img
以下のコマンドでQEMUでhelloos.img
を起動する
qemu-system-i386 helloos.img
ここで上手く動かない場合は打ち間違いの可能性があるのでとりあえずそのまま先に進む. そうすると今度はバイナリを手打ちするのではなくアセンブリを使って書くというのがあるので, それをアセンブル(後述)して生成されたイメージを使うと上手くいくかも.
気になる場合はアセンブリをアセンブルしたイメージとdiffを取ってみると良い.
diff <(xxd helloos.img) <(xxd helloos_from_asm.img)
How do I compare binary files in Linux?
アセンブリ
helloos.asm
を作る
; hello-os
; TAB=4
; 以下は標準的なFAT12フォーマットフロッピーディスクのための記述
DB 0xeb, 0x4e, 0x90
DB "HELLOIPL" ; ブートセクタの名前を自由に書いて良い(8バイト) ; IPL(Initial Program Loader) ; DB命令で文字列を書くとそれに対応する文字コードを調べて1バイトずつ並べてくれる
DW 512 ; 1セクタの大きさ (512にしなければいけない) ; DW (DataWord) 命令は2バイト(16bit)
DB 1 ; クラスタの大きさ (1セクタにしなければいけない)
DW 1 ; FATがどこから始まるか (普通は1セクタ目からにする)
DB 2 ; FATの個数 (2にしなければいけない)
DW 224 ; ルートディレクトリ領域の大きさ (普通は224エントリにする)
DW 2880 ; このドライブの大きさ (2880セクタにしなければならない)
DB 0xf0 ; メディアタイプ (0xf0にしなければならない)
DW 9 ; FAT領域の長さ (9セクタにしなければならない)
DW 18 ; 1トラックにいくつのセクタがあるか (18にしなければならない)
DW 2 ; ヘッドの数 (2にしなければならない)
DD 0 ; パーティションを使っていないのでここは必ず0 ; DD(Data DoubleWord) は4バイト(32bit)
DD 2880 ; このドライブの大きさをもう一度書く
DB 0, 0, 0x29 ; よくわからないけどこの値にしておくといいらしい
DD 0xffffffff ; たぶんボリュームシリアル番号
DB "HELLO-OS " ; ディスクの名前 (11Byte)
DB "FAT12 " ; フォーマットの名前 (8Byte)
RESB 18 ; とりあえず18バイト開けておく
; プログラム本体
DB 0xb8, 0x00, 0x00, 0x8e, 0xd0, 0xbc, 0x00, 0x7c
DB 0x8e, 0xd8, 0x8e, 0xc0, 0xbe, 0x74, 0x7c, 0x8a
DB 0x04, 0x83, 0xc6, 0x01, 0x3c, 0x00, 0x74, 0x09
DB 0xb4, 0x0e, 0xbb, 0x0f, 0x00, 0xcd, 0x10, 0xeb
DB 0xee, 0xf4, 0xeb, 0xfd
; メッセージ部分
DB 0x0a, 0x0a ; 改行を2つ
DB "hello, world"
DB 0x0a ; 改行
DB 0
RESB 0x1fe-($-$$) ; 0x001feまでを0x00で埋める命令 ;0x1fe = 510 ;($-$$) にはここまで並べた命令の大きさ(バイト)が入っている
DB 0x55, 0xaa ; 最初のセクタ(ブートセクタ)の最後の2バイトが 55 AA なら実行してくれる ; 1セクタ=512バイト
; 以下はブートセクタ以外の部分の記述
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
RESB 4600
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
RESB 1469432
; nasm hello.asm -o helloos_from_asm.img でバイナリーを出力できる
以下のコマンドでアセンブルする
nasm hello.asm -o helloos_from_asm.img
-lオプションで対応する機械語を出力出来る
nasm hello.asm -o helloos_from_asm.img -l helloos.lst
アセンブリ
DB命令: (data byte). ファイルの内容を1バイトだけ直接書く命令
RESB命令: (reserve byte): 指定したバイト数分予約(あけておく)する
レジスタ
AX: アキュムレータ(accumulator:累積演算機という意味)
CX: カウンタ(counter:数を数える機械という意味)
DX: データ(data:データという意味)
BX: ベース(base:土台とか基点という意味)
SP: スタックポインタ(stack pointer:スタック用ポインタ)
BP: ベースポインタ(base pointer:ベース用ポインタ)
SI: ソースインデックス(source index:読み込みインデックス)
DI: デスティネーションインデックス(destination index:書き込みインデックス)
Day03
フロッピーディスクの構造
フロッピーディスクは80シリンダあって, ヘッドが合計2つあって, さらにそれぞれのシリンダに18セクタがあり, 1セクタは512バイトなので
80 * 2 * 18 * 512 = 1474560 = 1440 [KB]
Day03
BIOSが設計された当時は32bitレジスタがCPUに載せれなかったらしく, そのままではレジスタが持てる値がメモリアドレスの範囲より小さいので, しょうがないので補助的な役割をするセグメントレジスタを作った.
今は32bitレジスタ(EBXレジスタとか)は普通にあるけど, 上記の理由でBIOSでは読み込み先アドレスを指定するのにEBXレジスタなどの32bitレジスタではなく, セグメントレジスタと他の16bitレジスタを使ってメモリアドレスを計算し, プログラムを読み込む位置を指定する
メモリ番地の指定には常にセグメントレジスタを一緒に指定しなければならないという決まりがある. 省略した場合, [DS:
を指定したものとみなされる.
例: MOV CX, [1234]
は正確に書くと MOV CX, [DS:1234]
となる.
そして[DS:1234]
は DS * 16 + 1234
を意味する. なので, ブートセクタが読み込まれた時にDSレジスタを0で初期化しないといけないのだ.
Day03
IPL(Initial Program Loader)の実装が一段落してきたところでOS本体の開発に取りかかるわけだが, Ubuntuでやる場合はツールとかが本とはだいぶ違うので他の人の記事見たほうが良いな
- 30日でできる!OS自作入門(3日目)[Ubuntu16.04/NASM]
- [OS作成]30日でできる!OS自作入門 3日目 (4)
-
『OS自作入門』を読んでみた。(その6)
mformat
やmcopy
についての説明がわかりやすかった
C言語の導入はLinux環境だとかなりツールが違うので他の人の記事を見たほうが良い
Day04
Ubuntu20.04LTS の環境でやろうとしたらなぜか harib01f
でコンパイル出来ずに詰まった.
どうやらリンカが上手くリンク出来てないのが原因だった.
解決策としてはgcc-7を使うとコンパイルすることが出来た. gccの別バージョンのインストール方法はこれ見ると良い
gcc-8以降を使うと .note.gnu.property
というセクションが自動的に作成され, これが原因でリンク出来ないようだ. gcc-7 ではこのセクションは生成されないので正しくリンクすることが出来た.
day08時点でのメモリマップ (p171)
day09c, 自分の環境だと最適化が悪さをせずに普通に動いた...
day13 タイマーの性能測定のところでウィンドウの文字列更新処理を削除するとマウスやキーボードが反応しなくなる
while (1)
{
// ここから
sprintf(s, "%d", timerctl.count);
putfonts8_asc_sht(sht_win, 40, 28, COL8_000000, COL8_C6C6C6, s, 10);
// ここまで消すと動かない
io_cli(); // 割り込み禁止!
マウスやキーボード, タイマーなどが全て効かなくなってるから, whileループが動いていない or 割り込みが発生していない?
day14 set490(&fifo, 1);
を追加するとマウスが動かなくなる.
day14 set490(&fifo, 1); を追加するとマウスが動かなくなる.
これはtimer_settime()
内のタイマを挿入する処理が悪かった.
day13 タイマーの性能測定のところでウィンドウの文字列更新処理を削除するとマウスやキーボードが反応しなくなる
io_cli(); // 割り込み禁止!
if (fifo32_getsize(&fifo) == 0) {
io_sti();
// io_stihlt(); // こっちにすると動く
io_cli()
と io_sti()
が高速に切り替わり過ぎてCLI実行した後に来た割り込みが破棄されてるかなんかなってる?
day14d 高解像度にしよう(1) にてサンプル通りにやっても動かなかった.
「30日でできる! OS自作入門」をMac向けに環境構築する に従ってasmhead.asm
に変更を加えたら正しく動いた
; MOV DWORD [VRAM], 0xe0000000 ; 元のサンプル
MOV DWORD [VRAM], 0xfd000000 ; こんな風に修正すると治る