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

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

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

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

この問題は乗算の応用です。ちょっと心配な方は駄菓子屋で5円チョコを買ったイメージで作ったサンプルプログラムをご覧下さい。
アセンブラCASL2で「2進数の乗算」を作ってみた

この問題は、2つの数を足すプログラムです。
0002と0003を加えたら0005になります。
Aに0002、Bに0003を設定して合計をAに入れて0005とします。
疑似言語風に書くと、「A←A+B」です。

設問1と2を一気に片付けるために、設問2の具体例を使ってプログラム1をトレースします。
AとBはともに64ビットなので、1語16ビットの為箱4つずつ使います。

A:「3F1D」「B759」「2E0C」「A684」
B: 「2E0C」「A684」「3F1D」「B759」
この加算結果がAに入ります。

では、実際にシミュレーターを使って実行していきます。

主記憶に2つの数AとBが入りました。

GR1にA+0、GR2にB+0のそれぞれのアドレスが入ってプログラム1のADD64を呼び出します。

演算に使うレジスタのGR0が初期化されました。

GR3にA+3、GR2にB+3が入りました。

桁上りのフラグに使うレジスタGR5が初期化されました。

GR0にAの3番地(最後の箱)の中身の数が入りました。

GR0にA+Bの3番地の演算結果が入りました。
OFが1になっているので、オーバーフローして、上の箱(上位桁)の演算結果に
加えます。

桁上りフラグが立ちました。…空欄「a」

演算結果がAの最後の箱に入りました。

桁上りの加算用にGR0に1が入りました。

GR3の「A+3」がGR1の「A+0」と比較されました。…空欄「b」
GR3の方が大きいのでSFは0です。

アドレスがデクリメントされ、GR3が「A+2」、GR4が「B+2」になりました。

LOOPに戻ります。・・・空欄「c」
この空欄ではアドレスがデクリメントされた「B+2」の状態について問われているので
それがマイナスになったりゼロになったりは無いので、単純分岐でJUMPです。

桁上りフラグが初期化されました。

GR0に先ほどの桁上り分の1に加えてA+2の「2E0C」が入ったので、
計算結果は「2E0D」です。…設問2の空欄「d」

あとは同じことの繰り返しなので、主記憶の演算結果のみを掲載します。

プログラミングです。

;平成29春64ビットの加算とそれを使った乗算
;メインプログラム
MAIN START
RPUSH
LAD GR1,A ;足し算に使う数Aの先頭アドレス
LAD GR2,B ;足し算に使う数Bの先頭アドレス
CALL ADD64
RPOP
RET
A DC #3F1D
DC #B759
DC #2E0C
DC #A684
B DC #2E0C
DC #A684
DC #3F1D
DC #B759


;プログラム1
ADD64
RPUSH
LD GR0,=0
LAD GR3,3,GR1 ;Aの一番後ろ4つ目
LAD GR4,3,GR2 ;Bの一番後ろ4つ目
LOOP1 LD GR5,=0 ;桁上りフラグ
ADDL GR0,0,GR3 ;Aを加算
JOV OV1 ;桁上りの分岐
JUMP NOV1 ;桁上りが無かった時
OV1 LD GR5,=1 ;空欄「a」桁上りフラグを立てる
NOV1 ADDL GR0,0,GR4 ;Bを加算
JOV OV2
JUMP NOV2
OV2 LD GR5,=1 ;空欄「a」
NOV2 ST GR0,0,GR3 ;加算結果をAへ格納
LD GR0,GR5 ;フラグを読み込んで次の加算へ
CPL GR3,GR1 ;空欄「b」全ての箱で処理が終わったか比較
JZE EXIT ;プログラムの修了へ分岐
SUBL GR3,=1 ;Aの加算対象を1つ上の箱にする
SUBL GR4,=1 ;Bの加算対象を1つ上の箱にする
JUMP LOOP1 ;空欄「c」アドレスの値はゼロやマイナスにならないから単純分岐
EXIT RPOP
RET
END

ここまでの流れをノートにまとめました。

設問3はプログラム1を使った足し算を呼び出してかけ算を行うプログラム2の問題です。32ビットのAとBという数の掛け算を行います。
乗算結果を64ビットのCに入れます。作業用に64ビットのTEMPという64ビットの領域を使います。それぞれの先頭アドレスを指すレジスタと演算に使う数値についてまとめました。

