アセンブラ(x86)の覚書
以下, Windows XP環境を前提にしています。
debugでコマンドプロンプトにhelloと表示してみる。 debugは対話型のツールでプログラムをアセンブラレベルで編集できる。 コマンドプロンプトにてdebugと入力。以下, 次のように入力
a 100 mov dl,00 mov dh,00 mov bl,08 mov bh,00 mov cx,0005 mov bp,011A mov ax,cs mov es,ax mov ax,1301 int 10 hlt jmp 0117 DB 'hello' rbx 0000 rcx 001F n hello.com w q
実行したときの例。次のような画面になっていたはず。 実行時により値が異なることに注意。
$ debug -a 100 35DF:0100 mov dl,00 35DF:0102 mov dh,00 35DF:0104 mov bl,08 35DF:0106 mov bh,00 35DF:0108 mov cx,0005 35DF:010B mov bp,011A 35DF:010E mov ax,cs 35DF:0110 mov es,ax 35DF:0112 mov ax,1301 35DF:0115 int 10 35DF:0117 hlt 35DF:0118 jmp 0117 35DF:011A DB 'hello' 35DF:011F -rbx BX 0000 :0000 -rcx CX 0000 :001F -n hello.com -w 0001F バイト書き込み中. -q
各行の意味は次のとおり。
a 100 ; 100からアセンブル開始 ; メモリ上にプログラムがロードされたときに ; 0100からプログラムは配置される。 ; 0000から00FFはMS-DOSが使用する。 ; ブートストラップではもちろんこの限りではない。 mov dl,00 ; 0100 0列から表示 mov dh,00 ; 0102 0行から表示 mov bl,08 ; 0104 表示属性 mov bh,00 ; 0106 ページ番号 mov cx,0005 ; 0108 表示文字数 mov bp,011A ; 010B 文字列のアドレス2 mov ax,cs ; 010E 直接csをesに代入するのは違反のようだ。 mov es,ax ; 0110 文字列のアドレス1 mov ax,1301 ; 0112 カーソル移動を伴う文字列表示 int 10 ; 0115 表示を実行 ; ax 1301では各レジスタは以下の意味を持ちます。 ; ES:BP 文字列の先頭ポインタ ; CX 書き込む文字数 ; DL 書き込む先頭の列位置 ; DH 書き込む先頭の行位置 ; BL 属性 ; BH ページ番号 hlt ; 0117 終了 jmp 0117 ; 0118 念のため DB 'hello' ; 011A 表示する文字列 rbx ; 書き込みセグメント長の設定 0000 ; 同上 rcx ; 書き込みバイト数の設定 001F ; 011F(011A+5)-0100 = 1F ; BXx10+CXバイト書き込まれる。 n hello.com ; ファイル名の指定 w ; nで指定したファイル名で保存 q ; debugの終了
ここまで終了するとhello.comという31バイトのファイルが作成される。 実行前の注意。hello.comを実行するとそのコマンドプロンプトは その後反応しなくなります。反応しなくなることを覚悟の上で hello.comを実行すると画面左上からhelloと表示されて, コマンドプロンプトが固まります。hltあるいは最後のjmpでループしている というのが正しいのでしょう。
保存はネットワーク上のディスクにはできないので注意。ローカルマシンの ディスク上で行うこと。
以下, windows XP, cygwin環境を対象としています。
gccでは-Sオプションでアセンブラファイル*.sに変換することが できます。たとえばhello.cが次のとおりだったとします。
#includeint main(void) { puts("hello world"); }
変換は次のようになります。
$ gcc -S hello.c
これでhello.sが作成されます。
hello.sはasでコンパイルできます。
$ as hello.s
出力に関し, 何も指定していないのでa.outというファイルが出力されます。 これはgcc -c hello.cの結果のオブジェクトファイルと同じものです。 ldで次のようにライブラリファイルと結合することで一つの実行ファイルとなります。
ld a.out /lib/crt0.o -lcygwin -lkernel32
もともとのhello.cがとても単純なプログラムでしたのでリンクする ライブラリもこれだけでよいのであって, もっといろんな機能を 使用する場合やcygwin環境に頼らない場合は別のライブラリとの リンクを必要とします。debugのときとは違い, 作成された a.exeはきちんと終了します。
なお, 以上の結果はgcc --verboseでいろいろ見た結果であります。
x86系のブートストラップを作成するときの注意は以下の3つ
- メモリ上には0x7c00(07c0:0000)から展開される。
- サイズは512バイト以内。
- 最後の2バイトは0x55aaと決まっている。2番目の 注意の512バイトの制限の最後の2バイトである。
あとは先のdebugと同じである。今度はnasmで書いてみる。
; hello.s ; helloとだけ表示するブートローダー ; コンパイル方法 ; nasm hello.s ; コンパイル後helloというファイルができるので, ; rawriteでフロッピーに書き込む [bits 16] [org 0] jmp 07c0h:start ; 07c0に移動後, startに移動? start: mov ah,00h ; ビデオモードの設定 mov al,13h ; 80x25に設定 int 10h ; 実行 mov dl,00h ; 0列から表示 mov dh,00h ; 0行から表示 mov bl,08h ; 表示属性 mov bh,00h ; ページ番号 mov cx,0005h ; 表示文字数 mov ax,cs ; msgの位置1 mov es,ax ; 同上 csから直接esへのコピーは不可? mov bp,msg ; msgの位置 mov ax,1301h ; カーソル移動を伴う表示命令 ; パラメータはdl,dh,bl,bh,cx,es,bpで指定 int 10h ; 実行 end: hlt ; 終了 jmp end ; 念のため msg db 'hello' ; 510バイトまで埋める times 512 - 2 - ($-$$) db 0 ; ($-$$)はプログラムサイズ) ; ブートストラップのマジックナンバー db 55h, 0aah
nasmでの注意点は0xaaなど文字で始まる即値は0aahのように先頭に0を つけること。基本は10進表記なので16進表記する場合は最後にhをつけること。
これをhello.sと名づけることにして, 次のようにコンパイルする。
$ nasm hello.s
コンパイル実行後, helloというファイルができるので これをフロッピーに書き込む。別章参照。
この後の操作について先に注意。 うまくいけばhelloと画面に表示されるが, それで終了する。 マシンはそのまま電源を落とすか, リセットする。
フロッピーを差し込んだままマシンを再起動すれば 画面にhelloと表示されるはず。実マシンでの再起動が 大変な場合はQemu(別章)などの仮想PCを使用する。
rawriteを使用します。rawrite.exeで検索すれば ヒットします。
$ rawrite Microsoft (R) KKCFUNC バージョン 1.10 Copyright (C) Microsoft Corp. 1991,1993. All rights reserved. KKCFUNC が組み込まれました. マイクロソフトかな漢字変換 バージョン 2.51 (C)Copyright Microsoft Corp. 1992-1993 RaWrite 1.2 - Write disk file to raw floppy diskette Enter source file name: hello Enter destination drive: a Please insert a formatted diskette into drive A: and press -ENTER- : Number of sectors per track for this disk is 18 Writing image to drive A:. Press ^C to abort. Track: 00 Head: 0 Sector: 1 Done. $
$ qemu-img create c.img 10M
$ qemu -L QEMU_PATH/bios/ -m 128 -boot a -fda hello.flp
hello.flpはフロッピーディスクイメージ。 取得方法は別章を参照
-fda hello.flpは-fda a:でもいいと思うのですが, これだと画面が立ち上がらず結果が確認できません。
$ dd if='\\.\A:' of=hello.flp