ニッシー
1990年生まれ
血液型 O型

こんにちは。ITブログアルケーナム管理人のニッシーです。

詳細プロフィールへ

お問い合わせへ






2018-06-28

アセンブリ言語CASL2での乗算のやり方







COMET2は、教育用のコンピュータであるため、最低限のハードウェアでしか構成されていません。


現在のコンピュータは、乗算回路が内臓されているため、ハードウェア的に高速処理で乗算を実現できますが、COMET2は乗算用の命令を持ち合わせていないため、ソフトウェア的に乗算を実現しなければならない、ということになります。


今回は、CASL2による「乗算」の方法を紹介したていきます。
















ループ処理を用いた乗算



例1: 5 × 4 の計算

1 MAIN START 
2   LD GR1, A 
3   LD GR2, B 
4   CALL MUL 
5   ST GR0, ANS 
6   RET 
7    
8 MUL LAD GR0, 0 ;GR0を答え格納用に初期化
9 LP ADDA GR0, GR1 ;答えに掛けられる数を加算
10   LAD GR2, -1, GR2 ;掛ける数をデクリメント
11   CPA GR2, =0 ;掛ける数と0を比較
12   JPL LP ;掛ける数が0より大きいならLPに移動
13   RET 
14    
15 A DC 5 ;掛けられる数
16 B DC 4 ;掛ける数
17 ANS DS 1 ;答え
18   END 


シミュレータにそのままコピー&ペーストで使えます。



解説



ループ処理を用いた乗算は、小学校で習う乗算を基本に組まれています。


例えば、5×4は、5を4回加算することで実現しています。


5×4 ・・・5+5+5+5


したがって、CASL2でも5を4回加算し乗算を実現します。


その際にループ処理を使って、5を加算する度に4から1を引き、4が0になった時点でループを抜ける、処理によって4回のループを行います。


すなわち、掛ける数がループ回数ということになります。


掛ける数 = ループ回数


それでは動作の流れをトレースで見てみたいと思います。



【例2のMUL(8~13行)のトレース】


初期状態

【汎用レジスタ】
GR0?
GR1101
GR2100

【フラグレジスタ】
ZFSFOF
000



8行 LAD GR0, 0

【汎用レジスタ】
GR00
GR1101
GR2100

【フラグレジスタ】
ZFSFOF
000



~1ループ目~

9行 ADDA GR0, GR1

【汎用レジスタ】
GR0101
GR1101
GR2100

【フラグレジスタ】
ZFSFOF
000

GR0の値が5になるためフラグレジスタに変化はありません。



10行 LAD GR2, -1, GR2

【汎用レジスタ】
GR0101
GR1101
GR211

【フラグレジスタ】
ZFSFOF
000



11行 CPA GR2, =0

【汎用レジスタ】
GR0101
GR1101
GR211

【フラグレジスタ】
ZFSFOF
000

CPA命令で、GR2の値 3 と 0 を比較します。
3 > 0 のためフラグレジスタに変化はありません。



12行 JPL LP

【フラグレジスタ】
ZFSFOF
000

ZFが0、SFが0のため、JPL命令で指定されたラベル「LP」に移動し9行を処理します。



~2ループ目~

9行 ADDA GR0, GR1

【汎用レジスタ】
GR01010
GR1101
GR211

【フラグレジスタ】
ZFSFOF
000

GR0の値が10になるためフラグレジスタに変化はありません。



10行 LAD GR2, -1, GR2

【汎用レジスタ】
GR01010
GR1101
GR210

【フラグレジスタ】
ZFSFOF
000



11行 CPA GR2, =0

【汎用レジスタ】
GR01010
GR1101
GR210

【フラグレジスタ】
ZFSFOF
000

CPA命令で、GR2の値 2 と 0 を比較します。
2 > 0 のためフラグレジスタに変化はありません。



12行 JPL LP

【フラグレジスタ】
ZFSFOF
000

ZFが0、SFが0のため、JPL命令で指定されたラベル「LP」に移動し9行を処理します。



~3ループ目~

9行 ADDA GR0, GR1

【汎用レジスタ】
GR01111
GR1101
GR210

【フラグレジスタ】
ZFSFOF
000

GR0の値が15になるためフラグレジスタに変化はありません。



