平成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回分のプログラミング及び
トレースが出来ました。
後は試験が終わったら残りの回のプログラミングとトレースをしながら試験結果を待ちたいと思います。

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

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

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

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

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

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

この回の過去問は少し難しいというか、ややこしいと思います。なので、この記事をご覧になる前に、
平成21年秋過去問問12アセンブラ
平成25年春過去問問12アセンブラ

をご覧下さい。

この記事を更新している今現在は設問全て理解出来ましたが、理解するまでに手間取ってしまいました。
この問題苦手かも知れません。この記事でトレースしたり明日も問題を解いてみたりして、もう少し練習したいです。
試験が心配になってきた・・・。

ではこの問題について見ていきます。
1語16ビットで2語、つまり31ビット分のαという領域があります。
こんな感じです。
α+0「0011011000000000」
α+1「0000000000000000」
このαの一部がβという領域のビットと一致するところがあり、その位置を求めます。

β「11011」

α+0の2ビット目(先頭は0ビット)から5ビット分がβと一致します。
プログラム1「BSRH」では一致した位置の「2」がGR0に設定して呼び出し元に戻ります。

βはGR2に左詰めで「1101100000000000」と設定されます。
n=5ビット分の5はGR3に5、GR1はα+0を設定します。

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

プログラミングする時は16進数で指定します。
α+0を16進数すると「3600」、α+1は「0000」、βは「D800」になります。
尚、シミュレータのレジスタの値は、2進数で表示します。

では、シミュレーターを使ってのトレースをします。

GR1にα+0の「アドレス」が、GR2にβ、GR3にビット数の5が設定されました。
プログラム1のBSRHを呼びます。

GR0に-1が入りました。αとβの一致するビットが見つからなかった時にGR0で-1を返します。
初期値として見つからなかった時の値が入りました。

GR6にマスク生成されます。
対象ビット以外をマスクする様にします。

対象の5ビット分右シフトされました。

マスクが反転されました。
これで、論理積を取ると対象の5ビット分以外が0になります。

GR4にα+0が入りました。

GR5にα+1が入りました。

GR1に2語分のビットの32が入りました。

GR1に32-5で27が入りました。

GR3に27が写されました。この差で一致するビットの位置を求めます。

GR7にGR4のα+0が写されました。

GR6のマスクと論理積を取って、最初の5ビットが取り出されました。
次に、GR2TO
GR7でGR2との排他的論理和を取ります。
もしも最初の5ビットが一致していたら0になります。・・・空欄「a」

0にならなかったので、一致しませんでした。

GR1が27から1を引いて26になりました。

GR4のα+0が左に1ビットシフトされました。
GR5のα+1も左に1ビットシフトされました。(0なので値変わらず)・・・空欄「b」

ループに戻ります。

GR7にα+0を左に1ビットした値が入りました。

GR6のマスクとの論理積で、GR7の先頭の5ビットが取り出されました。
GR7でGR2との排他的論理和を取ります。
もしも最初の5ビットが一致していたら0になります。

0にならなかったので、一致しませんでした。

26から1を引いて、25になりました。

GR4で、α+0を1ビット左シフトした値が更に1ビット左シフトされました。
GR5のα+1も左に1ビットシフトされました。(0なので値変わらず)

ループに戻ります。

GR4で、α+0が2ビット左シフトされた値がGR7に入ります。
GR6のマスクとの論理シフトされて先頭5ビットが取り出されました。
GR7でGR2との排他的論理和を取ります。
もしも最初の5ビットが一致していたら0になります。

GR2との排他的論理和で0になったので一致していました。
ラベルFOUNDに分岐します。

27-25で処理したビット分の2が求められました。

ビット位置2がGR0に入りました。

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

;平成29年秋一致するビットを求めてその分置換する
;主プログラム
MAIN START
RPUSH
LAD GR1,ALPHA ;アドレスα+0を設定
LD GR2,BETA ;βを読み込む
LD GR3,N ;ビット数を読み込む
CALL BSRH ;プログラム1へ
RPOP
RET
ALPHA DC #3600 ;α+0
DC #0000 ;α+1
BETA DC #D800 ;β
N DC 5 ;ビット数


;プログラム1
BSRH RPUSH
LD GR0,=-1 ;初期値設定
LD GR6,=#FFFF ;マスク作成
SRL GR6,0,GR3 ;Nビット分だけ右シフト
XOR GR6,=#FFFF ;マスクのビットを反転
LD GR4,0,GR1 ;α+0の取り出し
LD GR5,1,GR1 ;α+1の取り出し
LD GR1,=32
SUBA GR1,GR3 ;GR1←32-n
LD GR3,GR1 ;GR3←32-n
LPH LD GR7,GR4 ;α+0の退避
AND GR7,GR6 ;マスクで対象部分を抜き出す
XOR GR7,GR2 ;空欄「a」結果が0なら一致
JZE FOUND ;一致あり
SUBA GR1,=1 ;処理済み分デクリメント
JMI FIN ;一致無し
SLL GR4,1 ;α+0を1ビット左シフト
SLL GR5,1 ;α+1も。オーバーフローしたらα+0へ
JOV NEXT ;空欄「b」↑の処理へ分岐
JUMP LPH
NEXT OR GR4,=#0001 ;2語目のオーバーフローした分
JUMP LPH
FOUND SUBA GR3,GR1 ;pの算出
LD GR0,GR3
FIN RPOP
RET
END

設問1が全て解答出来ました。

4行目:「1111111111111111」
5行目:「0000011111111111」
6行目:「1111100000000000」
で、対象ビットを取り出すマスクが出来ました。
これと同じ処理は、

「1000000000000000」これを、
「1111100000000000」にするので、
「算術シフト」を使います。先頭のビットが1なので、同じビットが入る算術シフトを
作る方法でもマスクが作れます。
-1してあるのでは、先頭の1の分があるのでその分引くからです。
この具体例ではマスクの1は1が5つになるようにします。つまり、
右4ビット「算術」シフトします。

もしも伝わりにくかったら、平成25年春アセンブラに詳細を書きましたので、
そちらをご覧下さい(=^・^=)m

では、設問3と4に行きます。
設問3は設問4の具体例を使うと、空欄「d」と設問4が一度に解けるので、その具体例を使います。

α+0「1111111111110011」
α+1「0111111111111111」
β  「11011」
n  「5」
γ  「11110」

この例ですと、1語目の後ろから2ビット(14ビット)~2語目の2ビット目までの5ビット間を置換します。

なので、実行後は
α+0「1111111111110011」(こっちは変わらず)
α+1「1101111111111111」(先頭から3ビット分変わっています)
になります。

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

では、シミュレーターを動かしながらトレースをします。

主記憶の領域にα、β、n、γの所に問題文の値が設定されました。
実行後はα+0は変わりませんが、α+1が「DFFF」になります。
プログラム1のトレースの所は省略します。

αとβが一致するところが14ビット目なので、GR0に
14が求まります。それがGR2に入りました。

GR6でマスク生成されます。

右に5ビットシフトしました。

反転してマスクが完成しました。

対象のビット5が入りました。

1語のみなのか、1語と2語にまたがるのか、2語のみなのか判定するのに使うので、
16が入りました。

16から14を引いて2と求まりました。

GR3の2とGR7の対象ビット5が比較されました。
GR3の方が小さいので1語と2語にまたがります。

GR4のγがGR5に退避されました。

GR6のマスクがGR7に退避されました。

GR4が14ビットシフトされました。

GR6が14ビットシフトされました。

GR2にαの1語目が入りました。

マスクのGR6が反転されました。

GR6との論理積を取りました。

γの一部が足されて、1語目の置換処理が終了しました。

退避していたγがGR4に戻りました。

退避していたマスクがGR6に戻りました。

γが2語目の2ビット分GR4左シフトされました。

マスクも2ビットシフトされました・・・設問4の解答です(=^・^=)m

α+1のアドレスが入りました。

2語目が読み込まれました。

マスクが反転されました。

GR6のマスクとの論理積を取りました。

γの一部が足しこまれて置換が完了しました。・・・空欄「d」

2語目のα+1が主記憶に格納されました。

終了しました。

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


プログラム1の画像は省略しています。


;平成29年秋一致するビットを求めてその分置換する
;主プログラム
MAIN START
RPUSH
LAD GR1,ALPHA ;アドレスα+0を設定
LD GR2,BETA ;βを読み込む
LD GR3,N ;ビット数を読み込む
LD GR4,GAMMA ;γを読み込む
CALL BREP ;プログラム2へ
RPOP
RET
ALPHA DC #FFF3 ;α+0
DC #7FFF ;α+1
BETA DC #D800 ;β
N DC 5 ;ビット数
GAMMA DC #F000 ;γ


;プログラム2
BREP
RPUSH
CALL BSRH ;プログラム1を呼ぶ
LD GR2,GR0 ;一致したビットの位置が入る
JMI FIN
LD GR6,=#FFFF ;マスク生成
SRL GR6,0,GR3
XOR GR6,=#FFFF
LD GR7,GR3 ;GR7←n
LD GR3,=16
SUBA GR3,GR2 ;GR3←16-n
JMI ONL2 ;一致する部分ビット列が2語目だけのとき
JZE ONL2
CPA GR3,GR7 ;(16-p)とnの比較
JMI NEXTP ;2語にまたがる処理
JUMP ONL1 ;一致するビットが1語目だけのとき
NEXTP LD GR5,GR4 ;γを退避
LD GR7,GR6 ;マスクを退避
CALL S1
LD GR4,GR5
LD GR6,GR7
SLL GR4,0,GR3 ;2語目用γの調整
SLL GR6,0,GR3 ;2語目用マスクの調整
LAD GR1,1,GR1
CALL S2 ;2語目の最終処理
JUMP FINP
ONL1 CALL S1
JUMP FINP
ONL2 LD GR2,=0 ;空欄「c」シフトするビット数
SUBA GR2,GR3 ;GR2←p-16
LAD GR1,1,GR1 ;操作対象を2語目にして、
CALL S1 ;2語目の処理
FINP RPOP
RET
S1 SRL GR4,0,GR2 ;γの調整
SRL GR6,0,GR2 ;マスクの調整
S2 LD GR2,0,GR1 ;操作対象後の取り出し
XOR GR6,=#FFFF
AND GR2,GR6
OR GR2,GR4 ;空欄「d」で置換処理
ST GR2,0,GR1
RET
;プログラム1
BSRH RPUSH
LD GR0,=-1 ;初期値設定
LD GR6,=#FFFF ;マスク作成
SRL GR6,0,GR3 ;Nビット分だけ右シフト
XOR GR6,=#FFFF ;マスクのビットを反転
LD GR4,0,GR1 ;α+0の取り出し
LD GR5,1,GR1 ;α+1の取り出し
LD GR1,=32
SUBA GR1,GR3 ;GR1←32-n
LD GR3,GR1 ;GR3←32-n
LPH LD GR7,GR4 ;α+0の退避
AND GR7,GR6 ;マスクで対象部分を抜き出す
XOR GR7,GR2 ;空欄「a」結果が0なら一致
JZE FOUND ;一致あり
SUBA GR1,=1 ;処理済み分デクリメント
JMI FIN ;一致無し
SLL GR4,1 ;α+0を1ビット左シフト
SLL GR5,1 ;α+1も。オーバーフローしたらα+0へ
JOV NEXT ;空欄「b」↑の処理へ分岐
JUMP LPH
NEXT OR GR4,=#0001 ;2語目のオーバーフローした分
JUMP LPH
FOUND SUBA GR3,GR1 ;pの算出
LD GR0,GR3
FIN RPOP
RET
END

