LOCALS

.MODEL	SMALL


cpuid	MACRO
	DB	0Fh, 0A2h		;код инструкции cpuid (идентификация
					;процессора)
ENDM


;*******************************************************************************
;Среда сопроцессора
;*******************************************************************************
FPU_ENV16	STRUC
	fenv_wCW	DW	?
	fenv_wSW	DW	?
	fenv_wTW	DW	?
	fenv_fpCode	DD	?
	fenv_fpData	DD	?
FPU_ENV16	ENDS

BRAND_ENTRY	STRUC
	be_bBrandID	DB	?
	be_pszCPUType	DW	?
BRAND_ENTRY	ENDS


.DATA

PUBLIC	bCPU_Family
PUBLIC	bCPU_Model
PUBLIC	bCPU_Vendor
PUBLIC	dwCPUID_MaxNum
PUBLIC	dwCPU_Signature
PUBLIC	dwCPU_Features_EBX
PUBLIC	dwCPU_Features_ECX
PUBLIC	dwCPU_Features_EDX

;*******************************************************************************
;Таблицы данных для определения тактовой частоты
;*******************************************************************************
Intel_Data_Table	DW	1, 2A60h, 1, 2A60h, 2, 0CA2h, 0Ah, 3F48h
			DW	0Ah, 40A6h, 14h, 860Eh, 14h, 78D7h
AMD_Data_Table		DW	1, 2A60h, 1, 2A60h, 2, 0CA2h, 0Ah, 3F48h
			DW	0Ah, 40A6h, 14h, 9493h, 14h, 4416h
Cyrix_Data_Table	DW	1, 2A60h, 1, 2A60h, 2, 0CA2h, 0Ah, 3F48h
			DW	0Ah, 40A6h, 14h, 9A15h, 14h, 4509h
NexGen_Data_Table	DW	1, 2A60h, 1, 2A60h, 2, 0CA2h, 0Ah, 3F48h
			DW	0Ah, 40A6h, 14h, 7673h, 14h, 7A56h
Speed_Data_MaxIndex	=	($-Intel_Data_Table)/16-1

;*******************************************************************************
;Таблица тактовых частот
;*******************************************************************************
CPU_Speed_Table		DW	4, 8, 12, 16, 20, 25, 33, 40, 50, 60, 66
			DW	75, 90, 100, 120, 133, 150, 166, 180, 200
			DW	233, 266, 300, 333, 350, 366, 400, 433, 450
			DW	466, 500, 533, 550, 566, 600, 633, 650, 667
			DW	700, 733, 750, 800, 850, 866, 900, 933, 950
			DW	1000, 1500
CPU_Speed_Table_Count	=	($-CPU_Speed_Table)/2	;количество тактовых
							;частот в таблице

bCPU_Family		DB	0	;семейство процессора
bCPU_Model		DB	0	;модель процессора
bCPU_Vendor		DB	0	;фирма-изготовитель процессора
dwCPUID_MaxNum		DD	0	;максимальный номер функции cpuid
dwCPU_Signature		DD	0	;сигнатура процессора
dwCPU_Features_EBX	DD	0	;характеристики процессора
dwCPU_Features_ECX	DD	0
dwCPU_Features_EDX	DD	0

szCPU_V20		DB	'V20',0
szCPU_V30		DB	'V30',0
szCPU_8088		DB	'8088',0
szCPU_8086		DB	'8086',0
szCPU_80188		DB	'80188',0
szCPU_80186		DB	'80186',0
szCPU_80286		DB	'80286',0
szCPU_80386SX		DB	'80386SX',0
szCPU_80386DX		DB	'80386DX',0
szCPU_486		DB	'486',0
szCPU_486DX_DX2		DB	'486DX или 486DX2',0
szCPU_486DX		DB	'486DX',0
szCPU_486SX		DB	'486SX',0
szCPU_486DX2		DB	'486DX2',0
szCPU_486SL		DB	'486SL',0
szCPU_486SX2		DB	'486SX2',0
szCPU_486DX2_WB		DB	'486DX2(WB)',0
szCPU_486DX4		DB	'486DX4',0
szCPU_Pentium		DB	'Pentium',0
szCPU_Pentium_MMX	DB	'Pentium MMX',0
szCPU_Pentium_Pro	DB	'Pentium Pro',0
szCPU_Pentium_II	DB	'Pentium II',0
szCPU_Celeron		DB	'Celeron',0
szCPU_Xeon_II		DB	'Pentium II Xeon',0
szCPU_PentiumXeon_II	DB	'Pentium II или Pentium II Xeon',0
szCPU_PentiumXeon_III	DB	'Pentium III или Pentium III Xeon',0
szCPU_Pentium_III	DB	'Pentium III',0
szCPU_Xeon_III		DB	'Pentium III Xeon',0
szCPU_Pentium_4		DB	'Pentium 4',0
szCPU_Intel_Unknown	DB	'Intel Неизвестный',0
szCPU_Am5x86		DB	'Am5x86',0
szCPU_AMD_K5		DB	'AMD-K5',0
szCPU_AMD_K6		DB	'AMD-K6',0
szCPU_AMD_K6_2		DB	'AMD-K6-2',0
szCPU_AMD_K6_III	DB	'AMD-K6-III',0
szCPU_AMD_Athlon	DB	'AMD Athlon',0
szCPU_AMD_Duron		DB	'AMD Duron',0
szCPU_AMD_AthlonDuron	DB	'AMD Athlon или Duron',0
szCPU_AMD_Unknown	DB	'AMD Неизвестный',0
szCPU_Cyrix_486		DB	'Cyrix 486',0
szCPU_6x86		DB	'6x86',0
szCPU_6x86MX		DB	'6x86MX',0
szCPU_Cyrix_Unknown	DB	'Cyrix Неизвестный',0
szCPU_Nx586		DB	'Nx586',0
szCPU_Unknown		DB	'Неизвестный',0