(GR1→)A:「0002」「0003」(以下0023)
(GR2→)B:「0004」「0005」
(GR3→)C:64ビットの空き領域
(GR7→)TEMP:64ビットの空き領域

プログラム2を動かして見ます。
(主記憶の状態を見やすいように、TEMPはメインプログラム内に書きました。)

A、B、C、TEMPが入りました。

GR1にA+0、GR2にB+0、GR3にC+0が入って、
プログラム2のMULT32を呼び出しました。

GR0にA+0に入っている0002が読み込まれました。

0002がTEMP+2に格納されました。

GR0にA+1に入っている0003が読み込まれました。

0003がTEMP+3に格納されました。

初期化に使う0が読み込まれました。

TEMPの上位ビットが0で初期化されました。

演算結果の入るCが0で初期化されました。

ループカウンタが読み込まれました。
カウンタであり、乗数をシフトするビットを表しています。

B+0が退避されました。

カウンタが退避されました。

16を引いたらマイナスになったので、乗数の下位語を読み込む処理に入ります。

乗数の下位の「0005」が読み込まれました。
0ビットシフトなので値は変わりません。

0005の最下位ビットは1なので、論理積を取って0001になりました。

C+0が読み込まれました。

TEMP+0が読み込まれました。
演算を行う領域の先頭アドレスが設定されたので、プログラム1のADD64を呼びます。
C :「0000」
TEMP:「0023」

プログラム1の加算処理は同じなので省略します。

C:「0023」つまり、「0023」×1

カウントアップされました。

GR1とGR2にTEMPの先頭アドレスを入れてプログラム1を使って加算処理をします。2倍になります。

TEMP:「0023」
TEMP:「0023」

TEMPが「0046」になりました。

つまり、「0023」×2^1です。
べき乗の1はカウンタと一致します。

プログラム2に戻り、B+0がGR2に戻りました。
ループまで戻りました。
GR6にGR5のカウンタ1が入り、16が引かれました。
1-16なのでまだマイナス、下位語の処理です。

乗数の5が読み込まれました。

1ビット右にシフトして0002になりました。

最下位ビットは0です。

カウントアップされました。
GR1、GR2にTEMPの先頭アドレスが読み込まれてプログラム1を呼びます。

TEMP:「0023」×2^1
TEMP:「0023」×2^1

の加算結果なので、

TEMP:「0023」×2^2が入って「4倍」されました。

プログラム2に戻り、GR2にB+0のアドレスが戻りました。
ループに戻ります。

乗数の「0005」が入りました。

乗数の「0005」を2ビットシフトして、「0001」になりました。
最下位ビットが1です。

GR1にC+0が読み込まれました。

GR2にTEMP+0が入りました。
これでプログラム1を呼ぶ準備が終わりましたので、プログラム1を呼びます。
C :「0023」×1
TEMP:「0023」×4
この加算で、

C:「0023」×5が入りました。
これで、Bの「0045」の「5」の乗算結果が入りました。

カウントアップされました。
GR1、GR2にTEMP+0が読み込まれてプログラム1の加算処理が行われます。

TEMP:「0023」×2^2
TEMP:「0023」×2^2

TEMP:「0023」×2^3
カウントが3なのでべき乗も3です。
つまり、16になると、「0023」×2^16で、「0230」になります。
ここまで進めます。

カウントが16になった時の主記憶です。

TEMPで「0023」が2^16倍されて「0230」になりました。

次から上位語の処理です。

16-16で0が入り、ここで初めてマイナスに分岐しなくなります。
つまり上位語を読み込みます。

Bの「0004」「0005」の「0004」が読み込まれました。
0ビットシフトで値は変わりません。

下位桁は0です。

カウントアップされました。
GR1とGR2にTEMPの先頭アドレスが設定されました。
プログラム1を呼びます。

TEMP:「0230」×2^0
TEMP:「0230」×2^0

TEMP:「0230」×2^1
ループに戻ります。

乗数をシフトするビット数です。

「0004」が読まれました。

1ビット右にシフトされました。

最下位ビットが0です。

カウンタが加算されました。
GR1とGR22TEMPのアドレスが読み込まれ、プログラム1を呼びます。

TEMP:「0230」×2^1
TEMP:「0230」×2^1

