基本情報技術者 2018年 秋期 午後 問12
次のアセンブラプログラムの説明及びプログラムを読んで、設問1,2に答えよ。
〔プログラムの説明〕
1970年1月1日(以下、基準日という)から、指定された日付までの日数を求める副プログラム群である。日付は、全て西暦(グレゴリオ暦)の日付であり、基準日以降でなければならない。
(1) プログラム1は、基準日から指定された日付までの日数を計算する副プログラムDAYOFFSTである。日付は、GR2に設定されたアドレスから始まる連続した3語に年、月、日を表す数値でこの順に格納され、誤りはないものとする。基準日を0日目とし、指定された日付までの日数を、符号のない数値(0~65535)としてGR0に設定して呼出し元に戻る。ただし、日付は基準日から65535日目までの日付で与えられる。
(2) プログラム2は、うるう年を判定する副プログラムLEAPYEARである。GR2に設定された年が平年の場合は0を、うるう年の場合は1を、GR0に設定して呼出し元に戻る。年が平年であるか、うるう年であるかの判定は、次の順に行う。
① 年が4で割り切れない場合、平年とする。
② 年が4で割り切れ、かつ100で割り切れない場合、うるう年とする。
③ 年が400で割り切れる場合、うるう年とする。
④ ①〜③のいずれでも決定しなかった場合、平年とする。
(3) 副プログラムDIVISIBLは、GR2に設定された整数値がGR3に設定された整数値で割り切れる場合は1を、割り切れない場合は0を、GR0に設定して呼出し元に戻る。プログラムのソースコードは、省略する。
(4) 各副プログラムから戻るとき、汎用レジスタGR1〜GR7の内容は元に戻す。


