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

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

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

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

この過去問を解いたのは2020年(令和2年)11月8日から9日にかけてです。
丸2日もかかってしまったので、今後時間内に解けるかどうか心配です。
今は丁寧すぎるぐらいにトレースして、それをプログラミングにしたり、ノートに図を描いたりして、力を付けて行って、自信も付けたいです。

後手後手にしていた年末調整、やっと終わりましたので(褒めて褒めて!!)、安心して過去問に取り掛かれました。

さて、過去問に入ります。この過去問題も、アセンブラ色強いな、と思いました。
時間の換算の問題です。
例えば「12分34秒」を秒に直します。これは12×60+34で754秒になります。時間から秒がプログラム1(TOSEC)、逆に秒から時間がプログラム2(TOTIME)、これらを使ってランナーの平均タイムを求めるのがプログラム3(AVERAGE)です。

アセンブラ色が強いな、と思ったのは、特にプログラム1の時間換算で10倍にする処理が出て来て、10バイトは2倍×2倍×2倍の8倍に 2倍を加えた計算が出て来る所で、右シフト左シフトを使ってオーバーフローした場合の分岐が出て来る所です。

個人的にはプログラム1>プログラム3>プログラム2
の順で大変でした。プログラム2は少し楽に感じました。
とは言えボリューミーなので、無理せず休憩を取りながらこの記事を読んで頂けたら嬉しいです(=^・^=)

では、実際に見ていきます。
[プログラム1]は、具体例として数字列「1234」を時間「m1,m2,s1,s2」に当てはめて、12分34秒を754秒と計算する例でトレースしていきます。

主プログラムを呼び出して、主記憶のJIKANに数字列「1234」が1つずつ入りました。

レジスタGR0に0、GR1に数字列JIKANの先頭アドレスが入りました。

プログラム1のTOSECが呼び出されました。
GR2に桁数分のループカウンタが入りました。

秒に換算するのに使うVALUE1の先頭アドレスがGR3に入りました。
数字列の先頭の「1」がGR0に文字コード0031として入りました。

AND演算で数字列0031が1と数値に変換されました。

GR2の所で、桁数カウンタが4から3になりました。

GR4に秒数換算で10倍するのに使う10が入りました。

GR4で10倍の10が右シフトされて5倍になりました。

GR0で数値の1が2になりました。

ループ2へ

GR4で掛ける数の5倍から2倍になりました。
オーバーフローしたので設問2で問われている19行目に飛びます。

GR5に1×2=2が加算されました・・・19行目が1回目

GR0に数値2が更に2倍されて4になりました。

ループ2へ

GR4でかける数2倍が右シフトされて1倍になりました。

GR0で数値4が2倍されて8になりました。

ループ2へ

GR4で1倍が右シフトされ0になりました。
オーバーフローしたので19行目に飛びます。

GR5の2倍とGR0の8倍が加算されGR5の所で10倍になります。・・・19行目が2回目

GR0が2倍され16になりますが、これは後で次の数字列に上書きされます。

ループ2へ
GR4に0フラグが立ちました。

GR3でVAUE1、GR1でJIKANのアドレスが1つ先に進んで次の数字列の処理に入ります。

ループ1へ

GR0に数字列「2」の0032が読み込まれました。

数値の2に変換されました。

GR0にGR0の2とGR510が加算され、12になりました。

GR2の桁数のカウントが1つ減りました。

GR4にVALUE1のかける数6が読み込まれました。

計算の為にGR5が初期化されました。

ループ2

GR4のかける数が右シフトで6から3になりました。

GR0で12が2倍され24になりました。

ループ2へ

かける数が3から1に右シフトされました。
オーバーフローしたので19行目へ

GR5に24が加算されました。・・・19行目が3回目

GR0が2倍され48になりました。

ループ2
GR4のかける数が右シフトされて1から0になり、オーバーフローしました。
19行目に飛びます。

GR5の24ににGR0の48が加算されて、72になりました。・・・19行目が4回目
これで、12×6が実行されました。

