法政大学国際文化学部

情報システム概論

担当 重定 如彦

20091110

7回 機械語によるプログラミング その2

1.      前回の課題の答え

                                          START

                                          LD                     GR1, A

                                          ADD                  GR1, B

                                          SUB                  GR1, C

                                          ST                     GR1, D

                                          EXIT

                                       DC                     10

                                       DC                    

                                       DC                    

                                       DS                    

                                          END

 

このプログラムを実行するとメモリのD番地に(000C)16という数値が格納されるはずです。(000C)16は10進数になおすと12なので、このプログラムが10+5−3=12を正しく計算できていることを確かめることができます。なお、D番地が実際に何番地になるかは、アセンブラ結果のウィンドウの中のラベルがDの行の番地の部分を見て下さい。そこに書かれている(000D)16がプログラムの中の実際のDの番地です。

2.      条件分岐

前回の授業ではアセンブラ言語の使い方と、簡単なプログラム例について解説しました。今回の授業では、もう少し複雑なプログラムについて説明します。

プログラムでは、ある条件が満たされていた場合は「A」という動作を、その条件が満たされていなければ「B」という動作を行うといった、ある条件によってプログラムの動作を変えるということが良く行われます。これを条件分岐と呼びます。例えば、ある数字が50以上だったら画面に「合格」と表示し、50未満だったら画面に「不合格」と表示するといったものです。機械語命令では、一般にフラグレジスタ(FR)の内容を分岐の条件とし、FRの値によってPC(プログラムカウンタ)の値を変化させ、次に実行する機械語命令の場所を変化させることができます。COMETでは、条件分岐のための以下の5種類の命令コードが用意されています。

 

命令

命令コード

オペランド

adrへ分岐する条件

分岐する場合

のFRの値

正分岐

JPZ

adr

計算結果が0以上の場合

(00)(01)

負分岐

JMI

adr

計算結果が負の場合

(10)

非零分岐

JNZ

adr

計算結果が0でない場合

(00)(10)

零分岐

JZE

adr

計算結果が0の場合

(01)

無条件分岐

JMP

adr

無条件でadrへ分岐

すべて

 

 

演算結果

負の値

正の値

FRの値

(10)

(01)

(00)

 

FRは、ADDやSUBなどの算術演算命令などを行った場合、演算結果の値によって上記のように変化します。条件分岐命令はこのFRの値が条件に一致する場合に、プログラムカウンタをadrの値に変化させる命令です。このままでは、わかりにくいと思いますので、メモリのA番地の値が50以上の場合はメモリのC番地に1、そうでなければ2を代入するプログラム例を挙げてみます。

 

                                START

                                LD          GR1A                ;A番地の内容をGR1に代入

                                SUB       GR1B                ;GR1からB番地の内容を引く

                                JPZ         PRG1                    ;演算結果が0以上ならPRG1

                  PRG0      LEA       GR12                ;負の場合。GR12を代入

                                ST          GR1C                GR1の内容(2)をC番地に格納

                                EXIT                                  ;プログラム終了

                 PRG1     LEA       GR11                ;0以上の場合。GR1に1を代入

                               ST          GR1C               GR1の内容(1)をC番地に格納 

10                            EXIT                                  ;プログラム終了

11              A            DC         60                         ;A番地に60を代入

12              B            DC         50                         ;B番地に50を代入

13              C            DS          1                          ;結果を格納するC番地を用意する

14                            END

 

このプログラムを解説する前に一つ新しい命令について説明します。LEAはロードアドレス命令と呼ばれる命令で、以下のように記述します。

 

命令コード

オペランド

LEA

GR, 数値

 

 

 

LEAはGRで指定した汎用レジスタに数値で指定した値を直接代入する命令で、

LEA       GR1, 100

と記述すると汎用レジスタGR1に、100という数値が代入されます。LDとの違いは、LDが第二オペランドで指定したアドレスの内容を汎用レジスタに代入するのに対して、LEAは第二オペランドで指定した数値そのものを代入する点です。

COMETに用意されている条件判定のための命令は演算結果を「0以上」、「0未満」、「0に等しい」、「0以外」の4種類の条件のいずれかで判定します。これを使って、ある数値が50以上であるかを判定するには、その数値から50を引いた結果が「0以上」であるかどうかを判定すればOKです。従って、このプログラムでは、演算結果が0以上であるかどうかを判定する命令コードである「JPZ」を使って判定を行います。

それでは順を追ってプログラムを解説します。まず、11行目〜13行目でメモリのA番地に60、B番地に50を代入し、結果を代入するメモリのC番地を確保しています。

プログラムを実行すると、2行目と3行目でA番地の内容からB番地の内容(=50)を引き算し、4行目でその結果が0以上であるかどうかを判定します。もし0以上であった場合は、4行目のJPZ命令によって、PCの値がPRG1番地に変更され、次に8〜10行目のプログラムが実行されます。もし負であれば、4行目では何も行われずそのまま次の5〜7行目が実行されます。5〜7行目にはメモリのC番地に2を代入するプログラム8〜10行目ではメモリのC番地に1を代入するプログラムが記述されており、これによってこのプログラムは目的の動作を行うわけです。

