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

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

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

シミュレーターと過去問を解くまでの勉強に使った参考書はこちらです
関連記事:自作サンプルプログラム「うるう年の判定」

また、基本情報技術者試験の選択言語でアセンブラ言語を選んだ方と、芸能人の椿鬼奴さんに興味のある方でしたら、ちょっとした暇つぶしになるのかもしれません。え、暇なんて無い???そんなぁ、ちょっとだけ見て行ってよ( ;∀;)

この問題、うるう年の判定があるので、アルゴリズムの基礎からキッチリやっておいてよかったなぁと思います。
うるう年のアルゴリズムは、この本のが分かりやすいです。

1970年1月1日を基準の日として日数を計算するプログラムです。これは具体例を用意しないと、なかなか難しいなと思いました。それで、どうやって具体例の日付を考えたかと申しますと、計算が簡単そうで尚且つ、うるう年だとプログラミングの練習になると思ったので、1972年にしました。その中でもうるう年では2月29日まであるので、3月以降の日付にした方が練習になりそうです。
1972年3月生まれ以降の芸能人の方の生年月日を見てみた所、椿鬼奴(つばきおにやっこ)さんの1972年4月15日があったので、この日付をこの問題の具体例にしようかなと決めました。

まず、メインプログラムMAINで椿鬼奴さんの生年月日を設定します。
配列を誕生日だからBIRTHとして、BIRTH+0に1972年、BIRTH+1に4月、BIRTH+2に15日を入れます。それからプログラム1のDAYOFFSTを呼び出します。
それで、日数をカウントするのにうるう年かどうか調べる必要があるので、プログラム2のLEAPYEARを呼び出して、更に4、100、400で割りきれるか調べるのに使う割り算プログラムDIVISIBLを使います。
DIVISIBLはコード省略なので、引き算を利用して自作しました。

では、トレースしていきます。日付の数値が大きいのと計算を分かりやすくする為に、
10進数でトレースしました。

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

主プログラムが呼び出され、主記憶に「1972」、「4」、「15」が入りました。
この生年月日の入った配列BIRTHの先頭アドレスをGR2で指し示しました。
ここから副プログラム1のDAYOFFSTを呼びます。

年、月、日が各レジスタに入りました。

1日からの日数を求めるので、1が引かれました。

配列ACCMDAYSに1月1日からの平年の日数が入っていますので、これを使って1972内の日数計算を行います。
1972年1月1日から同年4月15日までですが、1月の「31日」分と、2月の「28日」分と、3月の「31日」分を足した90をGR1の14に加えるために、GR3の4月の「4」を使って配列のインデックスを指し示します。
配列の先頭は0から始まるので、そのまま足すと5月になり、まずその位置を指定します。空欄「a」の下にGR4とあるので、このGR4に何かするみたいですね。
GR4に配列ACCMDAYSのインデックスを指し示すようにしたいので、「LAD GR4,ACCMDAYS,GR3」が入ります。
このまま足すと5月の日数になってしまうので、1つ前にずらして3番目の(しつこいようですが、この問題での配列のインデックスは0~始まります。)
「90」をGR1に足しこみます。
GR1に元々あった14に90が足されて、104になりました。

次に、この足しこんだ日数は「平年の」場合です。うるう年でしたら3月以降は1を足します。なので、GR3の月が3月より小さかったらSKIPに分岐して、そうではなかったら1972年がうるう年かどうか調べる必要が出て来ます。
この図から分かるように、4月なのでSFも0になったので、1972年がうるう年かどうか調べます。

副プログラムを呼び出す前に、GR2に年を入れました。
CALLでLEAPYEARが呼び出されました。

うるう年かどうかのフラグをGR0に設定する為にゼロクリアしました。

まず、4で割れるか調べるので、GR3に年を入れました。

0なので、1972は4で割れました。
調べるために、3(…0011)と論理積を取ります。1余る場合は0001、2余る場合は0010、3余る場合は0011となり、0になりません。なので、0にならなかった時の分岐に使います。空欄「c」は「JNZ FIN」
1972は3と論理積を取って0になったので、4で割れます。なので、うるう年の「可能性」があります。4で割れて更に100で割れなければ過去問62ページの(2)②の条件に該当するのでうるう年になります。では、100で割れるかどうか調べるために割り算プログラムのDIVISIBLを使います。これはコードが省略されていたので、引き算を利用して調べられるように自作しました。

DIVISIBLを呼ぶ前にGR3に100が入りました。
1972から100を引いて行って、0より大きかったら(JPL)引き算LOOPDに分岐、0になったら割れるからWARERUに分岐、そうでなければマイナスになるので分岐せずに割り切れない処理を行います。では、DIVISIBLを呼びます。

100ずつ引いて行って、72が残りました。
まだプラスなのでLOOPDに分岐します。

マイナスになりました。

割り切れなかったので、GR0に0が入りました。
呼び出し元のLEAPYEARに戻りました。
ここまでで、4で割り切れて100で割り切れないことが分かったので、うるう年決定です。

