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

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

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


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

この問題はビット列を反転するプログラムを使って、その語内のビットや連続したビット、ビットの部分のみの反転をします。

図によるイメージが大事だと思うので、トレースに使ったノートを設問ごとに掲載します。まずこちらのトレースに使ったノート2ページ分をご覧下さい。

設問1では、プログラム1REVRSを使って、具体例としてビット列αを
実行前:「1011001111010001」
実行後:「1000101111001101」
と反転します。

レジスタはGR1にαの先頭アドレスを設定します。
メインプログラムからREVRSを呼び出します。

それでは一つずつ丁寧にトレースしていきます。
まず、主記憶のALPHAの所に、元のビット列αが入ります。

GR1にビット列αが格納されているアドレスが入りました。

プログラム1のREVRSを呼び出します。
反転するビットの結果用につかうGR4が初期化されました。

ループカウンタに使う処理待ちビット数の15がGR2に入りました。

元のビット列がGR3に退避されました。

ループ1回目
GR4が左シフトされましたが0なので変わりません。

GR3が右シフトされました。
OFが1になって末尾が1だった為、オーバーフローしました。

オーバーフローしたのでラベル「ON」に分岐しました。

16進数「0001」である、0000000000000001を結果用のGR4に加えます。

ループカウンタをデクリメントしました。
残14

ループ2回目

結果用のGR4が左シフトされました。

元のビット列が退避されたGR3が右シフトされました。

GR2のカウンタがデクリメントされました。
残13

ループ3回目

結果用のGR4が左シフトされました。

元のビット列退避用のGR3が右シフトされました。

GR2のカウンタがデクリメントされました。
残12

ループ4回目
結果用のGR4が左シフトされました。

元のビット列退避用のGR3が右シフトされました。

GR2のカウンタがデクリメントされました。
残11

ループ5回目
結果用のGR4が左シフトされました。

元のビット列退避用のGR3が右シフトされました。
OFが1になり、ONに分岐します。

結果用のGR4に1加わりました。

GR2のループカウンタがデクリメントされました。
残10

ループ6回目
結果用のGR4が左シフトされました。

元のビット列退避用のGR3が右シフトされました。

GR2のループカウンタがデクリメントされました。
残9

ループ7回目
結果用のGR4が左シフトされました。

元のビット列退避用のGR3が右シフトされました。
OFが1になり、ONに分岐します。

結果用のGR4に1加わりました。

GR2のループカウンタがデクリメントされました。
残8

ループ8回目
結果用のGR4が左シフトされました。

元のビット列退避用のGR3が右シフトされました。
OFが1になり、ONに分岐します。

結果用のGR4に1加わりました。

GR2のループカウンタがデクリメントされました。
残7

ループ9回目
結果用のGR4が左シフトされました。

元のビット列退避用のGR3が右シフトされました。
OFが1になり、ONに分岐します。

結果用のGR4に1加わりました。

GR2のループカウンタがデクリメントされました。
残6

ループ10回目
結果用のGR4が左シフトされました。

元のビット列退避用のGR3が右シフトされました。
OFが1になり、ONに分岐します。

結果用のGR4に1加わりました。

GR2のループカウンタがデクリメントされました。
残5

ループ11回目
結果用のGR4が左シフトされました。

元のビット列退避用のGR3が右シフトされました。

GR2のループカウンタがデクリメントされました。
残4

ループ12回目
結果用のGR4が左シフトされました。

元のビット列退避用のGR3が右シフトされました。

GR2のループカウンタがデクリメントされました。
残3

ループ13回目
結果用のGR4が左シフトされました。

元のビット列退避用のGR3が右シフトされました。
OFが1になり、ONに分岐します。

結果用のGR4に1加わりました。

GR2のループカウンタがデクリメントされました。
残2

ループ14回目
結果用のGR4が左シフトされました。

元のビット列退避用のGR3が右シフトされました。
OFが1になり、ONに分岐します。