GetCPUTypeStrProcs	DW	OFFSET GetUnknownCPUStr
			DW	OFFSET GetIntelCPUTypeStr
			DW	OFFSET GetAMDCPUTypeStr
			DW	OFFSET GetCyrixCPUTypeStr
			DW	OFFSET GetNexGenCPUTypeStr

i486_Types		DW	OFFSET szCPU_486DX
			DW	OFFSET szCPU_486DX
			DW	OFFSET szCPU_486SX
			DW	OFFSET szCPU_486DX2
			DW	OFFSET szCPU_486SL
			DW	OFFSET szCPU_486SX2
			DW	OFFSET szCPU_486
			DW	OFFSET szCPU_486DX2_WB
			DW	OFFSET szCPU_486DX4
			DW	OFFSET szCPU_486
			DW	OFFSET szCPU_486
			DW	OFFSET szCPU_486
			DW	OFFSET szCPU_486
			DW	OFFSET szCPU_486
			DW	OFFSET szCPU_486
			DW	OFFSET szCPU_486

Brand_Table		BRAND_ENTRY	<1, OFFSET szCPU_Celeron>
			BRAND_ENTRY	<2, OFFSET szCPU_Pentium_III>
			BRAND_ENTRY	<3, OFFSET szCPU_Xeon_III>
			BRAND_ENTRY	<8, OFFSET szCPU_Pentium_4>
Brand_Table_Count	=	($-Brand_Table)/SIZE BRAND_ENTRY


.CODE

PUBLIC	CPU_ID
PUBLIC	CPU_Speed
PUBLIC	FPUx87_ID
PUBLIC	GetCPUTypeStr

.8086

;*******************************************************************************
;CPU_ID		определяет тип процессора
;*******************************************************************************
;Использует:	нет
;
;Вызов:		нет
;
;Возврат:	AL = семейство процессора
;		AH = модель процессора
;
;Изменяемые
;  регистры:	EAX,EBX,ECX,EDX,Flags
;*******************************************************************************
CPU_ID		PROC

		mov	[bCPU_Vendor],1	;фирма-изготовитель - Intel

;Данный процессор - 8086?
		push	sp
		pop	ax
		cmp	ax,sp		;если AX<>SP, то данный процессор - 8086
		mov	al,0		;тип процессора - 8086
		jne	@@JmpExit

;Данный процессор - 80286?
		mov	ah,0F2h		;устанавливаем старшую тетраду регистра
					;флагов в 1
		push	ax
		popf
		pushf
		pop	ax		;AX = новое значение FLAGS
		test	ah,0F0h		;если старшая тетрада регистра флагов
					;установлена в 0, то данный
					;процессор - 80286
		mov	al,2		;тип процессора - 80286
		jz	@@JmpExit

;Данный процессор - 80386?
.386
		mov	dx,sp		;сохраняем указатель стека
		and	sp,NOT 3	;выравниваем указатель стека для
					;предотвращения исключения при установке
					;флага AC
		pushfd			;копируем регистр EFLAGS в регистр EAX
		pop	eax
		mov	ebx,eax		;сохраняем начальное значение
					;регистра EFLAGS
		xor	eax,40000h	;переключаем бит AC
		push	eax		;изменяем EFLAGS
		popfd
		pushfd			;получаем новое значение EFLAGS
		pop	eax
		push	ebx		;восстанавливаем регистр EFLAGS
		popfd
		mov	sp,dx		;восстанавливаем указатель стека
		xor	eax,ebx		;сравниваем старое и новое значения
					;EFLAGS
		test	eax,40000h	;бит 18 = 1  -  данный процессор 486
					;бит 18 = 0  -  данный процессор 80386
		jnz	@@486_or_higher