空欄「c」を埋めるのに2語目のみ置換対象となる具体例を考えましたので、それで
トレースをします。

α+0「0000000000000000」
α+1「0110110000000000」
β  「11011」
n 「3」
γ  「11111」

主プログラムのここの所のみを変えました。

空欄cのある所とその少し後までトレースをします。
2語目の1ビットなので、17を返してレジスタに移された所から始めます。

GR2に17が入りました。

GR6にマスクが作成されました。

対象のビットが入りました。

1語目のみ、2語目のみ、1、2語にまたがるを判定するのに使う16が入りました。

16-17で-1になり、2語目のみと判定されました。

GR2がゼロクリアされました。・・・空欄「c」
これは後でマスクをシフトするビットを求めるのに使います。

0-(-1)で1が求まりましたので1ビットシフトするのに使います。

以下略

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

お疲れ様でした。

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

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

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

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

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

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

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

この問題は平成26春のアセンブラの問題に似ています。
また、自作の2進数の乗算プログラムもあります。

この記事は、この記事は基本情報技術者試験の選択言語でアセンブラ言語を選んだ方と、似顔絵に興味のある方でしたら、ちょっとした暇つぶしになるのかもしれません。え、暇なんて無い???そんなぁ、ちょっとだけ見て行ってよ( ;∀;)(途中途中に、似顔絵が登場します。)

平成30年春のアセンブラの問題は、これもアセンブラ色が強いと思いました。
数字列を数値に変換して、管理テーブルと呼ばれるテーブルに格納して、それをかけ算するといった処理を行います。かけ算の仕方は頻出だと思うので(特に乗数の下一桁が0かどうかとか)真剣に書きたいと思います。

ではまず、数字列を数値に変換するプログラム1に入ります。
この具体例はそのまま使えると思います。

数字列「567」を数値にするとは、文字コード「0035」を数値の5である「0005」に変換して、数値なので演算をして100倍、文字コード「0036」を数値の6に変換して10倍、文字コード「0037」を数値の7に変換して合計して数値の「567」にします。

この時、10倍のやり方について少し見てみます。
例えば5を10倍すると50になりますが、まず5を2倍した10と、その10をどっかに退避して、退避した分を2倍すると20、更に2倍すると40(元の数5の8倍)となり、
それを元の数を2倍した10の所にこの40を足すと50になります。

この、まず2倍にして、2倍にした数を退避して、
退避した数を4倍すると元の数の8倍になり、元の数の2倍と足しこんで結果10倍にするというやり方も、
アセンブラ色が強い、かなり有力なパターンなのではと思いました。

プログラム1を呼び出すメインプログラムに、数字列の入った領域の先頭のアドレスを指定します。数字列の入った領域の名前は「SUUJI」とします。GR1が「SUUJI+0」を指します。「SUUJI+0」に数字5の文字コード0035、「SUUJI+1」に数字6の文字コード0036、「SUUJI+2」に数字7の文字コードが入っています。
また、文字数はこの具体例の倍は3文字なので、3をGR2に読み込みます。
戻り値である567はGR0に読み込んでメインプログラムに戻ります。

ここまでの内容をノートにまとめました。

実際に数字列「567」を数値「567」に変換するプログラムを過去問を参考にして作ってシミュレーターで動かしてみます。

主記憶に数字列「567」が入りました。数字列SUUJIの「5」が入っている所の先頭アドレスは「SUUJI+0」です。うちの環境では101Cになっています。

GR1にSUUJIの先頭アドレスが入って、GR2に文字数が入りました。

GR2にSUUJIのアドレスに文字数の3が加算され、「SUUJI+3」になりました。

数字列を数値に変換した数(戻り値)を格納するGR0を初期化しました。

GR1の「SUUJI+0」はGR2の「SUUJI+3」よりも小さいので、SFが1になりましたので、まだ変換処理が終わっていないです。

GR4に数字列の「5」が読み込まれました。

数値の「5」に変換されました。
GR0の0が10倍されます。ここは値が変わらないので、このループでは略して、
次からトレースします。

戻り値に使うレジスタGR0に数値の「5」が格納されました。

GR1のアドレスが「SUUJI+1」になりました。
ループに戻ります。

GR1は「SUUJI+1」とGR2の「SUUJI+3」よりも小さいのでSFが1です。
まだ処理をします。

GR4に数字列の「6」が読み込まれました。

GR4の所が数値の「6」になりました。

GR0の所で、さっき処理した「5」が2倍され「10」(A)になりました。

10がGR5に退避されました。

10が4倍されて40になりました。(28は2×16+8)

5の2倍と5の8倍が足されて50になりました。
(32は3×16+2)

今数値に変換された6が足されました。

GR1が指している先が「SUUJI+2」になりました。

GR1の「SUUJI+2」はGR2の「SUUJI+3」より小さいのでSFが1になり処理中です。

GR4に数字列の「7」が読み込まれました。

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

GR0が2倍されました。

2倍された数が退避されました。

2倍された数が4倍され、8倍になりました。

2倍された数に8倍された数が足されて10倍になりました。

ここに今数値に変換された7が足しこまれました。
これで567になったのですが、表示を10進数に切り替えて見てみます。

10進数表示で567になりました。
GR1のアドレスが「SUUJI+3」になり、ループに戻ります。
(表示を16進数に戻しました。)


GR1もGR2も「SUUJI+3」になったので、ZFが1になりました。

終了しました。
お疲れ様です。

主プログラムとプログラム1のプログラミングとノートを掲載します。

;平成30春 数字列を数値に変換して管理テーブルに格納してかけ算するプログラム
;メインプログラム
MAIN START
RPUSH
LAD GR1,SUUJI
LD GR2,=3
CALL DTOB
RPOP
RET
SUUJI DC ‘567’


;プログラム1
DTOB RPUSH
ADDL GR2,GR1 ;アドレスを文字数分だけ進めて終了条件の判定に使う
LAD GR0,0 ;戻り値の初期化
LPDT CPL GR1,GR2 ;変換終了?
JZE FINDT
LD GR4,0,GR1 ;数字1文字の取り出し
SUBL GR4,=’0′ ;1桁を数値に変換
SLL GR0,1 ;GR0を10倍してGR4を加算。まずここで2倍
LD GR5,GR0 ;2倍した数を退避
SLL GR5,2 ;空欄「a」退避した数を4倍(元の数の8倍)
ADDL GR0,GR5 ;8倍した結果を2倍した数に足しこむ
ADDL GR0,GR4 ;読み込んだ数値を足す
LAD GR1,1,GR1 ;次の数値を読み込むので1つ進める
JUMP LPDT ;ループへ
FINDT RPOP
RET
END

プログラム2のGETWDでは、さっきのDTOBを使って数字列を数値に変換したらそれを管理テーブルと呼ばれる保存領域に格納します。管理テーブルの領域を「KANRI+0」にします。

まんまじゃん。

JAROのマギラワシ編

GETWDでは、SUUJIの数字列の具体例は問題より一部簡略化して(オーバーフローを防ぐため)数字列SUUJI「△△1234△567△△△9876△.」をプログラム1のDTOBを使って数値に変換して、その際に空白文字△で区切って数値に変換します。
管理テーブルKANRIのKANRI+0、KANRI+1、KANRI+2‥‥と格納していきます。
KANRIの大きさは仮に4、つまりKANRI+3までとします。

さっきと同じようにGR1でSUUJI+0を、GR2でKANRI+0を指して、DTOBを呼ぶのに文字数を求める必要があるので、計算の為にGR1、GR2のアドレスをGR6、GR7に退避してアドレスの差から文字数を求めます。
それが求まったらDTOBを呼びます。数値に変換した後にGETWDに戻ります。
KANRIのアドレスを1つ先に進めて、変換及び格納処理に戻ります。
最後に「.」の時に「-1」を格納して終了の目印にします。

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

このプログラム2の名前、GETWDで、TM NETWORKさんの『Get Wild』のサビが浮かんできました。
同じような人いるかな??

では、プログラミングしてトレースします。

数字列SUUJI「△△1234△567△△△9876△.」が主記憶に読まれました。
先頭のアドレス「SUUJI+0」はうちの環境では101Cになっています。

管理テーブルKANRIの領域が確保されました。
先頭アドレス「KANRI+0」はうちの環境では102Fになります。

SUUJI+0とKANRI+0をセットしてGETWDを呼びます。

SUUJI+0をGR6、KANRI+0をGR7に退避しました。

処理状態のフラグが初期化されました。

位置調整の為にSUUJI-1になりました。

SUUJI+0になりました。
0番目の数字の処理になるのでインデックスと一致します。

GR4にSUUJI+0番目の「△」が読まれました。
何もせずに次に行きます。
次のSUUJI+1も「△」なので何もせずに次に行きます。

SUUJI+2は「1」です。

処理中フラグが0になりました。

GR1にSUUJI+2が退避されました。

GR4にSUUJI+3の数字列2が読まれました。

GR4にSUUJI+4の数字列3が読まれました。

GR4にSUUJI+5の数字列4が読まれました。

GR4にSUUJI+6の数字列△が読まれました。
空白なので格納処理に移ります。

GR2にSUUJI+6が格納されました。

GR2にアドレスの差から「SUUJI+6」-「SUUJI+2」で4文字と求められました。
これでGR1に「1234」の先頭の「SUUJI+2」、GR2に文字数の「4」が入ったので、プログラム1のDTOBを呼ぶ設定が完了しました。
DTOBを呼びました。数字列「1234」を数値の「1234」に変換します。
処理はプログラム1と同じなので省略して、GETWDに戻った所からトレースします。

