(since 1998/11)(更新 2004/02/26)

TNO:CASL入門[14]

情報系/IT系の広告を掲載しています。(広告一覧)
©Copyright 1998-2001,2004 小野智章(小野情報設計)
無断転載を禁止します。
本ページは、「Rook」仕様に対応済みです。
CASL入門[目次]
CASL入門[索引]
CASL入門[13]
CASL入門[15]

(chapter14-page1)
14-1.レジスタの保存

 今まで作成していたサブルーチン「MUL」は、 レジスタGR0とGR1を使って掛け算をして結果はGR0に入りました。 つまり、GR0とGR1を入力して、GR0を出力するサブルーチンだったわけです。
 ところで、サブルーチンの実行の結果、 レジスタの内容はどう変化するでしょうか? 出力されるGR0は当然変化するとして、 他のレジスタがどうなるかは、考えていませんでした。
 プログラムの実行後、 操作ボタン群の右側に表示されるレジスタの内容を確認してみて下さい。
 次の出力例は、4と6を入力した場合です。 GR1が、入力した値(この例では「6」)から変化して、 「0」になっています。
FR PR SP
GR0
GR1
GR2
GR3
GR4
GR5
GR6
GR7

 レジスタに入れたのと同じデータを、 二度三度と繰り返して使うことは良くあります。 ですから、サブルーチンを呼び出す側からすると、 元の内容が保存されていた方が都合が良いですね。
(chapter14-page2)
 次の様にサブルーチン「MUL」を修正して下さい。
MUL  START
  ST  GR1,SAVE;SAVE GR1
  ST  GR0,VAL ;VAL
LOOP 
  SUBA  GR1,=1 ;COUNT−1
  JZE  LEND ;IF COUNT=0
  ADDA  GR0,VAL ;SUM+VAL
  JUMP  LOOP ;LOOP
LEND 
  LD  GR1,SAVE;RECOVER GR1
  RET
VAL  DS  
SAVE  DS  
  END
 「ST GR1,SAVE」で、 レジスタGR1の内容が保存され、 「LD GR1,SAVE」で、 保存されていたデータがGR1に入れられます。
 前回と同様にアセンブル/リンクして、実際に実行してみて下さい。 レジスタの内容が実行に連れて変化する様子を見て、 GR1の内容が入力した数値に戻ることを確認して下さい。

(chapter14-page3)
14-2.ワーク・エリアとスタック

 今まで作成したプログラム中では、 「DS」命令を使って、データを記録する場所を用意していました。 この様な処理の途中で必要となるデータを記録する場所のことを、 ワーク・エリア(作業領域) といいます。
 サブルーチン中でワーク・エリアが必要になると、 そのサブルーチン専用の場所を用意していました。 しかし、サブルーチン内で使用するワーク・エリアは、 そのサブルーチンの処理が終われば、通常不要になります。 幾つものサブルーチンを使用する場合は、 それらのワーク・エリアを共通の場所に用意すれば、 データを記録する場所が節約出来るわけです。 但し、あるサブルーチンから別のサブルーチンを呼ぶと言う様な場合には、 呼ぶ側と呼ばれる側のそれぞれに、データを記録する場所が必要になります。 又、ワーク・エリアに必要な大きさは、一般にサブルーチン毎に異なるので、 最も大きいものに合わせて用意する必要があります。

 この様なデータの共通の記録場所として、 「スタック」があります。 「スタック」とは「積み重ねたもの」という意味で、 データを積み重ねて保存しておきます。
 先ほどのサブルーチンを次の様に修正して下さい。 実行してみて、終了アドレス以外は、 元のプログラムと同じ結果が出てくることを確認して下さい。
MUL  START
  PUSH  0,GR1 ;SAVE GR1
  ST  GR0,VAL ;VAL
LOOP
  SUBA  GR1,=1 ;COUNT−1
  JZE  LEND ;IF COUNT=0
  ADDA  GR0,VAL ;SUM+VAL
  JUMP  LOOP ;LOOP
LEND
  POP  GR1 ;RECOVER GR1
  RET
VAL  DS  
  END
(chapter14-page4)
 新しい命令は、「PUSH」命令と「POP」命令です。 「PUSH 0,GR1」で、実効アドレス(即ちレジスタGR1の内容)が 即値としてスタックに保存されます。 そして「POP GR1」で、 保存されていたデータがGR1に入れられます。

 この様に、スタックを利用してデータの保存が出来ることが分かりました。 しかし、保存場所はプログラム中には記述されていません。 それでは、スタックはどこに存在するのでしょうか?

 実は、スタックの場所は、OSが主記憶装置内に用意しています。
 CASLIIには、汎用レジスタであるGR0〜GR7以外に、 特殊な目的に使用されるスタック・ポインタSPというレジスタが存在します。 スタック・ポインタは、スタックのアドレスを指し示していて、 プログラムを実行する時に、 OSが自動的にそのスタックのアドレスをSPにセットしているのです。
 CASLIIにはスタック・ポインタSPの内容を読取る命令や、 スタック・ポインタSPを指標としてスタックの内容を読取る命令は、 存在しません。 しかしRookでは、他のレジスタと同様に、 操作ボタンの右にその値を表示しています。 又、「WRITE」命令でパラメタに「−1」を指定することで、 スタック・ポインタの内容が表示されます。 (スタックに格納された値ではありません。)
 尚、SPを変更出来る場合でも、SPの内容を不用意に変更すると、 場合によっては異常動作を起こすことがあります。 ですから、SPの内容を直接変更する様な命令は、 通常使用してはいけません。