結果用のGR4に1加わりました。

GR2のループカウンタがデクリメントされました。
残1

ループ15回目
結果用のGR4が左シフトされました。

元のビット列退避用のGR3が右シフトされました。

GR2のループカウンタがデクリメントされました。
残0

ループ16回目(最後です。)
結果用のGR4が左シフトされました。

元のビット列退避用のGR3が右シフトされました。
OFが1になり、ONに分岐します。

結果用のGR4に1加わりました。

GR2のループカウンタがデクリメントされました。
残-1

マイナスになったのでループを抜け、FIN2に飛びます。
主記憶のαに反転したビットが格納されました。

こちらがプログラム1のコードになります。
尚、「1011001111010001」は16進数にすると
「#B3D1」になりますので、そのように表記しております。

MAIN START ;メインの始まり
RPUSH
LAD GR1,ALPHA ;αの先頭アドレスをGR1に読み込む
CALL REVRS ;プログラム1を呼び出す
RPOP
RET

ALPHA DC #B3D1 ;αにビット列を格納する

REVRS ;プログラム1
RPUSH
LD GR4,=0 ;結果のビット列を初期化
LAD GR2,15 ;ループカウンタ
LD GR3,0,GR1 ;GR3←ビット列
LOOP SLL GR4,1 ;結果のビット列を左シフト
SRL GR3,1 ;元のビット列を右シフト
JOV ON ;元のビット列の末尾が1だったらONに分岐する
JZE FIN1 ;元のビット列の残りのビットはすべてゼロ
JUMP OFF ;元のビット列の末尾がゼロだった時の分岐
ON OR GR4,=#0001 ;結果のビット列に1を加える
OFF SUBA GR2,=1 ;ループカウンタをデクリメントする
JMI FIN2 ;16ビット処理済み
JUMP LOOP ;LOOPへ繰り返し処理
FIN1 SLL GR4,0,GR2 ;結果のビット列を残りのビット数だけシフト
FIN2 ST GR4,0,GR1 ;結果のビット列を元のビット列に格納(上書き)する
RPOP
RET
END

では、設問2に行きます。
連続した語の並びを逆転するプログラムです。
まずノートをご覧下さい。

例として1語目から5語目までの連続した語を交換するとします。
1語目は1、2語目は2…5語目は5とします。

それではプログラム2のトレースをします。
(プログラム1のトレースは部分的に省略して、プログラム2は丁寧にトレースをします。)

まず主記憶でαの先頭アドレスからそれぞれ、1、2、3、4、5が格納されています。

GR1、GR2にαの先頭アドレス、語数の5語が設定されました。

αの先頭のアドレスがGR3にコピーされ、語数がGR4に入りました。

語数を末尾アドレスを指し示す為の準備として1減算します。(先頭アドレスがα+0、末尾がα+4になる為)

末尾のアドレスが入りました。

ループ LOOP1 1回目
GR5に1語目の「1」が入りました。
「0000000000000001」

GR6に5語目の「5」が入りました。
「0000000000000101」

主記憶のαの末尾に「1」が格納されました。

主記憶のαの先頭に「5」が格納されました。

「α+0」が「α+1」を指し、
「α+4が「α+3」」を指しました。

ループ LOOP1 2回目
「2」がGR5のα+1に入りました。

「4」がGR6のα+3に入りました。

「2」がα+3に格納されました。

「4」がα+1に格納されました。

「α+1」が「α+2」を、
「α+3」が「α+2」を指しました。

ループを抜けて次のループに入ります。
ループ LOOP2 1回目
プログラム1のREVRSを呼び出します。
先頭アドレスα+0に格納された「5」(0000000000000101)がGR3に入りました。

GR4に反転された結果が求まりました。(1010000000000000)
(この処理は以降のトレースでは省略)

その結果が主記憶α+0に格納されました。

反転の対象の語がα+1になりました。

反転する語の処理待ちが4になりました。

