Powered by SmartDoc

マシン語・アセンブラ実習

実習の目的

この実習ではコンピュータがソフトウェアをどのように実行するかを確認し、プログラミングの理解を深めることにある。

今回の実習ではコマンドプロンプト上でdebugコマンドを使って実習します。ソースコードをテキストエディタで入力しません。マシン語のコードをファイルに保存します。

入力を誤るとプログラムが暴走することもあります。

最初の一歩

コマンドプロンプトの起動

まずは「コマンドプロンプト」または「MS-DOSプロンプト」を起動します。起動するには「スタート」→「プログラム」→「アクセサリ」の順にメニューを開くと、メニュー項目として表示されるのでそれを開きます。次のようなウィンドウが表示されれば起動成功です。

コマンドプロンプト

このウィンドウの中ではキーボードからコンピュータを操作するコマンドを入力します。

debugの起動

次にdebugを実行してみます。キーボードからコマンド名debugと入力し、Enterキーを押します。このコマンドを実行すると次のような表示に変わります。

debugコマンド起動後の画面

この状態はdebugがコマンドの入力を待っている状態です(プロンプトが点滅しているはずです)。

debugの終了

debugを終了するにはコマンドqを使います。debugのプロンプトにコマンドqを入力し、Enterキーを押します。コマンドプロンプトに戻るはずです。

debugのコマンド表示

debugコマンドで利用できるコマンドの一覧はこの状態で?を入力し、Enterキーを押すと表示されます。

debugコマンドのヘルプ一覧

この表示は左から動作、コマンド名、コマンドの引数やオプションです。[]で囲まれている引数は省略可能という意味です。

8086のメモリアドレス

今回の実習では32ビットCPUを搭載したPCを利用していますがdebugコマンドではリニアなアドレスを利用できず、16ビットCPU8086のセグメントを利用したアドレスしか扱えません。そのため、アドレスの表記が「セグメント:オフセットアドレス」という形式になります。セグメントはメモリを16バイト単位で区切った領域のアドレスです。セグメントとオフセットアドレスはそれぞれ16ビットで表現します。

8ビットCPUとの互換性を持たせるためにセグメントという考え方が利用されました。8086では最大1Mバイト(アドレスバスが20本ある)までのメモリを扱えます。

メモリの内容を表示する

メモリには実行されるプログラムやそのプログラムが利用するデータが一時的に記憶されます。メモリの内容を人間が読める形式(16進数とASCII)で表示することを「ダンプする」といいます。debugのコマンドdでダンプされます。

dコマンドで範囲を指定しない場合は128バイト単位でメモリの内容が表示されます。

メモリダンプ

表示は左側からメモリアドレス、16バイトごとのメモリの内容、メモリの内容の文字列表示となります。

これを見て分かるとおり、メモリの内容はすべて2進コードです。

レジスタ

CPU内部には処理するデータやメモリアドレスを一時的に記憶するメモリであるレジスタがあります。レジスタの種類、個数、ビット幅はCPUの種類によって異なります。

レジスタの表示

debugでレジスタの内容を確認するにはrコマンドを使います。引数を指定しなければすべてのレジスタの値が表示されます。

レジスタの表示

rコマンドの引数にレジスタ名を指定するとレジスタに値を設定できます。

汎用レジスタ

16ビット幅のレジスタだが上位8ビット、下位8ビットに分けて利用することもある。たとえば、AXは上位8ビットをAH、下位8ビットをALという名前で利用できる。他のレジスタも同様である。

汎用レジスタ
レジスタ 読み 役割
AX アキュムレータ 演算
BX ベースレジスタ 特定のメモリへのポインタ
CX カウントレジスタ カウンタ
DX データレジスタ データの一時的な記憶

インデックス・特殊レジスタ

IP以外は汎用レジスタと同じ機能を持つが通常メモリのアドレスを指定するポインターとして利用する。

インデックスレジスタ
レジスタ 読み 役割
SI ソースインデックス 転送命令の転送元を指定
DI ディスティネーションインデックス 転送命令の転送先を指定
特殊レジスタ
レジスタ 読み 役割
BP ベースポインタ
SP スタックポインタ スタックトップアドレスを保持
IP インストラクションポインタ 次に実行されるコードのアドレスを保持

