::Главная страница :: С++/Си :: Статьи

Определение типа процессора

Материал взят с сайта "Мир программирования"

Согласитесь, есть много причин для того, чтобы вашей программе знать о типе процессора, на которой она работает. Например, ваша программа может использовать инструкции, работающие лишь на некоторых моделях процессоров, или в зависимости от типа процессора загружать соответствующую DLLс кодом, оптимизированным для этого процессора. В любом случае вы должны правильно определить производителя, модель, и, что более важно, поддерживаемые функции и особенности процессора. В этой статье описаны приемы и методы, с помощью которых вы легко (и главное, правильно) определите тип процессора и будет предложен класс C++ , который поможет вам в этом, реализуя набор функций, определяющих большинство практически интересующих параметров процессоров от производителя до тактовой частоты. Конечно, DLL - наиболее подходящее место для реализации этого класса, но это будет уже вашим заданием.

Как определяется процессор и функции, им поддерживаемые
Определение модели процессора и FPU (если FPU присутствует) на компьютерах до Intel 486 основывалось на определении архитектурных особенностей процессора, плюс с очень большой достоверностью можно было предполагать, что это процессор Intel. Определение процессора на компьютере, где установлена ОС Windows немного легче, чем обычно - процессор должен быть как минимум 386 из-за того, что Win32 работает только в защищенном режиме, поэтому код процедуры определения может быть полностью 32-битным. Поэтому в статье не рассматривается определение процессоров слабее 386. Тем из вас, кто все же хочет определить 8086/8088 процессоры можно сказать, что биты 12-15 регистра флагов FLAGS (вы еще помните 16-битные регистры?) в этих моделях всегда установлены , а на 286 в реальном режиме эти биты сброшены.

Различие между 386 и 486 процессорами заключается в том, что в 486 в регистре флагов EFLAGS есть бит выравнивания AC (бит 18). Бит был добавлен в 486 и не может быть установлен на 386 процессоре. Если вы выяснили, что работаете на 486 процессоре, не забудьте восстановить регистр EFLAGS. Это показано в следующем фрагменте кода:

pushfd
pop eax // читаем EFLAGS
mov ebx,eax // сохраняем EFLAGS
xor eax,40000h // переключаем бит AC в EFLAGS
push eax // сохраняем в стеке
popfd // загружаем в EFLAGS
pushfd // заталкиваем EFLAGS в стек
pop eax // опять читаем EFLAGS
xor eax,ebx // проверяем бит AC
mov CPU, 386 // процессор - 386
je End_Detect // Если бит сброшен - определяемый процессор - 386 и выход
mov CPU, 486 // процессор - 486
push ebx // восстанавливаем EFlags
popfd
..
.. проверяем более старшие версии CPU с помощью CPUID, и т.д.
..

В 486 процессоре впервые появилась сигнатура, из которой можно вычислить модель и степпинг процессора, отсюда же можно вычислить поддерживаемые функции. Единственная проблема состоит в том, что эта сигнатура записывается в регистр EDX после перезагрузки компьютера и теряется, если BIOS не сделает ее копию. Некотрые BIOS имеют возможность считать сигнатуру или сохраняют ее в определенном месте сегмента данных BIOS, однако это мало помогает, если наше приложение работает под Windows. Данные, которые можно извлечь из этой сигнатуры, следующие:

Формат сигнатуры, записываемой в регистр EDX

Степпинг: Биты 0 - 3 Семейство и модель процессора

Модель: Биты 4 - 7 Модель семейства, например, Pentium III

Семейство: Биты 8 - 11 Архитектурное семейство, все процессоры Pentium II и III семейства 6

Тип: Биты 12 - 13 Тип процессра - OEM или OverDrive и т.д.

Замечание: В статье не используется определение типа процессора по полю типа, т.к. Pentium Overdrive (Type 01) для процессоров Pentium идентифицируется по этому полю как процессоры OEM (Type 00), поэтому эти процессоры будут определяться как стандартные процессоры Pentium. Pentium Overdrive для процессоров 486 не имеют такой проблемы. 486 Pentium Overdrive определяются по их уникальному степпингу.