10進数に直すと1234にあたる04D2がGR0に格納されました。

主記憶の管理テーブルのKANRI+0に格納されました。

数字列の処理中フラグが解除されました。

あと同様に「△△1234△567△△△9876△.」の567~進めて、
最後の「.」で終了処理の「-1」を格納します。
結果のみを表示します。

このようになりました。
表示は10進数表示にしました。

終了しました。お疲れ様です。
では、プログラムとノートです。

まいう~~~

プログラム1は同じなので画像は省略します。

;平成30春 数字列を数値に変換して管理テーブルに格納してかけ算するプログラム
;メインプログラム
MAIN START
RPUSH
LAD GR1,SUUJI ;数字列の先頭アドレス
LAD GR2,KANRI ;管理テーブルの先頭アドレス
CALL GETWD
RPOP
RET
SUUJI DC ‘ 1234 567 9876 .’ ;数字列
KANRI DS 4 ;管理テーブル


;プログラム2
GETWD
RPUSH
LD GR6,GR1 ;数字列の先頭アドレスを退避
LD GR7,GR2 ;管理テーブルの先頭アドレスを退避
LD GR3,=-1 ;数字列の処理状態フラグの初期化
LAD GR6,-1,GR6 ;ループ前の数字列を指す位置調整
LPGE LAD GR6,1,GR6 ;処理中の数字列の位置
LD GR4,0,GR6 ;1文字取り出し
CPL GR4,=’.’ ;ピリオドですか?
JZE FINGE
CPL GR4,=’ ‘ ;空白ですか?
JNZ NUM ;空欄「b」処理中のフラグのチェック
CALL SETWD ;管理テーブルに格納する処理
JUMP LPGE
NUM LD GR3,GR3 ;数字列の処理中?
JZE LPGE
LD GR3,=0 ;数字列の処理中フラグの設定
LD GR1,GR6 ;数字列の先頭アドレスを退避
JUMP LPGE
FINGE CALL SETWD
LD GR2,=-1 ;終了の目印
ST GR2,0,GR7 ;↑数値の終わりを示す印を格納
RPOP
RET
SETWD LD GR3,GR3 ;0(=処理中)ですか?
JNZ FIN2 ;空欄「c」違ったら何もせずに戻る
LD GR2,GR6 ;処理をしていた位置を読み込む
SUBL GR2,GR1 ;位置の差から文字数を求める
CALL DTOB ;数字列を数値に変換
ST GR0,0,GR7 ;数値を管理テーブルに格納する
LD GR3,=-1 ;数字列の処理中状態を解除
LAD GR7,1,GR7 ;空欄「d」管理テーブルを1つ先に進める
FIN2 RET

;プログラム1
DTOB RPUSH
ADDL GR2,GR1 ;アドレスを文字数分だけ進めて終了条件の判定に使う
LAD GR0,0 ;戻り値の初期化
LPDT CPL GR1,GR2 ;変換終了?
JZE FINDT
LD GR4,0,GR1 ;数字1文字の取り出し
SUBL GR4,=’0′ ;1桁を数値に変換
SLL GR0,1 ;GR0を10倍してGR4を加算。まずここで2倍
LD GR5,GR0 ;2倍した数を退避
SLL GR5,2 ;空欄「a」退避した数を4倍(元の数の8倍)
ADDL GR0,GR5 ;8倍した結果を2倍した数に足しこむ
ADDL GR0,GR4 ;読み込んだ数値を足す
LAD GR1,1,GR1 ;次の数値を読み込むので1つ進める
JUMP LPDT ;ループへ
FINDT RPOP
RET
END

プログラム3MULTは掛け算をします。具体例として、数字列「2△3.」からプログラム1のDTOBを使って数字列から数字に「2」と変換して、GETWDを使って管理テーブルCTBL+0に格納して次に「3」も同様にしてCTBL+1に格納します。それをMUTに戻ってかけ算をします。

では、数字列「2△3.」を数値「2」と「3」変換する所までのトレースはプログラム1と2と同じなので省略して、メインのかけ算プログラムのトレースを重点的に行います。

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

管理テーブルに数値に変換して格納されました。
ここからはレジスタを2進数表示にしてトレースします。

GR4に被乗数の「…0010」、GR5に乗数の「…0011」が入りました。

GR0が初期化されました。

乗数は0では無いのでZFは0です。

最下位ビットを調べるので退避しました。

0001と論理積を取って最下位ビットを調べました。
0??  ち、が、う、だ、ろ!!

なので、加算処理を行います。

「2」が入りました。

被乗数が「2」が左に1ビット論理シフトして「4」になりました。

乗数「3」が右に1ビット論理シフトして「1」になりました。

乗数1は最下位ビットが「1」なので、加算処理を行います。

GR4の「4」がGR0の「2」と加算されて「6」になりました。

GR4が1ビット左に論理シフトされて2倍されました。

GR5が1ビット右に論理シフトされて「0」になりました。
計算は終了して呼び出し元のメインプログラムに戻ります。

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

お疲れ様でした。

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

画像はプログラム1、2は共通なので省略します。

;平成30春 数字列を数値に変換して管理テーブルに格納してかけ算するプログラム
;メインプログラム


MAIN START
RPUSH
LAD GR1,SUUJI ;数字列の先頭アドレス
CALL MULT
RPOP
RET
SUUJI DC ‘2 3.’ ;数字列


;プログラム3
MULT
RPUSH
LAD GR2,CTBL ;管理テーブルの先頭アドレスを読み込む
CALL GETWD ;数値を管理テーブルに格納する処理
LD GR4,0,GR2 ;GR4←被乗数
LD GR5,1,GR2 ;GR5←乗数
LD GR0,=0 ;計算結果を初期化
LD GR5,GR5 ;乗数はゼロか?
LPMU JZE FINMU ;空欄「e」
LD GR3,GR5 ;乗数を退避
AND GR3,=#0001 ;乗数の最下位ビットのチェック
JZE NEXTMU ;↑ゼロだったら加算処理をスキップ
ADDL GR0,GR4 ;被乗数を結果に加算
NEXTMU SLL GR4,1 ;被乗数を1ビット左論理シフト
SRL GR5,1 ;空欄「f」乗数を1ビット右論理シフト
JUMP LPMU
FINMU RPOP
RET
CTBL DS 3 ;GETWD用管理テーブル

;プログラム2
GETWD
RPUSH
LD GR6,GR1 ;数字列の先頭アドレスを退避
LD GR7,GR2 ;管理テーブルの先頭アドレスを退避
LD GR3,=-1 ;数字列の処理状態フラグの初期化
LAD GR6,-1,GR6 ;ループ前の数字列を指す位置調整
LPGE LAD GR6,1,GR6 ;処理中の数字列の位置
LD GR4,0,GR6 ;1文字取り出し
CPL GR4,=’.’ ;ピリオドですか?
JZE FINGE
CPL GR4,=’ ‘ ;空白ですか?
JNZ NUM ;空欄「b」処理中のフラグのチェック
CALL SETWD ;管理テーブルに格納する処理
JUMP LPGE
NUM LD GR3,GR3 ;数字列の処理中?
JZE LPGE
LD GR3,=0 ;数字列の処理中フラグの設定
LD GR1,GR6 ;数字列の先頭アドレスを退避
JUMP LPGE
FINGE CALL SETWD
LD GR2,=-1 ;終了の目印
ST GR2,0,GR7 ;↑数値の終わりを示す印を格納
RPOP
RET
SETWD LD GR3,GR3 ;0(=処理中)ですか?
JNZ FIN2 ;空欄「c」違ったら何もせずに戻る
LD GR2,GR6 ;処理をしていた位置を読み込む
SUBL GR2,GR1 ;位置の差から文字数を求める
CALL DTOB ;数字列を数値に変換
ST GR0,0,GR7 ;数値を管理テーブルに格納する
LD GR3,=-1 ;数字列の処理中状態を解除
LAD GR7,1,GR7 ;空欄「d」管理テーブルを1つ先に進める
FIN2 RET

;プログラム1
DTOB RPUSH
ADDL GR2,GR1 ;アドレスを文字数分だけ進めて終了条件の判定に使う
LAD GR0,0 ;戻り値の初期化
LPDT CPL GR1,GR2 ;変換終了?
JZE FINDT
LD GR4,0,GR1 ;数字1文字の取り出し
SUBL GR4,=’0′ ;1桁を数値に変換
SLL GR0,1 ;GR0を10倍してGR4を加算。まずここで2倍
LD GR5,GR0 ;2倍した数を退避
SLL GR5,2 ;空欄「a」退避した数を4倍(元の数の8倍)
ADDL GR0,GR5 ;8倍した結果を2倍した数に足しこむ
ADDL GR0,GR4 ;読み込んだ数値を足す
LAD GR1,1,GR1 ;次の数値を読み込むので1つ進める
JUMP LPDT ;ループへ
FINDT RPOP
RET
END

今度受けるので、自分の勉強になりました。
どなたかのお役に立てたらうれしいです。
以前書き貯めて置いた(トレース中の落書き…)似顔絵イラストを入れて、遊び心を取り入れたりしたので、ウケなかったり、誰も見ていなかったら泣いちゃいます( ;∀;)
どなたか見ている人いますか?いたら手を振って下さい(=^・^=)
要領悪すぎにゃ~~~~~~( ;∀;)(=^・^=)( ;∀;) (^O^)/

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

この問題は平成26春のアセンブラの問題に似ています。
また、自作の2進数の乗算プログラムもあります。

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

保護者の方へ。子ども向け(主に小学生)のプログラミング教育の予習

この記事は、「大人も楽しめる小学生用プログラミング」について紹介しています。
お子様のプログラミング教育だけに留まらず、中学生以上の方のプログラミングの勉強の息抜きにもオススメです。
CやJavaなどのプログラミングのご経験のある保護者の方もご覧になって頂けるとうれしいです。

メインメニューに戻る


検索ワードで「プログラミング 疲れた」というワードで来られた方もいらっしゃるので、中学生以上の方は、JavaやC、Pythonなどのプログラミングに疲れた時に、息抜きとしてご覧いただけると嬉しいです。

基本情報技術者試験擬似言語(アルゴリズム)の息抜きにもなるかもしれません。

■ 関連記事 ■

これからプログラミングをはじめる方へ
プログラミング学習で関わってはいけない人達と逃げるべき環境
プログラミングと資格について  

2020年度から小学生の授業でプログラミングが取り入れられました。
そこで、プログラミングの理解に必要となる算数と理科の項目に絞った復習が一気に出来る動画を作りましたので、保護者の方、宜しければご覧下さい。

