在 Win7 等现代操作系统上 - 不允许使用 FPADD 或 FPMUL 等 Win10 指令
更有可能您的 CPU 不支持 3DNow! AMD 为 Bulldozer 系列放弃了它,而英特尔从未支持它。因此,除非您在 Athlon64 / Phenom(或 Via C3)上运行现代 Windows,否则您的 CPU 不支持它。
(有趣的事实:PREFETCHW
最初是 3DNow!指令,并且仍然受支持(具有自己的 CPUID 功能位)。很长一段时间以来,英特尔 CPU 将其作为 NOP 运行,但 Broadwell 和后来的(IIRC)确实将缓存线预取到具有 Read-For-Ownership 的独占状态。)
除非这个游戏只在 AMD 硬件上运行过,否则它必须有一个避免 3DNow 的代码路径。修复其 CPU 检测以停止将您的 CPU 检测为具有 3DNow。(也许你最近有一个 AMD,它假设任何AMD 都有 3DNow?)
(更新:OP的评论说其他代码路径由于某种原因不起作用。这是一个问题。)
从异常处理程序返回可能会从保存状态恢复寄存器,因此在异常处理程序中更改寄存器值对主程序没有影响也就不足为奇了。
但是,显然在内存中更新ExtendedRegisters
并不能解决问题,因此这只是已保存状态的副本。
从异常处理程序修改 MMX 寄存器的答案可能与整数或 XMM 寄存器相同,因此请查阅 MS 的文档。
替代建议:
重写 3DNow 代码以使用 SSE2。(你说只有很少量?)。SSE2 是 x86-64 的基线,通常可以安全地假设为 32 位 x86。
如果没有源代码,您仍然可以为使用 3DNow 的少数函数修改 asm。您可以从字面上更改指令以使用 64 位加载/存储到 XMM 寄存器而不是 3DNow!64 位加载/存储,并将 PFMUL 替换为mulps
等(如果您用完寄存器并且 3DNow 代码使用内存源操作数,这可能会有点麻烦。 addps xmm0, [mem]
需要 16B 对齐的内存,并执行 16 字节的加载。所以您可能必须添加溢出/重新加载以临时借用另一个寄存器)。
如果您没有空间就地重写函数,请将 ajmp
放在您确实有空间添加新代码的地方。
大多数3DNow 指令在 SSE 中都有等价物,但您可能需要一些额外的movaps
指令来复制寄存器以实现PFCMPGE
. 如果您可以忽略 NaN 的可能性,则可以使用cmpps
不小于谓词。(没有 AVX,SSE 只有基于小于或不小于的比较谓词)。
PFSUBR
使用备用寄存器很容易模拟,只需复制和subps
反转。(或 SUBPS 并使用 XORPS 反转符号)。 PFRCPIT1
(reciprocal-sqrt 第一次细化迭代)等没有单指令实现,但如果您不想使用 mulpssqrtps
和addps(或使用 AVX )实现 Newton-Raphson 迭代,您可能可以只使用. 现代 CPU 比这款游戏的设计速度要快得多。divps
vfmadd
movsd
您可以使用(SSE2 双精度加载/存储指令)将一对单精度浮点数从内存加载/存储到 XMM 寄存器的低 64 位。您也可以使用 存储一对movlps
,但仍movsd
用于加载,因为它将上半部分归零而不是合并,因此它不依赖于寄存器的旧值。
使用movdq2q mm0, xmm0
和movq2dq xmm0, mm0
在 XMM 和 MMX 之间移动数据。
用于movaps xmm1, xmm0
复制寄存器,即使您的数据仅在低半部分。(movsd xmm1, xmm0
将低半部分合并到原始高半部分。 movq xmm1, xmm0
将高半部分归零。)
addps
并mulps
在上半部分使用零正常工作。(如果任何垃圾(上半部分)产生异常结果,它们可能会减慢速度,因此最好将上半部分保持为零)。请参阅http://felixcloutier.com/x86/获取指令集参考(以及x86标签 wiki 中的其他链接。
任何 FP 数据的混洗都可以在 XMM 寄存器中完成,使用shufps
或pshufd
代替复制回 MMX 寄存器以使用任何 MMX 混洗。