アセンブラでのマイナス数値がどのように扱われているかの解説となります。ビットシフト系で計算を行う際は、この知識は必要となります。改めて理解しようとすると意外と難解なんです、マイナスの値って。
MSB = 1 の数値で少し確認してみます。以下の計算ではオーバーフローしたときは下位 8bit のみを表示します。
- コンピュータが扱う数値表現
アセンブラでは2進数からの構成で全ての数値は構成されています。2進数が表す数値は2のべき乗の合計で表されます。この2進数は電気信号の有る無しで 0 と 1 を表現しています。この桁の事を bit(ビット)と呼びます。各桁がべき乗の乗数です。例えば 8bit の場合は、
%00000001 = 1 %00000010 = 2 %00000100 = 4 %00001000 = 8 %00010000 = 16 %00100000 = 32 %01000000 = 64 %10000000 = 128
このような値と同等となります。その数値の組み合わせで全ての値を表現しています。8bit で 1byte(バイト)です。1byte で表現できるのは 0 ~ 255 までとなります。この値は 16進数だと $00 ~ $FF となります。2進数と16進数のより詳しい解説はこちらをご覧ください。
- マイナスの値とは
数値は bitの加算により表現されると説明しました。これは全て 2のべき乗の積算であるため、プラスの値となっています。では、マイナスの値はどう表現すればよいのでしょうか。
マイナスの値とは、加算すると元の値よりも数値が減ってしまう値のことです。1byte の最大で 255 まで表現できます。言い換えると 255 までしか表現できません。そのため結果が 255 を超える計算は、結果を格納できません。これをオーバーフローといいます。
120($78) + 90($5A) = 210($D2) ... 結果を 1byte で表現できる 180($B4) + 90($5A) = 270($010E) ... 結果を 1byte で表現できない
この 180 + 90 の結果はオーバーフローした結果、上位が捨てられて、下位 8bit の数値である $0E だけが結果として残ります。$0E は 14 ですね。…これ、数値が小さくなっていますよね。この特徴を利用しているのが、コンピュータにおける整数のマイナスの値なのです。
- 値の有効範囲
プラスの数値同士を加算して結果が壊れる可能性があれば、その数値は有効範囲外であると考えることが出来ます。正しい計算結果が保証されてこその有効範囲です。では、どの数値までが有効なのでしょうか。それは最大値同士を加算して桁あふれを起こさない限界となりますので、1byte の場合では、その最大値である 255 を 2で割った値が計算有効範囲となります。
255($FF) ÷ 2 = 127($7F)
これで 0 ~ 127 という数値までであれば、加算しても結果は保証されます。最大値同士の加算でも 127 + 127 = 254 なので問題ないですよね。この 127 という数値を 2進数で表すと 01111111 となります。一番上位の bit(MSB)が 0 になってますよね。この最上位ビットが立っている数値は加算するとオーバーフローする可能性があります。つまりはこの MSB = 1 の状態こそがマイナスの状態なのです。
※ MSB = Most Significant Bit の略
MSB = 1 の数値で少し確認してみます。以下の計算ではオーバーフローしたときは下位 8bit のみを表示します。
100(%01100100) + 246(%11110110) = 346 % 256 → 90(%01011010)
100 に MSB = 1 の値を加算したら、値が減ったのが分かるかと思います。同じ計算を 16進数で見たほうが分かりやすいかもしれません
$64(100) + $F6 = $015A → $5A(90)
100 に加算して 90 になる値は?そうですね、-10 です。この MSB = 1 の $F6 が 8bit 数値における -10 なのです。
※ここで解説している内容は実はほぼ基本情報試験の午前問題解法と変わりません。そこでこんな教本を読んでみるのは如何でしょうか。ついでに国家試験突破なんてどうでしょう?
- 2の補数表現
人間にとって、$F6 が -10 だと言われて直感的に理解出来る人は、世界広しと言えどもそうそういるとは思えません。やはり -10 は -10 として扱いたいものです。そこでアセンブラでは、ソースコードに -10 と記述すると出力バイナリでは $F6 になるような仕組みが備えられています。
※ DB(Define Byte), DW(Define Word)等
※ DB(Define Byte), DW(Define Word)等
では、どのようにして -10 を $F6 に変換しているのでしょうか。これには 2の補数表現と呼ばれる式を用いています。2の補数表現は、数値の bit を反転して +1 する事で得られます。先に例を挙げた -10 の場合は、
10 $0A %00001010 マイナス記号を省いた元の値 245 $F5 %11110101 ビット反転 246 $F6 %11110110 +1
どうでしょう。-10 から $F6 が導き出されました。MSB = 1 を保証せねばならないため、それを超える値だと正しい結果は得られません。-128 だと
128 $80 %10000000 マイナス記号を省いた元の値 127 $7F %01111111 ビット反転 128 $80 %10000000 +1
MSG = 1 なのでギリ有効範囲内です。では -129 だと…
129 $81 %10000001 マイナス記号を省いた元の値 126 $7E %01111110 ビット反転 127 $7F %01111111 +1
MSB = 0 となってしまったため、マイナスを表現できなくなってしまいました。ここから分かるのはマイナスを扱う数値の有効範囲は -128 ~ 127 という事です。C 言語等のコンパイラで char 型の有効範囲がこうなっていたのは、このような計算を行っていたためですね。
- ビットシフトの罠
アセンブラではシフトを行うことで数値の 2倍や 1/2 を簡単に計算することが出来ました。2倍は左シフトです。1/2 は右シフトです。この計算にマイナスの値を適用したらどうなるでしょうか。-10 で試してみます。
-10 $F6 %11110110
----------------------- 2倍 %11110110 << 1 = %11101100 (-20) 1/2 %11110110 >> 1 = %01111011 (123)
1/2 計算の結果がプラスの値に変化してしまいました。これは MSB に 0 を入れてしまったためです。このように符号付きの数値では、右シフトでは MSB を残すように計算しなければおかしくなります。Z80 CPU だと MSB を残すための右シフト専用に SRA r というMSB を保存する右シフト専用命令が存在します。MSB を残すシフトであれば
1/2 %11110110 >> 1 = %11111011 (-5)
このように問題なく 1/2 の計算を行うことが出来ます。ちなみに 2倍でも問題が発生する数値があります。例えば、-65 では…
-64 $BF %10111111
----------------------- 2倍 %10111111 << 1 = %01111110 (126)
10進数で計算すると -65×2 = -130 ですから有効桁数から外れます。そのため、値がプラスに転じてしまったというわけです。この 2倍によるフローは案外よく発生するので注意が必要です。
これがコンピュータ内部で行われている整数のマイナスの随念です。今回は理解を助けるため 8bit で説明していますが、16bit や 32bit でも全く同様に扱われています。特にアセンブラでは数値について気にする必要があります。コンパイラでは殆ど気にしなくても大丈夫なのですが、マイナスの値について覚えておいて頂けると幸いです。ではまた!
※ こういう問題で実力確認って意外と楽しいです。第1章から第3章がとくにアセンブラプログラミングには役立ちそうです。チャレンジしてみてはどうでしょう?
※ こういう問題で実力確認って意外と楽しいです。第1章から第3章がとくにアセンブラプログラミングには役立ちそうです。チャレンジしてみてはどうでしょう?
コメント
コメント一覧 (8)
内藤時浩
が
しました
論理シフトはCをクリアしてC付きのローテートするだけですが。
論理シフトと素のローテートしかないMOS6502での算術シフトも分かりません。
シフト後にいちいちSの条件分岐でMSBが1のORをしていたのでしょうか。
C付きのローテートがない場合のマルチバイトのシフトも気になります。
シフト後にいちいちCの条件分岐でMSBかLSBが1のORをしていたのでしょうか。
内藤時浩
が
しました