多分ここまでで、お子さまにプログラミングのことを聞かれてもある程度は答えられると思います。

もう少し詳しく知りたい、更にもっとお子さまのプログラミングの習熟に力を入れたいと思われている保護者様に、是非予習に使って欲しい学習コンテンツがあります。

QUREO(キュレオ)

この学習コンテンツでは、キャラクターのガイドを基にブロックを置いて行って、画面の中のキャラクターを動かしていきます。ブロックを配置した時の感触が良いのと、子ども向けとは言え、それなりに頭を使います。
そして、下記の本格的な内容をゲーム感覚で習得できます。

・もしも~~~なら…(IFによる分岐)
・くりかえし…(For、Whileループ)
・Amazonの段ボール箱みたいなもの…(変数)
・スイッチみたいなもの…(フラグ)
・数が色々とランダムに変わる…(乱数)

・団地みたいなもの(Pythonではリスト、CやJavaでは配列)

これは、C言語とかJavaをやったことのある保護者の方でしたら、かなり安心感あると思います。関数の呼び出しや引数についても理解が深められます。

まず最初に見本のゲームを動かしてみてから、コーディングして、ゲーム作りのプログラミング学習が始まります。

ゲームとコードを関連づけることによって、お子様が将来JavaやPythonなどの言語でプログラミングをする時に、この動きはこのコードだ、と、実際の動きと理論がリンクして考えられる習慣が身に付きます。

うちの子は将来プログラマーではなく、サッカー選手になる、女優さんになる、YouTuberになる、という方もいらっしゃると思うので、下記の記事が参考になると思います。

プログラマ以外の職業や文系の方向けの記事


突然のように始まったプログラミング教育で不安に思われている保護者の方向けに、少しでも不安が払拭出来たらと思い、この記事を書きました。


いい大人がこどもプログラミングなんて…などと気にならなくなるほど、内容は本格的です。
ちなみに私は事情があって独身で育児とかしている身でもないのですが、子どもを持たない人でも、情報を提供することで間接的に子育てに関わることも出来るのかなぁと思いました。

この動画学習サイトは子ども向けですが、実は現在44歳の私も楽しんでやっています(=^・^=)

2019年3月から利用し始めて、2021年6月に全チャプタークリアしました!!
チャプター56まであって、各チャプター4程度のレッスンがあるので、たっぷり学習できます。
また、クリア後も、「チャレンジコース」や「プロジェクトを作る」などもあるので、更に学び続けられます。

このブログには、プログラミングについて中学生以上の方の初心者向け関連記事もありますので、宜しかったらそちらもご覧いただけると嬉しいです!

■ 関連記事 ■

これからプログラミングをはじめる方へ
プログラミング学習で関わってはいけない人達と逃げるべき環境
プログラミングと資格について  

メインメニューに戻る

平成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^)/

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

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

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

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

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

シミュレーターと過去問を解くまでの勉強に使った参考書はこちらです
バブルソートの自作サンプルプログラム

この問題のブログ記事を書く前に久々にサザエさんを見たら、ほっこり出来た上に、最後のじゃんけん、勝ちました!!(どやっ(=^・^=)!!)という訳で、ノリノリで過去問のプログラミングとトレース行きます!!
今回はバブルソートの問題です。


主プログラムで配列ARRAYの先頭アドレス(ARRAY+0)をGR1に設定して、
ソートする数をGR2に設定します。ソート前、後でOUT命令で出力する様にしたので、文字数4をOLEN(OUT LENの略)でGR2に読み込ませるようにしました。
具体例は設問2の例を使います。
設定が終わったら、プログラミング1のSORTを呼び出します。
トレースしながら設問1、2同時に答えられるのでやって行きます。

配列ARRAYに「2、4、1、3」が格納され、それを「1、2、3、4」に並び変えながら設問に回答します。
普通のバブルソートはおケツからソートしますが、この問題では頭からソートしていきます。「2、4」の大小関係は小さい方が前だからそのまま交換無し、交換が無い時に「空欄a」で分岐して交換処理を飛ばし、
次に「4、1」の大小関係の比較…とと続いて行きます。
それではプログラミングをして、実際にシミュレーターで動かしながらトレースをやって行きます。

ARRAYの先頭アドレスARRAY+0(我が家の環境でのアドレス値)と、バブルソートを行う数値の数の4が入りました。
プログラム1が呼び出されました。

GR2の外ループカウンタが1つ減って4から3になりました。

外ループ、並び替えのターンの区切り線を見やすいように出力しました。

内ループカウンタがGR3に入りました。

GR4に、ARRAYの先頭アドレスARRAY+0の我が家の環境でのアドレスが退避されました。

ARRAY「2、4、1、3」の「2」がGR5に退避されました。

CPLで「2」と「4」が比べられて、2の方が小さいのでSFに1が入りました。・・・・7行目の1回目

並び替えの必要が無いので、空欄aの分岐により並び替えの処理を飛ばしてCONTに分岐しました。
GR3の内ループのカウンタが1つ減って、GR4のアドレスがARRAY+1になりました。

内ループ1回目を終えた状態が入りました。

ループ2へ戻って次の比較をします。
「2、4、1、3」の「4」がGR5に退避されました。

CPLで「4」と「1」が比べられて、4の方が大きいのでSFに0が入りました。・・・・7行目の2回目
その為2つの要素の入れ替えは飛ばさずに入れ替え処理を行います。

「1」がGR6に退避されました。

「4」が1つ後ろに格納されました。
「1」が1つ前に格納して交換が終わりました。・・・・11行目の1回目
GR3の内ループカウンタが1つ減って、GR4のアドレスがARRAY+2になりました。

現在の配列の様子が2行目に出力されました。
「2、1、4、3」になっています。

ループ2へ戻って次の比較をします。
「2、1、4、3」の「4」がGR5に退避されました。

CPLで「4」と「3」が比べられて、4の方が大きいのでSFに0が入りました。・・・・7行目の3回目
その為2つの要素の入れ替えは飛ばさずに入れ替え処理を行います。

「3」がGR6に退避されました。

「4」が1つ後ろに格納されました。
「3」が1つ前に格納して交換が終わりました。・・・・11行目の2回目
GR3の内ループカウンタが1つ減って0になり、GR4のアドレスがARRAY+3になりました。

現在の配列の様子が2行目に出力されました。
「2、1、3、4」になっています。

GR3の内ループのカウンタが0になったので、次のターンに行きます。
GR2の外ループカウンタが1つ減りました。

ループ1へ戻って次のターン用に区切り線を引きました。

外ループカウンタの2がうちループカウンタに入りました。

GR4に配列の先頭アドレスARRAY+0が入りました。

「2、1、3、4」の「2」がGR5に退避されました。

CPLで「2」と「1」が比べられて、2の方が大きいのでSFに0が入りました。・・・・7行目の4回目
その為2つの要素の入れ替えは飛ばさずに入れ替え処理を行います。

GR6に「1」が退避されました。

「2」が後ろに行って、退避した「1」が前に格納されました。・・・・11行目の3回目、設問2空欄「d」は3回でした。
GR4のアドレスが1つ後ろのARRAY+1、GR3の内ループカウンタが1つ減りました。

現在の配列の状態が出力されました。
「1、2、3、4」

ループ2に戻りました。
CPLで「2」と「3」が比べられて、2の方が小さいのでSFに1が入りました。・・・・7行目の5回目

並べ替えは行わずにCONTへ分岐して、GR4のアドレスがARRAY+2になって、内ループのカウンタが1つ減りました。

出力結果は変わらず「1、2、3、4」です。

内ループが0になったので、外ループが1つ減りました。

次のターンへ処理するのでループ1へ分岐します。
区切り線が引かれました。

GR2の外ループのカウンタがGR3の内ループのカウンタに入りました。

GR4に先頭アドレスARRAY+0が入りました。

「1、2、3、4」の「1」がGR5に退避され比較します。・・・・7行目の6回目
設問2空欄「c」は6回でした。

並び替えは行わないのでCONTに分岐します。
GR4のアドレスがARRAY+1になってGR3の内ループカウンタが0になりました。

出力結果です。

内、外ループカウンタ、共に0になりました。

プログラム1が終わりました。

;平成26年秋バブルソート
;主プログラム
MAIN START
RPUSH
LAD GR1,ARRAY ;GR1に配列ARRAYの先頭アドレスを設定する
LD GR2,OLEN ;GR2に並び替える数4を読み込む
CALL SORT ;プログラム1SORTを呼ぶ
RPOP
RET
ARRAY DC ‘2413’
OLEN DC 4
BUF DS 256
;プログラム1SORT
SORT RPUSH
SUBA GR2,=1 ;ループのカウンタ1を設定(外ループ)
LOOP1 OUT LINE,LLEN ;ターンの区切り線
LD GR3,GR2 ;ループのカウンタ2を設定(内ループ)
LD GR4,GR1 ;GR4←比較する要素のアドレス
LOOP2 LD GR5,0,GR4 ;現在のアドレス内の数を取り出す
CPL GR5,1,GR4 ;↑と次の要素を比較‥‥設問2「c」
JMI CONT ;空欄「a」交換が発生しない場合に分岐
LD GR6,1,GR4 ;2つの要素を入れ替え。後ろの要素をGR6に退避
ST GR5,1,GR4 ;現在の要素を後ろへ格納
ST GR6,0,GR4 ;退避した要素を前に格納
CONT ADDA GR4,=1 ;比較する位置を後ろに移動
SUBA GR3,=1 ;内ループカウンタを減らす
OUT ARRAY,OLEN ;途中の並び替えを出力
JPL LOOP2 ;次の比較へ
SUBA GR2,=1 ;外ループを減らして次のターンへ
JPL LOOP1 ;空欄「b」残っている場合はLOOP1へ
RPOP
RET
LINE DC ‘———-‘
LLEN DC 10
END

では、設問3へ行きます。
具体例では、入力ウィンドウでBUFに4桁の5つの数値を出力してそれを数字列から数値にして並び替えて出力します。
1つ目の数字列は「298」「3(文字数)」と入力します。
これを2進化10進数、「298」でしたら「0298」なので、
0…「0000」
2…「0010」
9…「1001」
8…「1000」
と変換して、ARRAY+0に格納します。
次は「30」「2(文字数)」を入力して同じように変換してARRAY+1に格納…というのを5つ目の数字をARRAY+4に格納する
所まで行います。

GR1にARRAY+0を読み込んでプログラム1のSORTを呼び出します。

