Как настроить регистры 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 я по-прежнему обнаруживаю, что исходные значения не изменились.

Как сделать задание эффективным?


person gho    schedule 27.10.2017    source источник
comment
Значит, эта игра когда-либо работала только на оборудовании AMD? Процессоры Intel никогда не поддерживали 3DNow, поэтому я удивлен, что у них нет пути кода, избегающего 3DNow. Если вы используете процессор AMD, возможно, он неправильно определяет и думает, что у вас есть 3DNow, и вам лучше всего взломать обнаружение процессора (ищите CPUID).   -  person Peter Cordes    schedule 27.10.2017
comment
Итак, вы используете _asm movq mm0 mm7 внутри своего обработчика исключений? Разве вы не должны обновлять ExtendedRegisters в памяти перед возвратом из исключения? Я предполагаю, что возврат из обработчика исключений заменяет текущие регистры сохраненными регистрами, поэтому все, что вы оставляете в регистрах перед возвратом, сдувается. (Такой дизайн ОС позволяет обработчикам исключений использовать обычное соглашение о вызовах вместо сохранения/восстановления всех регистров.)   -  person Peter Cordes    schedule 27.10.2017
comment
Приношу извинения, если я могу сделать некоторую путаницу между 3DNow! и MMX. Игра называется Arthur's Knights: Tales of Chivalry и предназначена не только для ЦП AMD, но, по-видимому, это средство рендеринга по умолчанию для любого ЦП, который отвечает положительным флагом MMX на инструкцию CPUID с eax=1, как и любой другой. современный процессор тоже от Intel. По сути, набор инструкций поддерживается, но отключается операционной системой: можно запустить игру на том же процессоре, но с использованием другой ОС, например XP или Win95/98/ME.   -  person gho    schedule 30.10.2017
comment
Действительно? Вы уверены, что он не проверяет также какие-либо другие флаги CPUID? Это приведет к сбою на любом процессоре Intel с MMX, если это действительно все, на что он смотрит. Вы уверены, что он также не проверяет AuthenticAMD в строке поставщика? Но в любом случае, нельзя ли просто исправить его, чтобы он не обнаруживал MMX, чтобы он использовал резервный x87? Это будет быстрее, чем делать исключения для каждой математической инструкции в программном рендерере! Если вы хотите, чтобы это было быстрее, подумайте о том, чтобы переписать некоторые из горячих функций на C, чтобы современный компилятор мог использовать SSE2 (и, возможно, авто-векторизацию).   -  person Peter Cordes    schedule 30.10.2017
comment
Кстати, взлом игры предназначен для DxWnd и представляет для меня технический интерес. Забыл упомянуть, но обновление структуры ExtendedRegisters было первым вариантом, но это просто не сработало, изменения в ней не отразились на конечном значении регистров, поэтому я попытался форсировать результат с помощью инструкций movq.   -  person gho    schedule 30.10.2017
comment
Хорошо, хорошо IDK, как изменить регистры основной программы из обработчика исключений. Это, вероятно, не относится к MMX и что-то, что вы можете найти в документации MS.   -  person Peter Cordes    schedule 30.10.2017
comment
Он проверяет наличие AuthenticAMD, но независимо от того, что я возвращаю в CPUID с eax=0, он либо использует инструкции MMX, либо (к сожалению) использует неработающий рендерер. Другая игра той же компании была исправлена ​​простым исправлением ответа на ответ CPUID, но эта кажется более сложной (или глючной?)   -  person gho    schedule 30.10.2017
comment
Что касается использования CPUID, в сборке игры есть несколько инструкций, все они используются с eax=0 или eax=1. Также есть CPUID с eax=0x80000001, но на моем процессоре Intel он никогда туда не проходит. В игре также есть очевидные следы других трюков, чтобы отличить архитектуру процессора от 8086, i386 или i486, и разбросаны по разным, а также дублированным местам, в общем, это большой беспорядок, написанный до идеи, что ускорение должно было быть привязаны к графическим драйверам.   -  person gho    schedule 30.10.2017
comment
Хорошо, в своем вопросе вы упустили, что он проверяет AMD и что резервный вариант, отличный от MMX, содержит ошибки. Может быть, это работает только для меньших разрешений экрана или что-то с использованием целочисленной математики? Вы пробовали это на процессоре Intel (или, если вы используете виртуальную машину, которая позволяет взламывать возвращаемые значения CPUID, просто пытались указать не-AMD с помощью MMX)? OTOH, попробовать это вообще без виртуальной машины может быть хорошей идеей. IDK, если это вообще может помешать 3DNow; Я искал, но ничего не нашел о необходимости установки бита включения где-либо, так что IDK, как Win10 делает ловушку вашего процессора на PFMUL. (Это PFMUL, а не FPMUL)   -  person Peter Cordes    schedule 30.10.2017


Ответы (1)


В современных ОС, таких как Win7 - Win10, такие инструкции, как FPADD или FPMUL, не разрешены.

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

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


Если эта игра когда-либо работала только на оборудовании 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] требует выровненной по 16 байтам памяти и выполняет 16-байтовую загрузку. Так что вам, возможно, придется добавить пролив/перезагрузку, чтобы позаимствовать еще один регистр как временный).

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

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

PFSUBR легко эмулировать с помощью запасного регистра, просто скопируйте и subps реверсируйте. (Или SUBPS и инвертировать знак с помощью XORPS). PFRCPIT1 (первая итерация уточнения reciprocal-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/ (и другие ссылки в x86 пометить вики.

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

person Peter Cordes    schedule 27.10.2017
comment
Игра является устаревшей, Arthur's Knights: Tales of Chivalry, поэтому, кроме некоторых ограниченных исправлений сборки, я, очевидно, не могу переписать ее части, я не разработчик игры и у меня нет исходного кода. - person gho; 30.10.2017
comment
@gho: вы все равно можете заменить несколько функций, использующих 3DNow, своими собственными, которые работают точно так же, но с использованием регистров XMM. Не должно быть слишком плохо пройти через asm и просто локально изменить каждую инструкцию 3DNow на эквивалент SSE для пары функций. Общие инструкции могут быть заменены 1-в-1. - person Peter Cordes; 30.10.2017