この問題の原因かはわかりませんが、とりあえずADD命令でのフラグの設定がおかしいようですね。
例えばadd.cの最初の方にあるsx86_ubyte op_add8関数を見ると
softx86/softx86/add.c さんが書きました:コード:
/* if carry/overflow */
if (ret < src)
ctx->state->reg_flags.val |=
(SX86_CPUFLAG_CARRY | SX86_CPUFLAG_OVERFLOW);
else
ctx->state->reg_flags.val &=
~(SX86_CPUFLAG_CARRY | SX86_CPUFLAG_OVERFLOW);
となっており、キャリーフラグとオーバーフローフラグが必ず一致するようになっていますが、これは明らかにおかしいでしょう。
add.cの他の関数にも同様のおかしい処理が書かれていました。
また、パリティフラグは計算結果の最下位バイトに含まれているビット1が偶数個の場合1に、奇数個の場合0になるはずなのに、
add.cのsx86_uword op_add16関数などではなぜか下から2バイト目も計算に含めてしまい、結果がおかしくなっています。
例えば、様々なエミュレータで以下のプログラムを実行し、出力結果をまとめると、
コード:
| ODITSZ A P C
Bochs | 0000000000010011
VirtualBox | 0000001000010011
xtvm | 0000100000010101
8086run | 1111000000010011
8086インタプリタ | 0000000000010011
となり、xtvmだけOFとPFの値が違っていることがわかります。
8086run
8086インタプリタ
プログラム (nasm 2.11.08で確認)
コード:
cpu 8086
bits 16
org 0x7c00
start:
; スタックポインタとセグメントレジスタの初期化
mov sp, 0x7ff0
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
jmp 0x0000:main
main:
; 0x7fff + (-1) の計算をする
mov ax, 0x7fff
mov bx, 0xffff
add ax, bx
; フラグレジスタの内容をスタックに保存する
pushf
; 各フラグの位置を出力する
mov si, flaglabel
strloop:
mov al, [si] ; 次の文字をとみ込む
test al, al
jz strloopend ; ゼロなら終わり
mov ah, 0x0e ; 一文字表示コマンドを設定
xor bx, bx
int 0x10
inc si ; 次の文字へ進む
jmp strloop
strloopend:
; フラグレジスタの内容を出力する
pop ax ; スタックに保存したフラグレジスタの内容を読みだす
mov cx, 16 ; ビット数
printloop:
rol ax, 1 ; 表示するビットを最下位に持ってくる
push ax ; データのバックアップを取る
and ax, strict word 1 ; 最下位のビットを取り出す
add ax, 0x0e30 ; 数字に変換し、一文字表示のコマンドを設定
xor bx, bx
int 0x10
pop ax ; バックアップしたデータをリストアする
loop printloop
; 終了
hltloop:
cli
hlt
jmp hltloop
ret
; フラグのラベル
flaglabel:
db " ODITSZ A P C", 13, 10, 0
; おまじないの位置を合わせる
times 510 - ($ - $$) db 0x90
; ブータブルディスクとして認識させるおまじない
db 0x55
db 0xaa
; FDイメージの残りの部分
times 1474560 - ($ - $$) db 0x00