;Данный процессор - Intel 80386 или Nx586?
		mov	ax,5555h
		xor	dx,dx
		mov	cx,2
		div	cx		;если ZF=1, то данный процессор - Nx586
					;если ZF=0, то данный
					;процессор - Intel 80386
		mov	al,3		;тип процессора - Intel 80386
		jnz	@@JmpExit

		mov	[bCPU_Vendor],4	;фирма-изготовитель - NexGen
		mov	al,5		;тип процессора - 586

@@JmpExit:
.8086
		jmp	@@Exit

@@486_or_higher:
;Проверка работоспособности команды процессора cpuid
.386
		pushfd			;копируем регистр EFLAGS в регистр EAX
		pop	eax
		mov	ebx,eax		;сохраняем начальное значение EFLAGS
		xor	eax,200000h	;переключаем бит ID
		push	eax		;изменяем EFLAGS
		popfd
		pushfd			;получаем новое значение EFLAGS
		pop	eax
		push	ebx		;восстанавливаем регистр EFLAGS
		popfd
		xor	eax,ebx		;сравниваем старое и новое значения
					;EFLAGS
		test	eax,200000h	;бит 21 = 1  -  команда cpuid работает
					;бит 21 = 0  -  команда cpuid не работает
		jz	@@Cyrix486

;Определение фирмы-изготовителя
		xor	eax,eax		;EAX=0 (получить ID фирмы-изготовителя)
		cpuid			;EAX = максимальный номер подфункции
					;инструкции cpuid
					;EBX:EDX:ECX - идентификационная строка
					;фирмы-изготовителя
		mov	[dwCPUID_MaxNum],eax
		or	eax,eax		;если EAX=0, то характеристики процессора
					;не определены
		jnz	@@Use_cpuid

@@Cyrix486:
;Данный процессор - Cyrix 486 или Intel 486?
		xor	ax,ax
		sahf
		mov	ax,5
		mov	bl,2
		div	bl
		lahf			;если AH=2, то данный процессор - Cyrix
					;если AH<>2, то данный
					;процессор - Intel 486
		cmp	ah,2
		mov	al,4		;тип процессора - 486
		jne	@@Cyrix486_Exit

		mov	[bCPU_Vendor],3	;фирма-изготовитель - Cyrix

@@Cyrix486_Exit:
		jmp	@@Exit

@@Use_cpuid:
;Фирма-изготовитель - Intel?
		cmp	ebx,'uneG'
		jne	@@AMD
		cmp	edx,'Ieni'
		jne	@@AMD
		cmp	ecx,'letn'
		je	@@GetSignature

@@AMD:
;Фирма-изготовитель - AMD?
		mov	[bCPU_Vendor],2	;фирма-изготовитель - AMD
		cmp	ebx,'htuA'
		jne	@@Cyrix
		cmp	edx,'itne'
		jne	@@Cyrix
		cmp	ecx,'DMAc'
		je	@@GetSignature

@@Cyrix:
;Фирма-изготовитель - Cyrix?
		mov	[bCPU_Vendor],3	;фирма-изготовитель - Cyrix
		cmp	ebx,'iryC'
		jne	@@Unknown
		cmp	edx,'snIx'
		jne	@@Unknown
		cmp	ecx,'daet'
		je	@@GetSignature

@@Unknown:
;Фирма-изготовитель - неизвестная?
		mov	[bCPU_Vendor],0	;фирма-изготовитель - неизвестная

@@GetSignature:
;Определение характеристик процессора с помощью команды процессора cpuid
		xor	eax,eax
		inc	eax		;EAX=1 (получить сигнатуру процессора)
		cpuid			;EAX = сигнатура процессора
					;EBX (биты 0-7) = Brand ID
					;EDX = характеристики процессора
		mov	[dwCPU_Signature],eax
		mov	[dwCPU_Features_EBX],ebx
		mov	[dwCPU_Features_ECX],ecx
		mov	[dwCPU_Features_EDX],ebx

		xchg	al,ah
		and	al,0Fh		;AL = семейство процессора
		shr	ah,4		;AH = модель процессора
		mov	[bCPU_Model],ah

@@Exit:
.8086
		mov	[bCPU_Family],al

		ret

CPU_ID		ENDP

;*******************************************************************************
;CPU_Speed_Rough определяет приближенное значение тактовой частоты процессора
;*******************************************************************************
;Использует:	нет
;
;Вызов:		нет
;
;Возврат:	AX = тактовая частота процессора
;
;Изменяемые
;  регистры:	AX,BX,CX,DX,Flags
;*******************************************************************************
CPU_Speed_Rough	PROC

		push	si		;сохраняем регистр SI в стеке

		mov	bl,[bCPU_Family]	;BL = семейство процессора

		mov	al,[bCPU_Vendor]	;AL = фирма-изготовитель
						;процессора

		cmp	al,2		;фирма-изготовитель - AMD?
		jne	@@Check_Index
		cmp	[bCPU_Model],5	;модель процессора <= 5?
		jbe	@@Check_Index
		inc	bl