並び替え終わったら、一番小さい数字を数字列(文字)に変換して出力します。
変換の仕方は000Fと論理積を取り、文字コードを+30して文字にして、それをBUFに格納します。
4桁終わったら次の数字列に行きます。

数字列の入力で、-1が入ったら入力処理を終えるようにします。

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

プログラム2を作りましたので動かして見ます。

まだ数字を1つも入力していないので、GR2に0が入ります。

まず、最初の数値「298」を入力します。

Enterを押して入力を終えます。

「298」は3桁なのでGR0に桁数の3が入りました。

配列に格納される内容が初期化されました。
あとで数字列から数値に変換する時に使います。

GR4にBUFのアドレスが入りました。
数値化の変換する位置の指定に使います。

「298」の「2」がGR5に読み込まれました。

「2」が数値に変換されました。

次の桁用に左シフトされました。
GR3は0なので値は変わりません。

2進化10進数に変換する為にGR5の0002がGR3に加算されました。

「298」の「9」を読むために進めました。

GROの処理待ち桁が1つ減りました。

ループ4に戻ります。

以下、「9」、「8」が読み込まれる処理を行います。
9が数値化され、格納される前に、GR3が4ビット右へシフトされました。
このシミュレーターは16進数表示にしてあるので、2進数の4桁で16進数の1桁が左に1つ移動したことになります。
0002から0020に移動して右端が0になりました。

0になった所に「9」が入って0029になりました。

「298」の「8」が読み込まれて数値化されました。
0029から0290に左にシフトされました。

290の空いた0の所に「8」が入って0298になりました。

数値変換処理が終わって、配列ARRAY+0に格納されました。

以上の処理が終わったので、処理済みの数字列の数が1つ加算されました。

同様に、「30」、「9」、「3240」、「508」を処理します。
(略)

何も入力せずEnterで5つの数字列を入力する処理を追えました。
以降、ソート処理になります。

CALLでプロうグラム1のソートを呼び出して、5つの数字列をバブルソートします。
プログラム1の所は省略します。
(略)
「9」、「30」、「298」、「508」、「3240」に並び替えらえれました。

主プログラムに戻りました。
バブルソートによる並べ替えが終わって出力処理に入ります。
GR4に空白文字が読み込まれました。

最も小さい「9」から数字列に変換します。
GR3に入りました。

下一桁が取り出され、(空欄e)数字列に変換されました。

この「9」がバッファ4桁のうちの1番右BUF+3に入りました。

0009が右にシフトされ、0000になりました。
(空欄f)
なので4桁分3桁空いてしまったので(0009だから、「△△△9」にする。△は空白。)ループ7で3つ分の△を埋める空白処理を行います。

空白を埋める処理が終わって、一番小さい数が出力されました。

以下、同様の処理をするので、ステップ実行から一括実行に切り替えて一気に表示します。

昇順に表示されました。

これで終わりましたので、プログラム2とノートを掲載します。

;平成26年秋バブルソート
;プログラム2主プログラム
MAIN START
RPUSH
LD GR2,=0 ;数字列のカウンタを初期化
LOOP3 IN BUF,ILEN ;数字列の長さ
LD GR0,ILEN ;GR0に長さを読み込んで配列格納の位置に使用する
JZE EOF ;何も入れなかったら入力処理を終える
LD GR3,=0 ;配列に格納する内容を初期化
LAD GR4,BUF ;GR4に入力に使用した領域BUFの先頭アドレスを入れる
LOOP4 LD GR5,0,GR4 ;GR5←数字
AND GR5,=#000F ;数字の数値化
SLL GR3,4 ;処理済の内容を桁上げ
OR GR3,GR5 ;空いた右端4ビットに数値を格納
ADDA GR4,=1 ;アドレスを1つ先へ
SUBA GR0,=1 ;格納する桁を1つ前へ
JNZ LOOP4 ;桁分の処理がある場合はLOOP4へ
ST GR3,ARRAY,GR2 ;ソート用に格納
ADDA GR2,=1 ;ソート用に格納する位置を1つ進める
JUMP LOOP3
EOF LAD GR1,ARRAY ;マイナスが入力されたらソートへ
CALL SORT
LD GR4,=’ ‘ ;空白文字を設定(4桁に満たない場合)
LOOP5 LD GR0,0,GR1 ;出力用に読み込む
LD GR5,=3 ;ループのカウンタを設定
LOOP6 LD GR3,GR0 ;数字に変換するために退避
AND GR3,=#000F ;「空欄e」下一桁を取り出す
OR GR3,=#0030 ;数値の数字化
ST GR3,BUF,GR5 ;バッファに数字を設定
SUBA GR5,=1 ;バッファ格納位置を1つ前へ移動
JMI WRITE ;格納が終わったら出力へ
SRL GR0,4 ;「空欄f」上の桁の数字化用に右シフト
JNZ LOOP6
LOOP7 ST GR4,BUF,GR5 ;バッファに空白を設定
SUBA GR5,=1
JMI WRITE
JUMP LOOP7
WRITE OUT BUF,OLEN ;4桁出力
LAD GR1,1,GR1 ;次の数値の数字化と出力処理へ
SUBA GR2,=1 ;処理待ち分を減らす
JNZ LOOP5
RPOP
RET
BUF DS 256 ;入出力領域
ILEN DS 1 ;入力桁数
OLEN DC 4 ;4文字出力
ARRAY DS 100 ;バブルソート用
;プログラム1SORT
SORT RPUSH
SUBA GR2,=1 ;ループのカウンタ1を設定(外ループ)
LOOP1
LD GR3,GR2 ;ループのカウンタ2を設定(内ループ)
LD GR4,GR1 ;GR4←比較する要素のアドレス
LOOP2 LD GR5,0,GR4 ;現在のアドレス内の数を取り出す
CPL GR5,1,GR4 ;↑と次の要素を比較‥‥設問2「c」
JMI CONT ;空欄「a」交換が発生しない場合に分岐
LD GR6,1,GR4 ;2つの要素を入れ替え。後ろの要素をGR6に退避
ST GR5,1,GR4 ;現在の要素を後ろへ格納
ST GR6,0,GR4 ;退避した要素を前に格納
CONT ADDA GR4,=1 ;比較する位置を後ろに移動
SUBA GR3,=1 ;内ループカウンタを減らす
JPL LOOP2 ;次の比較へ
SUBA GR2,=1 ;外ループを減らして次のターンへ
JPL LOOP1 ;空欄「b」残っている場合はLOOP1へ
RPOP
RET
END

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

シミュレーターと過去問を解くまでの勉強に使った参考書はこちらです
バブルソートの自作サンプルプログラム

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

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

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

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

シミュレーターと過去問を解くまでの勉強に使った参考書はこちらです
この問題は平成30年春のアセンブラの問題と似ています。

文字列の中から単語を切り出して、「その文字列の先頭アドレス、文字数、全部数字か英文字も含まれているかの種別」を単語管理テーブルに収めるプログラムです。

文字列の領域をMOJI、先頭アドレスをMOJI+0として、この問題の環境ではMOJI+0が100番地になるようです。
種別は全部数字の時は0、英字を含む時は1です。

単語管理テーブル領域はKANRI、先頭アドレスをKANRI+0とします。
問題文のプログラム1、GETTKNは主プログラムはGR1にMOJI+0、GR2にKANRI+0を設定してから呼びます。

トレースは、時間短縮の為、設問1、設問2が一気に解けるように、設問2の具体例を使います。スペースを△と表すので、「△1A2B△C3.」をMOJI領域に入れて、KANRIは「101,4,1,106,2,1,-1」となります。
最初の文字列「1A2B」は101番地から始まって4文字、英字を含むから種別が1で「101,4,1」次の文字列「C3」は106番地から始まって2文字で英字を含むから種別が1で「106,2,1」、最後が終端マークで「-1」が入ります。

では、主プログラムとプログラム1のGETTKNをプログラミングしてトレースをします。単語管理テーブルKANRIは、とりあえず10個ぐらいの領域を用意します。

では、主プログラムとプログラム1のプログラミングが終わりましたので、シミュレーターを動かしながらトレースをして設問1、2を解きます。
文字列の領域MOJIに、「△1A2B△C3.」が入りました。文字コードが入っています。この問題のアドレス100番地が、私のお家の環境ですと、MOJIの左を見ていくと「101C」に当たります。

次に、単語管理テーブルの領域を10個用意しました。

GR1に100番地に当たるアドレス101Cが入りました。

GR2にKANRI+0番地が入りました。

GR3に種別の初期化の「-1」が入りました。

ループLPに入る前にアドレスが調整されました。
99番地に当たる101Bが入りました。

ループLPに入り、アドレスが+1され、100番地に当たる101Cが入りました。

GR4に文字列「△1A2B△C3.」から、最初の「△」が取り出されました。

文字コード0020

GR3が0かどうかみます。
-1なので何もしないです。

文字列のアドレスが1つ先に進み、101番地にあたる101Dを指しました。

GR4に文字列「△1A2B△C3.」から「1」が取り出されました。

文字コード0031

文字列の取り出し処理が開始になるので、GR3に0が読み込まれました。
種別が0で数字だけからなるになりました。

処理開始の文字列のアドレス101番(101D)がGR6に退避されました。
これは後でアドレスの差から文字数を求めるのに使います。

文字列のアドレスが1つ先に進み、102番地にあたる101Eを指しました。

GR4に文字列「△1A2B△C3.」から「A」が取り出されました。

文字コード0041

英文字を含むのでGR3に1が設定されました。

文字列のアドレスが1つ先に進み、103番地にあたる101Fを指しました。

GR4に文字列「△1A2B△C3.」から「2」が取り出されました。

文字コード0032

文字列のアドレスが1つ先に進み、104番地にあたる1020を指しました。

GR4に文字列「△1A2B△C3.」から「B」が取り出されました。

文字コード0042

文字列のアドレスが1つ先に進み、105番地にあたる1021を指しました。

GR4に文字列「△1A2B△C3.」から2つ目の「△」が取り出されました。

文字コード0020

△なので、格納処理に進みます。
文字列「△1A2B△C3.」の先頭の「1」、101番地に当たる101Dが管理テーブルの先頭に格納されました。

現在の文字列を指すアドレス105番に当たる1021がGR5に退避されました。

105番地から(1021)101番地(101D)を引いた4が文字数としてGR5に求まりました。

文字数の4と、英数字混合の文字種1が単語管理テーブルに格納されました。

次の文字切り出し用にGR3を-1で初期化しました。

単語管理テーブルのアドレスが3つ進んで、次の単語切り出し用の準備になりました。

