8086のMULとIMUL:符号なし・符号付き乗算、結果、フラグの違い

メンテナンス層(2026-09-01確認)。 本稿は意図的に、初代8086の1オペランド形式だけを扱います。2017年公開・2023年最終更新の本文は末尾に全文保存しました。そこにあるAnswers.comのURLは来歴を示すアーカイブ内だけに残し、メンテナンス版の説明はIntelの資料に基づいています。

要点

8086では、MUL符号なし乗算、IMUL2の補数による符号付き乗算を行います。どちらも明示的なレジスタまたはメモリのソースオペランドを1つ取り、AL または AX をもう一方の暗黙の入力として、2倍幅の積を完全な形で返します。

  • バイトのソース:AL × r/m8 → AX
  • ワードのソース:AX × r/m16 → DX:AX

したがって結果の配置は同じですが、数値の解釈は同じではありません。フラグが検査する条件も異なり、MUL は上位半分がゼロか、IMUL は上位半分が下位半分の符号拡張だけになっているかを調べます。

8086にある正確な形式

8086の乗算エンコーディングは次の4つです。ここで r/m は、明示的なソースをレジスタにもメモリオペランドにもできることを表します。

ソース幅MULIMUL完全な結果
8ビットF6 /4、符号なし AL × r/m8F6 /5、符号付き AL × r/m8AX(上位が AH、下位が AL
16ビットF7 /4、符号なし AX × r/m16F7 /5、符号付き AX × r/m16DX:AX(上位が DX、下位が AX

Intel/NASM形式の構文では、デスティネーションレジスタは暗黙です。

mul bl          ; unsigned AL × BL -> AX
imul bl         ; signed AL × BL -> AX
mul word [n]    ; unsigned AX × word [n] -> DX:AX
imul word [n]   ; signed AX × word [n] -> DX:AX

1オペランド命令は上位半分を捨てません。CFとOFが「積は下位半分だけには収まらない」と示す場合でも、完全な16ビットまたは32ビットの結果は、仕様で定められたレジスタまたはレジスタ対に残っています。

同じビットでも数値は異なる

各命令の前に AL = 02h、バイトのソースを FFh に設定し直します。ソースのビットは同一ですが、FFh は符号なしバイトなら255、2の補数の符号付きバイトなら−1です。

命令数学上の演算AX 内の完全な結果CFとOF
MUL2 × 255 = 51001FEh1(AH = 01h がゼロでないため)
IMUL2 × (−1) = −2FFFEh0(AH = FFhAL = FEh の符号拡張だから)

このため、「アルゴリズムは同じ」だけではプログラミング上の規則として不十分です。レジスタ配置は対応していますが、符号付きの解釈によって数学上の積もオーバーフロー判定も変わります。

CFとOFが実際に表すこと

初代8086のどちらの乗算形式でも、実行後のCFとOFは同じ値になります。

  • バイトの MUL では AH = 00h のときに限って両方が0、ワードの MUL では DX = 0000h のときに限って両方が0です。それ以外では両方が1です。
  • バイトの IMUL では AHAL の符号拡張であるときに限って両方が0、ワードの IMUL では DXAX の符号拡張であるときに限って両方が0です。それ以外では両方が1です。

つまりCF/OFがクリアなら、その命令の符号なしまたは符号付きという解釈の下で、積が下位8ビットまたは16ビットに収まります。符号付きの積で上位半分が常にゼロになる、という意味ではありません

8086のマニュアルと現在のIntelリファレンスは、これらの命令後のSF、ZF、AF、PFをいずれも未定義としています。積の分類にこれらのフラグを使わず、ほかの条件が必要なら結果を明示的に検査してください。

実行可能な8ビット参照モデル

次の小さなPythonモデルは、02h × FFh の算術と仕様上のCF/OF規則を再現します。

def signed8(value):
    return value - 0x100 if value & 0x80 else value


al, src = 0x02, 0xFF

mul_bits = al * src
mul_cf_of = int((mul_bits >> 8) != 0)

imul_value = signed8(al) * signed8(src)
imul_bits = imul_value & 0xFFFF
imul_low = imul_bits & 0xFF
imul_high = imul_bits >> 8
expected_high = 0xFF if imul_low & 0x80 else 0x00
imul_cf_of = int(imul_high != expected_high)

print(f"MUL  AX={mul_bits:04X}h CF=OF={mul_cf_of}")
print(f"IMUL AX={imul_bits:04X}h CF=OF={imul_cf_of}")

期待される出力:

MUL  AX=01FEh CF=OF=1
IMUL AX=FFFEh CF=OF=0

これは算術の参照モデルであり、8086エミュレーターではありません。命令デコード、例外、実行タイミング、未定義のフラグはモデル化していません。

後年のIMUL形式を8086コードと混同しない

x86アーキテクチャは8086以後も拡張されたため、現在のIntelリファレンスは1・2・3オペランド形式を同じ項目で説明しています。一方、1979年10月の8086マニュアルにある命令表とデコードガイドが掲載するのは、前述したアキュムレーター基準の1オペランド形式だけです。

imul bx          ; original 8086: signed AX × BX -> DX:AX

imul cx, bx      ; later two-operand form: not an original 8086 encoding
imul cx, bx, 10  ; later three-operand form: not an original 8086 encoding

CPUターゲットを制限しなければ、現代のアセンブラーは3つとも受理することがあります。本物の8086用コードを書くときは、選択したターゲットと生成されたオペコードを確認してください。現在のマニュアルでレガシーモードについて「Valid」とされていることだけから、歴史上の8086でも利用できたと推測してはいけません。

実用チェックリスト

  1. ビット列が符号なし値(MUL)か、2の補数の符号付き値(IMUL)かを決めます。
  2. 暗黙の入力を忘れないようにします。バイトのソースなら AL、ワードのソースなら AX です。
  3. 下位レジスタだけでなく、AX または DX:AX から完全な積を読み取ります。
  4. CF/OFは選んだ命令の規則に従って使い、SF、ZF、AF、PFは未定義として扱います。
  5. 8086を対象にするなら1オペランド形式だけを使い、生成されたエンコーディングを確認します。

一次資料

---

2017年原文アーカイブ(2023年最終更新、逐語保存)

以下はWordPressエクスポートにある可視本文の全体です。リポジトリの書式に合わせ、不可視の行末空白だけを正規化しました。外部リンクと大まかな “same algorithm” という表現は歴史的来歴として保存したもので、現在の典拠として支持するものではありません。


Reference Link:
 http://www.answers.com/Q/What_is_the_basic_difference_between_MUL_and_IMUL_instruction_in_8086_microprocessor

mul is used for unsigned multiplication whereas imul is used for signed multiplication. Algorithm for both are same, which is as follows:
 when operand is a byte:
 AX = AL * operand.
 when operand is a word:
 (DX AX) = AX * operand.

Leave a Reply