平成24春 問12 アセンブラ のプログラミング(コメント、トレースのノート付き)

この記事では、過去問のアセンブラの問題に載っているプログラムを実際に自分の環境で作成して動かすのに使ったコードと、トレースのノートを掲載しています。
この記事を通してアセンブラだけでなくアルゴリズムのトレース力向上にもお役立て下さい。

アセンブラ過去問プログラミング
アセンブラ自作サンプルへ
基本情報技術者試験トップへ
令和2年度(令和3年1月合格報告)

シミュレーターと過去問を解くまでの勉強に使った参考書はこちらです

数字列の足し算プログラムです。
簡単だと思っていましたが、アセンブラのテクニックにタジタジしながら半分ぐらいしか解けなかった問題です。
検証にはだいぶ時間をかけて、このブログを書いている今は全て理解出来ました。

このテクニックがポイントなんですね。
例えば数字列の’1’、つまり#0031と数字列の’2’、つまり#0032を足す時、そのまま足すと’3’にはならず、#0063になってしまうんですね。
だからどちらか(この問題では数字列2)を#0032に#000Fの論理積をとって、#0002にしてから’1’の#0031を足して、

0033である’3’になるんですね。

これが時間測って解いている時は分からなくて、一度トレースを消してやり直したので、時間内に解き終わりませんでした。

では、設問1、設問2のプログラム1であるADDCをステップ実行でやってみます。
分かりやすいように数字列1、数字列2、加算結果をそれぞれ出力しています。

数字列1をBUF1に格納して、その先頭アドレスをGR1に設定します。文字数はGR4に設定します。
数字列2をBUF2に格納して、その先頭アドレスをGR2に設定します。文字数はGR5に設定します。
加算結果をBUF3に格納して、その先頭アドレスをGR3に設定します。文字数は実行後にGR0に格納されます。

入力ウィンドウに問題文の数字列1「9462」を入力してEnterを押します。

出力されました。

数字列1の文字数がGR4に入りました。

数字列2も問題文通り「673」と入力しました。

出力されました。

数字列2の文字数がGR5に入りました。

設問1、2の副プログラムADDCを呼び出しました。
文字数は数字列1の方が長いので、CONTに分岐しました。

数字列1の末尾の1つ後ろのアドレスが入りました。(配列は0から始まるので)

数字列2も同じように入りました。

桁上りフラグが設定されました。