セグメントレジスタ

セグメントアドレスを保持するレジスタ。プログラムやデータのセグメントを指定する。

セグメントレジスタ
レジスタ 読み 役割
CS コードセグメント コードの保存されているセグメントを保持
DS データセグメント データの保存されているセグメントを保持
ES エクストラセグメント DS以外のデータを保存しているセグメントを保持
SS スタックセグメント スタック用セグメントを保持

フラグレジスタ

加減算、比較演算などの演算結果などを保持するレジスタ。条件によって処理を分岐する場合に参照する。

フラグはレジスタ全体ではなく、ビット単位で設定されている。

フラグの種類を表に示す。

フラグの種類
フラグ 読み 役割 1のとき 0のとき
OF オーバーフローフラグ 演算結果がオーバーフローすると1になる OV NV
DF ディレクションフラグ ポインタの増減方向 DN UP
IF インタラプトイネーブル 割り込みを受け付けるときは1に EI DI
TF トラップフラグ シングルステップモードで利用
SF サインフラグ 演算結果の符号。負のとき1になる NG PL
ZF ゼロフラグ 演算結果が0のとき1になる ZR NZ
AF 補助キャリーフラグ BCD演算で使用 AC NA
PF パリティフラグ 演算結果が偶数パリティのとき1、奇数パリティのとき0 PE PO
CF キャリーフラグ 演算結果でけた上がりが有ると1に CY NC

アセンブル

debugを使ってアセンブラのプログラムを入力するにはaコマンドを使います。引数にオフセットアドレスを指定するとそのアドレスからの入力になります。

コマンドを入力するとアドレスが表示されるのでそれに続けてアセンブリ言語のプログラムを入力します。Enterキーを押すと次のアドレスが表示されます。もし、入力を終了するのであればアドレスが表示された状態でEtnerキーを押します。

入力に誤りがあるとその場で指摘されるので再入力します。入力する命令は大文字小文字のどちらでもかまいません。

逆アセンブル

メモリ内のでマシンコードをアセンブリ言語で表示することを逆アセンブルといいます。debugではuコマンドを使います。

トレース

debugはプログラムを一命令ずつ実行できます。そのときに使うコマンドがtです。命令を実行するとレジスタの内容が表示されます。

データ転送命令 MOV

さまざまな命令があるがその中で最も多く利用されている命令。CPUのレジスタ間、レジスタとメモリ間でのデータのやりとりを行う命令。次のような書式になる。

MOV データの受け側,データの送り側  

MOVはmoveを略した命令後だが、命令を実行後送り側のデータはそのまま残るので実際にはデータのコピーといえる。

1バイトのデータ転送

まず、1バイトのデータFFHをレジスタALにロードするコードを示します。

  MOV AL,0FF

データは16進数で、アルファベットで始まるので0を付けて、数値であることを明示しています。

次にレジスタALの値をオフセットアドレス0200Hのメモリにストアするコードを示します。

  MOV [0200],AL

メモリアドレスを示す場合にはアドレスを[]で囲みます。

では、この二つのコードを入力してトレースしてみましょう。次の手順で確認してみます。

  1. Aコマンドでコードをアセンブルする。(A 100)
  2. Dコマンドでオフセットアドレス200〜207のメモリをダンプする(D 200 207)
  3. Tコマンドでコードをトレースする。
  4. 再度Dコマンドでオフセットアドレス200〜207のメモリをダンプする

問題

では、逆にメモリ中のデータをレジスタにロードするにはどうしたらいいでしょうか。オフセットアドレス0201のデータをレジスタALにロードするコードを考えてみてください。

  MOV (1),(2)

メモリ間のデータ転送

次にメモリ間のデータ転送のコードを示す。次の例はメモリアドレス201にあるデータをメモリアドレス401に転送するコードである。

MOV AL,[0201]
MOV [0401],AL

このコードが示すようにメモリ間でデータを転送する場合はレジスタを経由する。MOV [0401],[0201]というコードは利用できない。

アドレッシングモード

メモリアドレスはこれまでの例のように直接指定する方法以外にレジスタを使うこともできる。これらを組み合わせることで複数の組み合わせが可能になる。