10行 LAD GR2, -1, GR2

【汎用レジスタ】
GR01111
GR1101
GR21

【フラグレジスタ】
ZFSFOF
000



11行 CPA GR2, =0

汎用レジスタ】
GR01111
GR1101
GR21

【フラグレジスタ】
ZFSFOF
000

CPA命令で、GR2の値 1 と 0 を比較します。
1 > 0 のためフラグレジスタに変化はありません。



12行 JPL LP

【フラグレジスタ】
ZFSFOF
000

ZFが0、SFが0のため、JPL命令で指定されたラベル「LP」に移動し9行を処理します。



~4ループ目~

9行 ADDA GR0, GR1

汎用レジスタ】
GR010100
GR1101
GR21

【フラグレジスタ】
ZFSFOF
000

GR0の値が20になるためフラグレジスタに変化はありません。



10行 LAD GR2, -1, GR2

汎用レジスタ】
GR010100
GR1101
GR20

【フラグレジスタ】
ZFSFOF
000



11行 CPA GR2, =0

汎用レジスタ】
GR010100
GR1101
GR20

【フラグレジスタ】
ZFSFOF
100

CPA命令で、GR2の値 0 と 0 を比較します。
0 = 0 のためフラグレジスタが変化します。



12行 JPL LP

【フラグレジスタ】
ZFSFOF
100

ZFが1、SFが0のため、JPL命令で指定されたラベル「LP」に移動せず13行を処理します。

13行 RET

RET命令で、「MUL」サブルーチンを抜けて、呼び出し元の「MAIN」サブルーチンに戻り処理が終了します。



以上で処理が終了し、GR0が答えです。


GR0:(10100)2 = 20


5 × 4 = 20
のため、正しい答えになっていることが分かります。





シフト演算を用いた乗算



例2: 5 × 9 の計算

1 MAIN START 
2   LD GR1, A 
3   LD GR2, B 
4   CALL MUL 
5   ST GR0, ANS 
6   RET 
7    
8 MUL LAD GR0, 0 ;GR0を答え格納用に初期化
9 LP LD GR3, GR2 ;編集用に掛ける数をGR3に転送
10   AND GR3, =#0001 ;掛ける数の1ビット目が1か0か判定
11   CPA GR3, =0 ;掛ける数と0を比較
12   JZE ZERO ;掛ける数が0ならZEROに移動
13   ADDA GR0, GR1 
14 ZERO SLL GR1, 1 
15   SRL GR2, 1 
16   CPA GR2, =0 ;掛ける数と0を比較
17   JNZ LP ;掛ける数が0でないならLPに移動
18   RET 
19    
20 A DC 5 ;掛けられる数
21 B DC 9 ;掛ける数
22 ANS DS 1 ;答え
23   END 


シミュレータにそのままコピー&ペーストで使えます。



解説



シフト演算を用いた乗算は、乗算の筆算による計算方法を基に組まれています。


このシフト演算を用いた乗算は、例1のループ処理を用いた乗算よりも、行数が多いにも関わらず高速に動作します。


計算対象の数値が100や1000と大きくなるほど、処理速度に差が出ます。


ちなみに、このプログラムの原理は乗算回路にも使用されています。



【5 × 9 の計算】



  1. まず、掛けられる数(5)と掛ける数(9)を2進数にして考える。
  2. 筆算の式にする。
  3. 2進数の掛ける数の各桁を見て1になっている桁位置に、掛けられる数を書き込む。
  4. 書き込んだ掛けられる数の総和が答え。


2進数の掛ける数の1になっている桁位置に、掛けるられる数を書き込む作業にシフト演算を用います。


それでは動作の流れをトレースで見てみたいと思います。


以下のデバッグも長いので興味がある方は見てみてください。



【例2のMUL(8~18行)のトレース】


初期状態

【汎用レジスタ】
GR0?
GR1101
GR21001

【フラグレジスタ】
ZFSFOF
000



8行 LAD GR0, 0

【汎用レジスタ】
GR00
GR1101
GR21001
GR3?

【フラグレジスタ】
ZFSFOF
000



~1ループ目~

9行 LD GR3, GR2

【汎用レジスタ】
GR00
GR1101
GR21001
GR31001

