2. nasm のインストールと特徴

NASM のインストール

入手先は http://nasm.sourceforge.net です.

ここでは 200/4/11時点の nasm-0.98 を解説していますが、現在(2009/02/07) 最新のnasm-2.05.01 でも同様です. nasm-0.98 を nasm-2.05.01 に読み替えてください.

Redhat (Vine, LASER5, Mandrake) な人や,Debian (Storm, Corel) な人も安心 して下さい./usr/local/ 以下は依存性(rpm, dselect)の管理外です. 他のモジュール(パッケージ)に影響しないはずです. Slackware な人には余計なお世話ですね.

nasm のソース nasm-0.98.tar.gz のコンパイルは簡単です. 適当なディレクトリで解凍します. ( $ や # は bash のプロンプトです.入力の必要はありません.)

  $ tar zxf nasm-0.98.tar.gz
  $ cd nasm-0.98
  $ ./configure
  $ make
  $ su
  # make install

以上のコマンドを実行すれば,nasm のコンパイルとインストールは終了です. /usr/local/bin/nasm としてインストールされます.

本当にインストールされたか確認してみます.

  $ ls -l /usr/local/bin
    -rwxr-xr-x   1 root root   545429 Jun  8 00:56 nasm
    -rwxr-xr-x   1 root root   168752 Jun  8 00:56 ndisasm

700k バイト程を消費しています.私はディスクの消費を抑えるため, strip して小型化しました.

  # cd /usr/local/bin
  # strip nasm ndisasm

確認すると:

  # ls -l
    -rwxr-xr-x   1 root root    82276 Jun  8 00:57 ndisasm
    -rwxr-xr-x   1 root root   211372 Jun  8 00:57 nasm

小さくなりました.300Kバイト程度ならディスクに置いていても,それほど邪魔 にはならないと思います.

/usr/local/bin/ が検索パスに入っていない場合は

  $ vi ~/.bash_profile
  PATH=$PATH:/usr/local/bin
  export PATH

などとして /usr/local/bin を検索パスに追加して下さい.

アセンブル,リンクは以下のコマンドで行います.

  nasm -f elf my_program.asm
  ld -s -o my_program my_program.o

次の様にシェルスクリプトを書けば楽になります.

  #!/bin/sh
  nasm -f elf $1.asm && ld -s -o $1 $1.o

このシェルスクリプトのファイル名を asm とした場合, chmod +x asm として実行属性を設定しておきます.

これで asm myprog とすれば,myprog.asm が myprog という実行形式にアセンブルされます.

実行して確認するには ./myprog と入力します.

Makefile を作成するまでもないでしょう.

NASM の特徴

nasm は GNU as より, DOS系のアセンブラに近く,MASM, TASM より単純なため 任意の1行を見れば,アセンブルされるマシン語がわかるようになっています. 強力なマクロ機能もあり,抽象化したコードの作成も可能です.

例えば次の例ではラベルの宣言が異なりますが,

    foo   equ  1
    bar   dw   2
          mov  ax, foo
          mov  ax, bar

この場合は ax には定数値が代入され,

          move ax, [foo]
          move ax, [bar]

この例では,ax には メモリの内容が代入されます. つまり,メモリ参照時には必ず[角カッコ]で囲む必要があり,[foo] となって いれば foo の宣言が何であってもメモリ参照であることが確定し, foo であれば定数として扱われます.

オペランドの記述(アドレッシングモード)も違いがあります.

MASM NASM
mov ax, table[bx] mov ax, [table + bx]
mov ax, es:[di] mov ax, [es:di]

また,NASM では宣言時の変数の型を保持しません.したがってサイズが あいまいな場合(メモリへの格納など)には明示的にサイズを指定します. var dw 0 と宣言されている場合でも,mov var, 2 ではなく, mov word [var], 2 とサイズを明記する必要があります. 同様に LODS, MOVS, STOS 等のサイズ指定の無い命令は使用できず, LODSB, MOVSW, SCASD のようにサイズ指定された命令を使用します.

NASM には メモリモデルがありません. CALL から戻るには RETN, CALL FAR から戻るには RETF を使用します. RET は常に RETN と認識されます.メモリモデルにしたがって適当な命令に 変換されることはありません.

MASM NASM
TBYTE TWORD
ST(0), ST(1) st0, st1
stack db 64 dup (?) stack resb 64 (dup は未サポート)

マクロを除くと基本的に1行だけで生成されるコードが決まるように 文法が単純になっています.

マニュアルページ (man nasm) の文法に関する部分を訳したものを示します. より詳細な解説はドキュメントを参照して下さい.

  SYNTAX
       この man page は nasm のアセンブリ言語の文法を完全に記述し
       ているわけではなく,他のアセンブラとの差の要旨を示している.

       gas と異なりレジスタ名の前の`%' は不要であり, 浮動小数点ス
       タックレジスタは st0, st1 等と参照される.

       浮動小数点命令は1オペランド形式と2オペランド形式を使用で
       きる. そのため TO キーワードが用意されている.
       したがって,以下の様に記述するしても良いし,

                      fadd st0,st1
                      fadd st1,st0

       また,1オペランド形式を使用することもできる.

                      fadd st1
                      fadd to st1

       初期化されないメモリの確保には RESB, RESW, RESD, RESQ, REST
       擬似コードを用いる. それぞれ1つのパラメータでバイト,
       ワード,ダブルワード,4倍ワード,10バイトワードを単位として
       確保する数を指定する.

       データの繰り返しを DUP で指定するDOS用のアセンブラと異なって
       次のように TIMES プレフィックスを用いる.

             message: times 3 db 'abc'
                      times 64-$+message db 0

       文字列 `abcabcabc' と全体の長さが64バイトになるように0がに続
       くデータが設定される.

       シンボルの参照は常にイミディエイト(すなわちシンボルのアドレス)
       と解釈される. ただし角括弧を使うとそのアドレスのメモリの内容
       を示す.

                      mov ax,wordvar

       これは,変数 wordvar のアドレスがAXに設定される.

                      mov ax,[wordvar]
                      mov ax,[wordvar+1]
                      mov ax,[es:wordvar+bx]

       これらのどの形式でもメモリの内容が参照される.

       以下の形式は使用できない.

                      mov ax,es:wordvar[bx]
                      es mov ax,wordvar[1]

       ただし,セグメントレジスタ名を命令のプリフィックスとして用
       いることはでき,他の方法でオーバーライドできない LODSB の
       ような命令に使われる.

       定数はほとんどの数値形式で表現できる.つまり H, Q, B を後ろ
       に付けて,16進数, 8進数,2進数としたり,0x か $ を前において
       16進数を表すことができる.
       文字定数はシングルクォートかダブルクォートで囲み,エスケープ
       文字はない. 並びはリトルエンディアン(逆順)であり,'abcd' と
       いう文字定数は 0x64636261 であって 0x61626364 ではない.

       ローカルラベルは名前がピリオドから始まるラベルであり,局所
       性は直前のローカルでないラベル以降となる.つまり `label' と
       いうラベルの後ろで `.loop' というローカルラベルを宣言した
       場合,実際には `label.loop' と宣言されたものとして扱われる.

   DIRECTIVES

       「SECTION 名前」または 「SEGMENT 名前」 はすべての後に続く
       コードは名前のつけられたセクションに属するものとして name
       に指定することになる.
       セクション名は出力ファイルの形式に依存するが,ほとんどの形
       式で .text, .data, .bss をサポートしている.  (例外は obj
       形式で, すべてのセグメント名はユーザ定義可能である.)

       「ABSOLUTE アドレス」は概念的なアセンブリ位置をその絶対アド
       レスに指定する.コードもデータも生成されないが,RESB, RESW,
       RESD を使ってアセンブリ位置を進めることができ,ラベルを定義
       することができる.この擬似命令をデータ構造の定義に使
       用することができる.絶対アドレスの指定を終わらせるために,
       別のセクションディレクティブを指定して通常の状態に戻さなけ
       ればならない.

       BITS 16 と BITS 32 は nasm が生成するプロセッサのデフォルト
       モードを切り替える.つまりDOS用のアセンブラの USE16とUSE32
       に相当する.

       「EXTERN シンボル名」と 「GLOBAL シンボル名」はシンボル定義
       をそれぞれ別のモジュールからインポートするか,別モジュールに
       エクスポートする. GLOBAL ディレクティブはシンボルが定義され
       る前に置く必要があることに注意すること.

        「STRUC  strucname」と ENDSTRUC をいくつかの RESB, RESW など
       の命令を囲むように使うとデータ構造を定義できる.
       構造体メンバのオフセットを定義することに加えて,この構造は
       構造体名に _size を付加した構造体の大きさのシンボルを定義
       する.