ループ LOOP2 2回目
反転するビットの「4」(0000000000000100)が入りました。

反転してビットが「0010000000000000」になってα+1に格納されました。

反転の対象の語がα+2になりました。

反転する語の処理待ちが3になりました。

ループ LOOP2 3回目
反転するビットの「3」(0000000000000011)が入りました。

反転してビットが「1100000000000000」になってα+1に格納されました。

反転の対象の語がα+3になりました。

反転する語の処理待ちが2になりました。

ループ LOOP2 4回目
反転するビットの「2」(0000000000000010)が入りました。

反転してビットが「0100000000000000」になってα+1に格納されました。

反転の対象の語が末尾のα+4になりました。

反転する語の処理待ちが1になりました。

ループ LOOP2 5回目
反転するビットの「1」(0000000000000001)が入りました。

反転してビットが「0100000000000000」になってα+1に格納されました。

終了しました。

こちらがプログラム2のコードになります。


MAIN START ;メインの始まり
RPUSH
LAD GR1,ALPHA ;αの先頭アドレスをGR1に読み込む
LD GR2,=5 ;全部で5語とする
CALL LREVRS ;プログラム2を呼び出す
RPOP
RET
;αにビット列を格納する
ALPHA DC #0001 ;1語目
DC #0002 ;2語目
DC #0003 ;3語目
DC #0004 ;4語目
DC #0005 ;5語目

LREVRS
RPUSH
LD GR3,GR1 ;αの先頭アドレスを読み込む
LD GR4,GR2 ;語数を読み込む。処理待ち語数カウンタ
SUBA GR4,=1 ;末尾アドレス用に減算
JZE LOOP2 ;単語数が1だったらREVRSを呼び出す
ADDA GR4,GR1 ;末尾アドレスを指す
LOOP1 LD GR5,0,GR3 ;先頭の語から格納する
LD GR6,0,GR4 ;末尾の語から格納する
ST GR5,0,GR4 ;交換処理
ST GR6,0,GR3 ;交換処理
LAD GR3,1,GR3 ;先頭の語のアドレスに1を加える
LAD GR4,-1,GR4 ;末尾の語のアドレスに1を減じる
CPA GR3,GR4 ;交換に使うアドレスの大小を比較
JMI LOOP1 ;先頭の語の方が小さかったら繰り返し処理に戻る
LOOP2 CALL REVRS ;プログラム1を呼び出す
LAD GR1,1,GR1 ;ビットを反転するアドレスを次の語へ進める
SUBA GR2,=1 ;処理待ち語数カウンタをデクリメントする
JNZ LOOP2 ;反転する語が残っている場合は繰返しに戻る
FIN3 RPOP
RET
REVRS ;プログラム1
RPUSH
LD GR4,=0 ;結果のビット列を初期化
LAD GR2,15 ;ループカウンタ
LD GR3,0,GR1 ;GR3←ビット列
LOOP SLL GR4,1 ;結果のビット列を左シフト
SRL GR3,1 ;元のビット列を右シフト
JOV ON ;元のビット列の末尾が1だったらONに分岐する
JZE FIN1 ;元のビット列の残りのビットはすべてゼロ
JUMP OFF ;元のビット列の末尾がゼロだった時の分岐
ON OR GR4,=#0001 ;結果のビット列に1を加える
OFF SUBA GR2,=1 ;ループカウンタをデクリメントする
JMI FIN2 ;16ビット処理済み
JUMP LOOP ;LOOPへ繰り返し処理
FIN1 SLL GR4,0,GR2 ;結果のビット列を残りのビット数だけシフト
FIN2 ST GR4,0,GR1 ;結果のビット列を元のビット列に格納(上書き)する
RPOP
RET
END

では、最後に設問3に行きます。
ビット列の中のある部分のみを反転するプログラムです。
具体例として、ビット列αを
10110011「110」10001の「110」の部分を「011」と反転して、
10110011「011」10001にします。
このビット列の先頭アドレスをGR1に設定します。
pが8でqが3になり、それぞれGR2、GR3に接てします。

