上に戻る
資料4.8ビットCPUで16ビット加減算
まず……
 資料3.-1と255は等しい?
 と、リンクを張ってみたが、あんまり関係ないかなぁ……
概要
 8ビットCPUで16ビット以上の計算するにはどうしたらいいか……ということ。
 とかいいながら、ファミコンのCPU限定な話ですが……
キャリーフラグ
 ファミコンのCPUでは、キャリーフラグが、加算や減算の結果に影響して“しまい”ます。
 これは一見して意味不明な仕様にも見えます。(※この手の仕様は、別に、ファミコンのCPU独自にあるようなものではないらしいけど)
 実はこれは、16ビット以上の計算をするときに大変役に立つ仕様なのです……たぶん。
 ちなみに、GBのCPUには、キャリーフラグに影響されない加算命令addがあったりします。
加算のときのキャリーフラグ作用
 加算adc時に、キャリーフラグセットだと、計算結果に1が足されてしまいます。
 これはどういった意図なのでしょうか……?
16ビット整数+8ビット整数
 一般的な表現ではありませんが、以下で『~』はビットを繋げて1つの数と見るとしておきます。このサイトでしか見かけない表記法です^^
 たとえば、12~34なら、1234hを表します。
 さて、メモリ上の2バイトを16ビット整数とみた、$01~$00、に8ビット整数を足すにはどうしたら良いでしょう?
 足したい数は、aレジスタに入っているものとします。
 以下のコードで実装できます。
1:clc
2:adc $00
3:sta $00
4:lda $01
5:adc #$00
6:sta $01
 1行目。加算の前には、キャリーフラグをクリアしないとダメです。
 2行目で、aレジスタと$00を加算します。
 この結果は9ビットになりえます。
 それが、c~a(cはキャリーフラグ)の9ビットに代入された……と見ることが出来ます。
 下位8ビットの結果はaレジスタに入っているので、
 3行目で$00に書き込みます。
 つづいて、上位8ビットを考えます。
 4行目で、まず、元の上位8ビット$01を取り出します。
 そして肝心なのが、
 5行目です。足し算命令なのになぜかゼロを足しています。
 実は、これは、ゼロを足しているというより、キャリーフラグを足しているのです。
 キャリーは繰り上がりということですが、下位8ビットの繰り上がり分が足されたわけです。
 6行目。最後に、その計算結果を上位8ビットに書き込んで終了です。
 キャリーフラグは、『1つ前の桁が繰り上がっているかどうか』と見ることが出来ます。
 したがって、2バイト目の計算で、その繰り上がり分も足すためにadcを使うと都合が良いわけです。
 逆に、1バイト目の計算では繰り上がりは絶対にないので、clcでキャリークリアしておかないとマズイことが起きるわけです。
16ビット整数+16ビット整数
 ほとんどかわらないですが、一応……  $01~$00 に $03~$02 と $05~$04 を足したものを代入します。
 以下のコードで実装できるはずです(試してないけど^^)
1:clc
2:lda $02
3:sta $00
4:lda $03
5:sta $01
6:lda $04
7:adc $00
8:sta $00
9:lda $05
10:adc $01
11:sta $01
 1行目は先ほどと同じく、キャリーフラグをクリアします。
 2〜5行目で、演算対象の片方を、結果のバッファにコピーします。
 6〜8行目で、1バイト目の計算を行います。
 9〜11行目で、2バイト目の計算を行います。
 こうしてみてみると、より多くの桁数を計算する事を考えたとき、整数のバイトオーダーは逆順に(下から)確保したほうが良さげなことがわかります。
減算のときのキャリーフラグの作用
 減算sbc時に、キャリーフラグがクリアなら、さらに1を引きます。
 これはどういった意図でしょうか……(書いている時点で自分がわかってないのは秘密^^)
16ビット整数−8ビット整数
 $01~$00から$02を引きます。
 以下のコードで実装できます。たぶん^^
1:sec
2:lda $00
3:sbc $02
4:sta $00
5:lda $01
6:sbc #$00
7:sta $01
 こうしてみると、加算とほとんど同じ手順です……
 1行目。減算のときは、加算と逆に、キャリーをセットしてから始めます。
 2行目で、引かれる数をaにロードします。引かれる数をaレジスタにする事に注意。
 3行目で減算実行です。キャリーはセットされているので、“フツーに”引き算です。
 結果がaレジスタに入ります。
 さらに、引く数のほうが小さいときは、キャリーがセットされ、引く数のほうが大きいときは、キャリーがクリアされます。
 イメージとしては、とりあえず上位バイトから借りた『1』を用いて……
 1~a - 対象 → c~a
 といったところでしょうか……
 もし、cがクリアされたら、それは、本当に上位バイトに借りを作ったことを意味します。
 4行目で結果の1桁目の結果を書き、
 5行目で2桁目のロードをします。
 6行目。adcと同じように00hを引いています。
 意味は同じようなもので、『もし下位バイトが借りを作っていたら』1引いてやる……ということです。
 7行目で結果を上位バイトに書いて終了です。

 足し算よりわかりづらいのですが、引き算のときは、キャリーフラグは『下位バイトに借りを作られていないか』と見ることが出来ます。
 1バイト目の計算では、当然、借りを作られていないので、セットする必要があります。
16ビット整数−16ビット整数
 もう説明いらん気もする……  $01~$00 に $03~$02 から $05~$04 を引いたものを代入します。
 以下のコードで実装できるはずです(やっぱり試してないけど……)
1:sec
2:lda $04
3:sta $00
4:lda $05
5:sta $01
6:lda $02
7:sbc $00
8:sta $00
9:lda $03
10:sbc $01
11:sta $01
 加算のときと変わらんです。引く数を先に代入しているのに違和感を覚えますが……
(24)2007年2月17日 プレさ兵衛
inserted by FC2 system