Самые поздние модели 486 процессоров решают проблему с сигнатурой с помощью новой инструкции CPUID, которая среди прочей информации возвращает сигнатуру процессора, что дает возможность в любое время определить его тип. Для определения, поддерживает ли исследуемый процессор инструкцию CPUID, попробуйте изменить бит ID регистра EFLAGS (бит 21). Если бит изменяет свое состояние, инструкция CPUID доступна. Очень важно сначала определить, что процессор не слабее 486, т.к. этот бит может быть изменен и на 386, который не поддерживает инструкцию CPUID, а попытка вызвать инструкцию вызовет исключение "Invalid opcode". Позже мы более подробно остановимся на инструкции CPUID, вам нужно лишь знать, что без этой инструкции и без сигнатуры, полученной после презагрузки, единственное, что мы можем определить - используется ли 386 или 486 процессор и ничего не можем сказать о его модели или степпинге.

Определение присутствия математического сопроцессора (FPU) в настоящий момент кажется несущественной, однако желательно всегда проверять присутсвие FPU. 486 процессор был первым процессором, имеющим встроенный FPU, и то только в моделях DX. Модель SX не имеет встроенного сопроцессора, но имеет возможность подключить внешний 80487 сопроцессор для операций с плавающей точкой. Поэтому даже если определен 486 процессор, то нет оснований предполагать, что есть FPU. Первым процессором, где можно с уверенностью предполагать присутствие FPU был Pentium.

Для приложения нет разницы, какой используется FPU - внешний или встроенный. Алгоритм определения ранних версий FPU основывается на чтении содержимого статусных регистров и регистров управления сопроцессора. Если получаемая информация неверна, то FPU отсутствует, иначе - если получены правильные данные - FPU присутствует. Код, приведенный в статье, определяет присутствие FPU - но не семейство сопроцессоров. Обычно семейство FPU определяется семейством процессора за исключением 386 процессора, который может использовать внешний сопроцессор 286 процессора, 80287, или собственный - 80387. Не думаю, что это может пригодиться для современных игр и приложений, но если нужно определить, с каким сопроцессором работает 386, нужно сравнить значения, определяющие бесконечность: в случае 80287 –inf = +inf, тогда как в 80387 - нет. Фрагмент кода, следующий ниже, определяет присутствие FPU:

fninit // сброс статусного слова FPU
mov [status], 5a5ah // инициализация промежуточного значения
fnstsw [status] // запомнить статусное слово FPU
mov ax,[status] // проверка статусного слова FPU
cmp al,0 // если получено корректное значение
jne NO_FPU_PRESENT

Приведенный фрагмент - минимум, что нужно сделать. Здесь определяется, можно ли читать из статусного регистра сопроцессора. Для получения более надежного результата значение, полученное при чтении, должно быть проверено. Код, приведенный ниже, показывает, как это сделать.

fnstcw [status] // сохранить управляющее слово FPU
mov ax,[status] // проверить управляющее слово FPU
and ax,103fh // выделить интересующие нас биты
cmp ax,3fh // проверка
jne NO_FPU_PRESENT
..
Сопроцессор присутствует, определить его семейство, если это необходимо
..

Если использовать инструкцию CPUID, определение сопроцессора становится намного легче. Для определение наличия FPU вам нужно проверить всего лишь значение одного бита. Также стало возможным другим производителям типа AMD или Cyrix идентифицировать свои процессоры, вместо того, чтобы сопоставлять их с аналогичными процессорами Intel. Действительно ли важно для данного приложения знать точную модель процессора зависит от уровня и степени оптимизации, а также от того, используются ли уникальные для этого типа процессоров регистры или команды. Если код будет использовать такие регистры или команды, то, естественно, необходимо точно знать производителя и модель процессора, на котором этот код работает. Необходимо также учитывать различия между процессорами одного семейства, но разных степпингов.