まずノートをご覧下さい。

では、トレースをして行きます。
GR1からGR3までの設定です。

GR4に元のビット列が入りました。

プログラム1のREVRSを呼び出します。
ビットが反転されて主記憶に格納されました。

プログラム3に戻ります。
反転されたビット列がGR5に入りました。

反転する3ビット分の調整をします。
16がGR6に入りました。

16-3で13が求まりました。

反転されたビットが8ビット分右シフトされて右端に移動しました。

反転されたビットが13ビット左にシフトされました。

反転された部分が8ビット右にシフトされました。

マスクが準備されます。#8000が入りました。

算術シフトされ、空いたビットに1が入りました。

マスクが右に8ビット移動しました。

排他的論理はでビットが反転されました。

GR4とGR6の論理積をGR6に格納しました。
GR4「1011001111010001」
GR6「1111111100011111」
AND「1011001100010001」

その演算結果に反転した部分を足しこみます。
GR6「1011001100010001」
GR5「0000000001100000」
OR 「1011001101110001」

結果をαに格納しました。

こちらがプログラム3のコードになります。

MAIN START ;メインの始まり
RPUSH
LAD GR1,ALPHA ;αの先頭アドレスをGR1に読み込む
LD GR2,=8 ;先頭を0ビットとして8ビット目から反転の対象(p)
LD GR3,=3 ;3ビット分が反転の対象(q)
CALL PREVRS ;プログラム2を呼び出す
RPOP
RET
;αにビット列を格納する
ALPHA DC #B3D1 ;1011 0011 「110」1 0001
PREVRS
RPUSH
LD GR4,0,GR1 ;ビット列を保存
CALL REVRS ;ビット列のビットの並びの逆転
LD GR5,0,GR1 ;GR5←逆転したビット列
LD GR6 ,=16
SUBA GR6,GR3 ;GR6←16-q
SRL GR5,0,GR2 ;逆転した部分ビット列αを8ビットシフトして右端に移動
SLL GR5,0,GR6 ;逆転した部分ビット列αを13ビットシフトして左端に移動
SRL GR5,0,GR2 ;逆転した部分ビット列αをpビット右に移動
LD GR6,=#8000 ;逆転に使うマスク作成の準備
SRA GR6,-1,GR3 ;算術シフトによるマスク作成
SRL GR6,0,GR2 ;マスクをpの分だけ右シフト
XOR GR6,=#FFFF ;マスクを逆転対象の3ビット分を1にする為排他的論理和を取る
AND GR6,GR4 ;元のビット列中の部分ビット列αにゼロを設定
OR GR6,GR5 ;反転した部分を足しこむ
ST GR6,0,GR1 ;結果を格納する
RPOP
RET


REVRS ;プログラム1
RPUSH
LD GR4,=0 ;結果のビット列を初期化
LAD GR2,15 ;ループカウンタ
LD GR3,0,GR1 ;GR3←ビット列
LOOP SLL GR4,1 ;結果のビット列を左シフト
SRL GR3,1 ;元のビット列を右シフト
JOV ON ;元のビット列の末尾が1だったらONに分岐する
JZE FIN1 ;元のビット列の残りのビットはすべてゼロ
JUMP OFF ;元のビット列の末尾がゼロだった時の分岐
ON OR GR4,=#0001 ;結果のビット列に1を加える
OFF SUBA GR2,=1 ;ループカウンタをデクリメントする
JMI FIN2 ;16ビット処理済み
JUMP LOOP ;LOOPへ繰り返し処理
FIN1 SLL GR4,0,GR2 ;結果のビット列を残りのビット数だけシフト
FIN2 ST GR4,0,GR1 ;結果のビット列を元のビット列に格納(上書き)する
RPOP
RET
END

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


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

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