Как установить регистры MMX в обработчике исключений Windows для эмуляции неподдерживаемого 3DNow! инструкции

Я пытаюсь оживить старую игру Win32, которая использует 3DNow! набор инструкций для 3D рендеринга.

На современных ОС, таких как Win7 — инструкции Win10, такие как FPADD или FPMUL, не допускаются, и программа выдает исключение.

Так как количество 3DNow! используемые в игре инструкции очень ограничены, в моей программе VS2008 MFC я пытался использовать векторную обработку исключений, чтобы получить значение регистров MMX, эмулировать 3DNow! инструкции по коду C и перенесите значения обратно в процессор 3DNow! регистры.

Пока я преуспел в первых двух шагах (я получаю значения регистра mmx из ExceptionInfo->ExtendedRegisters массив байтов со смещением 32 и использовать инструкции типа float C для выполнения расчетов), но моя проблема в том, что независимо от того, как я пытаюсь обновить значения регистра MMX, значения регистра, похоже, остаются неизменными.

Предполагая, что мой _asm утверждения могут быть неверными, я также провел минимальный тест, используя такие простые выражения:

_asm movq mm0 mm7

Этот оператор выполняется без дальнейших исключений, но при получении значений регистра MMX я все же обнаруживаю, что исходные значения не изменились.

Как я могу сделать назначение эффективным?

8

Решение

На современных ОС, таких как Win7 — инструкции Win10, такие как FPADD или FPMUL, не допускаются.

Скорее всего, ваш процессор не поддерживает 3DNow! AMD уронила его для семейства Bulldozer, и Intel никогда не поддерживал это. Поэтому, если вы не используете современную Windows на Athlon64 / Phenom (или Via C3), ваш процессор не поддерживает его.

(Интересный факт: PREFETCHW изначально был 3DNow! инструкция и является по-прежнему поддерживается (с собственным битом CPUID). В течение долгого времени процессоры Intel запускали его как NOP, но Broadwell и более поздние версии (IIRC) действительно предварительно извлекают строку кэша в эксклюзивное состояние с возможностью чтения для владельца.)


Если эта игра не работала только на оборудовании AMD, она должна иметь путь кода, который позволяет избежать 3DNow. Исправьте обнаружение процессора, чтобы перестать определять ваш процессор как имеющий 3DNow. (Может быть, у вас недавно AMD, и это предполагает любой У AMD есть 3DNow?)

(обновление на это: Комментарии ОП говорят, что другие пути кода по какой-то причине не работают. Это проблема.)


Возврат из обработчика исключений, вероятно, восстанавливает регистры из сохраненного состояния, поэтому неудивительно, что изменение значений регистров в обработчике исключений не влияет на основную программу.

По-видимому обновление ExtendedRegisters в памяти не работает, хотя, так что это всего лишь копия сохраненного состояния.

Ответ на изменение регистров MMX из обработчика исключений, вероятно, такой же, как и для целочисленных или XMM-регистров, поэтому посмотрите документацию MS для этого.


Альтернативное предложение:

Перепишите код 3DNow для использования SSE2. (Вы сказали, что есть только небольшое количество этого?). SSE2 является базовым для x86-64 и в целом безопасен для 32-битного x86.

Без исходного кода вы все равно можете изменить asm для нескольких функций, использующих 3DNow. Вы можете буквально изменить инструкции для использования 64-битных загрузок / хранилищ в регистры XMM вместо 3DNow! 64-битная загрузка / сохранение и замена PFMUL на mulpsи т. д. (Это может слегка испортиться, если у вас закончились регистры, а код 3DNow использовал операнд источника памяти. addps xmm0, [mem] требует памяти с выравниванием 16B и нагрузкой 16 байт. Поэтому вам, возможно, придется добавить разлив / перезагрузку, чтобы заимствовать другой регистр как временный).

Если у вас нет места для перезаписи функций на месте, вставьте jmp где-то у вас есть место, чтобы добавить новый код.

Большинство инструкции 3DNow есть эквиваленты в SSE, но вам может понадобиться movaps инструкции для копирования регистров вокруг, чтобы реализовать PFCMPGE, Если вы можете игнорировать возможность NaN, вы можете использовать cmpps с не менее чем предикатом. (Без AVX SSE имеет только сравниваемые предикаты, основанные на значениях меньше или не меньше).

PFSUBR легко эмулировать с помощью запасного регистра, просто скопируйте и subps повернуть вспять (Или SUBPS и инвертировать знак с помощью XORPS). PFRCPIT1 (первая итерация уточнения по принципу взаимности-sqrt) и т. д. не имеют реализации с одной инструкцией, но вы, вероятно, можете просто использовать sqrtps а также divps если ты не хочешь реализовать итерации Ньютона-Рафсона с помощью mulps и addps (или с AVX vfmadd). Современные процессоры намного быстрее, чем эта игра.


Вы можете загрузить / сохранить пару плавающих одинарной точности из / в память в нижние 64 бита регистра XMM, используя movsd (инструкция загрузки / хранения двойной точности SSE2). Вы также можете хранить пару с 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/ для ссылки на набор инструкций (и другие ссылки в тег вики.

Любая перестановка данных FP может быть выполнена в регистрах XMM с shufps или же pshufd вместо того, чтобы копировать обратно в регистры MMX, чтобы использовать любые перетасовки MMX.

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]