アセンブラ(x86)の覚書

 
tiutiu.net/ プログラム/language/assembler/
2007/5/10
Hello

以下, 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でループしている というのが正しいのでしょう。

保存はネットワーク上のディスクにはできないので注意。ローカルマシンの ディスク上で行うこと。

cのプログラムをサンプルにする

以下, windows XP, cygwin環境を対象としています。

gccでは-Sオプションでアセンブラファイル*.sに変換することが できます。たとえばhello.cが次のとおりだったとします。

#include 

int 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
準備
ハードディスクイメージの用意
$ 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

Google