9

我正在尝试恢复使用 3DNow 的旧 Win32 游戏!用于进行 3D 渲染的指令集。

在 Win7 等现代操作系统上 - 不允许使用 FPADD 或 FPMUL 等 Win10 指令,并且程序会引发异常。

自 3DNow 的数量!游戏使用的指令非常有限,在我的VS2008 MFC程序中我尝试使用向量异常处理来获取MMX寄存器的值,模拟3DNow!通过 C 代码指令并将值推送回处理器 3DNow!寄存器。

到目前为止,我在前两个步骤中取得了成功(我从ExceptionInfo->ExtendedRegisters偏移量 32 的字节数组中获取 mmx 寄存器值并使用浮点类型 C 指令进行计算),但我的问题是,无论我如何尝试更新 MMX 寄存器值,寄存器值似乎保持不变。

假设我的_asm陈述可能是错误的,我还使用如下简单的陈述做了一些最小的测试:

_asm movq mm0 mm7

这条语句的执行没有进一步的异常,但是在检索 MMX 寄存器值时,我仍然发现原始值没有改变。

我怎样才能使分配有效?

4

1 回答 1

4

在 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 第一次细化迭代)等没有单指令实现,但如果您不想使用 mulpssqrtpsaddps(或使用 AVX )实现 Newton-Raphson 迭代,您可能可以只使用. 现代 CPU 比这款游戏的设计速度要快得多。divpsvfmadd


movsd您可以使用(SSE2 双精度加载/存储指令)将一对单精度浮点数从内存加载/存储到 XMM 寄存器的低 64 位。您也可以使用 存储一对movlps,但仍movsd用于加载,因为它将上半部分归零而不是合并,因此它不依赖于寄存器的旧值。

使用movdq2q mm0, xmm0movq2dq xmm0, mm0在 XMM 和 MMX 之间移动数据。

用于movaps xmm1, xmm0复制寄存器,即使您的数据仅在低半部分。(movsd xmm1, xmm0将低半部分合并到原始高半部分。 movq xmm1, xmm0将高半部分归零。)

addpsmulps在上半部分使用零正常工作。(如果任何垃圾(上半部分)产生异常结果,它们可能会减慢速度,因此最好将上半部分保持为零)。请参阅http://felixcloutier.com/x86/获取指令集参考(以及标签 wiki 中的其他链接。

任何 FP 数据的混洗都可以在 XMM 寄存器中完成,使用shufpspshufd代替复制回 MMX 寄存器以使用任何 MMX 混洗。

于 2017-10-27T07:43:33.687 回答