x86互換のアセンブリ言語に触れてみた

アセンブリの実行環境は?

Windowsではどうも学習用程度のアセンブリだとdos窓で実行できるらしい。
ファイルを指定して実行ダイアログにcmdと打ち込んでコマンドプロンプトを立ち上げる。
debug」コマンドを打ちこむと対話モードが始まり、アセンブリの編集、実行ができるようになる。

debugコマンドの使い方

  • a [オフセット]

 アセンブリの記述

  • g =[オフセット1] [オフセット2]

 オフセット1からオフセット2までの機械語の実行

  • d [オフセット1] [オフセット2]

 オフセット1からオフセット2までのダンプ

  • u [オフセット1] [オフセット2]

 オフセット1からオフセット2までの逆アセンブル

 (指定した)レジスタを表示、及び新しい値の格納

  • t =[オフセット1] [オフセット2]

 オフセット1からオフセット2までをレジスタをトレースしながら実行

  • e [オフセット] (コードリスト)

 メモリへ直接マシンコードの記述

  • q

 debugコマンドの終了


オフセットはメモリのオフセットアドレスの事。
例えば編集の場合だと -a 100と打ち込むことで

-a 100
17DF:0100 mov ax,41
17DF:0103 _

のようにメモリの100番地から書き始める事ができる。

四則演算

  • 足し算
-a 100
17DF:0100 mov ax,10
17DF:0103 mov dx,20
17DF:0106 add ax,dx
17DF:0108

;実行
-g =100 108
AX=0030  BX=0000  CX=0000  DX=0020  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=17DF  ES=17DF  SS=17DF  CS=17DF  IP=0108   NV UP EI PL NZ NA PE NC
17DF:0108 0000          ADD     [BX+SI],AL                         DS:0000=CD

mov ax,10 でaxレジスタに10をコピー
mov dx,20 でdxレジスタに20をコピー
add ax,dx でaxとdxを足して解をaxにセット
-g =100 108 でメモリの100番地から108番地までを実行するという意味。
最後の3行が実行後のレジスタの状態。AXが10と20を足した後の値になっている。
ちなみに値の10、20は全て16進数。

  • 引き算
-a 100
17DF:0100 mov ax,1F
17DF:0103 sub ax,06
17DF:0106

-g =100 106
AX=0019  BX=0000  CX=0000  DX=0020  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=17DF  ES=17DF  SS=17DF  CS=17DF  IP=0106   NV UP EI PL NZ NA PO NC
17DF:0106 01D0          ADD     AX,DX

引き算はsubコマンド。1F - 06 = 19 でAXに19がセットされている。
subの第2オペランドに指定した06のような値はリテラルではなく即値と呼ぶ。

  • 掛け算
17DF:0100 mov ax,10
17DF:0103 mov dx,5
17DF:0106 mul ax
17DF:0108

-g =100 108
AX=0050  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=17DF  ES=17DF  SS=17DF  CS=17DF  IP=0108   NV UP EI PL NZ NA PO NC
17DF:0108 0000          ADD     [BX+SI],AL                         DS:0000=CD

掛け算、割り算はオペランドを一つしか指定しない。
しかも指定したオペランドが8ビットなのか16ビットなのかで違う。<8ビットの場合>
 ⇒AL(AXの下位8ビットを指す)と指定値を掛けて結果がAXに入る<16ビットの場合>
 ⇒AXと指定値を掛けて、結果の上位16ビットがDXに下位16ビットがAXに入る


16ビットを超えるを計算してみる

17DF:0100 mov ax,1FFF
17DF:0103 mov dx,10
17DF:0106 mul dx

AX=FFF0  BX=0000  CX=0000  DX=0001  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=17DF  ES=17DF  SS=17DF  CS=17DF  IP=0108   OV UP EI PL NZ NA PO CY
17DF:0108 0000          ADD     [BX+SI],AL                         DS:0000=CD

ちゃんと1桁繰り上がってDXに0001が入ってる。
このややこしい仕様は一体!?
こんなのがCPU毎にバラバラな上、コマンドセットとかも違うと。。。
機種依存の意味が少しわかった気がする。

メモリへの値の書き込み

17DF:0100 mov ax,4241
17DF:0103 mov [150],ax

-d 150 151
17DF:0150  41 42                                             AB

[]で数値を囲む事によりメモリのアドレス指定となる。
レジスタaxに入れた値を[150]にコピー。(これをストアと呼ぶ)
-d で指定された範囲のダンプをとれる。
正しくセットした41と42が表示されている。ASCIIコードでいうAとB。
42と41が逆になっているのはトルエンディアンといってx86系のメモリ管理の仕組み。


即値を直接メモリに書き込む場合はPTRコマンドを使う

17DF:0100 mov word ptr [150], 4443
17DF:0106

-d 150 151
17DF:0150  43 44                                             CD

書き込む値のビット長に応じてwordまたはbyteを指定する必要がある。
4443で16ビットの為、word。44だけならbyteでよい。



アセンブラは思っていたよりずっとシンプル。
コンピュータの仕組みを知るのには良いが、かなり非生産的というのがとりあえずの印象かな。