【フラグレジスタ】
ZFSFOF
000



10行 AND GR3, =#0001

【汎用レジスタ】
GR00
GR1101
GR21001
GR31

【フラグレジスタ】
ZFSFOF
000

AND演算で、GR3の値が1になるためフラグレジスタに変化はありません。



11行 CPA GR3, =0

【汎用レジスタ】
GR00
GR1101
GR21001
GR31

【フラグレジスタ】
ZFSFOF
000

CPA命令で、GR3の値 1 と 0 を比較します。
1 > 0 のためフラグレジスタに変化はありません



12行 JZE ZERO

【フラグレジスタ】
ZFSFOF
000

ZFが0のため、JZE命令で指定されたラベル「ZERO」に移動せず13行を処理します。



13行 ADDA GR0, GR1

【汎用レジスタ】
GR0101
GR1101
GR21001
GR31

【フラグレジスタ】
ZFSFOF
000

GR0の値が5になるためフラグレジスタに変化はありません。



14行 SLL GR1, 1

【汎用レジスタ】
GR0101
GR11010
GR21001
GR31

【フラグレジスタ】
ZFSFOF
000

GR1の値が10になるためフラグレジスタに変化はありません。



15行 SRL GR2, 1

【汎用レジスタ】
GR0101
GR11010
GR2100
GR31

【フラグレジスタ】
ZFSFOF
000

GR1の値が4になるためフラグレジスタに変化はありません。



16行 CPA GR2, =0

【汎用レジスタ】
GR0101
GR11010
GR2100
GR31

【フラグレジスタ】
ZFSFOF
000

CPA命令で、GR2の値 4 と 0 を比較します。
4 > 0 のためフラグレジスタに変化はありません。



17行 JNZ LP

【フラグレジスタ】
ZFSFOF
000

ZFが0のため、JNZ命令で指定されたラベル「LP」に移動し9行を処理します。



~2ループ目~

9行 LD GR3, GR2

【汎用レジスタ】
GR0101
GR11010
GR2100
GR3100

【フラグレジスタ】
ZFSFOF
000



10行 AND GR3, =#0001

【汎用レジスタ】
GR0101
GR11010
GR2100
GR30

【フラグレジスタ】
ZFSFOF
100

AND演算で、GR3の値が0になるためフラグレジスタが変化します。



11行 CPA GR3, =0

【汎用レジスタ】
GR0101
GR11010
GR2100
GR30

【フラグレジスタ】
ZFSFOF
100

CPA命令で、GR3の値 0 と 0 を比較します。
0 = 0 のためフラグレジスタに変化はありません。



12行 JZE ZERO

【フラグレジスタ】
ZFSFOF
100

ZFが1のため、JZE命令で指定されたラベル「ZERO」に移動し14行を処理します。



14行 SLL GR1, 1

【汎用レジスタ】
GR0101
GR110100
GR2100
GR30

【フラグレジスタ】
ZFSFOF
000

GR1の値が20になるためフラグレジスタが変化します。



15行 SRL GR2, 1

【汎用レジスタ】
GR0101
GR110100
GR210
GR30

【フラグレジスタ】
ZFSFOF
000

GR2の値が2になるためフラグレジスタに変化はありません。



16行 CPA GR2, =0

【汎用レジスタ】
GR0101
GR110100
GR210
GR30

【フラグレジスタ】
ZFSFOF
000

CPA命令で、GR2の値 2 と 0 を比較します。
2 > 0 のためフラグレジスタに変化はありません。



17行 JNZ LP

【フラグレジスタ】
ZFSFOF
000

ZFが0のため、JNZ命令で指定されたラベル「LP」に移動し9行を処理します。



~3ループ目~

9行 LD GR3, GR2

【汎用レジスタ】
GR0101
GR110100
GR210
GR310

【フラグレジスタ】
ZFSFOF
000



10行 AND GR3, =#0001

【汎用レジスタ】
GR0101
GR110100
GR210
GR30

【フラグレジスタ】
ZFSFOF
100

AND演算で、GR3の値が0になるためフラグレジスタが変化します。



11行 CPA GR3, =0

【汎用レジスタ】
GR0101
GR110100
GR210
GR30