(chapter14-page5)
14-3.スタックの積み重ね

 先ほどまではデータを1つだけ保存していました。 複数のデータを保存すると、どうなるでしょう。 次のプログラムを実行してみて下さい。
SAMPLE  START
  PUSH  55 ; SAVE
  PUSH  66 ; SAVE
  POP  GR0 ; RECOVER
  WRITE  
  POP  GR0 ; RECOVER
  WRITE  
  EXIT
  END
 PUSH命令は実効アドレスを保存するのですから、 上の様に数値やラベルで示したアドレスを直接保存することも可能ですね。
 2つのデータを保存した後、その2つのデータを取り出して表示しています。 どの様な結果になりましたか?
GR0=66(0042)
GR0=55(0037)
プログラムは、PR[000E;SAMPLE+14]が指定したEXIT命令で終了しました。
 この様に、「PUSH」の逆の順序で、データが「POP」されます。
(chapter14-page6)
 スタックとは「積み重ねたもの」です。 従って、データを保存する時は上へ上へとデータを積み重ねていって、 データを取り出す時は積み重ねた上の方から順に取り出します。 CASLIIでは、このスタックの一番上の位置のことを 「スタック・トップ」と呼びます。
 
 
  
PUSH A

 
  
A
PUSH B

  
B
A
PUSH C

C
B
A
C
B
A
POP

  
B
A
POP

 
  
A
POP

 
 
  
  
空の場所
  
データの入っている場所
  
これから取り出すデータの入っている場所

PUSH/POP命令の前後のスタック・ポインタの値は、 どの様になっているのでしょう。 次のプログラムを実行して、スタック・ポインタの値を確認してみましょう。
SAMPLE  START
WRITE  −1 ; SP
PUSH  55 ; SAVE
WRITE  −1 ; SP
PUSH  66 ; SAVE
WRITE  −1 ; SP
POP  GR0 ; RECOVER
WRITE  −1 ; SP
WRITE  
POP  GR0 ; RECOVER
WRITE  −1 ; SP=GR4
WRITE  
EXIT
END
(chapter14-page7)
SP=-32768(8000)
SP=32767(7FFF)
SP=32766(7FFE)
SP=32767(7FFF)
GR0=66(0042)
SP=-32768(8000)
GR0=55(0037))
プログラムは、PR[0022;SAMPLE+34]が指定したEXIT命令で終了しました。
 スタック・ポインタは、PUSHの時は「0800→07FF→07FE」と、 POPの時は「07FE→07FF→0800」と、変化しています。 また、以前のプログラムの実行結果から、 PUSHの直後にはSPは PUSHしたデータが入っているアドレスを指し示しています。 これらのことから、スタック機能は次の様になっていることが分かります。
(chapter14-page8)
14-4.レジスタの一括保存

 サブルーチン内でどの汎用レジスタを変更するか未定である時、 サブルーチンの処理部が完成するまで、 どのレジスタを保存すれば良いか判りません。 又、複数のレジスタを保存する必要がある時、 1つづつレジスタを保存する命令を記述するのは、 面倒です。2001/05/25付けのCASLIIの改訂で、 この様な場合に汎用レジスタを一括で保存するための命令が追加されました。 「RPUSH」と「RPOP」です。
 「RPUSH」は、GR1〜GR7を、この順でスタックへ一括保存します。 「RPOP」は、「RPUSH」でスタックへ保存されたデータを、 一括で元のレジスタへ戻します。 当然「RPOP」は、スタックの上から、 GR7〜GR1の順で戻すことになります。

 「RPUSH」と「RPOP」は通常、 次の様にサブルーチンの先頭とサブルーチンから戻る直前に使われます。 尚、対象レジスタが固定されているので、オペランドは付きません。
MUL  START
  RPUSH   ;SAVE Reg.
  ST  GR0,VAL ;VAL
LOOP
  SUBA  GR1,=1 ;COUNT−1
  JZE  LEND ;IF COUNT=0
  ADDA  GR0,VAL ;SUM+VAL
  JUMP  LOOP ;LOOP
LEND
  RPOP   ;Recover
  RET
VAL  DS  
  END

 「RPUSH」と「RPOP」の扱う汎用レジスタには、 GR0は含まれていないことに注意してください。 これは、PUSH命令でGR0の内容を保存出来ないことと、対応しています。 又、GR0は保存しないことが多いためでもあります。 GR0は通常、示した例の様に、 計算結果や異常の有無などのサブルーチンの結果を、 呼出し側へ伝えるために使われるからです。

 GR0も保存する場合は、一旦他のレジスタに移すなどの手段が必要です。 レジスタ間のデータ移動の練習として、 自分で、具体的な方法を考えて見てください。

CASL入門[目次]
CASL入門[索引]
CASL入門[13]
CASL入門[15]
学習室
トップ・ページへ

質問・ご意見等、お待ちしております。
小野智章(小野情報設計) 
Mail to Mail連絡先
©Copyright 1998-2001,2004 小野智章(小野情報設計)
無断転載を禁止します。