どうも高野です。
95でこの原稿のデータをぶっ飛ばされてしまいました。(T_T)
やはり8MBで95なんぞを使ったのが悪かったらしい。
貧乏人は損じゃのう....
データ アドレス □□■□■■□□ 0x01 □■■■□□□■ 0x02 ■■□■□■■□ 0x03 □□■□■□□□ 0x04 ■□□■■■□□ 0x05 □□□■□□□■ 0x06上の表を参考にしてみると、アドレスが0x04のデータは00101000です。このように8ビットを一つの単位としてとらえ、これを1バイトと呼ぶわけです。
MOV 受け取る側 送り出す側
(レジスタ、メモリ) (レジスタ、メモリ、数値)
MOVは右側で指定したデータを左側のデータ格納場所に移動させる、すなわち「転送」する命令です。データの移動する向きは「右から左」です。送り出す側はデータが転送された後にも、はじめのデータが残ります。
メモリからレジスタにデータを転送するときのことを「ロード」
レジスタからメモリにデータを転送することを「ストア」
と、言います。
例をあげて説明していきましょう。
16Hをいう数値をALレジスタに転送したいとき
MOV AL,16H
オフセットアドレス(オフセットアドレスの解説は前号を見て下さい)「0541H」にALレジスタの内容をストアしたいとき
MOV [0541H],AL
ここで注意してもらいたいのは受け取る側に[]がついているところです。これをつけることで数値とアドレスを分別しているのです。だからこの場合「MOV 0541H,AL」は意味をなさなくなり、また「MOV AL,16H」と「MOV AL,[16H]」とでは全く意味が違うと言うことです。後者はオフセットアドレスが0016HのメモリのデータをALに転送するという意味です。
次は1ワード、つまり一度に16ビット取り扱う場合です。1ワードの1A56HというデータをAXレジスタに転送するには
MOV AX 1A56H
となります。1バイトの時と同じです。1AHはAHに入り、56HはALに入ります。
逆にレジスタからメモリにストアする場合も1バイトの時と変わらないのですが、データの格納のされ方に注意する必要があります。というのも、8086系CPUの特徴として「メモリ上のデータは実際のデータの上位バイトと下位バイトとが入れ替わる」のです。詳しく説明すると
MOV [0123H],AX
という命令をCPUに与えたとします。このときAXのデータは16ビットですから、オフセットアドレスが0123Hと0124Hのメモリに格納される訳です。このときAH(上位バイト)は0124HにAL(下位バイト)は0123Hに格納されます。
AXレジスタからBXレジスタの値をオフセットアドレスとしてメモリへストアしたいとき
MOV [BX],AX
ここでは[]を使うことで、BXに直接AXから転送するのではなく、BXレジスタの指し示した(ポインティングした)メモリへAXの内容を転送します。この考え方は実はC言語でいうところのポインタそのものなのです。(ポインタについては部長の記事を参照して下さい)これの応用として、
MOV [BX + 5],AX
とすることで、BXから5番目のメモリにAXのデータを転送することもできます。
さて以上で大体MOV命令の特徴がわかっていただけたでしょうか?MOV命令はマシン語のプログラムを組む上でもっともよく使う命令なので、しっかり理解して下さい。
ところでデータの格納場所はご存じの通りレジスタとメモリの2種類があります。レジスタにも前号で書いたように特殊なモノもあり、なかでもセグメントレジスタは操作が制限されています。例えば、CS(コードセグメント)はMOV命令を使って直接書き込むことは
できません。よってセグメントレジスタは他のレジスタと区別して扱います。
データ転送は「セグメントレジスタ、その他のレジスタ、メモリ」という3つのデータ格納場所とデータそのものを様々に組み合わせて行うことができます。たとえば、
MOV AL,0AAH
MOV [0123H],AL
は一つの命令に書き直すことが可能で、
MOV [0123H],0AAH
と数値を直接メモリに書き込むことができます。
しかし、
MOV AL,[0123H]
MOV [3210H],AL
を
MOV [3210H],[0123H]
と書き直すことはできません。
このようにデータを転送できる組み合わせが決まっています。この組み合わせのことをアドレッシングモードといいます。
レジスタ(AX,BX,CX,DX,SI,DI,BP,SP)
データ(10H,1234H等)
セグメントレジスタ(CS,DS,ES,SS)
メモリ([01234h][45ACH]等)
とするとき
レジスタ | <----> | レジスタ |
データ | -----> | レジスタ |
データ | -----> | メモリ |
セグメントレジスタ | <----> | レジスタ |
レジスタ | <----> | メモリ |
セグメントレジスタ | <----> | メモリ |
以上の様な組み合わせが可能です。
レジスタを使ったアドレス指定を先に少しだけ説明しましたがここでもう少し詳しく説明します。たとえばMOV AX,[BX+5]とした以外にもMOV AX,[BX+SI+5]と書くこともできます。ここでの「5」の値のことをディスプレーメントといいます。ここでもアドレッシングモードの時と同じく、レジスタとディスプレーメントの組み合わせに制限があります。
(BX,BP) + | ディスプレーメント | 例:[BP+1] |
(SI,DI) + | ディスプレーメント | 例:[SI+6] |
(BX,BP) + (SI,DI) + | ディスプレーメント | 例:[BX+DI+4],[BP+SI+6],[BX+DI+7] |
ADD 足したい数 足したい数
です。計算結果は左側に入りますので、左側には必ずレジスタかメモリを指定してやらなくてはなりません。(以後レジスタと言ったらセグメントレジスタ以外のモノを指すことにします。)例えばALレジスタに1バイトのデータ2AHを加算する場合
ADD AL,2AH
と書きます。ALに最初11Hが入っていた場合、この命令の後にはALには3Bが入っていることになります。
次に引き算ですが、これも簡単で
SUB 引かれる数 引く数
です。ALレジスタからオフセットアドレスが1234Hに格納されているデータで減算するとき、
SUB AL,[1234H]
です。この場合も計算結果がALレジスタに格納されることは言うまでもありません。
次はインクリメントとデクリメントです。この二つの言葉はあまり聞かれない言葉ですが、インクリメントとはレジスタを-1、デクリメントとはレジスタを-1することを言います。ここでなぜこんな命令が必要なのか疑問に思う方がいるかもしれません。単純にADD命令を使えばいいような気もします。しかし、マシン語のプログラミングでは+1や-1を頻繁に行います。そこで、ADD命令よりも高速な(つまりクロック数が少ない)この命令を使います。これによって実行効率を高めるわけです。インクリメントの命令は
INC 数を一つ増やしたいレジスタ
とします。
INC AL
は
ADD AL,1
と同じ意味です。同様にしてデクリメントの命令は
DEC 数を一つ増やしたいレジスタ
と書きます。
それでは復習として少しマシン語で書いたプログラムを書いてみましょう
MOV BL,34H | ;BLレジスタに1バイトデータ34をロード |
MOV AL,BL | ;ALレジスタにBLレジスタに転送 |
INC AL | ;ALをインクリメント |
MOV [A123H],AL | ;ALレジスタのデータをストア |
DEC BL | ;BLをデクリメント |
ADD AL,BL | ;ALとBLを足す |
SUB AL,BL | ;ALからBLを引く |
なんかあまり意味のないプログラムですね(^_^;
2つのデータを比較する命令をコンペア命令、比較命令といいます。具体的に何をやるかというと、引き算をやるのですが、引き算の結果そのものをメモリやレジスタ上には残しません。じゃあいったいなんのための命令かわかりません。そこでこの算術演算命令の項のはじめで述べたことを思い出して下さい。ALUで計算された時に一緒にその演算結果の状態をフラグレジスタに保存されると言うことを書きました。そうです、この比較命令はこのフラグレジスタに状態を保存する命令なのです。
ここで詳しくフラグレジスタの説明をしておきましょう。フラグレジスタとは読んで字の如く「旗」レジスタなのですが、このレジスタにはビットごとに意味を持っていて、個々のビットごとつまりフラグごとに単独に動作します。そのため他のレジスタのように16ビットのレジスタとしてプログラムから自由に操作することができません。フラグを1にすることを「セット」するといい、0にすることを「リセット」または「クリア」と言います。フラグレジスタのビットごと(フラグごと)の持つ意味を下の表に書きます。
フラグレジスタ | ■■■■OFDFIFTFSFZF■AF■PF■CF | |
OF | オーバーフローフラグ | 符号付き演算で桁あふれが生じたときにセットさせる |
---|---|---|
DF | ディレクションフラグ | ストリング操作命令においてポインタの増減方向を示す |
IF | インタラプトイネーブルフラグ | セットすると外部割込を受け付けなくなる |
TF | トラップフラグ | シングルステップモードで実行するときのフラグ |
SF | サインフラグ | 演算結果の符号を表す。負ならばセットされる |
ZF | ゼロフラグ | 演算結果がゼロであればセットされる |
AF | 補助キャリーフラグ | BCD演算で使用されるキャリーフラグ |
PF | パリティーフラグ | 演算の結果、1となるビット数が偶数の時セットされ、奇数の時クリアされる |
CF | キャリーフラグ | 演算の結果、桁上がりが生じた場合にセットされる |
全角一文字が1ビットです。■は何も役割を割り当てられていないことを示しています。
「ストリング命令」とか「シングルステップモード」とか聞き慣れない言葉が出てきますがここではあまり気にしないで下さい。
フラグレジスタはこのように命令ごとに影響を受けるフラグが決まっています。もちろんフラグに何も影響を与えない命令も存在します。
さて話を比較命令に移して考えて見ましょう。比較命令は
CMP 比較されるデータ 比較するデータ
比較されるデータから比較するデータを減算しその結果は保存されませんが、フラグレジスタには保存されます。比較命令の場合はSF,ZF,PFが変化します。この比較命令は単独では使用されず、「条件分岐命令」と組み合わせて使用されます。具体的な使用法は「条件分岐命令」のところで解説します。
MUL 掛ける数
掛ける数が8ビットの時はALとかけ算をし、その結果をAXに転送されます。掛ける数が16ビットの時はAXとかけ算をし、結果を上位ビットをDXに、下位ビットをAXに転送します。たとえば
MUL BX
とかけばAXとかけ算をし、その結果をDXとAXに転送するわけです。
わり算の命令は
DIV 割る数
割る数が8ビットの場合はAXレジスタをわり算し、商をALレジスタに、余りをAHレジスタに転送します。割る数が16ビットの時はDXを上位16ビットにしAXを下位16ビットとして、割られる数を32ビットにしわり算をします。その後、商をAXレジスタに、余りをDXレジスタに転送します。MUL,DIVともに割る数にはレジスタ、数値、メモリのオフセットアドレスを指定してやることができます。
BYTE PTR [オフセットアドレス]
WORD PTR [オフセットアドレス]
前者はバイト単位のデータをメモリから読み出し、後者はワード単位(つまり2バイト)でデータを読み出します。例えば
INC [0123H]
ではだめで
INC BYTE PTR [0123H]
または
INC WORD PTR [0123H]
としなければなりません。
またデータ転送命令等の場合も同様です。
MOV [0FFFH], 3AH
これは一見あっているようですが、実はエラーが出るのです。
MOV BYTE PTR [0125H],3AH
または
MOV WORD PTR [0125H],3AH
としなければなりません。前者は納得行きますが、後者はなにかおかしな気もします。しかし、[0126H]には「00H」がはいるのです。