CASL U - 合計の計算 |
整数データの合計を計算する副プログラムGOUKEIを作成する。合計値は−32768〜32767の範囲に収まるものとする。ここでは、次の2通りについて作成する。
(1) データが格納された領域の先頭アドレスと、データの個数を副プログラムに渡す。
(2) データが格納された領域の先頭アドレスだけを副プログラムに渡す。
(1)の方法 副プログラムGOUKEI1
データが格納された領域の先頭アドレスをGR1に、データの個数をGR2に格納して副プログラムに渡し、合計値をGR0に格納して戻る。手順は次のとおりである。
- レジスタGR0(合計用)を0で初期化する。
- GR1番地(データ領域)のデータをレジスタGR3に格納する。
- 合計用GR0とデータGR3を加算する。(結果はGR0に格納される。)
- GR1番地に1を加算する。
- データ個数GR2から1を減算する。
- 0でなければ 2. へ移動する。0ならば次へ。
- 呼び出し元に戻る。
MAIN1 START
LAD GR1, AREA
LD GR2, KOSUU
CALL GOUKEI1
ST GR0, ANS
RET
KOSUU DC 10
AREA DC 5,4,3,2,1,10,9,8,7,6
ANS DS 1
END
GOUKEI1 START
RPUSH
LAD GR0, 0 ;1.
LOOP LD GR3, 0, GR1 ;2. データをGR3に格納.
ADDA GR0, GR3 ;3. 合計.
LAD GR1, 1, GR1 ;4. アドレス1加算.
SUBA GR2, =1 ;5. 個数を1減.
JNZ LOOP ;6. 0でなければLOOPへジャンプ.
RPOP
RET ;7.
END
2. GR1番地(データ領域)のデータをレジスタGR3に格納する。
レジスタに記憶された値を番地として、その番地のデータを取り出すにはアドレスを0として、レジスタの値を指標レジスタとして使う。したがって、番地としてレジスタに記憶させるときは、GR1〜GR7の指標レジスタとして使えるレジスタを選択する。
4. GR1番地に1を加算する。
GR1にはデータ領域の先頭アドレスを記憶している。すなわち、値をアドレスとして利用している。したがって、GR1に対しては論理加算をするのが適当である。論理加算命令ADDLを使うと、ADDL GR1, =1 となり、LAD命令を使った場合よりメモリ使用量が増えてしまうからである。
5. データ個数GR2から1を減算する。
SUBA命令の代わりにLAD命令を使って、LAD GR2, -1, GR2 でもGR2から1を減じることはできる。しかし、次の 6. で判定(条件ジャンプ)をしているので、フラグFRが変化しないLAD命令は使えない。
加算するデータを5, 6, 7 の3つとしたときトレースすると次のようになる。
KOSUU DC 3
AREA DC 5, 6, 7
命令 | GR0 | GR1 | GR2 | GR3 | 動作 |
? | AREA | 3 | ? | MAIN1.でGR1とGR2にデータを格納して副プログラムを呼び出す。 | |
RPUSH | ↑ | ↑ | ↑ | ↑ | GR1〜GR7をスタックする。 |
LAD GR0, 0 | 0 | ↑ | ↑ | 1 | 1. GR0を0で初期化する。 |
LOOP GR3, 0, GR1 | ↑ | ↑ | ↑ | 5 | 2. GR1(AREA)番地の内容をGR3に記憶する。 |
ADDA GR0, GR3 | 5 | ↑ | ↑ | ↑ | 3. 合計を計算する。 GR0+GR3をGR0に記憶する。 |
LAD GR1, 1, GR1 | ↑ | AREA+1 | ↑ | ↑ | 4. GR1に1を加算する。GR1を次の番地にする。 |
SUBA GR2, =1 | ↑ | ↑ | 2 | ↑ | 5. 個数GR2から1を減算する。 GR2-1をGR2に記憶する。 |
JNZ LOOP | ↑ | ↑ | ↑ | ↑ | 6. 個数(GR2)が0でなければ 2. へ移動する。0ならば次へ。 |
LOOP GR3, 0, GR1 | ↑ | ↑ | ↑ | 6 | 2. GR1(AREA+1)番地の内容をGR3に記憶する。 |
ADDA GR0, GR3 | 11 | ↑ | ↑ | ↑ | 3. 合計を計算する。 GR0+GR3をGR0に記憶する。 |
LAD GR1, 1, GR1 | ↑ | AREA+2 | ↑ | ↑ | 4. GR1に1を加算する。GR1を次の番地にする。 |
SUBA GR2, =1 | ↑ | ↑ | 1 | ↑ | 5. 個数GR2から1を減算する。 GR2-1をGR2に記憶する。 |
JNZ LOOP | ↑ | ↑ | ↑ | ↑ | 6. 個数(GR2)が0でなければ 2. へ移動する。0ならば次へ。 |
LOOP GR3, 0, GR1 | ↑ | ↑ | ↑ | 7 | 2. GR1(AREA+1)番地の内容をGR3に記憶する。 |
ADDA GR0, GR3 | 18 | ↑ | ↑ | ↑ | 3. 合計を計算する。 GR0+GR3をGR0に記憶する。 |
LAD GR1, 1, GR1 | ↑ | AREA+3 | ↑ | ↑ | 4. GR1に1を加算する。GR1を次の番地にする。 |
SUBA GR2, =1 | ↑ | ↑ | 0 | ↑ | 5. 個数GR2から1を減算する。 GR2-1をGR2に記憶する。 |
JNZ LOOP | ↑ | ↑ | ↑ | ↑ | 6. 個数(GR2)が0でなければ 2. へ移動する。0ならば次へ。 |
RPOP | ↑ | AREA | 3 | ? | スタックからデータをGR1〜GR7に戻す。 |
RET | 18 | AREA | 3 | ? | 最終結果。 7. 呼出し元へ戻る。 |
(2)の方法 副プログラムGOUKEI2
データが格納された領域の先頭アドレスをGR1に格納して副プログラムに渡し、合計値をGR0に格納して戻る。手順は次のとおりである。
- レジスタGR0(合計用)を0で初期化する。
- GR1番地(データ領域)のデータをレジスタGR3に格納する。
- 取りだしたデータ(GR3)と終わりのデータ'#'と比較する。
- 一致したら 8. へ移動する。
- 合計用GR0とデータGR3を加算する。(結果はGR0に格納される。)
- GR1番地に1を加算する。
- 2. へ移動する。
- 呼び出し元に戻る。
MAIN2 START
LAD GR1, AREA
CALL GOUKEI2
ST GR0, ANS
RET
AREA DC 5,4,3,2,1,10,9,8,7,6,'#'
ANS DS 1
END
GOUKEI2 START
RPUSH
LAD GR0, 0 ;1.
LOOP LD GR3, 0, GR1 ;2. データをGR3に格納.
CPL GR3, ='#' ;3. データ終わりの判定.
JZE OWARI ;4. 判定結果によってジャンプ.
ADDA GR0, GR3 ;5. 合計.
LAD GR1, 1, GR1 ;6. アドレス1加算.
JUMP LOOP ;7. LOOPへジャンプ.
OWARI RPOP
RET ;8.
END
データの個数が変わっても、データの終わりである'#'が見つかるまで処理される。
加算するデータを5, 6, 7 の3つとしたときトレースすると次のようになる。
AREA DC 5, 6, 7, '#'
命令 GR0 GR1 GR3 動作 ? AREA ? MAIN1.でGR1にデータを格納して副プログラムを呼び出す。 RPUSH ↑ ↑ ↑ GR1〜GR7をスタックする。 LAD GR0, 0 0 ↑ ↑ 1. GR0を0で初期化する。 LOOP GR3, 0, GR1 ↑ ↑ 5 2. GR1(AREA)番地の内容をGR3に記憶する。 CPL GR3, ='#' ↑ ↑ ↑ 3. データ終わり判定。 GR3と'#'を比較する。 JZE OWARI ↑ ↑ ↑ 4. 一致したときOWARI番地へ移動する。一致しなければ次へ。 ADDA GR0, GR3 5 ↑ ↑ 5. 合計を計算する。 GR0+GR3をGR0に記憶する。 LAD GR1, 1, GR1 ↑ AREA+1 ↑ 6. GR1に1を加算する。GR1を次の番地にする。 JUMP LOOP ↑ ↑ ↑ 7. LOOP番地へ移動する。 LOOP GR3, 0, GR1 ↑ ↑ 6 2. GR1(AREA)番地の内容をGR3に記憶する。 CPL GR3, ='#' ↑ ↑ ↑ 3. データ終わり判定。 GR3と'#'を比較する。 JZE OWARI ↑ ↑ ↑ 4. 一致したときOWARI番地へ移動する。一致しなければ次へ。 ADDA GR0, GR3 11 ↑ ↑ 5. 合計を計算する。 GR0+GR3をGR0に記憶する。 LAD GR1, 1, GR1 ↑ AREA+2 ↑ 6. GR1に1を加算する。GR1を次の番地にする。 JUMP LOOP ↑ ↑ ↑ 7. LOOP番地へ移動する。 LOOP GR3, 0, GR1 ↑ ↑ 7 2. GR1(AREA)番地の内容をGR3に記憶する。 CPL GR3, ='#' ↑ ↑ ↑ 3. データ終わり判定。 GR3と'#'を比較する。 JZE OWARI ↑ ↑ ↑ 4. 一致したときOWARI番地へ移動する。一致しなければ次へ。 ADDA GR0, GR3 18 ↑ ↑ 5. 合計を計算する。 GR0+GR3をGR0に記憶する。 LAD GR1, 1, GR1 ↑ AREA+3 ↑ 6. GR1に1を加算する。GR1を次の番地にする。 JUMP LOOP ↑ ↑ ↑ 7. LOOP番地へ移動する。 LOOP GR3, 0, GR1 ↑ ↑ '#' 2. GR1(AREA)番地の内容をGR3に記憶する。 CPL GR3, ='#' ↑ ↑ ↑ 3. データ終わり判定。 GR3と'#'を比較する。 JZE OWARI ↑ ↑ ↑ 4. 一致したときOWARI番地へ移動する。一致しなければ次へ。 OWARI RPOP ↑ AREA ? スタックからデータをGR1〜GR7に戻す。 RET 18 AREA ? 最終結果。 7. 呼出し元へ戻る。
−=* ポイント *=−
あるデータ領域に格納されているデータを1つずつ順に取り出して調べる方法。
(1) データ領域の先頭アドレスとデータ個数を設定する。
(2) データ領域の先頭アドレス設定し、データの終わりに番兵(終わりの目印)を付ける。
の2つの方法がある。どちらの場合も、データの取り出し、アドレスの加算は次のように行う。
課題1 (1)の方法で合計を計算するプログラムで、合計値が−32768〜32767の範囲を超えた(オーバーフロー)とき、エラーメッセージを出して終わるようなプログラムを作成せよ。
加算するデータを AREA DC 10000,20000,30000,…というように変えてエラーメッセージが出るか確認せよ。同じように AREA DC -10000,-20000,-30000,…というように変えてエラーメッセージが出るか確認せよ。
副プログラムにエラー処理を考える。
計算結果である合計値は、−32768〜32767の全範囲の場合が想定されるので、オーバーフローを起こしたときにエラーの値として「0や-1(#FFFF)を返却する」とすることはできない。
オーバーフローを起こしたかどうかは、FR(フラグレジスタ)のOF(オーバーフロー フラグ)を調べればわかる。合計を計算した後でJOV命令で判定ができる。
(1)の場合 (2)の場合 ADDA GR0, GR3 JOV ERR1 … ERR1 RPOP RET ENDADDA GR0, GR3 JOV OWARI … OWARI RPOP RET END
エラーが起こった場合、JOV、OUT、RPOP、RET命令の順で実行されて呼出し元へ戻る。この4つの命令は実行してもFRが変化しないので、呼出し側でもOFを調べるようにする。
MAIN1 START LAD GR1, AREA LD GR2, KOSUU CALL GOUKEI1 ;CALL GOUKEI2 JOV ERROR ST GR0, ANS RET ERROR OUT EMSG, MLEN RET KOSUU DC 10 AREA DC 5,4,3,2,1,10,9,8,7,6 ANS DS 1 EMSG DC 'Ove(2)rflow' ELEN DC 8 END
CASL U | Copyright © 2003,2017 Hiroshi Masuda |