PDP-11のSXT命令を 8086のアセンブリ言語に変換しようとしたメモ

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

SXT命令とは

  • sign extendの意味
  • 1つのオペランドを取る
  • Nフラグ(Negativeの意味で8086のSignフラグに相当)が立っていたら、オペランドを0xffffに、立ってなければ0にする。

対応するような命令が8086にないけど、割とシンプルな命令なのですぐ実装できる気がして取りかかった

最初の実装 *1

mov dest, #0    ! オペランドのdestに0を入れておく
jns 8f          ! SFが立ってなければ、先の'8'ラベルへジャンプ
  not dest      ! 飛ばされなければ(SFが立ってれば)destを反転 -> 0xffff
8:              ! 分岐のためのラベル

あまりラベルを使いたくないけど、まぁしょうがないかと *2

実装後、試してみて問題が見つかる

  • オペランドにデクリメントのメモリアドレスが指定された場合が問題に
    • PDP-11ではインクリメントやデクリメント指定付きのアドレッシングができて、デクリメント指定なら演算前にレジスタの値が減算される
  • 8086ではそのようなオペランドの指定がないので、まずレジスタの値を減算してから演算をするように変換していた
  • 例えばsxt -(r3)なら *3
sub si, 2       ! ここの減算でフラグが上書きされる...
mov (si), #0
jns 8f          ! ここの分岐はsub命令の結果次第
  not (si)
8:
  • 呼び出し時点のフラグの状態で結果の値が決まる命令なのに、おかしなことに...

考えた代わりの案

  • フラグは元の値に戻そう案
movb tmp, ah    ! tmpという名の領域にahの値を退避
lahf            ! フラグがahにロードされる命令
sub si, 2       ! フラグが上書きされるが
sahf            ! 元のフラグの状態に戻す
movb ah, tmp    ! ahの値を復元
mov (si), #0
jns 8f
  not (si)
8:
  • ジャンプ後にデクリメントしよう案
js 8f           ! SF立っていたら8までジャンプ
  ! SF立っていない時のみ来る
  sub si, 2
  mov (si), #0
  jmp 9f        ! 9までジャンプ
8:
  ! SF立っている時のみ来る
  sub si, 2
  mov (si), #0xff
9:
  • 分岐しなければいいじゃん案
movb tmp, ah    ! tmpという名の領域にahの値を退避
lahf            ! フラグがahにロードされる命令
rol ax, #1      ! SFのbitが0bit目(一番右の位置に来る)
and ax, #1      ! SFのbit以外クリア
neg ax          ! 2の補数をとる 1 -> 0xffff. 0 -> 0
sub si, 2
mov (si), ax
movb ah, tmp    ! ahの値を復元

結局は

  • インクリメント・デクリメントはlea命令を使うことにした
  • lea命令はメモリのアドレスをレジスタにロードする
  • lea命令はフラグの変更が起こらない
lea si, -2(si)   ! ([si - 2]が指すメモリ、のアドレス) = si - 2 が、siに入る
mov (si), #0
jns 8f
  not (si)
8:
mov bx, ax       ! メモリ参照ができないのでbxにコピー
lea ax, -2(bx)   ! ([bx - 2]が指すメモリ、のアドレス) = bx - 2 が、axに入る
mov bx, ax
mov (bx), #0
jns 8f
  not (bx)
8:

*1:8086のアセンブリはACKの記述

*2:前後で同じ名前を使われてるとぶつかるかもしれないので

*3:PDP-11のr3レジスタは、8086のsiレジスタに割り当てている