文字列のアドレスが1つ先に進み、106番地にあたる1022を指しました。

GR4に文字列「△1A2B△C3.」から2つ目の「C」が取り出されました。

文字コード0043・・・設問2

フラグが-1から0になりました。
処理中として、暫定的に全て数字を表す0が入りました。

GR6に今指しているアドレス、106番地に当たる1022が入りました。

「C」は9より大きいので英文字が含まれるため、GR3のフラグが1になりました。

文字列のアドレスが1つ先に進み、107番地にある1023を指しました。

GR4に文字列「△1A2B△C3.」から2つ目の「3」が取り出されました。

文字コード0033

文字列のアドレスが1つ先に進み、108番地にある1024を指しました。

GR4に文字列「△1A2B△C3.」から2つ目の「.」が取り出されました。

文字コード002E

「.」なので終了処理に入ります。

単語管理テーブルに文字列「C3」の先頭のアドレスが入りました。

GR5に文字列の現在指しているアドレス108番地に当たる1024が入りました。

アドレス108番地に当たる1024から、アドレス106番地に当たる1022を引いて2が文字数として求まりました。

「C3」のアドレス、文字数、種別が入りました。

終端マークの-1が入りました。

以上でプログラム1、設問1、2のトレースを終わります。

プログラミングのコードとトレースに使ったノートの写真です。

;平成26春主プログラムとプログラム1
;設問1、2
;主プログラム
MAIN START
RPUSH
LAD GR1,MOJI ;設問2の具体例の文字列を用意
LAD GR2,KANRI ;単語管理テーブルの領域をとりあえず10個確保
CALL GETTKN ;プログラム1を呼ぶ
RPOP
RET
MOJI DC ‘ 1A2B C3.’ ;文字列
KANRI DS 10 ;単語管理テーブル
;プログラム1
GETTKN
RPUSH
LD GR3,=-1 ;単語種別の初期化
LAD GR1,-1,GR1 ;文字列の位置調整
LP LAD GR1,1,GR1 ;MOJIの位置を順番に指す
LD GR4,0,GR1 ;1文字取り出す(↑で指した位置の中身を入れる)
CPL GR4,=’.’ ;比較
JZE FIN ;[.]なら終わり
CPL GR4,=’ ‘ ;比較
JNZ ALNUM ;[△]なら格納処理へ
CALL SETTKN ;単語管理テーブルへの格納処理へ
JUMP LP ;文字の取り出し処理へ「空欄a」
ALNUM LD GR3,GR3 ;0ですか?(単語の処理中?)
JPL LP ;処理中の単語が英字を含む場合はLPへ
JZE ACHK ;処理中の単語が数字だけからなる場合はACHKへ
LD GR3,=0 ;次の単語の処理開始
;単語種別を”数字だけからなる”に設定
LD GR6,GR1 ;先頭アドレスを退避(後で文字数の計算に使う)
ACHK CPL GR4,=’9′ ;9よりデカかったら文字決定!
JPL NEXT ;英字を含む処理へ
CPL GR4,=’0′ ;0より小さかったら文字決定!
JMI NEXT ;文字記号を含む処理へ
JUMP LP ;取り出した文字が数字の場合はLPへ
NEXT LD GR3,=1 ;単語種別を”英字を含む”に設定
JUMP LP
FIN CALL SETTKN ;単語管理テーブルへの格納処理へ
LD GR5,=-1
ST GR5,0,GR2 ;終端マークを格納
RPOP
RET
SETTKN LD GR3,GR3 ;0ですか?
JMI FIN2 ;単語処理中でなければ何もしない
ST GR6,0,GR2 ;単語の先頭アドレスを単語管理テーブルに格納
LD GR5,GR1 ;文字列の今のアドレスを入れる
SUBL GR5,GR6 ;今のアドレス-先頭アドレス=文字数が求まる「空欄b」
ST GR5,1,GR2 ;単語の長さを格納
ST GR3,2,GR2 ;単語の種別を格納
LD GR3,=-1 ;単語種別を初期化(処理中状態を解除)
LAD GR2,3,GR2 ;次の語のために単語管理テーブルを進める
FIN2 RET
END

設問3では具体例「12×3」を計算します。
文字列で表すとMOJIに「12△*△3△.」が入ります。
プログラム2のCALCでは、12と3はそれぞれGETTKNを使って数字列を切り出して、
その数字列を2進数の数値に変換して計算するので、DTOBという副プログラムを使って2進数にしてから計算します。
GETTKNを呼ぶ時、単語管理テーブルとして使う領域はCTBLという風呂敷を使って主記憶の場所取りをします。
単語管理テーブルのCTBLは「MOJI+0、2、0、MOJI+3、1、1、MOJI+5、1、0、-1」となります。「MOJI+0、2、0」で、「12×3」の「12」の先頭アドレスと文字数の2、数字だけなので文字種別が0、「MOJI+3、1、1」は、「12×3」の「×」の先頭アドレスと文字数の1、英数字含むので種別が1、「MOJI+5、1、0」は、「12×3」の「3」の先頭アドレスと文字数の1、数字だけの0、最後が終端マークの-1です。

計算するのに使うDTOBでは、数字列の12でしたら2進数で1100に、3でしたら、0011に変換します。×はで表します。演算子は「、+、ダミー、-」の順で領域LTBLに入っています。「MULT、PLUS、ダミー、MINUS」
この順番は文字コードを使って対応させています。
先頭である「*」は、アドレスLTBLの0番値目で0番地という位置は文字コードの差を使っています。*の文字コードは2Aで2A-2Aは0だから0番地、「+」は文字コード2Bで2B-2Aは1だから1番地、ダミーは2Cは、演算とは関係のない文字(,)で2C。2C-2Aは2だから2番地はダミー、「-」は文字コード2Dで2D-2Aは3だから3番地、とそれぞれ対応付けています。

参考 文字コード

設問3の流れをノートに書きました。

では、プログラム2を含めたプログラミングが終わったのでトレースをします。
実行すると主記憶のMOJIに「12△*△3△.」が入りました。

主プログラムでGR1に文字列アドレスMOJI+0が入ってCALCを呼び出します。

GR2に単語管理テーブルのアドレスCTBL+0が入ってGETTKNプログラム1のを呼び出します。「12」と「*」と「3」を切り出し、GR2で指す単語管理テーブルに切り出した単語それぞれの、「先頭アドレス、文字数、種別」が入ります。
CALCに戻って最初の文字「12」のGR2に入っている先頭アドレスをGR1に移して、
DTOBを呼びます。

数字列「12」を数値にして後ろから2×1、1×10を計算して足して数値12にします。
12を表すCが入ってCALCに戻りました。

演算子「*」の文字コードが読まれました。

文字コードから「」の2Aが引かれました。 この場合、なので2A-2Aで0が求まり、演算子テーブルの1番目のMULTを指すのに使います。

2番目の数字列を指す先頭アドレスがGR1に読み込まれて再びDTOBを呼びます。

「3」を数値に変換しました。

CALCに戻って、「12」がGR4、「3」がGR5に入りました。

MULTに分岐しました。
GR4×GR5で、「12×3」を求めます。


36(16進数表記で24)が求まりました。

プログラム2を含むコードと、トレースに使ったノートです。


プログラム1は同じなので画像は省略します。

;平成26春主プログラムとプログラム1、2
;設問3;主プログラム
MAIN START
RPUSH
LAD GR1,MOJI ;計算に使う数字列を含む文字列
CALL CALC ;プログラム2を呼ぶ
RPOP
RET
MOJI DC ’12 * 3 .’ ;文字列「12×3」を求める
;プログラム2
CALC
RPUSH
LAD GR2,CTBL ;管理テーブルの先頭アドレス
CALL GETTKN ;プログラム1を呼んで2つの数字列を切り出す
LD GR1,GR2 ;「空欄c」2進数変換につかうDTOBを呼ぶ準備
CALL DTOB ;数字文字列1を2進数に変換して
LD GR4,GR0 ;GR4に設定する。
LD GR1,3,GR2 ;演算子を取り出す
LD GR3,0,GR1 ;GR3←演算子の文字コード
SUBL GR3,=’*’ ;文字コードの差から分岐による演算処理を調べる
LAD GR1,6,GR2
CALL DTOB ;数字文字列2を2進数に変換して
LD GR5,GR0 ;GR5に設定する。
LD GR3,LTBL,GR3 ;「空欄d」どの演算をするのかLTBLの位置から求めて分岐
JUMP 0,GR3 ;演算して指定された処理にジャンプ
MULT LD GR0,=0 ;乗算MULTに分岐
LD GR5,GR5 ;乗数は0ですか?
LPM JZE FINC
LD GR3,GR5
AND GR3,=#0001 ;乗数の最下位ビットのチェック
JZE NEXTC
ADDL GR0,GR4 ;最下位ビットが0では無かったら加算
NEXTC SLL GR4,1 ;被乗数を1ビット左論理シフト
SRL GR5,1 ;乗数を1ビット右論理シフト
JUMP LPM
PLUS ADDL GR4,GR5 ;加算PLUSに分岐
LD GR0,GR4
JUMP FINC
MINUS SUBL GR4,GR5 ;減算MINUSに分岐
LD GR0,GR4
FINC RPOP
RET
CTBL DS 10 ;GETTKN用単語管理テーブル
LTBL DC MULT ;演算の分岐先アドレステーブル
DC PLUS
DS 1 ;ダミー
DC MINUS
;二進数変換プログラムDTOBでは1の桁(後ろから)から変換していく
DTOB RPUSH
LD GR5,=0
LD GR4,1,GR1 ;文字数を入れる
LAD GR6,KETA ;2進数に直す時の桁が入っているテーブル
LP2
SUBL GR4,=1 ;文字数マイナス1
JMI FINK2 ;これがマイナスになったら終了
LD GR3,0,GR1 ;切り出した文字のアドレスが入る
ADDL GR3,GR4 ;アドレスに文字数マイナス1を加える
LD GR3,0,GR3 ;↑の中身
SUBL GR3,=’0′ ;数値に変換
KAKERU LD GR0,=0 ;乗算処理の初期化
LD GR2,0,GR6 ;乗数になる桁が入っているテーブルの桁を読み込む
JZE FINK
LPK LD GR7,GR2 ;最下位ビットチェックの為に退避
AND GR7,=#0001 ;乗数の最下位ビットのチェック
JZE NEXTD
ADDL GR0,GR3 ;最下位ビットが0では無かったら加算
NEXTD
SLL GR3,1 ;被乗数を1ビット左論理シフト
SRL GR2,1 ;乗数を1ビット右論理シフト
JZE FINK
JUMP LPK
FINK
ADDL GR5,GR0 ;前の桁に今回の桁での演算結果を加える
LAD GR6,1,GR6 ;桁テーブルを1つ進める
JUMP LP2
FINK2 LD GR0,GR5 ;2進数に変換した値を退避
RPOP
RET
KETA DC 1,10,100 ;桁テーブル
;プログラム1
GETTKN
RPUSH
LD GR3,=-1 ;単語種別の初期化
LAD GR1,-1,GR1 ;文字列の位置調整
LP LAD GR1,1,GR1 ;MOJIの位置を順番に指す
LD GR4,0,GR1 ;1文字取り出す(↑で指した位置の中身を入れる)
CPL GR4,=’.’ ;比較
JZE FIN ;[.]なら終わり
CPL GR4,=’ ‘ ;比較
JNZ ALNUM ;[△]なら格納処理へ
CALL SETTKN ;単語管理テーブルへの格納処理へ
JUMP LP ;文字の取り出し処理へ「空欄a」
ALNUM LD GR3,GR3 ;0ですか?(単語の処理中?)
JPL LP ;処理中の単語が英字を含む場合はLPへ
JZE ACHK ;処理中の単語が数字だけからなる場合はACHKへ
LD GR3,=0 ;次の単語の処理開始
;単語種別を”数字だけからなる”に設定
LD GR6,GR1 ;先頭アドレスを退避(後で文字数の計算に使う)
ACHK CPL GR4,=’9′ ;9よりデカかったら文字決定!
JPL NEXT ;英字を含む処理へ
CPL GR4,=’0′ ;0より小さかったら文字決定!
JMI NEXT ;文字記号を含む処理へ
JUMP LP ;取り出した文字が数字の場合はLPへ
NEXT LD GR3,=1 ;単語種別を”英字を含む”に設定
JUMP LP
FIN CALL SETTKN ;単語管理テーブルへの格納処理へ
LD GR5,=-1
ST GR5,0,GR2 ;終端マークを格納
RPOP
RET
SETTKN LD GR3,GR3 ;0ですか?
JMI FIN2 ;単語処理中でなければ何もしない
ST GR6,0,GR2 ;単語の先頭アドレスを単語管理テーブルに格納
LD GR5,GR1 ;文字列の今のアドレスを入れる
SUBL GR5,GR6 ;今のアドレス-先頭アドレス=文字数が求まる「空欄b」
ST GR5,1,GR2 ;単語の長さを格納
ST GR3,2,GR2 ;単語の種別を格納
LD GR3,=-1 ;単語種別を初期化(処理中状態を解除)
LAD GR2,3,GR2 ;次の語のために単語管理テーブルを進める
FIN2 RET
END

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