上記のプログラムを実際に入力し(コメントの部分は入力しなくてもかまいません)、sample1というファイル名をつけて保存し、アセンブルして下さい。アセンブル結果のウィンドウに、PRG1が(000C)16番地、Aが(0012)16番地、Bが(0013)16番地、Cが(0014)16番地であることが示されていることを確認して下さい。

次にCOMETのウィンドウで「1ステップ実行」ボタンを押し、プログラムの実行によって、GR1,FR,メモリのA、B、C番地がどのように変化するかを見て下さい。この場合、Aの内容は60で50より大きいので4行目の次に8行目が実行され、最終的にメモリのC番地、すなわち(0014)16番地に1が代入されることになります。

また、メモリのA番地の内容を20に変更し、アセンブルし同様の作業を行って下さい。今度はA番地の内容は50より大きいのでC番地に最終的に2が代入されます。

 

 

 

 


メモリの

(0012)16番地

 

プログラムカウンタ

 

汎用レジスタ

 

フラグレジスタ

 

 

メモリに表示されているカーソルは「赤色:次に実行する機械語の命令」、「青色:直前に実行した機械語の命令」を表します。

3.      条件分岐と繰り返し

条件分岐のもう少し高度な例として、乗算を行うプログラムを作ってみましょう。このプログラムでは、メモリのA番地の内容とメモリのB番地の内容を掛け算してメモリのC番地に格納するという動作を行います。ところで、CASLの命令コードの中には足し算、引き算を行う命令はありますが、乗算を行う命令はありません。そこで、A*Bという計算を、AをB回繰り返し足し算するという方法をとります。

 

                                START

                                LEA       GR10                ;GR1に0を代入

                                LD          GR2B                ;B番地の内容をGR2に代入

                  LOOP     ADD       GR1A                ;GR1にA番地の内容を足す

                                LEA       GR2-1GR2     ;GR2から1を引く

                                JNZ        LOOP                   ;演算結果が0でなければLOOP

                                ST          GR1C                ;GR1の内容をC番地に格納

                                EXIT                                  ;プログラム終了

                  A            DC         7                          ;A番地に7を代入

10              B            DC         5                          ;B番地に5を代入

11              C            DS          1                          ;結果を格納するC番地を用意する

12                            END

 

なお、このプログラムの5行目で、 LEA  GR2, -1, GR2    と記述されていますが、これはGR2の内容に-1を足す(GR2の内容から1を引く)という意味を持ちます。
 LEA命令は実は以下のように3オペランド命令として記述することが可能です。

命令コード

オペランド

LEA

GR, 数値, GR

 

 

 

 

LEAで3つのオペランドを記述した場合、第一オペランドで指定した汎用レジスタに、第二オペランドの数値と第三オペランドで指定した汎用レジスタの値を足し算した結果を格納するという動作を行います。上記の例のように、特定の汎用レジスタの内容と具体的な数字の足し算(または引き算)を行いたい場合に使用すると良いでしょう。

 

次にプログラムのそれぞれの行の解説を行います。

まず、9〜11行目で、メモリのA番地に7、メモリのB番地に5が格納され、結果を格納する為にメモリのC番地が用意されます。

このプログラムでは、汎用レジスタ1(GR1)を計算の途中結果、汎用レジスタ2

(GR2)を何回足し算を行ったかを数えるために使います。プログラムの実行が開始された時点では、直前に実行したプログラムの計算結果が汎用レジスタに残っている場合などがあるため、それぞれの汎用レジスタに何の数字が入っているかはわかりません。そこで、計算結果を格納するGR1は最初に0を代入する必要があり、この作業を2行目で行っています。このような初期設定を行う作業のことを初期化呼びます。

次に3行目でGR2に足し算を行う回数、すなわちメモリのB番地の内容を代入しています。これで、前準備が整いましたので4〜6行目で足し算を繰り返す作業を行います。

4〜6行目で何が行うかをまとめると以下のようになります。

 4行目     GR1 に GR1 + A番地の内容(この場合7) を代入

 5行目     GR2 の内容を1減らす

 6行目     直前(5行目の計算結果。すなわちGR2の値)の計算の結果が

                        0でなければ4行目に戻る

4〜6行目の作業が行われるたびに、GR1の値にA番地の内容が加算され、GR2の値が1減らされます。この行はGR2の値が0になるまで繰り返されるので、結果としてこの4〜6行目はメモリのB番地の値の回数だけ繰り返されることになります。従って、GR1の中には最終的にAをB回足し算した値、すなわちA*Bが格納されることになります。最後に7行目で結果(GR1)をC番地に書込みこのプログラムは終了します。