うるう年の場合はGR0に1を設定しますので、反転する処理を行います。
空欄「d」は「XOR GR0=,=1」です。呼び出し元のDAYOFFSTに戻りました。

このGR0の1が、日数の入っているGR1の104に加わって105になります。
これで、1972年分の日数は105と求まったので、次に1970年、1971年の分を行います。

まずは1970年からうるう年かどうか調べるのでGR2に入りました。

GR2の1970がGR5の1972と比較されます。
GR2の方が小さいので、SFが1になりました。
1970年と1971年の日数加算処理を行いたいということは、1972と等しかったら日数加算処理が終了だから終了のBREAKに分岐します。なので、空欄「b」は「JZE BREAK」です。では、LEAPYEARを読んで、1970年がうるう年かどうか調べます。

3(…0011)との論理演算の結果、2(…0010)となったので、うるう年ではなく「平年」であることが決定しました。呼び出し元に戻ります。

平年なので、365をGR1に元々入っていた105に加えます。

470になりました。

次に1971年がうるう年かどうか調べるので、年数が1加算されました。

GR2の1971とGR5の1972が比較され、GR2の方が小さいので、SFが1になりました。
LEAPYEARが呼ばれました。

4で割ったあまりが3になったので、割り切れないで平年確定です。
呼び出し元に戻りました。

平年だから365が加算され、もともとあった470に加わって835になりました。

年が1つ加算されて、1972年になりました。

GR2、GR5と比較して、どっちも1972で等しいので、ZFが0になりました。
これで日数加算処理は終わります。

プログラムを終了しました。

次に設問2に入ります。オーバーフローの場合ですよね。オーバーフローするとOFが1になります。なので、オーバーフローの分岐になるので、空欄「f」は「JOV」です。

空欄「e」は、オーバーフローするのは日数なので、日数が加算されるレジスタはGR1であることから、19行目になります。

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

;平成30秋日数を求めるプログラム
;主プログラム及び副プログラム群
;これは自作の主プログラム
MAIN START
RPUSH
LAD GR2,BIRTH
CALL DAYOFFST
RPOP
RET
BIRTH DC 1972 ;椿鬼奴(つばきおにやっこ)さんの生年月日
DC 4
DC 15


;プログラム1
DAYOFFST
RPUSH
LD GR5,0,GR2 ;GR5 :年
LD GR3,1,GR2 ;GR3 :月
LD GR1,2,GR2 ;GR1 :日
SUBL GR1,=1 ;GR1で日数をカウント
LAD GR4,ACCMDAYS,GR3 ;空欄「a」月をインデックスとして指し示す
ADDL GR1,-1,GR4 ;1月1日からの日数(平年)を求める
CPA GR3,=3 ;月が3月以降のときうるう年を考慮
JMI SKIP ;1月と2月だったらスキップ
LD GR2,GR5 ;年を読み込んで、
CALL LEAPYEAR ;うるう年かどうか調べる
ADDL GR1,GR0 ;副プログラムから戻ってGR0の値を加算
SKIP LD GR2,=1970 ;1970年から(年-1)年までの間(ただし、
LOOP CPA GR2,GR5 ;年>1970)、1年の日数を加算
JZE BREAK ;空欄「b」1970年または年の日数加算が終わったら終了
CALL LEAPYEAR ;1970年、1971年…具体例の年の1年前まで
ADDL GR0,=365 ;GR0の0または1に365を加算
ADDL GR1,GR0 ;日数加算
ADDA GR2,=1 ;年を1970年~具体例の年まで進める
JUMP LOOP
BREAK LD GR0,GR1 ;日付をGR0に設定して主プログラムに戻る
EXIT RPOP
RET
;ACCMDAYSは、平年の各月1日の1月1日からの日数(1月1日は0日目)
ACCMDAYS DC 0,31,59,90,120,151,181,212,243,273,304,334

;プログラム2
LEAPYEAR
RPUSH
SUBA GR0,GR0 ;ゼロクリア
LD GR3,GR2 ;年を設定
AND GR3,=3 ;…0011と論理積
JNZ FIN ;空欄「c」論理積が0出ないなら4で割り切れていない→平年
LD GR3,=100 ;4で割れたら100でも割れるか調べる
CALL DIVISIBL ;割り算プログラムを呼ぶ
XOR GR0,=1 ;空欄「d」で100で割れ無かったらうるう年確定で呼び出し元に戻る
JNZ FIN
LD GR3,=400 ;100で割れた年は、400でも割れるか調べる
CALL DIVISIBL
FIN
RPOP
RET
;これは自作の割り算副プログラム
DIVISIBL
RPUSH
LOOPD SUBL GR2,GR3
JPL LOOPD
JZE WARERU
LD GR0,=0
JUMP FIND
WARERU
LD GR0,=1
FIND
RPOP
RET
END

<<のーと3、4、5>>

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

シミュレーターと過去問を解くまでの勉強に使った参考書はこちらです
関連記事:自作サンプルプログラム「うるう年の判定」

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