自作OS Advent Calendar 2016 11日目の @Hiroyuki-Nagata です.
前の人は 自分でした,次の人も たぶん自分です.
この記事はいろいろな用語が出てくるので、全て参考リンクを張っています。
OS自作入門でのアセンブラの使われ方
まず、なぜアセンブラが使われているのか?
-
ブートローダー
- 正確には一次ブートローダーがBIOSなので、2次ブートローダーと言うべきでしょうか
- 詳しくはWikipediaで Wikipedia - IBM PC互換機のブート手順
- OS自作入門の中で、このブートローダーに読み込んでもらうファイルとして作成していくところのものを マスターブートレコード と呼ぶ(←Linuxユーザーならよく聞くやつですね)
-
ipl10.nas
という名前で作成するアセンブラが、このマスターブートレコード -
asmhead.nas
は リアルモード と プロテクトモード の切り替えを行い、後述のbootpack.c
に書かれたHariMain
関数の内容を実行するために、そのメモリ上の場所を指し示しています - つまり、OS自作入門の初期に出てくるアセンブラは、PC/AT互換機共通のブートを実行するためのものなんですね
次にアプリケーションレベルの話
- libcを使わずシステムコールレベルの命令を実行
- 作りたてのOSにはシステムコールを呼び出せる関数やライブラリが存在しません
- これはつまり標準出力に何か出したりメモリを確保したり、割り込み制御かけたりできないということで
- ということは、そういった低レイヤをまかなうためのライブラリをビルドさせるか、自作するか2択になるわけです
- OS自作入門では、
bootpack.c
にそういった命令のシンボル名を書き、その実装をnaskfunc.nas
に書いてます - 最終的に作ったオブジェクトファイルは作った関数のシンボル名と実装を含むことになり、処理の中で関数が呼ばれると、
naskfunc.nas
に書かれたアセンブラ処理を実行するという流れになるわけです
アセンブラを実装していく方法
次に、アセンブラを実装していく方法です
今回のターゲットは単にnask
の構文を理解して、構文に対応する機械語を吐ければそれで十分です
だいたい全部大文字
MOV AL,0x13 ; VGAグラフィックス、320x200x8bitカラー
MOV AH,0x00
INT 0x10
-
nask
はインテル記法なので- 命令の次にまず
DEST(送り先)
が来る - その次に
SRC(送り元)
が来る - 送り元とか送り先の意味合いは、命令の種類によって変化します
- 命令の次にまず
これをもうちょい一般化すると
-
MOV DEST, SRC
← 動作:DEST ← SRC
と書けそうです
準備する資料
大元の種本とすべきなのは インテル® アーキテクチャー・ソフトウェア・デベロッパーズ・マニュアル
なんでしょうが、わたしはめんどくさいのでWEBサイトに載っているそれの書き下しを参考にしています。
これを参考にすれば、どういう命令を出したい時にどういう機械語を出せばよいか仕様がわかります
-
インテルのデベロッパーズマニュアルへのリンク
-
よく参考にしているIA32(x86)汎用命令一覧
-
x86の命令を表にしたものがあるようです、これから実装される方はこれを参考にしたら簡単かも
16bit, 32bitモードで出力される機械語の仕様
オペコードから導出される機械語の見取り図
以下は、i386のCPUを動作させるときの機械語(バイナリ)の並びです。
(a) 16-bit instruction mode (リアルモード)
_________ ___________ ____________ _________
| Opcode ||MOD-REG-R/M||Displacement||Immediate|
| || || ||0-4 bytes|
|1-2 bytes|| 0-1 bytes || || |
| || || || |
| || || || |
--------- ----------- ------------ ---------
(b) 32-bit instruction mode (プロテクトモード)
........ ........ _________ ___________ ......... _________ _________
:Address ::Operand :| Opcode ||MOD-REG-R/M|:Scaled :|Displace-||Immediate|
:size ::size :| || |:Index :|ment ||0-4bytes |
:0-1bytes::0-1bytes:|1-2 bytes|| 0-1 bytes |:0-1 bytes:| || |
:Prefix :: :| || |: :| || |
:67H ::66H :| || |: :| || |
'''''''' '''''''' --------- ----------- ''''''''' --------- ---------
図の中の用語の説明をしておきます
機能レベルの話をしだすと記事がまとまらないので、まず概要のみ書きます。残りは別の記事で。。。
16-bit/32-bit共通
(a) 16-bit instruction mode (リアルモード)
_________ ___________ ____________ _________
| Opcode ||MOD-REG-R/M||Displacement||Immediate|
| || || ||0-4 bytes|
|1-2 bytes|| 0-1 bytes || || |
| || || || |
| || || || |
--------- ----------- ------------ ---------
-
Opcode (オペコード)
- 例えば
INT 0x13
という命令であれば0xCD ib
というオペコードがそれに対応する - アセンブラの出力は
0xcd, 0x13
になる
- 例えば
-
MOD-REG-R/M (ModR/M)
- 命令自体のオペコードと命令の対象となるレジスタによって変化する1byteのデータ
- x86のCPUの命令が汚い原因の一つかも
-
Displacement (Disp)
- 命令が間接アドレッシング・モードを使用している際に使われるデータ
-
[EAX+4]
とかそんな表記されたときの+4
のこと
-
Immediate (即値)
- 即値はそのままの数字のことです、大抵
0x
が先頭につく
- 即値はそのままの数字のことです、大抵
32-bit
(b) 32-bit instruction mode (プロテクトモード)
........ ........ _________ ___________ ......... _________ _________
:Address ::Operand :| Opcode ||MOD-REG-R/M|:Scaled :|Displace-||Immediate|
:size ::size :| || |:Index :|ment ||0-4bytes |
:0-1bytes::0-1bytes:|1-2 bytes|| 0-1 bytes |:0-1 bytes:| || |
:Prefix :: :| || |: :| || |
:67H ::66H :| || |: :| || |
'''''''' '''''''' --------- ----------- ''''''''' --------- ---------
32bitモードの最初の2バイトはOverride prefixes
と呼ばれる。なぜならそれらは常に存在するとは限らないからだ。 最初の1バイト目は命令に使われるオペランドのアドレスのサイズを変更する。 次の2バイト目はレジスタのサイズを変更する。
-
Address size Prefix byte
- もし、CPUが16bit命令モードとして(プロテクトモードのみ)で動き、32bitレジスタが使われていればこれは無視される。もし、16bit命令が32bit命令モードで現れるのであれば、 16bitレジスタを選択するためこれが必要
- 逆に、リアルモードで32bitのアドレッシング・モードを使うときもこれが必要
-
Operand size Prefix byte
- 16bit命令モードで動作中(リアルモードもしくはプロテクトモード)、32bitレジスタが使われていれば、Override prefixes(0x66)が命令の前に付加される
-
Scaled Index byte
(SIB)- x86の機械語コード中にあり、実効アドレスを指定したメモリーアクセスをする際の情報を与えるためのバイト。i386の32ビット以降で追加された。ModR/Mではカバーできん部分を指し示す。
これでようやく実装のための準備終了です、次回はより具体的な話を書きたいところです。
-
Difference between: Opcode, byte code, mnemonics, machine code and assembly ... MNEMONIC: English word MNEMONIC means "A device such as a pattern of letters, ideas, or associations that assists in remembering something.". So, its usually used by assembly language programmers to remember the "OPERATIONS" a machine can do, like "ADD" and "MUL" and "MOV" etc. This is assembler specific. ↩