@@Check_Index:	cmp	bl,Speed_Data_MaxIndex	;индекс не выше максимального?
		jbe	@@Get_Table_Offs
		mov	bl,Speed_Data_MaxIndex

@@Get_Table_Offs:
		xor	bh,bh
		shl	bx,1
		shl	bx,1		;BX = смещение в таблице с данными
					;для определения тактовой частоты

		mov	si,OFFSET AMD_Data_Table
		cmp	al,2		;фирма-изготовитель - AMD?
		je	@@Get_Data_Offs

		mov	si,OFFSET Cyrix_Data_Table
		cmp	al,3		;фирма-изготовитель - Cyrix?
		je	@@Get_Data_Offs

		mov	si,OFFSET NexGen_Data_Table
		cmp	al,4		;фирма-изготовитель - NexGen?
		je	@@Get_Data_Offs

		mov	si,OFFSET Intel_Data_Table

@@Get_Data_Offs:add	si,bx		;DS:SI -> количество циклов

;Инициализация канала 2 таймера
		mov	al,0B0h		;AL=10110000b (управляющий байт таймера)
					;канал 2, запись/чтение сначала
					;младшего, а затем старшего байта
					;счетчика, режим 0, двоичный счетчик
		out	43h,al		;устанавливаем режимы работы канала
		jmp	SHORT $+2	;задержка ввода/вывода
		jmp	SHORT $+2
		mov	al,0FFh		;устанавливаем начальное значение
					;счетчика 0FFFFh
		out	42h,al		;загружаем младший байт счетчика
		jmp	SHORT $+2	;задержка ввода/вывода
		jmp	SHORT $+2
		out	42h,al		;загружаем старший байт счетчика
		jmp	SHORT $+2	;задержка ввода/вывода
		jmp	SHORT $+2

		cli

;Запрет немаскируемых прерываний (NMI)
		mov	al,80h		;AL=10000000b (номер регистра RT/CMOS)
					;бит 7 = 1 - запрет NMI
		out	70h,al		;запрещаем NMI
		jmp	SHORT $+2	;задержка ввода/вывода
		jmp	SHORT $+2
		in	al,71h		;считываем значение регистра RT/CMOS
		jmp	SHORT $+2	;задержка ввода/вывода
		jmp	SHORT $+2

;Разрешение работы канала 2 таймера
		in	al,61h		;AL = значение управляющего порта
		jmp	SHORT $+2	;задержка ввода/вывода
		jmp	SHORT $+2
		push	ax		;сохраняем исходное значение управляющего
					;порта в стеке
		or	al,1		;открываем канал 2 таймера
		out	61h,al		;устанавливаем новое значение управляющего
					;порта

;Измерение времени выполнения цикла
		xor	dx,dx		;DX=0
		mov	bx,1		;BX = делитель на 1
		lodsw			;AX = начальное значение счетчика цикла

@@Time_Count_Loop_1:
		mov	cx,10h

@@Time_Count_Loop_2:
		div	bx
		div	bx
		div	bx
		div	bx
		div	bx
		div	bx
		div	bx
		div	bx
		div	bx
		div	bx
		div	bx
		div	bx
		div	bx
		div	bx
		loop	@@Time_Count_Loop_2

		dec	ax
		jne	@@Time_Count_Loop_1

;Запрет работы канала 2 таймера
		pop	ax		;восстанавливаем исходное значение
					;управляющего порта
		out	61h,al		;закрываем канал 2 таймера

;Разрешение немаскируемых прерываний (NMI)
		xor	al,al		;AL=0 (номер регистра RT/CMOS)
					;бит 7 = 0 - разрешение NMI
		out	70h,al		;разрешаем NMI
		jmp	SHORT $+2	;задержка ввода/вывода
		jmp	SHORT $+2
		in	al,71h		;считываем значение регистра RT/CMOS

		sti

;Чтение счетчика канала 2 таймера
		mov	al,80h		;AL=10000000b - команда защелкивания
					;выходного регистра счетчика канала 2
		out	43h,al		;защелкиваем выходной регистр счетчика
		jmp	SHORT $+2	;задержка ввода/вывода
		jmp	SHORT $+2
		in	al,42h		;считываем младший байт счетчика
		jmp	SHORT $+2	;задержка ввода/вывода
		jmp	SHORT $+2
		mov	bl,al
		in	al,42h		;считываем старший байт счетчика
		mov	bh,al		;BX = значение счетчика
		not	bx		;BX = количество тактов генератора
					;(время выполнения цикла)