この掛け算のプログラムのように、プログラムでは同じ内容の作業を特定の回数だけ繰り返し行うという動作が頻繁に記述されます。プログラムの中でこのような繰り返しを行う部分のことをループと呼び、このプログラムのGR2のように、ループの中で繰り返しの回数を数えるための役割をもつレジスタのことをカウンタ(counterと呼びます。

4.      繰り返しの例 その2

繰り返しを使ったプログラム例をもう一つ紹介します。次の例題では、1からメモリのA番地に格納されている数字までの合計を計算し、メモリのB番地に格納します。

このプログラムでは、汎用レジスタGR1を計算の途中経過の格納用に、GR2をカウンタとして使用します。COMETは2つのレジスタ同士を直接(1命令で)演算する命令を持っていないので、レジスタ同士の例えば GR1 + GR2 のようなレジスタ同士の足し算は以下のように、片方のレジスタの内容を一旦メモリに格納してから行う必要がある点に注意が必要です。このプログラムではメモリのC番地をこの用途に使います。

  1. GR2の内容をメモリにST命令を使って格納する

 2. ADD命令を使って GR1とメモリに格納したGR2の値を加算する

 

                                START

                                LEA       GR10                ;GR1に0を代入

                                LD          GR2A                ;A番地の内容をGR2に代入

                  LOOP     ST          GR2C                ;C番地にGR2の内容を代入

                                ADD       GR1C                ;GR1にC番地の内容を足す

                                LEA       GR2-1GR2     ;GR2から1を引く

                                JNZ        LOOP                   ;演算結果が0でなければLOOP

                                ST          GR1B                ;GR1の内容をB番地に格納

                                EXIT                                  ;プログラム終了

10              A            DC         10                         ;A番地に10を代入

11              B            DS          1                          ;結果を格納するB番地を用意する

12              C            DS          1                          ;カウンタを格納するC番地を用意

13                            END

 

次にプログラムのそれぞれの行の解説を行います。

まず、10〜12行目で、メモリのA番地に10が格納され、結果を格納する為にメモリのB番地と、汎用レジスタの中身を格納するためのメモリのC番地が用意されます。

2行目:先ほどの掛け算の例と同様に、結果を格納する汎用レジスタGR1の値を初期化する必要があるので、その作業を2行目で行います。
 3行目:GR2に足し算を行う回数、すなわちメモリのB番地の内容を代入します。
 これで、前準備が整いましたので4〜6行目で足し算を繰り返す作業を行います。

4〜7行目で何が行うかをまとめると以下のようになります。

 4行目     メモリのC番地にGR2の値を代入する

 5行目     GR1とC番地の内容を加算しGR1に格納する。4行目と5行目で  GR1 ← GR1 + GR2 が実行される

 6行目     GR2 の内容を1減らす

 7行目     直前(6行目の計算結果。すなわちGR2の値)の計算の結果が

                        0でなければ4行目に戻る

4〜7行目の作業が行われるたびに、GR1の値にGR2の内容が加算され、GR2の値が1減らされます。この行はGR2の値が0になるまで繰り返されるので、結果としてこの4〜7行目はメモリのA番地の値の回数だけ繰り返されることになります。従って、GR1には 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 が計算されることになり、1からA番地の内容(=10)までの合計の計算結果が格納されます。最後に8行目で結果(GR1)をC番地に書込みこのプログラムは終了します。このプログラムを入力し、各自COMETシミュレータを使ってプログラムの動作を確かめてみてください。

5.      課題

以下のプログラムを作り実行せよ。また、作成したプログラムをGドライブにkadai2という名前で保存し、課題のメールとして添付ファイルで送ること。

メモリのA番地の内容をメモリのB番地の内容で割り算し、商をメモリのC番地、余りをメモリのD番地に格納するプログラムを作成せよ。A番地に10、B番地に3を格納し、作成したプログラムを実行し、メモリのC番地とD番地に正しい答えが格納されていることを確かめよ。

ヒント

 式で書くと、

A / B = C 余り D

  のCとDを計算するプログラムを作れということです。

 CASLには割り算を行う命令はありませんので、足し算と引き算を使うことになります。A/Bの商を求めるには、AからBを何回引き算できるかを数えるプログラムを作ればOKです。プログラムでは、最初にA番地の値をGR1に格納し、答えをGR2に格納するとした場合、以下のようなループを作れば良いでしょう。

     

  LOOP    ・GR1からB番地の内容を引き算し、GR1<0 ならば

ANSへ条件分岐させる(ループの終了)

          ・GR2に1を足す

          ・LOOPへ戻る(JMPによる無条件分岐)

  ANS     ・GR1にB番地の内容を足す(GR1の値が負になっているため)

ループが終了した時点で、GR2には商が、GR1には余りが入っているはずです。

この方法で実際にうまくいくということを例えばA=5、B=2の場合、で示します。

 ループに入る前は GR1=5、GR2=0

一回目のループで GR1=3、GR2=1

二回目のループで GR1=1、GR2=2

三回目のループで GR1=−1(<0)となるのでここでループが終了します。
ANSでGR1から余分に引いた数を戻した結果、GR2には2、GR1には1が格納されているので、5/2 = 2 余り 1という答えが上記の方法で求められます。

 

 

課題のメールは masaki.yamashita.67@gs-art.hosei.ac.jp までお願いします。

質問のメールなどは、sigesada@.hosei.ac.jpまでお願いします。

授業の資料の最新版はhttp://www.edu.i.hosei.ac.jp/~sigesada/にあります。