【フラグレジスタ】
ZFSFOF
100

CPA命令で、GR3の値 0 と 0 を比較します。
0 = 0 のためフラグレジスタに変化はありません。



12行 JZE ZERO

【フラグレジスタ】
ZFSFOF
100

ZFが1のため、JZE命令で指定されたラベル「ZERO」に移動し14行を処理します。



14行 SLL GR1, 1

【汎用レジスタ】
GR0101
GR1101000
GR210
GR30

【フラグレジスタ】
ZFSFOF
000

GR1の値が40になるためフラグレジスタが変化します。



15行 SRL GR2, 1

【汎用レジスタ】
GR0101
GR1101000
GR21
GR30

【フラグレジスタ】
ZFSFOF
000

GR2の値が1になるためフラグレジスタに変化はありません。



16行 CPA GR2, =0

【汎用レジスタ】
GR0101
GR1101000
GR21
GR30

【フラグレジスタ】
ZFSFOF
000

CPA命令で、GR2の値 1 と 0 を比較します。
1 > 0 のためフラグレジスタに変化はありません。



17行 JNZ LP

【フラグレジスタ】
ZFSFOF
000

ZFが0のため、JNZ命令で指定されたラベル「LP」に移動し9行を処理します。



~4ループ目~

9行 LD GR3, GR2

【汎用レジスタ】
GR0101
GR1101000
GR21
GR31

【フラグレジスタ】
ZFSFOF
000



10行 AND GR3, =#0001

【汎用レジスタ】
GR0101
GR1101000
GR21
GR31

【フラグレジスタ】
ZFSFOF
000

AND演算で、GR3の値が1になるためフラグレジスタに変化はありません。



11行 CPA GR3, =0

【汎用レジスタ】
GR0101
GR1101000
GR21
GR31

【フラグレジスタ】
ZFSFOF
000

CPA命令で、GR3の値 1 と 0 を比較します。
1 > 0 のためフラグレジスタに変化はありません。



12行 JZE ZERO

【フラグレジスタ】
ZFSFOF
000

ZFが0のため、JZE命令で指定されたラベル「ZERO」に移動せず13行を処理します。



13行 ADDA GR0, GR1

【汎用レジスタ】
GR0101101
GR1101000
GR21
GR31

【フラグレジスタ】
ZFSFOF
000

GR0の値が45になるためフラグレジスタに変化はありません。



14行 SLL GR1, 1

【汎用レジスタ】
GR0101101
GR11010000
GR21
GR31

【フラグレジスタ】
ZFSFOF
000

GR1の値が80になるためフラグレジスタに変化はありません。



15行 SRL GR2, 1

【汎用レジスタ】
GR0101101
GR11010000
GR20
GR31

【フラグレジスタ】
ZFSFOF
100

GR2の値が0になるためフラグレジスタが変化します。



16行 CPA GR2, =0

【汎用レジスタ】
GR0101101
GR11010000
GR20
GR31

【フラグレジスタ】
ZFSFOF
100

CPA命令で、GR2の値 0 と 0 を比較します。
0 = 0 のためフラグレジスタに変化はありません。



17行 JNZ LP

【フラグレジスタ】
ZFSFOF
100

ZFが1のため、JNZ命令で指定されたラベル「LP」に移動せず18行を処理します。



18行 RET

RET命令で、「MUL」サブルーチンを抜けて、呼び出し元の「MAIN」サブルーチンに戻り処理が終了します。



以上で処理が終了し、GR0が答えです。


GR0:(101101)2 = 45


5 × 9 = 45
のため、正しい答えになっていることが分かります。











再起処理を用いた乗算



例3:5 × 2 の計算

1 MAIN START 
2   LAD GR0, 0 ;GR0を答え格納用に初期化
3   LD GR1, A 
4   LD GR2, B 
5   CALL MUL 
6   ST GR0, ANS 
7   RET 
8    
9 MUL CPA GR2, =0 ;掛ける数と0を比較
10   JZE FIN ;掛ける数が0ならFINに移動
11   LAD GR2, -1, GR2 ;掛ける数をデクリメント
12   CALL MUL ;再起処理
13   ADDA GR0, GR1 
14 FIN RET 
15    
16 A DC 5 ;掛けられる数
17 B DC 2 ;掛ける数
18 ANS DS 1 ;答え
19   END 