;Расчет приближенного значения тактовой частоты процессора
		lodsw			;AX = (число тактов в цикле * частота
					;генератора в МГц)/4 =
					;= (число тактов в цикле * 1,190МГц)/4
		xor	dx,dx		;DX=0
		shl	ax,1		;умножение DX:AX на 4 (сдвиг влево на
		rcl	dx,1		;на 2 разряда)
		shl	ax,1
		rcl	dx,1
		cmp	dx,bx
		jb	@@Div_by_Time

		xor	ax,ax		;AX=0
		jmp	@@Exit

@@Div_by_Time:	div	bx		;AX = приближенное значение тактовой
					;частоты процессора

@@Exit:
;Восстановление состояния канала 2 таймера по умолчанию и выход
		push	ax		;сохраняем значение тактовой частоты
					;процессора в стеке

		mov	al,0B6h		;AL=10110110b (управляющий байт таймера)
					;канал 2, запись/чтение сначала
					;младшего, а затем старшего байта
					;счетчика, режим 3, двоичный счетчик
		out	43h,al		;устанавливаем режимы работы канала
		jmp	SHORT $+2	;задержка ввода/вывода
		jmp	SHORT $+2
		mov	al,0		;устанавливаем начальное значение
					;счетчика 400h
		out	42h,al		;загружаем младший байт счетчика
		jmp	SHORT $+2	;задержка ввода/вывода
		jmp	SHORT $+2
		mov	al,4
		out	42h,al		;загружаем старший байт счетчика
		jmp	SHORT $+2	;задержка ввода/вывода
		jmp	SHORT $+2

		pop	ax		;AX = приближенное значение тактовой
					;частоты процессора

		pop	si		;восстанавливаем регистр SI

		ret

CPU_Speed_Rough	ENDP

;*******************************************************************************
;CPU_Speed	определяет тактовую частоту процессора
;*******************************************************************************
;Использует:	CPU_Speed_Rough
;
;Вызов:		нет
;
;Возврат:	AX = тактовая частота процессора
;
;Изменяемые
;  регистры:	AX,BX,CX,DX,Flags
;*******************************************************************************
CPU_Speed	PROC

		push	es		;сохраняем регистры в стеке
		push	di

		xor	bx,bx		;используем BX для суммирования значений
					;тактовой частоты
		mov	cx,4		;определяем 4 раза тактовую частоту
					;процессора

@@Sum_Loop:	push	bx		;сохраняем текущую сумму тактовых частот
		push	cx		;сохраняем счетчик цикла

		call	CPU_Speed_Rough	;AX = приближенное значение тактовой
					;частоты процессора

		pop	cx		;восстанавливаем счетчик цикла
		pop	bx		;восстанавливаем текущую сумму тактовых
					;частот

		add	bx,ax		;прибавляем полученное значение
					;тактовой частоты
		loop	@@Sum_Loop

		shr	bx,1
		shr	bx,1		;BX = усредненное значение тактовой
					;частоты

		mov	ax,bx
		sub	ax,2		;AX = начальное значение тактовой
					;частоты для поиска в таблице
		mov	cx,5		;5 попыток поиска тактовой частоты
					;в таблице

		push	ds
		pop	es		;ES = сегмент данных

		cld

@@Search_Loop:	mov	di,OFFSET CPU_Speed_Table	;ES:DI -> таблица
							;тактовых частот

		push	cx			;сохраняем счетчик цикла
		mov	cx,CPU_Speed_Table_Count;CX = количество тактовых частот
						;в таблице
		repne	scasw			;ищем текущее значение тактовой
						;частоты в таблице
						;если находим, то выход из цикла
		pop	cx			;восстанавливаем счетчик цикла
		je	@@Exit

		inc	ax		;увеличиваем на единицу значение
					;тактовой частоты
		loop	@@Search_Loop

		mov	ax,bx		;восстанавливаем рассчитанное значение
					;тактовой частоты процессора

@@Exit:		pop	di		;восстанавливаем регистры
		pop	es

		ret

CPU_Speed	ENDP

.8087

;*******************************************************************************
;FPUx87_ID	определяет тип сопроцессора (80287, 80387, 486 FPU)
;*******************************************************************************
;Использует:	нет
;
;Вызов:		нет
;
;Возврат:	AL = тип сопроцессора (x)
;
;Изменяемые
;  регистры:	EAX,ECX,Flags
;*******************************************************************************
FPUx87_ID	PROC

		push	bp
		mov	bp,sp		;BP=SP

;Проверка наличия сопроцессора
		finit			;инициализация сопроцессора
		mov	ax,5A5Ah
		push	ax
		mov	cx,0Ch
@@Wait_Loop_1:	loop	@@Wait_Loop_1	;цикл ожидания
		fstcw	WORD PTR [bp-2]	;сохраняем слово управления сопроцессора
		mov	cl,0FFh