GR0が2倍されますが、これは次の数字列に上書きされます。
ループ2へ
かける数にゼロフラグが立ちました。
次の数字列用にGR3のVALUE1とGR1のJIKANが1つ先へ進みます。

ループ1へ

GR0の数字列「3」の0033が入りました。

数字列0033から数値の3に換算されました。

GR0にGR0の3とGR5の72が加算されて75になりました。

GR2で桁数のカウンタが2から1に減りました。

GR4にかける数10倍の10が入りました。

75を10倍する為の計算用レジスタGR5が初期化されました。

GR4でかける数の10倍が右シフトされ5倍になりました。
(色が変わったタイミング逃してしまったので、急遽枠でくくりました。)

GR0で75が2倍されて150になりました。

ループ2へ

GR4でかける数が5倍から右シフトされ2倍になり、オーバーフローしたので
19行目に飛びます。

GR5に150が加算されました。・・・19行目が5回目

GR0の150が2倍され300になりました。

ループ2へ

GR4のかける数2倍が右シフトされて、1倍になりました。

GR0の300が2倍されて600になりました。

ループ2へ
GR4のかける数1倍が右シフトされて0になりました。
オーバーフローしたので19行目に飛びます。

GR5の150にGR0の600が加算されて750になりました。・・・19行目が6回目
ここで、設問2のdの答えが6回になりました。

GR0が2倍されますが、次の文字が上書きされます。
ループ2へ
次の文字用にGR3のVALUE1とGR1のJIKANが1つ先に進み、ループ1へ

GR0に数字列「4」の0034が読み込まれました。

GR0で数字列4から数値4に変換されました。

GR0にGR5が加算され754になりました。

ちょっと数が大きくなったので、10進数表記にします。

また2進数に戻します。
GR2の桁数カウンタが1つ減って0になりました。

では、プログラム1です。

;平成25年秋メインプログラム、プログラム1
;メインプログラム1
MAIN START
RPUSH
LD GR0,=0
LAD GR1,JIKAN
CALL TOSEC
RPOP
RET
JIKAN DC ‘1234’


;プログラム1
TOSEC RPUSH
LD GR5,=0 ;秒の加算処理用
LD GR2,=4 ;ループ回数
LAD GR3,VALUE1 ;掛ける数用
LOOP1 LD GR0,0,GR1 ;GR0←時間の1桁
AND GR0,=#000F ;数字列から数値に換算
ADDA GR0,GR5 ;加算乗算用
SUBA GR2,=1 ;カウンタのデクリメント
JZE FIN
LD GR4,0,GR3 ;GR4←掛ける数
LD GR5,=0 ;加算処理初期化
LOOP2 SRL GR4,1 ;GR0×GR4
JOV INCR ;設問2で問われる19行目へ
JNZ CONT ;空欄a
LAD GR3,1,GR3 ;掛ける数のアドレスを1つ先へ
LAD GR1,1,GR1 ;JIKAN(時間)の数字列を1つ先へ
JUMP LOOP1
INCR ADDA GR5,GR0 ;19行目
CONT SLL GR0,1 ;乗算
JUMP LOOP2
FIN RPOP
RET
VALUE1 DC 10,6,10
END

ノートです。

終了しました。
チョット疲れたからお気に入りのお茶、コーン茶を飲んでプログラム2に入ります。
ついでにストレッチしてトイレも済ましておこう。

コーン茶です。個人的にはとても美味しいと思いますが、どうなんでしょうね。

では、プログラム2へ行きます。

[プログラム2] TOTIME
設問2の具体例を使います。この例を使ったトレースで、10行目の処理の回数を求めるからです。あと、このプログラム2にはシフト演算が無いので、最初から10進数で表示します。レジスタごとに表示を変えられると便利とか思ったのですが、そうなると混乱する場合も。274秒を4分34秒、0434に変換します。
その結果を見やすくする為に、出力ウィンドウに出力させました。
では、プログラム2のトレースに入ります。