シミュレーターと過去問を解くまでの勉強に使った参考書はこちらです
この問題は平成30年春のアセンブラの問題と似ています。

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

平成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^)/

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

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

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

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

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

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

令和2年度から比重が多くなる言語で、悪戦苦闘しておりますが、今日解いた過去問は、比較的易しくて正直ホッとしました。
何故易しく感じたのかと申すと、以前解いた、平成21年秋過去問アセンブラ問12と非常に似ていたからです。


なので、この記事を書き終えたら後手後手になっている年末調整の書類に取り掛かれそうです。うちの会社年末調整の締め切りが11月2×日までです。といった全然関係無い話は置いておいて(というと、志村けんさんの「こっちへ置いといて…といって持ってくるコントを思い出す」。
こんなこと書いていたら今読んでいる方に突っ込まれるかな( ´艸`))、
ではでは、アセンブラのお話しに移ります。

この問題は、平成21年秋の過去問と同じように、ビット列の指定の部分を別のビット列に置換します。よくあるパターンなのかな??? 

試験問題の図1に、イラストが描いてありますね。
これを私が分かるように下記のようにノートにまとめました。
字が汚くて大変恐縮ですが、宜しかったらお目汚しに見てやってください(=^・^=)m

ビット列を置き換える所を黒丸で塗りつぶしています。
これも良くあるパターンの様ですが、GR1が対象ビットの先頭アドレスを指していますね。そして、置換開始位置pがGR2、対象ビットの長さqがGR3です。
元のビット列αですが、この具体例は載っていなかったので、ベタですが、
1語目も2語目も「0101010101010101」、16進数で#5555にしたいと思います。

問題を解く前に、ざっと(選択肢はちょっと重めに見て)この問12を見てみると下記のポイントに気づきました。

・設問2の具体例をトレースに利用するとラク
・ビット置換の問題はMASKの生成が好きみたい。特に「算術」右シフト!!!!!
・14行目のレジスタGR4とGR6が設問2で問われるから良く見ておこう!
・16行目と17行目の別の表現もあるんだ…。
・やっぱりアセンブラって、シフト好きだね。

という特徴を掴みかけました。
まだまだ未熟な学習過程ながら、今日気づけましたので記事にしました。

以上を踏まえて、汚いノートをご覧下さい( ;∀;)

では、プログラミングをして、実際にシミュレーターを動かして見ます。

主記憶にα2語、対象位置のp=12、対象ビット長の10、βに置換するビット列が入りました。αのアドレスは少し見辛いですが、左寄りに見ると、この環境では「#1020」です。

GR1にαのアドレスが入りました。(#1020の2進数表記)

GR2に置換対象位置のp=12が入りました。

GR3に置換ビット長の長さq=10が入りました。

置換する文字列、βの#D6C0の2進数表記で入りました。

プログラム1「BREP」が呼び出されました。

GR4にβが退避されました。
これは、置換するビット長10文字のうちの最初の4ビット用に使います。

GR5にβが退避されました。
これは、置換するビット長10文字のうちの残りの6ビット用に使います。

GR6にマスク生成に使う#8000が入りました。・・・空欄a
この問題は、ビット列αの一部をβに置き換えます。
なので、置き換える所を0にして置換するビット列をぶち込むように
色々準備が必要で、そのうちの1つだと以前の似たような過去問を解いた時に覚えました。

GR3が1減算され、10から9になりました。
これは何をしているのかというと、詳細は後述しますが、これもマスク生成の準備に必要な処理です。

GR6にマスクが入りました。
先頭の10ビットに1が入っています。これを作りたかったのですね。
シフトは算術シフトです。
午前でやったことが役に立ちました。
算術シフトなので、空いたビットには先頭ビットと同じビット、
この場合は「1」が入ります。9ビット右にシフトすることで、
先頭から10ビット、つまりビット長の分だけ1が入ります。
なんか、アセンブラのトリビアの泉みたいな話だと思いました。

マスクが2語目用に退避されました。

GR3に16が読み込まれました。アセンブラのレジスタは1語に16ビット格納するから、
それを後にビット開始位置調整に使うからの様です。

GR3に16からGR2の12が引かれて4が入りました。
これは置換処理のビット位置調整に使います。

最初の語の置換用に右に12ビットシフトされました。・・・空欄c

2語目の置換用に左に4ビットシフトされました。

1語目用に12ビット右シフトされました。・・・空欄d及び設問3

2語目用に4ビット左シフトされました。

元のビット列が読み込まれました。・・・設問3

マスクが排他的論理和XORで反転しました。

元のビット列αの、置換対象の部分がゼロクリアされました。
後にここに置換に使うβをぶち込みます。

ORで置換処理が実行されました。・・・空欄b及び設問3

主記憶のαの先頭に置換後のビット列が格納されました。

GR2にαの2語目が読み込まれました。

GR7のマスクが排他的論理和で反転しました。

GR2の置換対象部分に当たる所がゼロクリアされました。

ORで置換処理が実行されました。・・・空欄b

主記憶のαの先頭+1である2語目に置換後のビット列が格納されました。

ここまでで設問1、2、3が終わりました。

以下がプログラム1と、それを呼び出すのに使ったMAINです。
それとノートです。

;平成25年春プログラム1を含む設問1、2、3
;メインプログラム
MAIN START
RPUSH
LAD GR1,ALPHA ;αの設定
LD GR2,P ;置換開始位置の設定
LD GR3,Q ;置換対象文字長の設定
LD GR0,BETA ;βの設定
CALL BREP ;プログラム1を呼び出す
RPOP
RET
ALPHA DC #5555 ;問題文の具体例に沿ったデータを配置
DC #5555
P DC 12
Q DC 10
BETA DC #D6C0
;[プログラム1]の「BREP」のはじまりはじまり~~~~(^O^)/
BREP RPUSH
LD GR4,GR0 ;βを退避(10ビット中の最初の4ビット用)
LD GR5,GR0 ;βを退避(10ビット中の残りの4ビット用)
LD GR6,=#8000 ;MASKパターン生成の準備(7行目の「算術」シフト用)
LAD GR3,-1,GR3 ;MASKパターン生成の準備その2(シフトするビット数用)
SRA GR6,0,GR3 ;GR6←qビットのMASKパターン生成(くどいようですが「算術」シフト)
;「算術」シフトということは空いた所が1なら1が入るって午前対策でやった。
LD GR7,GR6 ;生成したマスクを後半6ビット用に読み込み
LD GR3,=16 ;開始位置を合わせる準備
SUBA GR3,GR2 ;GR3←16-p
SRL GR4,0,GR2 ;1語目用置換文字列の準備
SLL GR5,0,GR3 ;2語目用置換文字列の準備
SRL GR6,0,GR2 ;1語目用MASKパターン生成
SLL GR7,0,GR3 ;2語目用MASKパターン生成
LD GR2,0,GR1 ;1語目の処理
XOR GR6,=#FFFF ;置換用にビットを反転する
AND GR2,GR6 ;置換対象の所をゼロにして置く
OR GR2,GR4 ;↑でゼロにした所に置換対象の部分をぶち込む
ST GR2,0,GR1 ;置換後のビット列をαの1語目に格納する
LD GR2,1,GR1 ;2語目の処理
XOR GR7,=#FFFF ;置換用にビットを反転する
AND GR2,GR7 ;置換対象の所をゼロにして置く
OR GR2,GR5 ;↑でゼロにした所に置換対象の部分をぶち込む
ST GR2,1,GR1 ;置換後のビット列をαの2語目に格納する
RPOP
RET
END

では、設問4用にノートも用いて行います。
もっと長いビット列αも対応したいんだよ!!!っていう訳で追加のプログラムの様ですね。ちょっと疲れましたが、もう少し頑張ります。

すみません、深呼吸します。

・・・落ち着いた所で、設問4に入ります。
具体例として、n=4、4語分用意して、その中の3語目(α+2)と4語目を置換対象にしました。
αの置換開始位置をp=44です。(16+16+上記の設問と同じ12ビット)
qは同じで10にします。

では、ノートです。

では、プログラム2のトレースを行います。

主記憶で、αの領域を設問4用に多く(4語)場所取りしました。

GR2にビット開始の位置44が入りました。

ビット位置と指し示す語の位置の調整の為、GR7にp=44のビット開始位置が退避されました。

ビット位置44の44は00000000「0010」1100で、
「0010」を右端にシフトすることによって、3語目(α+2)を指すように調整しています。

これで、αの指し示す位置が2語先になりました。

ビット位置44の000000000010「1100」の「」の所のみにしたいので、
000Fとの論理積ANDを取りました。

ここからプログラム1のBREPを呼び出します。
重複する所は省いて、設問4に関係のある所のみを掲載します。

最初の置換ビット列がα+2の位置に格納されました。

最初の置換ビット列がα+3の位置に格納されました。

最後に、プログラム2を掲載します。

;平成25年春プログラム2及び1を含む設問4
;メインプログラム
MAIN START
RPUSH
LAD GR1,ALPHA ;αの設定
LD GR2,P ;置換開始位置の設定。設問4用。
LD GR3,Q ;置換対象文字長の設定
LD GR0,BETA ;βの設定
CALL XBREP ;プログラム2を呼び出す
RPOP
RET
ALPHA DC #5555 ;問題文の具体例に沿ったデータを配置
DC #5555
DC #5555 ;設問4用にαを追加(α+2)
DC #5555 ;設問4用にαを追加(α+3)
P DC 44 ;設問4用に3語目~4語目の先頭を0とした44ビットにする
Q DC 10
BETA DC #D6C0
;[プログラム2]の「XBREP」です(=^・^=)m
XBREP RPUSH
LD GR7,GR2 ;3語目を指し示すようにする
SRL GR7,4 ;α+2の2を作る為にシフトする
ADDL GR1,GR7 ;α+2にする
AND GR2,=#000F ;44の下位ビットを取り出す
CALL BREP ;プログラム1を呼び出す
RPOP
RET
;[プログラム1]の「BREP」のはじまりはじまり~~~~(^O^)/
BREP RPUSH
LD GR4,GR0 ;βを退避(10ビット中の最初の4ビット用)
LD GR5,GR0 ;βを退避(10ビット中の残りの4ビット用)
LD GR6,=#8000 ;MASKパターン生成の準備(7行目の「算術」シフト用)
LAD GR3,-1,GR3 ;MASKパターン生成の準備その2(シフトするビット数用)
SRA GR6,0,GR3 ;GR6←qビットのMASKパターン生成(くどいようですが「算術」シフト)
;「算術」シフトということは空いた所が1なら1が入るって午前対策でやった。
LD GR7,GR6 ;生成したマスクを後半6ビット用に読み込み
LD GR3,=16 ;開始位置を合わせる準備
SUBA GR3,GR2 ;GR3←16-p
SRL GR4,0,GR2 ;1語目用置換文字列の準備
SLL GR5,0,GR3 ;2語目用置換文字列の準備
SRL GR6,0,GR2 ;1語目用MASKパターン生成
SLL GR7,0,GR3 ;2語目用MASKパターン生成
LD GR2,0,GR1 ;1語目の処理
XOR GR6,=#FFFF ;置換用にビットを反転する
AND GR2,GR6 ;置換対象の所をゼロにして置く
OR GR2,GR4 ;↑でゼロにした所に置換対象の部分をぶち込む
ST GR2,0,GR1 ;置換後のビット列をαの1語目に格納する
LD GR2,1,GR1 ;2語目の処理
XOR GR7,=#FFFF ;置換用にビットを反転する
AND GR2,GR7 ;置換対象の所をゼロにして置く
OR GR2,GR5 ;↑でゼロにした所に置換対象の部分をぶち込む
ST GR2,1,GR1 ;置換後のビット列をαの2語目に格納する
RPOP
RET
END

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

今回は、今までの記事と違って、下らないことも色々書いて失礼もあったかもしれませんが、ここまでお付き合い頂き、誠にありがとうございました。

あ、年末調整頑張ります!!

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

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

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

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

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

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

多項式の値を求める問題です。
この問題の具体例として、問題文の具体例よりも簡単になるように、計算しやすい例を用いました。
xを2、n=3として、
(2^3)+(2^2)+(2^1)+(2^0)=8+4+2+1=15を求めます。

では、プログラム1からトレースして行きます。

メインプログラムでx=2をGR2に、n=3をGR1に設定しました。

ここからプログラム1のPOLYを呼び出します。
演算結果を格納するGR4が初期化されました。

かける回数のカウンタが入りました。

被乗数(かけられる数)が初期化されました。

かけ算プログラムMULTが呼び出されました。
1×2=2が求まれれます。
被乗数(かけられる数)が入りました。
(乗数(かける数)はGR2に入っています。)

かけ算の結果が初期化されました。

乗数を退避して、下一桁が0かどうか調べます。

2は2進数で0010なので、0001をかけて下一桁が0であることが
判明しました。

被乗数が左シフト、乗数が右シフトされました。

乗数が退避されました。

演算結果が格納されました。

被乗数が左シフト、乗数が右シフトされました。

呼び出し元に戻りました。
かける回数が1つ減って2になりました。

かけられる数、かける数が設定され、2×2=4が求まります。
MULTが呼び出されました。
(かけ算処理は省略)

呼び出し元に戻って、かける回数が1回減りました。

かけられる数、かける数が設定され、4×2=8が求まります。
MULTが呼び出されました。
(かけ算処理は省略)

呼び出し元に戻りました。
GR3が0になり、かけ算処理が終わり、2の3乗の2×2×2=8がGR4に加算されました。

次は、2の2乗の4を求めてGR4に加算します。
(かけ算処理省略)

2の3乗の8に2の2乗の4が加算されて12(000C)になりました。

12(000C)に2の1乗の2が加算されて14(000E)になりました。

14(000E)に2の0乗の1が加算されて15(000F)になりました。

マイナスになったので多項式の演算処理が終了しました。

GR0に演算結果が求まりました。

プログラム1、プログラム2、トレースのノートはこちらになります。

;メインプログラム
MAIN START
RPUSH

;F(x,n)は問題文より少し簡単な「xに2」、「nに3」を設定する
LD GR2,X
LD GR1,N
CALL POLY
RPOP
RET
X DC 2
N DC 3
;プログラム1
POLY RPUSH
LD GR4,=0 ;計算結果格納レジスタの初期化
LP1 LD GR3,GR1 ;nをカウンタとして加算乗算に利用
JMI FIN ;0乗まで計算するのでマイナスになったら分岐
LD GR0,=1 ;x^nの計算開始(初期化)
LP2 LD GR3,GR3 ;0乗まで計算しましたか?
JZE BRK ;x^nの計算終了ならBRKへ
CALL MULT ;(GR0)←(GR0)×(GR2)
LAD GR3,-1,GR3 ;かける回数のカウンタを1つ減らす
JUMP LP2
BRK ADDL GR4,GR0 ;計算結果格納レジスタにx^nの値を加算
LAD GR1,-1,GR1 ;nから1を減じ、低次の項の計算へ
JUMP LP1
FIN LD GR0,GR4 ;計算結果を読み込む
RPOP
RET


;プログラム2
MULT ;シフトによる乗算
RPUSH
LD GR1,GR0
LD GR0,=0 ;乗算結果の初期化
LD GR2,GR2 ;乗数(かける数)は0ですか?
LP JZE FIN2
LD GR3,GR2 ;乗数を退避
AND GR3,=#0001 ;最下位ビットのチェック
JZE CONT
ADDL GR0,GR1
CONT
SLL GR1,1
SRL GR2,1
JUMP LP
FIN2 RPOP
RET
END

では、再帰呼び出しによるプログラム3のトレースを行います。
xとnが入ってPOLY2を呼び出します。

再帰処理が始まります。
スタックに呼び出し元アドレスが積まれました。

F(x,n)=x×F(x,n-1)+1 (n>=1)のn-1にあたるnの減算処理が行われました。

呼び出し元の1つ先のアドレスがスタックに積まれて再帰呼び出しに行きました。

n-1が行われました。

呼び出し元の1つ先のアドレスがスタックに積まれて再帰呼び出しに行きました。

n-1が行われました。

呼び出し元の1つ先のアドレスがスタックに積まれて再帰呼び出しに行きました。

n=0の時の処理が実行されました。

スタックから1つ取り出されて呼び出し元の1つ先のアドレスに行きました。


1×2が設定されMULTを呼び、かけ算処理を行います。

1×2の計算結果の2が求まりました。

スタックから1つ取り出されて呼び出し元の1つ先のアドレスに行きました。


2に1が加算され3になり、MULTを読んでかけ算処理を行います。

演算結果の2×3の6が求まりました。

1が加算され7になりました。

スタックから1つ取り出されて呼び出し元の1つ先のアドレスに行きました。
MULTを読んでかけ算処理を行います。

7×2=14が実行されました。

1が加えられました。

スタックから1つ取り出されて呼び出し元の1つ先のアドレスに行きました。

以上で終了になります。

プログラム1、プログラム2、トレースのノートはこちらになります。

;メインプログラム
;F(x,n)は問題文より少し簡単な「xに2」、「nに3」を設定する

MAIN START
RPUSH
LD GR2,X
LD GR1,N
CALL POLY2
RPOP
RET
X DC 2
N DC 3
POLY2
RPUSH
CALL RSUB ;再帰処理の本体を呼ぶ
RPOP
RET ;主プログラムに戻る
RSUB
LD GR1,GR1 ;n=0?
JNZ CONT3 ;n<>0ならCONT3へ
LD GR0,=1 ;GR0←F(x,0)の値(=1)
RET
CONT3 LAD GR1,-1,GR1 ;再帰呼び出しnの回数を1減らす
CALL RSUB ;F(x,n-1)を計算
CALL MULT ;かけ算処理
ADDL GR0,=1 ;被乗数(かけられる数に1加える)
RET


;プログラム2
MULT ;シフトによる乗算
RPUSH
LD GR1,GR0
LD GR0,=0 ;乗算結果の初期化
LD GR2,GR2 ;乗数(かける数)は0ですか?
LP JZE FIN2
LD GR3,GR2 ;乗数を退避
AND GR3,=#0001 ;最下位ビットのチェック
JZE CONT
ADDL GR0,GR1
CONT
SLL GR1,1
SRL GR2,1
JUMP LP
FIN2 RPOP
RET
END

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

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

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