PDP-11 MUL命令を、8086のアセンブリ言語に変換したメモ

作成中の、PDP-11アセンブリ -> 8086アセンブリトランスレーター(https://github.com/kusabanachi/p11trans)で、 MUL命令の変換部分を書いた

PDP-11 MUL命令

mul src, dest
  • destがR0,R2,R4(偶数)の場合
  • destがR1,R3,R5(奇数)の場合
    • dest レジスタ = src * destの下位16bit
    • 上位16bitは格納されない

8086 IMUL命令

符号付き乗算命令なので、x86ではIMUL命令が相当する

imul src

大まかな違い

どちらも同じ目的で使われる命令だが、PDP-11のmul命令がsrcとdestの2つのオペランドを指定するのに対して、8086のimul命令はdestがAXレジスタに固定される。

PDP-11のmul命令はdestレジスタの番号が偶数か奇数かで動作が変わるが、8086のimul命令は1パターン。

PDP11 mul --> 8086 imul の変換

PDP11 mulのdest(以下'dest'と表記)の値により、大きく4パターンに場合分けする

レジスタ

    * R0 --> AX
    * R1 --> DX
    * R2 --> CX
    * R3 --> SI
    * R4 --> DI
    * R5 --> BP
    *    --> BX(余り)

の変換割り当て。

'dest'がR0の場合

imul src         ! ちょうどAX(R0)の位置なのでそのまま計算できる
xchg ax, dx      ! 'dest'+1がDX(R1)なので、8086計算結果の、上位16bitと下位16bitを交換すればOK

'dest'がR1の場合

mov tmp, ax      ! tmpという名の領域に元のAXの値を退避

mov ax, 'dest'   ! 'dest'の値をAXにコピーする
imul src         ! AXに'dest'の値が入ったので計算
mov 'dest' ax    ! 計算結果の下位16bitを'dest'(R1)に入れる

mov ax, tmp      ! 元のAXの値を復元
  • 8086IMUL命令で計算結果がAXとDXに入る。
    • DX(R1)は計算結果の下位16bitが最終的に入るので、元の値は消えて良い
    • AX(R0)の値は変化して欲しくないので、退避/復元する

'dest'がR2,R4の場合 (R0以外の偶数レジスタ)

mov tmp, ax      ! tmpという名の領域に元のAXの値を退避
mov tmp2, dx     ! tmp2という名の領域に元のDXの値を退避

mov ax, 'dest'   ! 'dest'の値をAXにコピーする
imul src         ! AXに'dest'の値が入ったので計算
mov 'dest' dx    ! 計算結果の上位16bitを'dest'に入れる
mov 'dest+1' ax  ! 計算結果の下位16bitを'dest+1'に入れる

mov ax, tmp      ! 元のAXの値を復元
mov dx, tmp2     ! 元のDXの値を復元
  • 計算結果がAXとDXに入るので、AXとDXの元の値の退避/復元をする
  • 計算結果を'dest'と'dest+1'に格納する

  • 元の'dest'と'dest+1'の場所の値は、計算結果で上書きされてどうせ消えるので、AXとDXの退避場所に使ってxchg命令を使うと、命令数を減らせられる

xchg 'dest', ax  ! 'dest'の場所に元のAXの値を退避させ、'dest'の値をAXに入れる
mov 'dest+1' dx  ! 'dest+1'の場所に元のDXの値を退避

imul src         ! AXに'dest'の値が入ったので計算
xchg 'dest' dx   ! 計算結果の上位16bitを'dest'に入れる。DXには元AXの値。
xchg 'dest+1' ax ! 計算結果の下位16bitを'dest+1'に入れる。AXには元DXの値。

xchg ax, dx      ! 元のAXとDXの値が逆になっているので交換する

'dest'がR3,R5の場合 (R1以外の奇数レジスタ)

mov tmp, ax      ! tmpという名の領域に元のAXの値を退避
mov tmp2, dx     ! tmp2という名の領域に元のDXの値を退避

mov ax, 'dest'   ! 'dest'の値をAXにコピーする
imul src         ! AXに'dest'の値が入ったので計算
mov 'dest' ax    ! 計算結果の下位16bitを'dest'に入れる

mov ax, tmp      ! 元のAXの値を復元
mov dx, tmp2     ! 元のDXの値を復元
  • 1つ上の偶数レジスタの場合と比べて、'dest+1'に値を格納しないという違いのみ

  • 同様にして命令数を減らすと、

mov tmp, dx      ! tmpという名の領域に元のDXの値を退避
xchg 'dest', ax  ! 'dest'の場所に元のAXの値を退避させ、'dest'の値をAXに入れる

imul src         ! AXに'dest'の値が入ったので計算
xchg 'dest' ax   ! 計算結果の下位16bitを'dest'に入れる。AXには元AXの値。

mov dx, tmp      ! 元のDXの値を復元

srcの値に注意

  • srcの値が、R0,R1,'dest','dest+1'レジスタを使用している場合は、上記の操作により、imul命令で使う前にレジスタの値が上書きされてしまっている可能性があるので、先に退避しておいて計算に使用する必要がある *2

  • srcの値が即値の場合は、8086のimul命令に使えないので、値をレジスタやメモリにコピーしてから使用する必要がある

*1:R1,R3,R5

*2:更なる場合分けが発生