GR0に274秒が入りました。

GR1にJIKAN(時間)の先頭アドレスが入り、プログラム2のTOTIMEが呼び出されました。ループカウンタ3が入りました。

割る数の先頭アドレスが入り、10分の600秒が入りました。

計算に使うGR5が初期化されました。

274秒と600秒が比較され、サインフラグが1、つまり10分の桁分が無かったと判断されます。

0が数字列「0」になりました。48と入っていますが、これは16進数で0030です。
16×3なので。(16×1が16だから16進数で10、16×2が32だから16進数で20)

「0」が格納されました。

GR3の割る数VALUE2が1つ先に進み、GR1の格納領域JIKANも1つ進み、ループ回数が1つ減りました。

ループ3へ

割る数に60が入りました。

演算結果が初期化されました。

1分60秒と比較して、274秒の方が多いので、サインフラグが0です。

1分の分がカウントされました。

GR0の274からGR4の60が減算され、GR0が214になりました。・・・10行目の1回目

ループ4へ

1分60秒と比較して、214秒の方が多いので、サインフラグが0です。

2分の分がカウントされました。

GR0の214からGR4の60が減算され、GR0が154になりました。・・・10行目の2回目

ループ4へ

1分60秒と比較して、154秒の方が多いので、サインフラグが0です。

3分の分がカウントされました。

GR0の154からGR4の60が減算され、GR0が94になりました。・・・10行目の3回目

ループ4へ
1分60秒と比較して、94秒の方が多いので、サインフラグが0です。

4分の分がカウントされました。

GR0の94からGR4の60が減算され、GR0が34になりました。・・・10行目の4回目

ループ4へ
1分60秒と比較して、34秒の方が少ないので、サインフラグが1になりました。

4が数字列「4」になりました。52と入っていますが、これは16進数で0034です。
16×3=48、それに4を足して52です。

「4」が格納されました。

GR3の割る数VALUE2が1つ先に進み、GR1の格納領域JIKANも1つ進み、ループ回数が1つ減りました。

ループ3へ

割る数の10が入りました。残りの秒数34を10×3で「3」、余りの分の「4」にする為に、この割る数10を使います。

計算用レジスタのGR5が初期化されました。

10秒と比較して、34秒の方が多いので、サインフラグが0になりました。

10秒1つ分がカウントされました。

GR0の34からGR4の10が減算され、GR0が24になりました。・・・10行目の5回目

ループ4へ
10秒と比較して、24秒の方が多いので、サインフラグが0になりました。

10秒2つ分がカウントされました。

GR0の24からGR4の10が減算され、GR0が14になりました。・・・10行目の6回目

ループ4へ
10秒と比較して、14秒の方が多いので、サインフラグが0になりました。

10秒3つ分がカウントされました。

GR0の14からGR4の10が減算され、GR0が4になりました。・・・10行目の7回目

これより、設問2のeは7回になります。

ループ4へ
10秒と比較して、4秒の方が少ないので、サインフラグが1になりました。

3が数字列「3」になりました。51と入っていますが、これは16進数で0033です。
16×3=48、それに3を足して51です。

「3」が格納されました。

GR3の割る数VALUE2が1つ先に進み、GR1の格納領域JIKANも1つ進み、ループ回数が1つ減って0になりました。なので、分岐せず下に行きます。

GR0の4が数字列「4」になりました。52と入っていますが、これは16進数で0034です。16×3=48、それに4を足して52です。

「4」が格納されました。

メインプログラムに戻ります。

数字列が表示されました。

プログラム2はこちらになります。

;平成25年秋メインプログラム、プログラム2
;メインプログラム2
MAIN START
RPUSH
LD GR0,=274
LAD GR1,JIKAN
CALL TOTIME
LAD GR1,JIKAN
OUT JIKAN,LEN
RPOP
RET
JIKAN DS 4
LEN DC 4