スタックに加算結果の末尾の目印(番兵)となる0(#0000)が入りました。

数字列2の末尾のアドレスが入りました。
「’3’」を指しています。

数字列2の下一桁が入りました。
「’3’」が入りました。

数字から数値に変換されました。
「’3’」から「3」になりました。

数字列1の末尾のアドレスが入りました。
「’2’」を指しています。

数値に変換された数字列2と数字列1の下一桁が加算されました。

桁上げフラグも加算されました。
(0の為、値は変わらず)

桁上げチェックで、’9’である#0039を超えていないので、
分岐はしません。

加算結果がスタックに積まれました。

文字列1の残りが1つ減りました。デクリメントしてカウンタの役割をしています。

文字列2の残りが1つ減りました。デクリメントしてカウンタの役割をしています。

文字列2のアドレスが1つ前になりました。「’7’」を指しています。

「’7’」が読み込まれました。

「’7’」が「7」になりました。

文字列1のアドレスが1つ前になりました。「’6’」を指しています。

数値の「7」と数字列の「’6’」が加算されたので、
「#0007」と「#0036」が加算され、「#003D」になりました。


Dは16進数で13なんですよね。
私は「#0007」と「#0036」を足して、「#0043」だ、なんて思ってしまったのですが、怖いコワイ・・・。


こういう所、出たりするのかな??

では、次行きます。
下位桁からの繰上りが加算されました。0なので値変わらずです。

「’9’」である「#0039」と比較しました。「#003D」の方が大きいので分岐します。

桁上りのフラグが1に設定されました。・・・設問2の25行目1回目

「#003D」を1の桁「’3’」にするため、「#003D」から10を引いて、「#0033」になりました。

スタックに「’3’」が積まれました。

文字列1の残り文字数がデクリメントされました。

文字列2の残り文字数がデクリメントされました。

文字列2のアドレスが1つ前に移動して「’6’」を指しました。

「’6’」が読み込まれました。

数値の「6」に変換されました。

文字列1のアドレスが1つ前に移動して、「’4’」を指しました。

加算されて「#003A」になりました。

桁上げ分が加算されて「#003B」になりました。

‘9’より大きいので桁上げ処理に分岐します。
桁上げフラグが1になりました。・・・設問2の25行目2回目
1の桁「#003B」から10を引いて「#0031」にします。

スタックに積まれました。

文字列1の残りがデクリメントされました。

文字列2の残りがデクリメントされて0になりました。

文字列2が終わったので、その分「0」が入って文字列1の加算準備をしました。

文字列1が1つ前に移動して、「’9’」を指しました。

‘9’が加算されました。

桁上り分が加算されて「#003A」ました。

‘9’より大きいので桁上りの分岐をします。
桁上げフラグが1になりました。・・・設問2の25行目3回目。設問2の答えは「3」
「#003A」から10を引いて、「#0030」になります。

スタックに積まれました。

数字列1の残りが0になりました。

左下の赤くなっている「ZF」が0、つまり、GR0は0では無いので、桁上げ分の’1’の処理をします。

‘1’を読み込みました。

‘1’が主記憶の、GR3の指し示すBUF3の先頭に格納されました。

結果を指し示すBUF3のアドレスが1つ先になりました。

スタックから取り出された’0’がGR1に格納されました。

結果の文字数の長さが1つ加算されました。

‘0’が主記憶の、GR3の指し示すBUF3の先頭に格納されました。

結果を指し示すBUF3のアドレスが1つ先になりました。

スタックから取り出された’1’がGR1に格納されました。

結果の文字数の長さが1つ加算されました。

‘1’が主記憶の、GR3の指し示すBUF3の先頭に格納されました。

結果を指し示すBUF3のアドレスが1つ先になりました。

スタックから取り出された’3’がGR1に格納されました。

結果の文字数の長さが1つ加算されました。

‘3’が主記憶の、GR3の指し示すBUF3の先頭に格納されました。

結果を指し示すBUF3のアドレスが1つ先になりました。

スタックから取り出された’5’がGR1に格納されました。

結果の文字数の長さが1つ加算されました。

‘5’が主記憶の、GR3の指し示すBUF3の先頭に格納されました。

結果を指し示すBUF3のアドレスが1つ先になりました。

スタックから取り出された「0(#0000)」がGR1に格納されました。
終わりの目印、番兵とかターミネーターとかの為に設定した「0」です。

終わって、主プログラムに戻りました。
加算結果が入りました。

これで、プログラム1の設問1と設問2は終わります。

プログラムです。


;平成24春プログラム1
;メインプログラム。プログラム1を呼ぶ設定をプログラム2を参考に作りました。
MAIN START
RPUSH
LAD GR1,BUF1 ;数字列1のポインタを設定
IN BUF1,LEN1 ;最初の数字列を入力し数字列1とする
OUT BUF1,LEN1 ;数字列1の表示
LD GR4,LEN1 ;数字列1の長さを設定
LAD GR3,BUF3 ;BUF3を数字列1と数字列2の和の領域として設定
LAD GR2,BUF2 ;数字列2のポインタを設定
IN BUF2,LEN2 ;数字列2を入力する
OUT BUF2,LEN2 ;数字列2の表示
LD GR5,LEN2 ;数字列2の長さを設定
CALL ADDC
ST GR0,LEN3
OUT BUF3,LEN3 ;計算結果の表示
RPOP
RET
BUF1 DS 256 ;数字列1の領域
BUF2 DS 256 ;数字列2
BUF3 DS 256 ;足し算の結果
LEN1 DS 1 ;数字列1の長さ
LEN2 DS 1 ;数字列2の長さ
LEN3 DS 1 ;数字列3の長さ


;設問1、設問2のプログラム
ADDC
RPUSH
CPA GR4,GR5 ;文字列1と文字列2ではどっちが長い?
JPL CONT ;文字列1の方が長かったら分岐
;文字列2の方が長かったら、長い方が1に交換する処理
;GR4>=GR5となるように数字列1と数字列2のポインタを入れ替え
PUSH 0,GR4 ;数字列1の長さをスタックに積む
LD GR4,GR5 ;数字列2の長さを読み込む
POP GR5 ;スタックに積んだ長さを取り出す
PUSH 0,GR1 ;数字列1をスタックに積む
LD GR1,GR2 ;数字列2を読み込む
POP GR2 ;スタックに積んだ数字列を取り出す
CONT ADDA GR1,GR4 ;数字列1の末尾番地+1(0番地から始まるから)
ADDA GR2,GR5 ;数字列2の末尾番地+1(0番地から始まるから)
LD GR0,=0 ;桁上げフラグを初期化
PUSH 0 ;スタックデータの終わりの目印
LOOP1 LAD GR2,-1,GR2 ;数字列2の末尾番地
LD GR6,0,GR2 ;数字列2の下1桁から加算
AND GR6,=#000F ;数字列(文字)から数字(数値)に変換
LOOP2 LAD GR1,-1,GR1 ;数字列1の末尾番地
ADDA GR6,0,GR1 ;数字列1も下一桁から足す(こっちは数値に変換しないから、数字列になる)
ADDA GR6,GR0 ;桁上げフラグも足す
CPA GR6,=’9′ ;’9’である「0039」より大きかったら桁上げ
JPL CARRY ;桁上げ分岐
LD GR0,=0 ;桁上げフラグを初期化
JUMP NEXT ;計算結果のスタック積み上げ処理に分岐
CARRY LD GR0,=1 ;桁上げフラグの設定
SUBA GR6,=10 ;スタックに下一桁を積めるように文字コードから10をマイナスする
NEXT PUSH 0,GR6 ;加算結果1桁を保存(スタックに積むツムツム)
SUBA GR4,=1 ;数字列1からデクリメントしてカウンタの役割を果たす
JZE END1 ;数字列1の加算処理が終了なら分岐
SUBA GR5,=1 ;数字列2からデクリメントしてカウンタの役割を果たす
JPL LOOP1 ;数字列1も文字列2も残っている場合に分岐
LD GR6,=0 ;数字列2が全部終わって文字列1のみが残っている時0を足す
JUMP LOOP2 ;0に数字列1の加算処理へ分岐
END1 LD GR0,GR0 ;左端を桁上げ?
JZE NOCARRY
LD GR1,=’1′ ;左端に’1’を追加
LOOP3 ST GR1,0,GR3 ;(スタックから取り出した数字を)結果格納
LAD GR3,1,GR3 ;結果格納領域をインクリメント
NOCARRY POP GR1 ;スタックに積んだ加算結果を取り出す
LD GR1,GR1 ;スタックに最初に積んだ目印の0(0000)だったら
JZE END2 ;全て積んだのでおしまい分岐
ADDA GR0,=1 ;結果の文字数をインクリメントでカウントアップ
JUMP LOOP3
END2 RPOP
RET
END

ノートです。

では設問3に行きます。

設問の具体例では5つの数字列の総和を求めます。

プログラム2はSUMCなので、LOOPやFINのあとに「S」を付けています。
LOOPから2行下の「JMI FIN1」で、数字列2の長さがマイナスだったら終了処理に分岐するのですが、
うちのシミュレーターではマイナスの長さというのを入力できないので、長さが「0」、
すなわち、何も入力しないでENTERを押した時に終了処理になるように致しました。

では、問題の具体例でプログラム2のポイントに関する所をステップ実行と最後の所のみ一括実行して
トレースしていきます。

最初の数字を入力して出力しました。

次の数字を入出力しました。

設問1、2で使った、副プログラムADDCを呼び出します。
加算処理が終わって、呼び出し元の設問3のSUMCが呼び出されました。
数字列1と数字列2の加算結果の文字数がGR4に読み込まれました。

ここから、加算結果を数字列1にして、再度呼び出して次の文字列を加算する準備に入ります。
その為に、GR3の結果のアドレスをGR1に読み込むのでプログラムのコメントのように総和(計算結果)と、
数字列1のポインタをスタックを使って入れ替えします。


この図の通り、今、GR1に格納されているGR1の数字列1の先頭アドレスは「#107E」番地です。
GR3の結果のアドレスは「#127E」です。
GR1に格納されたアドレスをスタックに格納します。

次に、GR3のアドレスをGR1に読み込みます。

スタックに積んでいたGR1に格納されていたアドレスが取り出されて、GR3に格納されました。


これで、数字列1に計算結果が入って、新たな数字列2の所に次の数字列を読み込んで、
繰返し計算が出来ます。

以下、一括実行で処理します。
計算結果になります。

プログラムとノートです。


;平成24春プログラム2
;メインプログラムのSUMC。プログラム1を呼ぶ。
SUMC START
RPUSH
LAD GR1,BUF1 ;数字列1のポインタを設定
IN BUF1,LEN1 ;最初の数字列を入力し数字列1とする
OUT BUF1,LEN1 ;数字列1の表示
LD GR4,LEN1 ;数字列1の長さを設定
LAD GR3,BUF3 ;BUF3を総和の和の領域として設定
LAD GR2,BUF2 ;数字列2のポインタを設定
LOOPS IN BUF2,LEN2 ;数字列2を入力する
OUT BUF2,LEN2 ;数字列2の表示
LD GR5,LEN2 ;数字列2の長さを設定
JZE FINS1 ;マイナスの値が入力されたら終了
CALL ADDC
LD GR4,GR0 ;合計の文字数を次の数字列1の文字数にする
;総和の領域のポインタと数字列1のポインタを入れ替え
PUSH 0,GR1 ;数字列1のアドレスをスタックに積む
LD GR1,GR3 ;結果のアドレスを読み込む
POP GR3 ;スタックに積んだ数字列1のアドレスをGR3へ
JUMP LOOPS
FINS1 LAD GR3,BUF3 ;結果の先頭アドレス
CPA GR1,GR3 ;総和の判定
JZE FINS2
ST GR4,LEN1
OUT BUF1,LEN1
JUMP FINS3
FINS2 ST GR4,LEN3
OUT BUF3,LEN3
FINS3
RPOP
RET
BUF1 DS 256 ;数字列1の領域
BUF2 DS 256 ;数字列2
BUF3 DS 256 ;足し算の結果
LEN1 DS 1 ;数字列1の長さ
LEN2 DS 1 ;数字列2の長さ
LEN3 DS 1 ;数字列3の長さ


;設3でも利用。設問1、設問2のプログラム
ADDC
RPUSH
CPA GR4,GR5 ;文字列1と文字列2ではどっちが長い?
JPL CONT ;文字列1の方が長かったら分岐
;文字列2の方が長かったら、長い方が1に交換する処理
;GR4>=GR5となるように数字列1と数字列2のポインタを入れ替え
PUSH 0,GR4 ;数字列1の長さをスタックに積む
LD GR4,GR5 ;数字列2の長さを読み込む
POP GR5 ;スタックに積んだ長さを取り出す
PUSH 0,GR1 ;数字列1をスタックに積む
LD GR1,GR2 ;数字列2を読み込む
POP GR2 ;スタックに積んだ数字列を取り出す
CONT ADDA GR1,GR4 ;数字列1の末尾番地+1(0番地から始まるから)
ADDA GR2,GR5 ;数字列2の末尾番地+1(0番地から始まるから)
LD GR0,=0 ;桁上げフラグを初期化
PUSH 0 ;スタックデータの終わりの目印
LOOP1 LAD GR2,-1,GR2 ;数字列2の末尾番地
LD GR6,0,GR2 ;数字列2の下1桁から加算
AND GR6,=#000F ;数字列(文字)から数字(数値)に変換
LOOP2 LAD GR1,-1,GR1 ;数字列1の末尾番地
ADDA GR6,0,GR1 ;数字列1も下一桁から足す(こっちは数値に変換しないから、数字列になる)
ADDA GR6,GR0 ;桁上げフラグも足す
CPA GR6,=’9′ ;’9’である「0039」より大きかったら桁上げ
JPL CARRY ;桁上げ分岐
LD GR0,=0 ;桁上げフラグを初期化
JUMP NEXT ;計算結果のスタック積み上げ処理に分岐
CARRY LD GR0,=1 ;桁上げフラグの設定
SUBA GR6,=10 ;スタックに下一桁を積めるように文字コードから10をマイナスする
NEXT PUSH 0,GR6 ;加算結果1桁を保存(スタックに積むツムツム)
SUBA GR4,=1 ;数字列1からデクリメントしてカウンタの役割を果たす
JZE END1 ;数字列1の加算処理が終了なら分岐
SUBA GR5,=1 ;数字列2からデクリメントしてカウンタの役割を果たす
JPL LOOP1 ;数字列1も文字列2も残っている場合に分岐
LD GR6,=0 ;数字列2が全部終わって文字列1のみが残っている時0を足す
JUMP LOOP2 ;0に数字列1の加算処理へ分岐
END1 LD GR0,GR0 ;左端を桁上げ?
JZE NOCARRY
LD GR1,=’1′ ;左端に’1’を追加
LOOP3 ST GR1,0,GR3 ;(スタックから取り出した数字を)結果格納
LAD GR3,1,GR3 ;結果格納領域をインクリメント
NOCARRY POP GR1 ;スタックに積んだ加算結果を取り出す
LD GR1,GR1 ;スタックに最初に積んだ目印の0(0000)だったら
JZE END2 ;全て積んだのでおしまい分岐
ADDA GR0,=1 ;結果の文字数をインクリメントでカウントアップ
JUMP LOOP3
END2 RPOP
RET
END

今度受けるので、自分の勉強になりました。
どなたかのお役に立てたらうれしいです。
この記事を書くのに6時間かかったので、
誰も見ていなかったら泣いちゃいます( ;∀;)
どなたか見ている人いますか?いたら手を振って下さい(=^・^=)
要領悪すぎにゃ~~~~~~( ;∀;)(=^・^=)( ;∀;) (^O^)/

シミュレーターと過去問を解くまでの勉強に使った参考書はこちらです

アセンブラ過去問プログラミング
アセンブラ自作サンプルへ
基本情報技術者試験トップへ
息抜きに、写真で癒し(=^・^=)