設問1:
問題文を見る プログラム中のに入れる正しい答えを、解答群の中から選べ。
aに関する解答群
ア:LAD GR4,ACCMDAYS
イ:LAD GR4,ACCMDAYS,GR3
ウ:LAD GR4,ACCMDAYS,GR5
エ:LD GR4,ACCMDAYS
オ:LD GR4,ACCMDAYS,GR3
カ:LD GR4,ACCMDAYS,GR5
bに関する解答群
ア:JMI BREAK
イ:JNZ BREAK
ウ:JOV BREAK
エ:JPL BREAK
オ:JUMP BREAK
カ:JZE BREAK
cに関する解答群
ア:JMI FIN
イ:JNZ FIN
ウ:JOV FIN
エ:JUMP FIN
オ:JZE FIN
dに関する解答群
ア:AND GR0,=#FFFE
イ:AND GR0,=1
ウ:OR GR0,=#FFFE
エ:OR GR0,=1
オ:XOR GR0,=#FFFE
カ:XOR GR0,=1
模範解答
a:イ、b:カ、c:イ、d:カ
解説
解答の導き方
DAYOFFSTは、指定された日付までの日数を「その年の1月1日からの日数」と「それより前の年の日数」に分けて計算します。プログラムでは、GR0などのGRは汎用レジスタ(General Register:計算中の値を一時的に置く場所)を表します。
a:月に応じた日数を表から取り出す準備
GR3には月、GR1には日が入ります。6行目でGR1から1を引くのは、1日を「その月の0日目」として扱うためです。
ACCMDAYSには、平年の各月の1日が、1月1日から何日後かを並べています。たとえば、1月の値は0、2月の値は31です。月を表すGR3は1から始まりますが、表の位置は0から始まります。そのため、必要な位置は「GR3から1を引いた位置」です。
ここでGR4には、表の先頭アドレスにGR3を加えたアドレスを設定します。LAD(Load Address:アドレスを設定する命令)は、データそのものではなく、データの置き場所をGR4に設定します。その後の「ADDL GR1,-1,GR4」では、GR4から1を引いた位置にある表の値をGR1に加えます。結果として、月に対応する「1月1日からの日数」が加算されます。
したがって、GR4を「表の先頭アドレス+GR3」にする「LAD GR4,ACCMDAYS,GR3」が必要です。aはイです。
b:指定された年より前の年だけを加算する
14行目でGR2に1970を設定し、15行目の「CPA GR2,GR5」で、現在の年GR2と指定された年GR5を比べます。
ループでは、現在の年について平年の日数365を加え、うるう年ならさらに1を加えます。そのあとGR2を1増やし、次の年を調べます。つまり、加算するのは1970年から「指定された年の1年前」までです。指定された年そのものの日数は、後で月と日の計算に含めます。
そのため、GR2とGR5が等しくなった時点でループを終える必要があります。CPAで比べた結果が等しいときに分岐する命令はJZEです。よって、bはカです。
c:4で割り切れない年を判定する
3行目の「SUBA GR0,GR0」でGR0を0にしています。平年の場合はこの値のまま戻り、うるう年の場合は後の処理で1になります。
5行目の「AND GR3,=3」は、年の下位2ビットを調べます。AND(論理積)は、両方の値でビットが1になっている部分を取り出す処理です。4で割り切れる数は2進数で下位2ビットが00になるため、この処理の結果が0になります。4で割り切れない数は結果が0以外になります。
結果が0以外なら、その年は4で割り切れません。うるう年ではないので、GR0を0のままにしてFINへ進みます。したがって、0以外のときにFINへ分岐する「JNZ FIN」が必要です。cはイです。
d:100で割り切れる年は、400での判定に進む
DIVISIBLは、割り切れる場合にGR0を1、割り切れない場合に0にします。したがって、100で割り切れるかを調べた直後のGR0は、次のようになります。
年が100で割り切れない場合は、うるう年としてGR0を1にして終了します。一方、100で割り切れる場合は、400で割り切れるかを続けて調べます。
そこで、GR0が0と1のどちらであっても反転させます。XOR(exclusive OR:排他的論理和)で1との演算を行うと、0は1に、1は0になります。
この動きになるのは「XOR GR0,=1」です。dはカです。400で割り切れるかを調べた後は、DIVISIBLが設定したGR0の値をそのまま判定結果として戻します。
誤りやすいポイント
- LADとLDを取り違えないようにします。LADはデータの置き場所を設定し、LDはその場所にあるデータを読み込みます。aでは、後の命令で表の値を使うためのアドレスを作るのでLADが必要です。
- 月は1から始まりますが、表の位置は0から始まります。月に対応する表の位置は、GR3から1を引いた位置です。
- bのJZEは、GR0が0かどうかではなく、直前のCPAによる比較結果が等しいかどうかで分岐します。
- 100で割り切れる年は、その時点で平年と決まるわけではありません。400でも割り切れる場合はうるう年です。dでは、100で割り切れない場合だけ先に終了するようにGR0を反転させています。
FAQ
Q: 1日から1を引くのはなぜですか?
A: 1月1日を、その月の0日目として数えるためです。たとえば、1月1日なら日による加算は0、1月2日なら1になります。
Q: うるう年の1日を月の計算に加えるのは、なぜ3月以降なのですか?
A: うるう年で増える日は2月29日です。2月までの日数にはまだ加えず、3月以降の日付を計算するときに加える必要があるためです。
Q: 100で割り切れるかを調べた後、なぜGR0を反転させるのですか?
A: 100で割り切れない場合は、うるう年としてすぐ終了します。100で割り切れる場合だけ400での判定に進む必要があるため、DIVISIBLの結果を反転させてJNZの分岐条件に合わせています。
関連キーワード: アセンブリ言語、汎用レジスタ、インデックス修飾、論理積、排他的論理和、うるう年判定
設問2:
問題文を見る 次の記述中のに入れる正しい答えを、解答群の中から選べ。
副プログラムDAYOFFSTに、基準日から65536日目以降32767年12月31日までの日付を与えた場合には、あふれが発生し、正しい結果を返すことができない。あふれが発生したことを呼出し元に通知するために、フラグレジスタFRのOFが1の状態で呼出し元に戻るようにしたい。このためには、プログラム1の行番号eの行の直後にf命令を挿入し、ラベルEXITの行に分岐する必要がある。
eに関する解答群
ア:8
イ:13
ウ:18
エ:19
オ:20
fに関する解答群
ア:JMI
イ:JNZ
ウ:JOV
エ:JPL
オ:JZE
模範解答
e:エ、f:ウ
解説
解答の導き方
この設問では、計算結果が扱える範囲を超えたことを、どの行の直後で、どの命令を使って検出するかを考えます。
まず、問題文には、日数を「符号のない数値(0~65535)」としてGR0に設定するとあります。符号のない数値では、負の値を表すための場所を使わず、数値を表します。そのため、扱える最大値は65535です。基準日を0日目とするので、65535日目までは範囲内ですが、65536日目以降は範囲を超えます。
次に、DAYOFFSTで日数を計算する流れを見ます。
- 行番号6で、日付の日の値から1を引きます。
- 行番号8では、月に応じた日数を加えます。行番号13では、うるう年で3月以降の場合に1日を加えます。ここまでで、指定された年の1月1日からの日数を求めます。
- 行番号18では、GR0に1年分の日数を設定します。LEAPYEARの判定結果に応じて、平年なら365、うるう年なら366になります。
- 行番号19では、その1年分の日数を、それまでの合計であるGR1に加えます。
GR1は、日付までの日数を計算している汎用レジスタ(General Register:計算途中の値を置くレジスタ)です。年ごとの日数をGR1に加えていく途中で、合計が65535を超える可能性があります。
符号なしの加算命令ADDLは、計算結果が表せる範囲を超えると、FR(Flag Register:演算結果の状態を記録するレジスタ)のOF(Overflow Flag:桁あふれを示すフラグ)を1にします。したがって、桁あふれを検出するには、合計を更新する行番号19の直後でOFを確認します。
OFが1のときに分岐する命令はJOVです。そのため、行番号19の直後にJOV EXITを挿入すれば、桁あふれが起きた場合にEXITへ進めます。判定を直後に行うのは、後続の演算命令によってOFの状態が変わる可能性があるためです。
誤りやすいポイント
- 行番号18ではなく、行番号19の直後を選びます。行番号18でGR0に設定するのは1年分の365または366だけです。日数の合計であるGR1に加算するのは行番号19です。
- 65535日目もあふれる、と考えないようにします。65535は扱える最大値で、範囲を超えるのは65536日目からです。
- JNZは「結果が0ではないとき」の分岐であり、桁あふれの判定には使えません。JMIやJPLは符号に関する判定なので、符号なしの数値の範囲超過を調べる目的とは異なります。
- 桁あふれが起きたら、結果をGR0に移す処理を通らずEXITへ分岐します。これにより、あふれた値を正常な計算結果として返す処理を避けられます。
FAQ
Q: 65536日目を加算すると、GR1には何が入りますか?
A: 16ビットで表せる範囲を超えるため、GR1には下位の値が残り、OFが1になります。OFを確認すれば、GR1の値だけでは分からない桁あふれを検出できます。
A: 16ビットで表せる範囲を超えるため、GR1には下位の値が残り、OFが1になります。OFを確認すれば、GR1の値だけでは分からない桁あふれを検出できます。
Q: なぜJNZではなくJOVを使うのですか?
A: あふれた結果が0になるとは限りません。JNZは結果が0かどうかを調べますが、JOVはOFが1かどうかを調べるため、桁あふれの検出に適しています。
A: あふれた結果が0になるとは限りません。JNZは結果が0かどうかを調べますが、JOVはOFが1かどうかを調べるため、桁あふれの検出に適しています。
Q: EXITへ分岐すると、どのように呼出し元へ戻りますか?
A: EXITの行でGR1~GR7を元の内容に戻し、その後RETで呼出し元へ戻ります。あふれた結果をGR0に設定する処理は通りません。
A: EXITの行でGR1~GR7を元の内容に戻し、その後RETで呼出し元へ戻ります。あふれた結果をGR0に設定する処理は通りません。
関連キーワード: 符号なし整数、オーバーフロー、フラグレジスタ、条件分岐、加算命令