;プログラム2
TOTIME RPUSH
LD GR2,=3 ;ループ回数
LAD GR3,VALUE2 ;秒を分などに換算する割り算領域
LOOP3 LD GR4,0,GR3 ;GR4←割る数
LD GR5,=0
LOOP4 CPA GR0,GR4 ;GR0÷GR4
JMI NEXT
ADDA GR5,=1
SUBA GR0,GR4 ;割る数分引いていく
JUMP LOOP4
NEXT OR GR5,=’0′ ;数値を数字列に換算
ST GR5,0,GR1 ;JIKANに格納
LAD GR3,1,GR3 ;割り算の割る数の位置を進める
LAD GR1,1,GR1 ;時間を格納する数字列を進める
SUBA GR2,=1 ;ループ回数を減算
JNZ LOOP3
OR GR0,=’0′ ;残りの秒数を数字列に変換
ST GR0,0,GR1 ;残りの秒を格納
RPOP
RET
VALUE2 DC 600,60,10
END


↑下手字御免m(__)m
ノートNO.7下の方が見辛いです( ;∀;)
ちょっと疲れたので、また少し休んでからプログラム3に行きます。

・・・・で、結局寝て次の日になりました(=^・^=)

プログラム3のAVERAGEは、選手4人のタイムの平均を求めます。
まずはメインプログラムに下記の設定をします。
タイムは選手1さんは「1234」、選手2さんは「1235」、選手3さんは「1236」、選手4さんは「1237」とします。
4人分のタイムを下記のように選手1さんはRUNNER1、…選手4さんはRUNNER4に格納します。

RUNNER1 ‘1234’…(RUNNER1+0に数字列の「1」、RUNNER1+1に数字列の「2」、RUNNER1+2に数字列の「3」、RUNNER1+3に数字列の「4」)
RUNNER2 ‘1235’…(RUNNER2+0に数字列の「1」…RUNNER2+3に数字列の「5」)
RUNNER3 ‘1236’…(RUNNER3+0に数字列の「1」…RUNNER3+3に数字列の「6」)
RUNNER4 ‘1237’…(RUNNER4+0に数字列の「1」…RUNNER4+3に数字列の「7」)

これらの先頭アドレスを領域ALLに格納します。

ALL+0 RUNNER1+0
ALL+1 RUNNER2+0
ALL+2 RUNNER3+0
ALL+3 RUNNER4+0

RUNNER1のアドレスはGR1、ALLのアドレスはGR3を使用します。

これでプログラム3の(AVERAGE)を呼びます。
プログラム1(TOSEC)を使って秒にして、GR0に選手1の秒を求め、プログラム3(AVERAGE)に戻り、GR4に退避します。繰返し呼び出し、GR0に選手1人分求めたらそれを戻ってGR4に足していきます。
そうするとGR4に4人分の合計の秒数が求まります。そうしたらその値を右に2つシフトして、4で割って平均を求めます。
それからその秒をプログラム2(TOTIME)を使って時間を求めてプログラム3に戻って平均時間を出力します。

ここまでの説明をノートにまとめました。

今回は、シミュレーターの一括実行(このシミュレーターの場合は「実行メニューから」)で行い、出力結果はこのようになりました。

以下がプログラムになります。

プログラム1,2は変わらないので、画像は省略します。

;平成25年秋メインプログラム、プログラム1,2,3
;メインプログラム3
MAIN START
RPUSH
LAD GR3,ALL ;選手4人の先頭アドレスが格納された領域「ALL」の先頭アドレス
LAD GR1,RUNNER1 ;選手1のタイムが格納されている領域の先頭アドレス
ST GR1,0,GR3 ;ALLの先頭に↑を格納する
LAD GR1,RUNNER2 ;選手2のタイムが格納されている領域の先頭アドレス
ST GR1,1,GR3 ;ALL+1に↑を格納する
LAD GR1,RUNNER3 ;選手3のタイムが格納されている領域の先頭アドレス
ST GR1,2,GR3 ;ALL+2に↑を格納する
LAD GR1,RUNNER4 ;選手4のタイムが格納されている領域の先頭アドレス
ST GR1,3,GR3 ;ALL+3に↑を格納する
LAD GR1,ALL ;ALLの先頭アドレスを指定する
CALL AVERAGE ;プログラム3を呼ぶ
RPOP
RET
RUNNER1 DC ‘1234’ ;選手1のタイム
RUNNER2 DC ‘1235’ ;選手2のタイム
RUNNER3 DC ‘1236’ ;選手3のタイム
RUNNER4 DC ‘1237’ ;選手4のタイム
ALL DS 4 ;4人分のタイムの先頭アドレスを入れる領域