@@Wait_Loop_2:	loop	@@Wait_Loop_2	;цикл ожидания
		pop	ax		;AX = слово управления сопроцессора
		and	ax,1F3Fh
		cmp	ax,33Fh		;если AX<>33Fh, то сопроцессор
					;не установлен
		jne	@@NoMathCo

		mov	ax,5A5Ah
		push	ax
		fstsw	WORD PTR [bp-2]	;сохраняем слово состояния сопроцессора
		mov	cl,64h
@@Wait_Loop_3:	loop	@@Wait_Loop_3	;цикл ожидания
		pop	ax
		test	ax,0B8BFh	;если в слове состояния сопроцессора
					;установлен любой бит за исключением
					;6,8,9,10 и 14-го битов, то сопроцессор
					;не установлен
		jz	@@Ok

@@NoMathCo:	xor	al,al
		jmp	@@Exit

@@Ok:
;Данный сопроцессор - 8087/80287 или выше?
		finit			;режим бесконечности по умолчанию:
					;  проективный для 8087/80287,
					;  афинный для 80387
		fld1			;образовать бесконечность
		fldz
		fdiv
		fld	st		;образовать отрицательную бесконечность
		fchs
		fcompp			;сравнить -бесконечность и +бесконечность
		push	ax
		fstsw	WORD PTR [bp-2]	;сопроцессоры 8087/80287 покажут равенство
		pop	ax
		sahf
		mov	al,2		;тип сопроцессора - 80287
		jz	@@Exit

;Данный сопроцессор - 80387 или FPU 486?
.386
		finit			;инициализация сопроцессора
		push	eax
		push	ecx
		fstenv	[bp-SIZE FPU_ENV16]	;сохраняем среду сопроцессора
						;если FPU 486, указатели
						;команды и данных сброшены в 0
						;если 80387, то указатели
						;команды и данных не изменены
		pop	ecx
		pop	eax
		or	eax,ecx		;указатели команды и данных равны 0?
		mov	al,3		;тип сопроцессора - 80387
		jnz	@@Exit

		mov	al,4		;тип сопроцессора - FPU процессора 486

@@Exit:
.8086
		pop	bp

		ret

FPUx87_ID	ENDP

;*******************************************************************************
;GetCPUTypeStr	возвращает смещение строки с типом процессора
;*******************************************************************************
;Использует:	CPU_ID
;
;Вызов:		нет
;
;Возврат:	DX = смещение ASCIIZ-строки с типом процессора
;
;Изменяемые
;  регистры:	EAX,EBX,ECX,EDX,Flags
;*******************************************************************************
GetCPUTypeStr	PROC

		call	CPU_ID		;AL = тип процессора
					;AH = модель процессора
		xchg	al,ah
		mov	bl,[bCPU_Vendor];BL = фирма-изготовитель процессора
		xor	bh,bh
		shl	bx,1
		jmp	[GetCPUTypeStrProcs+bx]

GetCPUTypeStr	ENDP

;*******************************************************************************
;GetIntelCPUTypeStr возвращает смещение строки с типом процессора Intel
;*******************************************************************************
;Использует:	нет
;
;Вызов:		AH = семейство процессора
;		AL = модель процессора
;
;Возврат:	DX = смещение ASCIIZ-строки с типом процессора
;
;Изменяемые
;  регистры:	EAX,EBX,ECX,EDX,Flags
;*******************************************************************************
GetIntelCPUTypeStr	PROC

		mov	dx,WORD PTR [dwCPUID_MaxNum]
		or	dx,WORD PTR [dwCPUID_MaxNum+2]
		jz	@@Not_Use_cpuid
		jmp	@@Use_cpuid

@@Not_Use_cpuid:
		cmp	ah,1
		ja	@@286

;Определение процессоров V20/V30, 8088/8086, 80188/80186
		push	es		;сохраняем регистры в стеке
		push	di

		mov	ax,0FFFFh
		mov	cl,21h
		shr	ax,cl		;если AX<>0, то данный
					;процессор - 80188/80186
		mov	dx,OFFSET szCPU_80188
		mov	bx,OFFSET szCPU_80186
		jnz	@@8_or_16

		mov	ax,14h
		aam	10h
		aad	10h		;если AX=14h, то данный
					;процессор - 8088/8086
		mov	dx,OFFSET szCPU_8088
		mov	bx,OFFSET szCPU_8086
		cmp	ax,14h
		je	@@8_or_16

		mov	dx,OFFSET szCPU_V20
		mov	bx,OFFSET szCPU_V30

@@8_or_16:	mov	BYTE PTR cs:[Modified_byte],0F9h

		mov	al,0FBh
		mov	di,OFFSET cs:Modified_data
		mov	cx,3
		push	cs
		pop	es

		clc
		std
		cli

		rep	stosb

		cld
		cld
		cld
		cld
