LoginSignup
17
9

More than 5 years have passed since last update.

30日でできる!OS自作入門(2日目)[Ubuntu16.04/NASM]

Last updated at Posted at 2018-03-23

30日でできる!OS自作入門(記事一覧)[Ubuntu16.04/NASM]

目的

"30日でできる!OS自作入門"の内容をUbuntu(Linux)で実行するには本の内容だけでは厳しいので調べた結果をメモ。(リンクと動作確認済みコード・コメント)

image.png

このテキストを読む上でUbuntuとnasmを使う方の参考になればと思っております。
Ubuntu 16.04 LTS

(追記:2018/05/29)
ソースコードは以下のGitHubにあげています。
https://github.com/pollenjp/myHariboteOS

helloos3

0x00007c00 - 0x00007dff : ブートセクタが読み込まれるアドレス

ブートセクタ説明用のコードはもう少し下で掲載

ipl.asm
; hello-os
; TAB=4

        ORG     0x7c00          ; メモリ上の開始位置

; ディスクのための記述

        JMP     entry
        DB      0x90
        DB      "HELLOIPL"      ; ブートセレクタの名前を自由にかいていよい  (8Byte)
        DW      512             ; 1セクタの大きさ                           (512にしなければならない)
        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      2880            ; このドライブの大きさをもう一度書く
        DB      0, 0, 0x29      ; よくわからないけどこの値にしておくといいらしい
        DD      0xffffffff      ; たぶんボリュームシリアル番号
        DB      "HELLO-OS   "   ; ディスクの名前                            (11Byte)
        DB      "FAT12   "      ; フォーマットの名前                        (8Byte)
        RESB    18              ; とりあえず18バイト開けておく

; Program Main Body
entry:
        MOV     AX, 0            ; レジスタの初期化
        MOV     SS, AX
        MOV     SP, 0x7c00
        MOV     DS, AX
        MOV     ES, AX

        MOV     SI, msg
putloop:
        MOV     AL, [SI]        ; BYTE (accumulator low)
        ADD     SI, 1           ; increment
        CMP     AL, 0           ; compare (<end msg>)
        JE      fin             ; jump to fin if equal to 0
        MOV     AH, 0x0e        ; AH = 0x0e
        MOV     BX, 15          ; BH = 0, BL = <color code>
        INT     0x10            ; interrupt BIOS
        JMP     putloop
fin:
        HLT
        JMP     fin

msg:
        DB      0x0a, 0x0a
        DB      "hello, world"
        DB      0x0a
        DB      0               ; end msg

        ;RESB    0x7dfe-($-$$)  ; これだとエラーが出た。。。
        RESB    0x7dfe-0x7c00-($-$$)

        DB      0x55, 0xaa

; ブート以外の記述

        DB      0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
        RESB    4600
        DB      0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
        RESB    1469432

helloos3で詰まったところ

        ;RESB    0x7dfe-($-$$)  ; これだとエラーが出た。。。

サンプルコードではRESB 0x7dfe-$と記述されていたが、そのまま(上のように)qemuで実行したところ以下のような挙動が起きた。
image.png

image.png

値からさらに0x7c00を引いたら治った。
image.png

ブートセクタ

ブートセクタについて少し詳しく見ていこうと思います。面倒な方は次の節に飛んでください。

今まで記述してきたhelloos.asmの中で

helloos3.img
        ......
; ディスクのための記述

        JMP     entry
        DB      0x90
        ......

という箇所についての解説ですが、ブート セクタとBPBの中に出てくる表にまとまっています。
以下のコードのコメントに名前を書いて置きます。

helloos3.asm
; hello-os
; TAB=4

        ORG     0x7c00          ; このプログラムがメモリ上のどこによみこまれるのか

