Blog. Just Blog

Как на Ассемблере получить строку названия процессора

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Для получения различной информации о процессоре используется ассемблерная команда CPUID. С ее помощью можно, например, узнать марку процессора и набор поддерживаемых им функций. На старых процессорах инструкция CPUID отсутствовала, поэтому во всех руководствах рекомендуют сперва проверить, что процессор ее поддерживает. Конечно, сейчас придется немало напрячься, чтобы найти такие древние компьютеры для ваших приложений, но порядок есть порядок. Итак, чтобы удостоверится, что команда CPUID доступна, достаточно попытаться изменить 21-й бит расширенного регистра EFLAGS. Если бит успешно поменяется, то инструкция CPUID процессором поддерживается, если регистр флагов остался без изменений, то процессор команду CPUID не поддерживает.
  1.         ; Проверить, поддерживается ли команда CPUID
  2.         pushfd
  3.         pop     eax
  4.         ; Получить регистр флагов
  5.         mov     ebx,eax
  6.         ; Изменить 21-й бит в регистре флагов
  7.         xor     eax, 200000h
  8.         ; Сохранить и снова получить регистр флагов
  9.         push    eax
  10.         popfd
  11.         pushfd
  12.         pop     eax
  13.         ; Регистр флагов изменился?
  14.         cmp     eax,ebx
  15.         ; Нет, значит команда CPUID не поддерживается
  16.         je      cpuid_not_supported
При вызове инструкция CPUID по содержимому регистра EAX определяет какую информацию о процессоре необходимо вернуть. Вся информация, даже текстовая, возвращается в регистрах EAX, EBX, ECX и EDX. Поэтому, если их значения будут нужны для дальнейшей работы, то перед вызовом СPUID их надо сохранить. При EAX=0 CPUID возвращает в регистрах EBX, ECX и EDX идентификатор производителя процессора (Vendor ID) в виде 12 символов ASCII. В приведенной ниже табличке список самых распространенных идентификаторов производителей процессоров.

ASCII-строкаHEX-значения EBX:EDX:ECXПроизводитель
GenuineIntel756E6547:49656E69:6C65746EIntel
AuthenticAMD68747541:69746E65:444D4163AMD
CyrixInstead69727943:736E4978:64616574Cyrix
CentaurHauls746E6543:48727561:736C7561Centaur
SiS SiS SiS20536953:20536953:20536953SiS
NexGenDriven4778654E:72446E65:6E657669NexGen
GenuineTMx86756E6547:54656E69:3638784DTransmeta
RiseRiseRise65736952:65736952:65736952Rise
UMC UMC UMC20434D55:20434D55:20434D55UMC
Geode by NSC646F6547:79622065:43534E20National Semiconductor

Но это лишь общее название производителя, а нам надо получить полное наименование. Для этого надо последовательно вызвать команду CPUID с параметрами в EAX = 80000002h, 80000003h и 80000004h. На каждый запрос в регистрах EAX:EBX:ECX:EDX будет возвращаться фрагмент 48-символьной строки полного наименования процессора, например, что-то типа Intel(R) Core(TM) i7-3770K CPU @ 3.50GHz. Если строка короче 48 символов, то она будет дополняться пробелами, причем в разных местах. Для удобного получения строки названия процессора я написал вот такую функцию.
  1. ;-------------------------------------------------------------------
  2. ; Функция получения строки названия процессора
  3. ;-------------------------------------------------------------------
  4. ; Параметры:
  5. ;   lpBuff - указатель на строку-приемник
  6. ;   dFixString - надо ли чистить результат от лишних пробелов и
  7. ;            заменять строки (TM) и (R) на отдельные символы
  8. ;-------------------------------------------------------------------
  9. proc    GetCPUString lpBuff:DWORD, dFixString:DWORD
  10.         pusha
  11.  
  12.         ; Указатель на строку-приемник
  13.         mov     esi,[lpBuff]
  14.         mov     edi,esi
  15.         cld
  16.  
  17.         ; Прочитать информацию о процессоре
  18.         mov     eax,80000002h
  19. @@:
  20.         push    eax
  21.         cpuid
  22.         stosd
  23.         xchg    eax,ebx
  24.         stosd
  25.         xchg    eax,ecx
  26.         stosd
  27.         xchg    eax,edx
  28.         stosd
  29.         pop     eax
  30.         inc     eax
  31.         cmp     eax,80000004h
  32.         jbe     @b
  33.  
  34.         ; Привести строку к формату ASCIIZ
  35.         xor     eax,eax 
  36.         stosb
  37.  
  38.         ; Нужно ли чистить строку?
  39.         cmp     [dFixString],0
  40.         je      .loc_ret
  41.  
  42.         ; Почистить строку от лишних и неправильных символов
  43.         mov     edi,esi
  44. @@:
  45.         ; Убрать лидирующие пробелы, если они есть
  46.         cmp     byte [esi],' '
  47.         jne     @f
  48.         inc     esi
  49.         jmp     @b
  50. @@:
  51.         xor     ebx,ebx
  52. .loc_clean_string:
  53.         lodsb
  54.  
  55.         cmp     al,' '
  56.         jnz     @f
  57.  
  58.         ; Убрать повторяющиеся пробелы
  59.         cmp     bl,1
  60.         je      .loc_clean_string
  61.  
  62.         ; Установить флажок, что начались пробелы
  63.         mov     bl,1
  64.         jmp     .loc_store_char
  65. @@:
  66.         ; Сбросить флажок пробелов
  67.         mov     bl,0
  68.  
  69.         ; Преобразовать символы "(R)" в один
  70.         cmp     al,'('
  71.         jne     @f
  72.         cmp     word [esi],'R)'
  73.         jne     @f
  74.  
  75.         ; ESI + 2
  76.         lodsw
  77.         ; Символ (R)
  78.         mov     al,0AEh
  79.         jmp     .loc_store_char
  80. @@:
  81.         ; Преобразовать символы "(TM)" в один
  82.         cmp     dword [esi-1],'(TM)'
  83.         je      @f
  84.         ; Написание может быть различным
  85.         cmp     dword [esi-1],'(tm)'
  86.         jne     .loc_store_char
  87. @@:
  88.         ; ESI + 3
  89.         lodsw
  90.         lodsb
  91.         ; Символ (TM)
  92.         mov     al,099h
  93.  
  94. .loc_store_char:
  95.         ; Записать символ в строку
  96.         stosb
  97.         ; Конец строки достигнут?
  98.         or      al,al
  99.         jnz     .loc_clean_string
  100.  
  101.         ; Удалить финальный пробел если есть
  102.         cmp     byte [edi-1],' '
  103.         jne     .loc_ret
  104.         mov     byte [edi-1],0
  105.  
  106. .loc_ret:
  107.         popa
  108.         ret
  109. endp