Modified_byte:	stc
Modified_data:	cld			;CF=1 - 16-разрядный процессор
					;CF=0 - 8-разрядный процессор
		jnc	@@8086_Exit

		mov	dx,bx

@@8086_Exit:	pop	di		;восстанавливаем регистры
		pop	es

		ret

@@286:		cmp	ah,2
		jne	@@386

;Процессор - 80286
		mov	dx,OFFSET szCPU_80286
		ret

@@386:		cmp	ah,3
		jne	@@486

;Определение процессоров 80386SX и 80386DX
.386P
		cli

		mov	eax,cr0		;копируем регистр управления CR0
					;в регистр EAX
		mov	edx,eax		;сохраняем начальное значение
					;регистра CR0
		xor	al,10h		;переключаем флаг ET
		mov	cr0,eax		;изменяем регистр CR0
		mov	eax,cr0		;получаем новое значение регистра CR0
		mov	cr0,edx		;восстанавливаем регистр CR0

		sti

		xor	al,dl		;сравниваем старое и новое значения
					;регистра CR0
		test	al,10h		;бит 4 = 1 - данный процессор 80386DX
					;бит 4 = 0 - данный процессор 80386SX
		mov	dx,OFFSET szCPU_80386SX
		jz	@@386_Exit

		mov	dx,OFFSET szCPU_80386DX

@@386_Exit:
.8086
		ret

@@486:
;Определение процессоров 486SX, 486DX/DX2
		call	FPUx87_ID	;AL = тип сопроцессора
		cmp	al,4		;тип сопроцессора - ниже 486 FPU?
		mov	dx,OFFSET szCPU_486SX
		jb	@@486_Exit

		mov	dx,OFFSET szCPU_486DX_DX2

@@486_Exit:	ret

@@Use_cpuid:
;Определение типа процессора с помощью инструкции cpuid
		cmp	ah,4		;семейство процессора = 4?
		jne	@@Pentium

		mov	bl,al
		xor	bh,bh
		shl	bx,1
		mov	dx,[i486_Types+bx]
		ret

@@Pentium:	cmp	ah,5		;семейство процессора = 5?
		jne	@@P6

		mov	dx,OFFSET szCPU_Pentium_MMX
		cmp	al,4		;модель процессора = 4?
		je	@@Pentium_Exit

		mov	dx,OFFSET szCPU_Pentium

@@Pentium_Exit:	ret

@@P6:		cmp	ah,6		;семейство процессора = 6?
		jne	@@Brand_Detect

		mov	dx,OFFSET szCPU_Pentium_Pro
		cmp	al,1		;модель процессора = 1?
		je	@@Exit

		mov	dx,OFFSET szCPU_Pentium_II
		cmp	al,3		;модель процессора = 3?
		je	@@Exit

		cmp	al,5		;модель процессора = 5?
		jb	@@Unknown
		je	@@Celeron_Xeon
		cmp	al,7		;модель процессора = 7?
		jne	@@Celeron_Model6

@@Celeron_Xeon:
;Определение процессоров Pentium II, Pentium II Xeon, Celeron (модель 5),
;Pentium III, Pentium III Xeon (модель 7) по значениям дескрипторов кэша
.386
		push	di

		mov	eax,2		;EAX=2 (получить информацию о кэше и
					;TLB процессора)
		cpuid			;EAX:EBX:ECX:EDX - дескрипторы кэша

		mov	di,15
@@Cache_Descr_Loop:
		shrd	eax,ebx,8	;AL = дескриптор кэша
		shrd	ebx,ecx,8
		shrd	ecx,edx,8
		shr	edx,8

		cmp	al,40h		;нет кэша L2?
		je	@@Celeron
		cmp	al,44h		;кэш L2 >= 1Мб?
		jae	@@Xeon
		dec	di
		jnz	@@Cache_Descr_Loop

		mov	dx,OFFSET szCPU_PentiumXeon_II
		mov	bx,OFFSET szCPU_PentiumXeon_III
		jmp	@@Model5_or_7

@@Xeon: 	mov	dx,OFFSET szCPU_Xeon_II
		mov	bx,OFFSET szCPU_Xeon_III

@@Model5_or_7:	cmp	[bCPU_Model],5	;модель процессора = 5?
		je	@@Celeron_Xeon_Exit

		mov	dx,bx
		jmp	@@Celeron_Xeon_Exit

@@Celeron:	mov	dx,OFFSET szCPU_Celeron

@@Celeron_Xeon_Exit:
		pop	di
.8086
		ret

@@Celeron_Model6:
		mov	dx,OFFSET szCPU_Celeron
		cmp	al,6		;модель процессора = 6?
		je	@@Exit

@@Brand_Detect:
;Определение типа процессора по Brand ID
		mov	al,BYTE PTR [dwCPU_Features_EBX]	;AL = Brand ID
		or	al,al
		jz	@@Unknown

		mov	bx,OFFSET Brand_Table
		mov	cx,Brand_Table_Count