TEMP:「0230」×2^2(4倍)

ループに戻ります。
GR0に0004が読み込まれ、2ビットシフトして0001になり、
GR1にC+0、GR2にTEMP+0が読み込まれました。
C :「0023」×5倍
TEMP:「0230」×4倍
これで、「0023」×「0045」の演算が出来ました。


プログラム1は変わっていないので画像は省きます。


;平成29春64ビットの加算とそれを使った乗算
;メインプログラム
MAIN START
RPUSH
LAD GR1,A ;掛け算に使う数Aの先頭アドレス
LAD GR2,B ;掛け算に使う数Bの先頭アドレス
LAD GR3,C ;A×Bの結果領域
CALL MUL32
RPOP
RET
A DC #0002
DC #0003
B DC #0004
DC #0005
C DS 4
TEMP DS 4 ;演算結果を見やすくする為にメインプログラムへ移動

;プログラム2
MUL32
RPUSH
LAD GR7,TEMP ;初期化
LD GR0,0,GR1 ;GR1から始まる2語の領域の値を
ST GR0,2,GR7 ;TEMPから始まる4語の領域のうち
LD GR0,1,GR1 ;下位2語に格納
ST GR0,3,GR7
LD GR0,=0
ST GR0,0,GR7 ;TEMPから始まる4語の領域のうちの
ST GR0,1,GR7 ;上位2語に0を格納
ST GR0,0,GR3 ;GR3から始まる4語の領域に0を格納
ST GR0,1,GR3
ST GR0,2,GR3
ST GR0,3,GR3
LD GR5,=0 ;ループカウンタ
LD GR4,GR2 ;GR2の値をGR4に退避
LOOP2 LD GR6,GR5
SUBL GR6,=16 ;数Bの上位語か下位語か判断
JMI LOWORD ;空欄「e」32ビットのうちマイナスになるのは下位語
LD GR0,0,GR2 ;上位語の場合の処理
SRL GR0,0,GR6
JUMP TESTBIT
LOWORD LD GR0,1,GR2 ;下位語の場合の処理
SRL GR0,0,GR5
TESTBIT AND GR0,=#0001 ;最下位ビットが0か1か
JZE ELOOP
LD GR1,GR3 ;ADD64を呼ぶ準備で乗数の末尾が1の時
LAD GR2,TEMP
CALL ADD64
ELOOP CPL GR5,=31
JZE EXIT2
ADDL GR5,=1 ;カウントアップ
LAD GR1,TEMP ;ADD64を呼ぶ準備
LAD GR2,TEMP ;空欄「f」乗数の末尾が1の時も0の時も行う
CALL ADD64
LD GR2,GR4 ;GR4に退避した値をGR2に復帰
JUMP LOOP2
EXIT2 RPOP
RET

;プログラム1
ADD64
RPUSH
LD GR0,=0
LAD GR3,3,GR1 ;Aの一番後ろ4つ目
LAD GR4,3,GR2 ;Bの一番後ろ4つ目
LOOP1 LD GR5,=0 ;桁上りフラグ
ADDL GR0,0,GR3 ;Aを加算
JOV OV1 ;桁上りの分岐
JUMP NOV1 ;桁上りが無かった時
OV1 LD GR5,=1 ;空欄「a」桁上りフラグを立てる
NOV1 ADDL GR0,0,GR4 ;Bを加算
JOV OV2
JUMP NOV2
OV2 LD GR5,=1 ;空欄「a」
NOV2 ST GR0,0,GR3 ;加算結果をAへ格納
LD GR0,GR5 ;フラグを読み込んで次の加算へ
CPL GR3,GR1 ;空欄「b」全ての箱で処理が終わったか比較
JZE EXIT ;プログラムの修了へ分岐
SUBL GR3,=1 ;Aの加算対象を1つ上の箱にする
SUBL GR4,=1 ;Bの加算対象を1つ上の箱にする
JUMP LOOP1 ;空欄「c」アドレスの値はゼロやマイナスにならないから単純分岐
EXIT RPOP
RET
END

ご精読ありがとうございました。
平成21年春から平成30年秋までの20回分のうち、16回分のプログラミング及び
トレースが出来ました。
後は試験が終わったら残りの回のプログラミングとトレースをしながら試験結果を待ちたいと思います。

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

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