Для использования инструкции CPUID нужно указать функцию в регистре EAX. Результаты возвращаются в регистрах EAX, EBX, ECX, EDX. Первое, что нужно определить - какой диапазон функций поддерживает CPUID данного процессора. К счастью, существует документированный метод как это сделать, так что не нужно делать никаких предположений о производителе и модели процессора. Вы вызываете CPUID с EAX=0 и после выполнения инструкции EAX содержит максимальный номер поддерживаемой CPUID функции. Функция CPUID 0 часто используется для определения производителя процессора. EBX, EDX, ECX (обратите внимание на последовательность) после выполнения этой функции содержит 12-байтную строку, уникальную для каждого производителя, которая на данный момент выглядит так:

Производитель Строка
Intel GenuineIntel
AMD AuthenticAMD
Cyrix CyrixInstead
IDT CentaurHauls

Функция CPUID 1 используется для определения сигнатуры и особенностей процессора; она поддерживается всеми процессорами, способными выполнить инструкцию CPUID. Оставшиеся функции поддерживаются только процессорами Intel, что, конечно, может измениться в будущем. Эти "Intel-only" функции возвращают информацию о размере кэша, в случае Pentium III так же можно определить серийный номер процессора. В статье не будет приведено полное описание инструкции CPUID, т.к. она полностью документирована каждым производителем процессоров, ссылки на сайты которых приведены в конце статьи. Следующая табличка содержит краткие сведения об инструкции:

Функция Описание Поддержка EAX EBX ECX EDX
0 Signature/
Vendor Sting ВСЕ процессоры с CPUID Highest CPUID Vendor 0-3 Vendor 8-11 Vendor 4-7
1 Features ВСЕ процессоры с CPUID Signature Reserved Reserved Feature Set
2 Configuration Intel Family 6 Config Data Config Data Config Data Config Data
3 Serial Number Intel Pentium III Reserved Reserved Lower 32 bits Middle 32 bits

AMD поддерживает инструкцию CPUID начиная с AMD486 DX4, и сначала поддерживались только две функции: Vendor String и Features. AMD изменила инструкцию CPUID с введением процессора K5 - были введены дополнительные функции для определения информации, которую нельзя получить с помощью стандартной инструкции CPUID. Из-за того, что номера расширенных функций могли совпасть с используемыми фирмой Intel, компания AMD добавила функции начиная с кода 0x80000000. Расширенные функции работают так же, как и стандартные, и как в случае со стандартными функциями вы вызываете CPUID 0x80000000 для получения диапазона поддерживаемых расширенных функций. Эти функции очень важно, т.к. это единственная возможность определить поддержку 3DNow!. В следующей таблице приведен список расширенных функций, введенных компанией AMD:

Функция Описание Поддержка EAX EBX ECX EDX
80000000 Extended CPUID Range ALL Highest extended CPUID Reserved Reserved Reserved
80000001 Extended Features ALL Extended Signature Reserved Reserved Extended Features
80000002 Name 0 ALL Name Name Name Name
80000003 Name 1 ALL Name Name Name Name
80000004 Name 2 ALL Name Name Name Name
80000005 Level 1 Cache ALL Reserved TLB Info L1 D Cache L1 I Cache
80000006 Level 2 Cache K6III, K7 Reserved Reserved L2 Cache Reserved

Расширенные функции могут поддерживаться другими производителями. Во время написания статьи, все процессоры, поддерживающие AMD 3DNow! поддерживают и набор расширенных функций AMD, приведенных выше. Intel не поддерживает расширенные функции, и, к сожалению, не существует документированного способа определения поддержки расширенного набора функций по модели процессора. На процессорах Intel функции CPUID ограничены диапазоном функций, возвращаемым функцией CPUID 0, что также недокументировано. Так что если максимальный номер функции CPUID - 2, то функция CPUID 0x80000000 вернет то же, что и функция 2.

Другим способом поддержки расширенных функций является вызов функции CPUID 0x80000000, после чего EAX должен содержать максимальный номер расширенной функции. Результат должен быть больше 0x80000000 - не равен ему. Если результат равен 0x80000000, проверьте, изменились ли другие регистры. Наконец, поместите код проверки в блок try/except на случай возникновения исключительной ситуации "Invalid opcode". После успешного окончания всех этих тестов вы можете быть уверены, что расширенные функции поддерживаются.