@@Find_BrandID_Loop:
		mov	dx,[(BRAND_ENTRY PTR bx).be_pszCPUType]
		cmp	al,[(BRAND_ENTRY PTR bx).be_bBrandID]
		je	@@Exit
		add	bx,SIZE BRAND_ENTRY
		loop	@@Find_BrandID_Loop

@@Unknown:	mov	dx,OFFSET szCPU_Intel_Unknown

@@Exit:		ret

GetIntelCPUTypeStr	ENDP

;*******************************************************************************
;GetAMDCPUTypeStr возвращает смещение строки с типом процессора AMD
;*******************************************************************************
;Использует:	нет
;
;Вызов:		AH = семейство процессора
;		AL = модель процессора
;
;Возврат:	DX = смещение ASCIIZ-строки с типом процессора
;
;Изменяемые
;  регистры:	DX,Flags
;*******************************************************************************
GetAMDCPUTypeStr	PROC

		mov	dx,OFFSET szCPU_Am5x86
		cmp	ah,4		;семейство процессора = 4?
		je	@@Exit

		cmp	ah,5		;семейство процессора = 5?
		jne	@@Family6

		mov	dx,OFFSET szCPU_AMD_K5
		cmp	al,3		;модель процессора <= 3?
		jbe	@@Exit

		mov	dx,OFFSET szCPU_AMD_K6
		cmp	al,6		;модель процессора = 6?
		je	@@Exit
		cmp	al,7		;модель процессора = 7?
		je	@@Exit

		mov	dx,OFFSET szCPU_AMD_K6_2
		cmp	al,8		;модель процессора = 8?
		je	@@Exit

		mov	dx,OFFSET szCPU_AMD_K6_III
		cmp	al,9		;модель процессора = 9?
		je	@@Exit

		jmp	@@Unknown

@@Family6:	cmp	ah,6		;семейство процессора = 6?
		jne	@@Unknown

		mov	dx,OFFSET szCPU_AMD_Athlon
		cmp	al,1		;модель процессора = 1?
		je	@@Exit
		cmp	al,2		;модель процессора = 2?
		je	@@Exit
		cmp	al,4		;модель процессора = 4?
		je	@@Exit

		mov	dx,OFFSET szCPU_AMD_Duron
		cmp	al,3		;модель процессора = 3?
		je	@@Exit

		mov	dx,OFFSET szCPU_AMD_AthlonDuron
		cmp	al,6		;модель процессора = 6?
		je	@@Exit
		cmp	al,7		;модель процессора = 7?
		je	@@Exit

@@Unknown:	mov	dx,OFFSET szCPU_AMD_Unknown

@@Exit:		ret

GetAMDCPUTypeStr	ENDP

;*******************************************************************************
;GetCyrixCPUTypeStr возвращает смещение строки с типом процессора Cyrix
;*******************************************************************************
;Использует:	нет
;
;Вызов:		AH = семейство процессора
;		AL = модель процессора
;
;Возврат:	DX = смещение ASCIIZ-строки с типом процессора
;
;Изменяемые
;  регистры:	DX,Flags
;*******************************************************************************
GetCyrixCPUTypeStr	PROC

		mov	dx,OFFSET szCPU_Cyrix_486
		cmp	ah,4		;семейство процессора = 4?
		je	@@Exit

		mov	dx,OFFSET szCPU_6x86
		cmp	ah,5		;семейство процессора = 5?
		je	@@Exit

		mov	dx,OFFSET szCPU_6x86MX
		cmp	ah,6		;семейство процессора = 6?
		je	@@Exit

		mov	dx,OFFSET szCPU_Cyrix_Unknown

@@Exit:		ret

GetCyrixCPUTypeStr	ENDP

;*******************************************************************************
;GetNexGenCPUTypeStr возвращает смещение строки с типом процессора NexGen
;*******************************************************************************
;Использует:	нет
;
;Вызов:		AH = семейство процессора
;		AL = модель процессора
;
;Возврат:	DX = смещение ASCIIZ-строки с типом процессора
;
;Изменяемые
;  регистры:	DX,Flags
;*******************************************************************************
GetNexGenCPUTypeStr	PROC

		mov	dx,OFFSET szCPU_Nx586
		ret

GetNexGenCPUTypeStr	ENDP

;*******************************************************************************
;GetUnknownCPUStr возвращает смещение строки для неизвестного процессора
;*******************************************************************************
;Использует:	нет
;
;Вызов:		нет
;
;Возврат:	DX = смещение ASCIIZ-строки для неизвестного процессора
;
;Изменяемые
;  регистры:	DX,Flags
;*******************************************************************************
GetUnknownCPUStr	PROC

		mov	dx,OFFSET szCPU_Unknown
		ret

GetUnknownCPUStr	ENDP


END