; ディスクのための記述
;                                                   offset  byte
        JMP     entry           ; BS_JmpBoot        0
        DB      0x90            ; BS_JmpBoot                1
        DB      "HELLOIPL"      ; BS_OEMName        3       8
        DW      512             ; BPB_BytsPerSec    11      2 : バイト単位のセクタ サイズ
        DB      1               ; BPB_SecPerClus    13      1 : アロケーション ユニット(<-クラスタ)(割り当て単位)当たりのセクタ数
        DW      1               ; BPB_RsvdSecCnt    14      2 : 予約領域のセクタ数 (少なくともこのBPBを含むブートセクタそれ自身が存在するため、0であってはならない)
        DB      2               ; BPB_NumFATs       16      1 : FATの個数 (このフィールドの値は常に2に設定すべきである)
        DW      224             ; BPB_RootEntCnt    17      2 : ルートディレクトリに含まれるディレクトリエントリの数を示す
        DW      2880            ; BPB_TotSec16      19      2 : ボリュームの総セクタ数(古い16ビット フィールド)
        DB      0xf0            ; BPB_Media         21      1 : メディアタイプ(区画分けされた固定ディスク ドライブでは0xF8が標準値である。区画分けされないリムーバブル メディアでは0xF0がしばしば使われる)
        DW      9               ; BPB_FATSz16       22      2 : 1個のFATが占めるセクタ数
        DW      18              ; BPB_SecPerTrk     24      2 : トラック当たりのセクタ数
        DW      2               ; BPB_NumHeads      26      2 : ヘッドの数
        DD      0               ; BPB_HiddSec       28      4 : ストレージ上でこのボリュームの手前に存在する隠れた物理セクタの数(ボリュームがストレージの先頭から始まる場合(つまりフロッピー ディスクなど区画分けされていないもの)では常に0であるべきである。)
        DD      2880            ; BPB_TotSec32      32      4 : ボリュームの総セクタ数(新しい32ビット フィールド)


; FAT12/16におけるオフセット36以降のフィールド
        ;DB      0, 0, 0x29      ; 以下の3行に分けて記述
        DB      0x00            ; BS_DrvNum         36      1
        DB      0x00            ; BS_Reserved1      37      1
        DB      0x29            ; BS_BootSig        38      1

        DD      0xffffffff      ; BS_VolID          39      4 : ボリュームシリアル番号
        DB      "HELLO-OS   "   ; BS_VolLab         43      11 : ディスクの名前(ルート ディレクトリに記録される11バイトのボリューム ラベルに一致する)
        DB      "FAT12   "      ; BS_FilSysType     54      8 : フォーマットの名前
        RESB    18              ; とりあえず18バイト開けておく

; START BS_BootCode                                 64      448
; (ブートストラップ プログラム。システム依存フィールドで、未使用時はゼロで埋める。)
entry:
        MOV     AX, 0            ; レジスタの初期化
        MOV     SS, AX
        MOV     SP, 0x7c00
        MOV     DS, AX
        MOV     ES, AX

        MOV     SI, msg
putloop:
        MOV     AL, [SI]        ; BYTE (accumulator low)
        ADD     SI, 1           ; increment
        CMP     AL, 0           ; compare (<end msg>)
        JE      fin             ; jump to fin if equal to 0
        MOV     AH, 0x0e        ; AH = 0x0e
        MOV     BX, 15          ; BH = 0, BL = <color code>
        INT     0x10            ; interrupt BIOS
        JMP     putloop
fin:
        HLT
        JMP     fin

msg:
        DB      0x0a, 0x0a
        DB      "hello, world"
        DB      0x0a
        DB      0               ; end msg

        ;RESB    0x7dfe-($-$$)  ; これだとエラーが出た。。。
        RESB    0x7dfe-0x7c00-($-$$)    ; 現在の場所から0x1fdまで(残りの未使用領域)を0で埋める。
; END BS_BootCode

        DB      0x55, 0xaa      ; BS_BootSign       510     2 : 以下の記述と同様
        ;DW      0xAA55

このMakefile作成して以下のコマンド実行でエミュレータでいつものHello, Worldが出力される。

terminal(入力)
$ make run

※makeコマンドはUbuntuの中にデフォルトで入っている。

Makefile

説明はおおよそ本に書いてあるとおり、一応Ubuntuで最低限動作するコードを載せときます。

Makefile
# ファイル生成規則

ipl.bin : ipl.asm Makefile
    nasm ipl.asm -o ipl.bin -l ipl.lst

#helloos.img : ipl.bin tail.bin Makefile
helloos.img : ipl.bin Makefile
    #cat ipl.bin tail.bin > helloos.img
    cat ipl.bin > helloos.img

asm :
    make -r ipl.bin

img :
    make -r helloos.img

run :
    make img
    qemu-system-i386 helloos.img

【追記】helloos4

