目的
NASM等のアセンブラを使わず直接機械語でプログラムを書き、動かすことで機械語を深く理解することが目的です。
WINDOWSやLinuxのプログラムだとバイナリにした時に、機械語以外の情報も書き込まれ、煩雑になるため避けました。
方法
そうは行っても直接16進数で書くのは流石に難しいため、一旦アセンブラで簡単なブートローダのプログラムを書き、命令対応表を見ながら機械語へ変換(手作業でコンパイル)していきます。
プログラムは以下の通りです。
org 0x7C00
start:
mov ah, 0x0E ; 1文字出力
mov al, 'A' ; 画面に出力する文字のASCIIコードをALレジスタへ格納
int 0x10 ; BIOS呼び出しでalレジスタの文字を画面に表示
jmp $ ;無限に現在のアドレスに飛びCPUを止める
times 510-($-$$) db 0; 512バイトにするため0埋め
dw 0xAA55 ; ブートローダの印
逐次訳
NASM等のアセンブラがやっていることをここでは手作業で行っていきます。
行番号 | アセンブリ言語 | 機械語(16進数) | 説明 |
---|---|---|---|
1 | org 0x7C00 |
- | 疑似命令。コードを0x7C00 番地に配置することを明示(機械語生成なし)。 |
2 | mov ah, 0x0E |
B4 0E |
AH レジスタに即値0x0E を代入(B4 =MOV AH, 即値8bit)。 |
3 | mov al, 'A' |
B0 41 |
AL レジスタに文字'A'のASCIIコード0x41 を代入(B0 =MOV AL, 即値8bit)。 |
4 | int 0x10 |
CD 10 |
BIOSビデオ割り込み0x10 を呼び出し(CD =INT 即値8bit)。 |
5 | jmp $ |
EB FE |
無限ループ($ =現在アドレス)。 |
6 | times 510-($-$$) db 0 |
00... (ゼロ埋め) |
合計512バイトになるまで残りを0x00 で埋める(510バイト目まで)。 |
7 | dw 0xAA55 |
55 AA |
0xAA55 を書き込む)。 |
※ORGが機械語を生成しないことは以下の記事で検証しています。
X86 ORG命令の本質
バイナリで書き込み
hexeditを導入する。
touchでファイルを生成し、hexeditを用いて編集する。
地道に入力していきます。
touch newfile.bin
hexedit newfile.bin
生成したプログラムをコンパクトフラッシュへ書き込む
sudo dd if=newfile.bin of=/dev/sdc bs=512 count=1 conv=notrunc
※ddで直接データを書き込む時は対象をよく確認しないと非常に危険です。
実機にて動作確認
8086の実機にて動作に成功しました!地味ですが感動ものです。
昨日ネットで1980年代のプログラマーが、まず紙にプログラムを書き、それを手で入力して紙テープに穴を開けている動画を見つけました。
それを見て、自分も一度は直接機械語を書いてみたくなりこの記事を書きました。