Инструкция CPUID поддерживается ассемблером Visual C++ начиная с версии 5.0, а компилятор Intel поддерживал ее всегда. Visual C++ 4.x может поместить байты опкода (0f, a2) напрямую в поток инструкций с помощью следующего макроса C/C++.Аналогичный макрос можно написать для ранних версий MASM.

Макро C/C++:

#define CPUID _asm _emit 0x0f _asm _emit 0xa2

Макро MASM:

CPUID MACRO
db 0fh
db 0a2h
ENDM

Для большинства приложений необходимо знать о возможностях и особенностях процессора, а не о его модели. Эти возможности НЕОБХОДИМО определить. Предполагать что-нибудь о возможностях и особенностях процесссора исходя из его модели - очень плохая практика. Например, очень много игр, использующих инструкции MMX не работали на процессорах AMD K6 из-за того, что программа проверяла только поддержку MMX на процессорах Intel (т.е. если обнаруживался AMD, никаких других проверок не проводилось). Если процессор поддерживает MMX, то это может быть Pentium, Pentium II, Pentium III или даже AMD.

Чтобы облегчить проблемы определения типа процессора, разработанный класс содержит набор флагов для идентификации различных наборов инструкций. С помощью этих флагов вы можете очень просто определить поддержку процессором инструкций 3DNow! или Pentium III SIMD, не зная ничего о модели или производителе процессора. Эта информация возвращается в старших битах члена класса get_features(); определены следующие константы: CPU_PENTIUM, CPU_PENTIUMPRO, CPU_3DNOW and CPU_SIMD. Функция, приведенная ниже, показывает пример проверки, которая необходима, если ваше приложение использует инструкции 3DNow!. Эта функция возвратит true, если процессор способен выполнить инструкции 3DNow!. Иначе, функция вернет false и ваше приложение должно сообщить пользователю, что дальнейшая работа невозможна. Заметьте, что в функции не делается проверка на наличие процессора AMD, т.к. эта проверка вызовет ошибки на процессорах IDT WinChip2.

 

bool will_execute_3DNow()
{


ProcessorDetect proc;
Uint32_t features;
if (!proc.get_features(0, &features))
{

// произошла ошибка, так что для безопасности возвращаем false
return false;

}

// возвращаем состояние флага 3Dnow
return (features & CPU_3DNOW) != 0;

}

И чтобы уж совсем быть уверенным в том, что ваш код будет работать на данной машине, нужно провести аналогичный тест для каждого процессора системы (в случае многопроцессорной конфигурации).

Далее следует список рекомендаций, который поможет вам написать код, работающий на большинстве платформ, как будущих, так и уже существующих. Некоторые из пунктов этого списка будут обсуждены далее.

Не полагайтесь на исключение "Invalid opcode" при определении особенностей процессора. Недокументированные инструкции могут присутствовать в одних моделях процессоров, и тот же опкод в последующих версиях может быть использован абсолютно для других целей. Все особенности новых процессоров имеют способы определения, обычно с помощью инструкции CPUID.
Не используйте недокументированные или тестовые возможности процессоров, что относится также и к недокументированным регистрам и флагам. Обычно такие функции убираются вообще или модифицируются в последующих моделях процессоров, что не отражается в сигнатуре процессора.
Не делайте никаких предположений о наборе инструкций процессора. Например, не предполагайте, что процессор Pentium имеет FPU. Также нельзя делать никаких предположений о том, например, что Pentium II поддерживает все возможности предыдущих процессоров - к примеру, Pentium Pro.
Не делайте никаких предположений о скорости процессора, исходя из модели и производителя процессора и не используйте циклы для измерения или получения временных интервалов. Вместо этого используйте таймер RDTSC для калибровки и критических ко времени участков кода.

Тематические ссылки
Ваша ссылка Ваша ссылка

Обмен кнопками, ведение статистики, реклама.