ここまで見てきたプログラムでもプログラム全体を繰り返すループ処理が出てきました。ここではプログラムの一部分だけを指定した回数だけ繰り返すループ処理について見ていきたいと思います。
以下のプログラムは26番地と27番地に書き込んだ数値を掛け算して答えを29番地に保存するプログラムです。29番地に保存された答えは最後にP1出力端子から出力しています。
今回のプログラムでは26番地の数値分だけ27番地の数値の足し算を繰り返す、という方法で掛け算の計算を行っています。一定回数ループを繰り返した後、条件分岐命令によってループの外に出る。というのが指定回数分だけループを繰り返す時の具体的な方法です。以下でプログラミングの詳細について見ていきたいと思います。
これまで見てきたように、自作CPUのハンドアセンブルでは数値の保存先やジャンプ命令の飛び先アドレスが全て単なる数値なので識別するのがとても困難です。また数値の保存先をプログラム自身の保存領域と重ならないように配置したり、ジャンプ命令の飛び先のアドレスを決めたりする作業はプログラミングが終わってからでないと判断できないところがあります。
なので最初の段階では数値の保存先やジャンプ命令の飛び先アドレスに仮の名前をつけておくとその後のプログラミングがしやすくなります。今回のプログラムでは6つの数値と2つの飛び先アドレスが出てくるので、それぞれ以下のような仮の名前をつけてプログラミングを行います。
数値1:掛け算の対象となる数値1
数値2:掛け算の対象となる数値2
数値3:残りのループ回数
数値4:合計
数値5:マスク用数値 255(2進数表記:11111111)
数値6:ループ回数の減算数 1
飛び先アドレス1:ループ開始アドレス
飛び先アドレス2:ループ終了アドレス
上記の名前を使って以下のようなプログラミングを行いました。
【 0 】 LD 【 1 】 掛け算の対象となる数値1
【 2 】 ST 【 3 】 残りのループ回数
ループ開始アドレス 【 4 】 LD 【 5 】 残りのループ回数
【 6 】 AND 【 7 】 マスク用数値 255
【 8 】 JZC 【 9 】 ループ終了アドレス
【 10】 SUB 【 11】 ループ回数の減算数 1
【 12】 ST 【 13】 残りのループ回数
【 14】 LD 【 15】 合計
【 16】 ADD 【 17】 掛け算の対象となる数値2
【 18】 ST 【 19】 合計
【 20】 JUMP 【 21】 ループ開始アドレス
ループ終了アドレス 【 22】 OUTA 【 23】 合計
【 24】 JUMP 【 25】 ループ終了アドレス
まずLD命令によって「掛け算の対象となる数値1」をワーキングレジスタへ読み込み、続くST命令によって「掛け算の対象となる数値1」の内容を「残りのループ回数」へ書き込みます。
次にLD命令によって「残りのループ回数」の内容をワーキングレジスタへ読み込み、この地点のアドレスを「ループ開始アドレス」にします。
「残りのループ回数」をワーキングレジスタへ読み込んだ後、その値と「マスク用数値 255」(2進数表記:11111111)をAND演算にかけます。2つの数値をAND演算にかける時、片方の数値が0になっているビットは答えが0になり、逆に1になっているビットはもう片方の数値がそのまま残る、という機能があります。この機能のことをマスクと呼んでいます。(マスクというのは「覆い隠す」というような意味があります。)
255という数値は2進数表記で”11111111”となるのでワーキングレジスタの値に変化は起きませんが、この演算を実行することによってALUのフラグを変化させる効果があります。この6番地に配置されたAND命令の目的はワーキングレジスタに読み込んだ値(残りのループ回数)が”0”であるかどうかを判断することにあります。
AND演算の結果、「残りのループ回数」が0になった場合にはALUのフラグが「1」になるので、次に来るJZC命令を使ってループの外側へ出ることができます。飛び先である「ループ終了アドレス」では掛け算の結果が保存されてある「合計」の内容をP1出力端子から出力し、以降はJUMP命令によってP1への出力を繰り返します。(ここでJUMP命令によってプログラムの最初へ戻さない理由は再度ループによる足し算が実行されて答えが変わってしまうことを防ぐためです。)
スポンサーリンク
AND演算の結果、「残りのループ回数」が0ではなかった場合にはループによる足し算を行う必要があるので、処理の流れは次の番地である10番地へと移動します。
10番地のSUB命令ではワーキングレジスタに入っている「残りのループ回数」の値を1回分減らし、続くST命令でメモリの中の「残りのループ回数」の値を減算されたワーキングレジスタの値で更新しています。
続く14番地のLD命令では「合計」の内容をワーキングレジスタへ読み込み、続くADD命令で「掛け算の対象となる数値2」を足した後、再びST命令でメモリの中の「合計」の値を更新しています。
20番地のJUMP命令で処理の流れを「ループ開始アドレス」まで戻し、再び「残りのループ回数」が残っているかどうかの判断から繰り返します。
アセンブリ言語でのプログラミングが完了したので、ここまで仮の名前で表していた数値の保存先やジャンプ命令の飛び先アドレスを以下に示す実際の数値へと置き換えます。
数値1:掛け算の対象となる数値1 → 26番地
数値2:掛け算の対象となる数値2 → 27番地
数値3:残りのループ回数 → 28番地
数値4:合計 → 29番地
数値5:マスク用数値 255(2進数表記:11111111) → 30番地
数値6:ループ回数の減算数 1 → 31番地
飛び先アドレス1:ループ開始アドレス → 4番地
飛び先アドレス2:ループ終了アドレス → 22番地
置き換えた結果は以下のようになります。なお、ここでは例として「掛け算の対象となる数値1」に”13”を、「掛け算の対象となる数値2」に”5”を書き込むことによって、”13×5”の計算を行うものとします。
【 0 】 LD 【 1 】 26
【 2 】 ST 【 3 】 28
【 4 】 LD 【 5 】 28
【 6 】 AND 【 7 】 30
【 8 】 JZC 【 9 】 22
【 10】 SUB 【 11】 31
【 12】 ST 【 13】 28
【 14】 LD 【 15】 29
【 16】 ADD 【 17】 27
【 18】 ST 【 19】 29
【 20】 JUMP 【 21】 4
【 22】 OUTA 【 23】 29
【 24】 JUMP 【 25】 22
【 26】 13 【 27】 5
【 28】 0 【 29】 0
【 30】 255 【 31】 1
さらに各命令を数値へと変換することで完全な機械語の形にします。
【 0 】 3 【 1 】 26
【 2 】 4 【 3 】 28
【 4 】 3 【 5 】 28
【 6 】 9 【 7 】 30
【 8 】 2 【 9 】 22
【 10】 8 【 11】 31
【 12】 4 【 13】 28
【 14】 3 【 15】 29
【 16】 7 【 17】 27
【 18】 4 【 19】 29
【 20】 1 【 21】 4
【 22】 16 【 23】 29
【 24】 1 【 25】 22
【 26】 13 【 27】 5
【 28】 0 【 29】 0
【 30】 255 【 31】1
※「FPGAとVerilog HDLで作るCPU」の方で実際にFPGA上にCPUを作った方は”cpu.mif”に上記の機械語を書き込むことでプログラムを実行することができます。”cpu.mif”の変更を適用するには再度、Quartusプロジェクトのコンパイルを行う必要があります。
各命令の機械語への対応表
JUMP :1
JZC :2
LD :3
ST :4
SET :5
LHI :6
ADD :7
SUB :8
AND :9
OR :10
NOT :11
SL :12
SR :13
INA :14
INB :15
OUTA :16
OUTB :17
<戻る