あほみたいにコメント追加してMakefileでGNU Makeらしく書いた。

ipl.asm
; hello-os
; FAT12 format

; - [Tips IA32(x86)命令一覧](http://softwaretechnique.jp/OS_Development/Tips/IA32_instructions.html)
;   - [Add命令](http://softwaretechnique.jp/OS_Development/Tips/IA32_Instructions/ADD.html)
;   - [MOV命令](http://softwaretechnique.jp/OS_Development/Tips/IA32_Instructions/MOV.html)

;=======================================================================================================================
; ブートセクタ (512バイト)
; > 0x00007c00 - 0x00007dff : ブートセクタが読み込まれるアドレス
; > [ソフトウェア的用途区分 - (AT)memorymap - os-wiki](http://oswiki.osask.jp/?%28AT%29memorymap#qd4cd666)
; リトルエンディアン

        ;=======================================================================
        ; このプログラムがメモリ上のどこによみこまれるのか
        ; > [7.1.1 ORG: Binary File Program Origin - NASM - The Netwide Assembler](https://www.nasm.us/doc/nasmdoc7.html#section-7.1.1)
        ORG     0x7c00

        ;=======================================================================
        ; ディスクのための記述
        ; http://elm-chan.org/docs/fat.html#notes
        ; BPB(BIOS Parameter Block)
                                ; Name             | Offset              | Byte | Description
                                ;                                               | 
        ; FAT12/16/32共通フィールド(オフセット0~35)
        JMP     entry           ; BS_JmpBoot       | 0x0000-0x0002 0-2   |    3 | Jump to Bootstrap
        DB      0x90            ;                                               | ブートストラッププログラムへのジャンプ命令(x86命令)。
                                ;                                               | 0xEB, 0x??, 0x90 (ショート ジャンプ+NOP)
        DB      "HELLOIPL"      ; BS_OEMName       | 0x0003-0x000a 3-10  |    8 | これは単なる名前である
        DW      512             ; BPB_BytsPerSec   | 0x000b-0x000c 11-12 |    2 | セクタあたりのバイト数.
                                ;                                               | 512, 1024, 2048, 4096
                                ;                                               | Bytes Per Cluster
        DB      1               ; BPB_SecPerClus   | 0x000d           13 |    1 | アロケーションユニット(割り当て単位)当たりのセクタ数
                                ;                                               | アロケーションユニットはクラスタと呼ばれている
                                ;                                               | Secters Per Cluster
        DW      1               ; BPB_RsvdSecCnt   | 0x000e-0x000f 14-15 |    2 | 予約領域のセクタ数 (少なくとも
                                ;                                               | このBPB(BIOS Parameter Block)を含むブート
                                ;                                               | セクタそれ自身が存在するため0であってはならない)
        DB      2               ; BPB_NumFATs      | 0x0010           16 |    1 | FATの個数 
                                ;                                               | (このフィールドの値は常に2に設定すべきである)
        DW      224             ; BPB_RootEntCnt   | 0x0011-0x0012 17-18 |    2 | FAT12/16ボリュームではルートディレクトリに
                                ;                                               | 含まれるディレクトリエントリの数を示す.
                                ;                                               | このフィールドにはディレクトリテーブルのサイズが
                                ;                                               | 2セクタ境界にアライメントする値,つまり,
                                ;                                               | BPB_RootEntCnt*32がBPB_BytsPerSecの偶数倍になる値
                                ;                                               | を設定すべきである. (32というのはディレクトリエントリ1個のサイズ)
                                ;                                               | 最大の互換性のためにはFAT16では512に設定すべき.
                                ;                                               | FAT32ボリュームではこのフィールドは使われず,
                                ;                                               | 常に0でなければならない.
                                ;                                               | 224x32=4x16x32=4x512
                                ;                                               | 512=32x16
                                ;                  |                     |      | 
        DW      2880            ; BPB_TotSec16     | 0x0013-0x0014 19-20 |    2 | ボリュームの総セクタ数(古い16ビットフィールド).
                                ;                                               | ボリュームの4つの領域全てを含んだセクタ数.
                                ;                                               | FAT12/16でボリュームのセクタ数が0x10000以上になる
                                ;                                               | ときは,このフィールドには無効値(0)が設定され,真の
                                ;                                               | 値がBPB_TotSec32に設定される.
                                ;                                               | FAT32ボリュームでは,このフィールドは必ず無効値で
                                ;                                               | なければならない.
                                ;                                               | 0x10000=(2^4)^4=65536 > 2880
                                ;                  |                     |      | 
        DB      0xf0            ; BPB_Media        | 0x0015           21 |    1 | 区画分けされた固定ディスクドライブでは0xF8が標準
                                ;                                               | 値である. 区画分けされないリムーバブルメディアで
                                ;                                               | は0xF0がしばしば使われる. このフィールドに有効な
                                ;                                               | 値は,0xF0,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
                                ;                                               | で,ほかに重要な点はこれと同じ値をFAT[0]の下位8
                                ;                                               | ビットに置かなければならないということだけである.
                                ;                                               | これはMS-DOS 1.xでメディアタイプの設定に遡り,
                                ;                                               | 既に使われていない。
                                ;                  |                     |      | 
        DW      9               ; BPB_FATSz16      | 0x0016-0x0017 22-23 |    2 | 1個のFATが占めるセクタ数.
                                ;                                               | このフィールドはFAT12/FAT16ボリュームでのみ使われる.
                                ;                                               | FAT32ボリュームでは必ず無効値(0)でなければならず,
                                ;                                               | 代わりにBPB_FATSz32が使われる. FAT領域のサイズは,
                                ;                                               | この値 * BPB_NumFATsセクタとなる。
                                ;                  |                     |      | 
        DW      18              ; BPB_SecPerTrk    | 0x0018-0x0019 24-25 |    2 | トラック当たりのセクタ数
        DW      2               ; BPB_NumHeads     | 0x001a-0x001b 26-27 |    2 | ヘッドの数
        DD      0               ; BPB_HiddSec      | 0x001c-0x001f 28-31 |    4 | ストレージ上でこのボリュームの手前に存在する隠れ
                                ;                                               | た物理セクタの数. 一般的にIBM PCのディスクBIOSで
                                ;                                               | アクセスされるストレージに関するものであり,どの
                                ;                                               | ような値が入るかはシステム依存. ボリュームがスト
                                ;                                               | レージの先頭から始まる場合(つまりフロッピーディ
                                ;                                               | スクなど区画分けされていないもの)では常に0である
                                ;                                               | べきである.
                                ;                  |                     |      | 
        DD      2880            ; BPB_TotSec32     | 0x0020-0x0023 32-35 |    4 | ボリュームの総セクタ数(新しい32ビットフィールド).
                                ;                                               | この値はボリュームの4つの領域全てを含んだセクタ数
                                ;                                               | である.
                                ;                                               | FAT12/16ボリュームで総セクタ数が0x10000未満のとき,
                                ;                                               | このフィールドは無効値(0)でなければならなず,真の
                                ;                                               | 値はBPB_TotSec16に設定される.
                                ;                                               | FAT32ボリュームでは常に有効値が入る.

        ;=======================================================================
        ; FAT12/16におけるオフセット36以降のフィールド
                                ; Name             | Offset              | Byte | Description
                                ;                  |                     |      | 
        DB      0x00            ; BS_DrvNum        | 0x0024           36 |    1 |
        DB      0x00            ; BS_Reserved1     | 0x0025           37 |    1 |
        DB      0x29            ; BS_BootSig       | 0x0026           38 |    1 |

        DD      0xffffffff      ; BS_VolID         | 0x0027-0x002a 39-42 |    4 | ボリュームシリアル番号
        DB      "HELLO-OS   "   ; BS_VolLab        | 0x002a-0x0036 43-54 |   11 | ディスクの名前(ルートディレクトリに記録される11バイトのボリュームラベルに一致する)
        DB      "FAT12   "      ; BS_FilSysType    | 0x0036-0x003d 54-61 |    8 | フォーマットの名前
        RESB    18              ;                  | 0x003e-0x004f 62-79 |    8 | Reserve Bytes : [3.2.2 RESB and Friends: Declaring Uninitialized Data](https://www.nasm.us/doc/nasmdoc3.html#section-3.2.2)
                                ;                                               | 18バイト空けて 0x7c50 の直前まで埋める
                                ;                                               | naskでは0で初期化するみたいだがnasmだ
                                ;                                               | と初期化しない


        ;=======================================================================
        ; START BS_BootCode                                | 64      448
        ; (ブートストラッププログラム. システム依存フィールドで、未使用時はゼロで埋める。)
        ; 0x7c50
        ;
        ; rb (register byte),  rw (register word), rd (register double-word) 等の表記は
        ; https://www.intel.co.jp/content/dam/www/public/ijkk/jp/ja/documents/developer/IA32_Arh_Dev_Man_Vol2A_i.pdf
        ; によっている.
        ; >   | rb | rw | rd  |
        ; > 0 | AL | AX | EAX |
        ; > 1 | CL | CX | ECX |
        ; > 2 | DL | DX | EDX |
        ; > 3 | BL | BX | EBX |
        ; > 4 | AL | SP | ESP |
        ; > 5 | CL | BP | EBP |
        ; > 6 | DL | SI | ESI |
        ; > 7 | BL | DI | EDI |
        ; > imm8 - 即値バイト値。記号 imm8 は -128 から +127 までの符号付き数値である
        ; == 16 bit register ==
        ; AX : acumulator
        ; CX : Counter
        ; DX : Data
        ; BX : Base
        ; SP : Stack Pointer
        ; BP : Base  Pointer
        ; SI : Source      Index
        ; DI : Destination Index
        ; ES : Extra Segment
        ; CS : Code  Segment
        ; SS : Stack Segmengt
        ; DS : Data  Segmengt
        ; FS : no-name
        ; GS : no-name

entry:
        MOV     AX, 0           ; AX (rw1) に0(imm8)代入
        MOV     SS, AX
        MOV     SP, 0x7c00
        MOV     DS, AX
        MOV     ES, AX

        MOV     SI, msg
putloop:
        MOV     AL, BYTE [SI]   ; BYTE (accumulator low)
        ADD     SI, 1           ; increment stack index
        CMP     AL, 0           ; compare (<end msg>)
        JE      fin             ; jump to fin if equal to 0

                                                                ; 一文字表示
        MOV     AH, 0x0e        ; AH = 0x0e
        MOV     BX, 15          ; BH = 0, BL = <color code>
        INT     0x10            ; interrupt BIOS
                                                                ; [INT(0x10); ビデオ関係 - (AT)BIOS - os-wiki](http://oswiki.osask.jp/?%28AT%29BIOS#n5884802)
        JMP     putloop
fin:
        HLT
        JMP     fin

msg:
        DB      0x0a, 0x0a
        DB      "hello, world"
        DB      0x0a
        DB      0                   ; end msg

        ;RESB    0x7dfe-($-$$)      ; これだとエラーが出た。。。
                                                                      ; セクタサイズ 512 Byte なので 510 Byte目までを埋めたいときは
                                                                      ; 0x1fe - ($-$$) としてやればいい
                                    ; > you can tell how far into the section you are by using ($-$$)
                                    ; > [3.5 Expressions - NASM - The Netwide Assembler](https://www.nasm.us/doc/nasmdoc3.html#section-3.5)
        RESB    0x1fe-($-$$)              ; 現在の場所から 0x1fd (0x1fe の直前)
                                    ; まで(残りの未使用領域)を0で埋める
                                    ; (naskでは0で初期化するみたいだがnasm
                                    ; だと初期化しない) 
                                    ; 0x7dfe-0x7c00 = 32254−31744 = 510

        ;=======================================================================
        ; END BS_BootCode       ; Name             | Offset              | Byte | Description
        DB      0x55, 0xaa      ; BS_BootSign      | 0x7dfe-0x7dff       | 510  |
Makefile
.DEFAULT_GOAL : all
.PHONY : all
all : img

ipl.bin : ipl.asm

%.bin : %.asm
    nasm $^ -o $@ -l $*.lst

helloos.img : ipl.bin
    cat $^ > $@


.PHONY : asm
asm :
# -r, --no-builtin-rules
#     Eliminate use of the built-in implicit rules.
#     Also clear out the default list of suffixes for suffix rules.
#   make --no-builtin-rules ipl.bin
    make ipl.bin

.PHONY : img
img :
    make helloos.img

.PHONY : run
run :
    make img
    qemu-system-i386 helloos.img

.PHONY : clean
clean :
# lstは残しておいてもいいと思うのでcleanに入れていない
    @rm *.img *.bin

実行

$ cd helloos4
($ make)
$ make run

参考

17
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
9