シミュレータにそのままコピー&ペーストで使えます。



解説



再起処理を利用した乗算は、再起処理によって加算を繰り返すことで実現しています。


例1のループ処理を用いた乗算と原理が似ており、ループ処理が再起処理に置き換わっただけのものになります。




すなわち、掛ける数が再起する数ということになります。


  • 掛ける数 = 再起の回数


それではトレースで見てみたいと思います。



【例3のMUL(9~14行)のトレース】


初期状態

【汎用レジスタ】
GR00
GR1101
GR210

【フラグレジスタ】
ZFSFOF
000



9行 CPA GR2, =0

【汎用レジスタ】
GR00
GR1101
GR210

【フラグレジスタ】
ZFSFOF
000

CPA命令で、GR2の値 2 と 0 を比較します。
2 > 0 のためフラグレジスタに変化はありません。



10行 JZE FIN

【フラグレジスタ】
ZFSFOF
000

ZFが0のため、JZE命令で指定されたラベル「FIN」に移動せず11行を処理します。



11行 LAD GR2, -1, GR2

【汎用レジスタ】
GR00
GR1101
GR21

【フラグレジスタ】
ZFSFOF
000



12行 CALL MUL

CALL命令で、「MUL」サブルーチンを呼び出し、再起処理により再び9行を処理します。




~再起1回目~

9行 CPA GR2, =0

【汎用レジスタ】
GR00
GR1101
GR21

【フラグレジスタ】
ZFSFOF
000

CPA命令で、GR2の値 1 と 0 を比較します。
1 > 0 のためフラグレジスタに変化はありません。



10行 JZE FIN

【フラグレジスタ】
ZFSFOF
000

ZFが0のため、JZE命令で指定されたラベル「FIN」に移動せず11行を処理します。



11行 LAD GR2, -1, GR2

【汎用レジスタ】
GR00
GR1101
GR20

【フラグレジスタ】
ZFSFOF
000



13行 CALL MUL

CALL命令で、「MUL」サブルーチンを呼び出し、再起処理により再び9行を処理します。




~再起2回目~

9行 CPA GR2, =0

【汎用レジスタ】
GR00
GR1101
GR20

【フラグレジスタ】
ZFSFOF
100

CPA命令で、GR2の値 0 と 0 を比較します。
0 = 0 のためフラグレジスタが変化します。



10行 JZE FIN

【フラグレジスタ】
ZFSFOF
100

ZFが1のため、JZE命令で指定されたラベル「FIN」に移動し14行を処理します。



14行 RET

RET命令で、「MUL」サブルーチンを抜けて、呼び出し元の「MUL」サブルーチンに戻り13行を処理します。




~再起1回目に戻る~

13行 ADDA GR0, GR1

【汎用レジスタ】
GR0101
GR1101
GR20

【フラグレジスタ】
ZFSFOF
000

GR0の値が5になるためフラグレジスタが変化します。



14行 RET

RET命令で、「MUL」サブルーチンを抜けて、呼び出し元の「MUL」サブルーチンに戻り13行を処理します。




~再起を抜ける~

13行 ADDA GR0, GR1

【汎用レジスタ】
GR01010
GR1101
GR20

【フラグレジスタ】
ZFSFOF
000

GR0の値が10になるためフラグレジスタに変化はありません。



14行 RET

RET命令で、「MUL」サブルーチンを抜けて、呼び出し元の「MAIN」サブルーチンに戻り処理を終了します。



以上で処理が終了し、GR0が答えです。


GR0:(1010)2 = 10


5 × 2 = 10
のため、正しい答えになっていますね。





最後に



今回はCASL2で乗算を実現する方法について紹介いたしました。


CASL2でも乗算用の命令が用意されていれば、苦労することはないのですが、教育用ですから仕方ありませんよね。


乗算は、シフト演算と再起処理を利用したものが、少し理解し難いと思いますが、頑張って挑んでいってください。


それでは、また次回にご期待を!














プロフィール

ニッシー
1990年生まれ
血液型 O型

こんにちは。ITブログアルケーナム管理人のニッシーです。

詳細プロフィールへ

お問い合わせへ