Параметры вызова: lpBuff - указатель на строку-приемник в формате ASCIIZ, куда будет записано название процессора. Строка должна быть не менее 49 символов длиной с учетом финального нулевого символа. dFixString - заменять или нет в названии процессора текстовые строки "(TM)" и "(R)" на соответствующие символы, а также надо ли удалять из строки лишние пробелы (начальные, финальные, два и более пробелов подряд). Так, например, при вызове функции с параметром dFixString = TRUE, исходная строка Intel(R) Core(TM) i7 CPU 920 @ 2.67GHz будет заменена на красивую строчку Intel® Core™ i7 CPU 920 @ 2.67GHz.

В приложении пример программы с исходным текстом, показывающей Vendor ID и полное название вашего процессора. Файл "x86 Architecture CPUID Manual" - подробное описание всех параметров команды CPUID и расшифровкой возвращаемых ей значений (на английском).

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

CPUID.Demo.zip (3,164 bytes)

x86 Architecture CPUID Manual (ENG)x86 Architecture CPUID Manual (ENG)

x86.Architecture.CPUID.zip (31,980 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 12998 | Комментариев: 5

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
Андрей (16.05.2014 в 16:36):
ManHunter, сабж вполне наглядно отображает вехи развития x86.
ManHunter (16.05.2014 в 16:14):
DW 310Fh - это запись команды RDTSC, подробнее http://ru.wikipedia.org/wiki/Rdtsc

Код ставит процессу приоритет реального времени, замеряет количество тактов, делает паузу в 500 мс, снова замеряет количество тактов, считает разницу между начальным и конечным значением. Дальше простая математика,
(скорость процессора) = (разница в тактах) / (время паузы).
Doxtup (16.05.2014 в 16:05):
Мой кирпич i5 определил точно, годная утилита.
Вот нашел кодес, когда-то скомуниздил с какого-то форума, писал под windows 98, но работает и сейчас:
Сорри за много кодеса, если можете вкратце объяснить его суть ?

function GetCPUSpeed: Double;
const
DelayTime = 500;
var
TimerHi : DWORD;
TimerLo : DWORD;
PriorityClass : Integer;
Priority : Integer;
begin
PriorityClass := GetPriorityClass(GetCurrentProcess);
Priority := GetThreadPriority(GetCurrentThread);
SetPriorityClass(GetCurrentProcess, REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_TIME_CRITICAL);
Sleep(10);
asm
DW 310Fh
MOV TimerLo, EAX
MOV TimerHi, EDX
end;
Sleep(DelayTime);
asm
DW 310Fh
SUB EAX, TimerLo
SBB EDX, TimerHi
MOV TimerLo, EAX
MOV TimerHi, EDX
end;
SetThreadPriority(GetCurrentThread, Priority);
SetPriorityClass(GetCurrentProcess, PriorityClass);
Result := TimerLo / (1000.0 * DelayTime);
end;
ManHunter (16.05.2014 в 10:37):
Которое тут костыли?
Андрей (16.05.2014 в 09:26):
"Порядок есть порядок", костыли есть костыли.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.09 сек. / MySQL: 2 (0.0121 сек.) / Память: 4.5 Mb
Наверх