;プログラム3
AVERAGE RPUSH
LD GR2,=4 ;加算人数4人の処理待ち人数のカウンター
LD GR3,GR1 ;先頭アドレスを格納
LD GR4,=0 ;4人分の秒の加算領域の初期化
LOOP5 LD GR1,0,GR3 ;4人分のタイムの先頭アドレスを読み込む
CALL TOSEC ;「空欄f」プログラム1を呼ぶ
ADDA GR4,GR0 ;選手の秒換算した分を足していく
LAD GR3,1,GR3 ;ALLのアドレスを先に進める
SUBA GR2,=1 ;処理待ち人数カウンタを1減らす
JNZ LOOP5
SRL GR4,2 ;「空欄g」
LD GR0,GR4 ;プログラム2を呼ぶ準備
LAD GR1,RESULT ;プログラム2を呼ぶ準備
CALL TOTIME ;プログラム2を呼ぶ
OUT RESULT,LEN4 ;出力
RPOP
RET
LEN4 DC 4
RESULT DS 4


;プログラム1
TOSEC RPUSH
LD GR5,=0 ;秒の加算処理用
LD GR2,=4 ;ループ回数
LAD GR3,VALUE1 ;掛ける数用
LOOP1 LD GR0,0,GR1 ;GR0←時間の1桁
AND GR0,=#000F ;数字列から数値に換算
ADDA GR0,GR5 ;加算乗算用
SUBA GR2,=1 ;カウンタのデクリメント
JZE FIN
LD GR4,0,GR3 ;GR4←掛ける数
LD GR5,=0 ;加算処理初期化
LOOP2 SRL GR4,1 ;GR0×GR4
JOV INCR ;設問2で問われる19行目へ
JNZ CONT ;空欄a
LAD GR3,1,GR3 ;掛ける数のアドレスを1つ先へ
LAD GR1,1,GR1 ;JIKAN(時間)の数字列を1つ先へ
JUMP LOOP1
INCR ADDA GR5,GR0 ;19行目
CONT SLL GR0,1 ;乗算
JUMP LOOP2
FIN RPOP
RET
VALUE1 DC 10,6,10


;プログラム2
TOTIME RPUSH
LD GR2,=3 ;ループ回数
LAD GR3,VALUE2 ;秒を分などに換算する割り算領域
LOOP3 LD GR4,0,GR3 ;GR4←割る数
LD GR5,=0
LOOP4 CPA GR0,GR4 ;GR0÷GR4
JMI NEXT
ADDA GR5,=1
SUBA GR0,GR4 ;割る数分引いていく
JUMP LOOP4
NEXT OR GR5,=’0′ ;数値を数字列に換算
ST GR5,0,GR1 ;RESULTに格納
LAD GR3,1,GR3 ;割り算の割る数の位置を進める
LAD GR1,1,GR1 ;時間を格納する数字列を進める
SUBA GR2,=1 ;ループ回数を減算
JNZ LOOP3
OR GR0,=’0′ ;残りの秒数を数字列に変換
ST GR0,0,GR1 ;残りの秒を格納
RPOP
RET
VALUE2 DC 600,60,10
END

トレースノートはこちらです。

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

長々とまとまらなかったり、色々と下らないことも書いて失礼もあったかもしれませんが、ここまでお付き合い頂き本当にありがとうございました。

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

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