blob: 60ebed713ffc785c773eb62a9b157c4a390b9ec8 [file] [log] [blame]
;****************************************************************************
;*
;* SciTech OS Portability Manager Library
;*
;* ========================================================================
;*
;* The contents of this file are subject to the SciTech MGL Public
;* License Version 1.0 (the "License"); you may not use this file
;* except in compliance with the License. You may obtain a copy of
;* the License at http://www.scitechsoft.com/mgl-license.txt
;*
;* Software distributed under the License is distributed on an
;* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
;* implied. See the License for the specific language governing
;* rights and limitations under the License.
;*
;* The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
;*
;* The Initial Developer of the Original Code is SciTech Software, Inc.
;* All Rights Reserved.
;*
;* ========================================================================
;*
;* Language: NASM or TASM Assembler
;* Environment: Intel 32 bit Protected Mode.
;*
;* Description: Code to determine the Intel processor type.
;*
;****************************************************************************
IDEAL
include "scitech.mac"
header _cpuinfo
begdataseg _cpuinfo ; Start of data segment
cache_id db "01234567890123456"
intel_id db "GenuineIntel" ; Intel vendor ID
cyrix_id db "CyrixInstead" ; Cyrix vendor ID
amd_id db "AuthenticAMD" ; AMD vendor ID
idt_id db "CentaurHauls" ; IDT vendor ID
CPU_IDT EQU 01000h ; Flag for IDT processors
CPU_Cyrix EQU 02000h ; Flag for Cyrix processors
CPU_AMD EQU 04000h ; Flag for AMD processors
CPU_Intel EQU 08000h ; Flag for Intel processors
enddataseg _cpuinfo
begcodeseg _cpuinfo ; Start of code segment
ifdef USE_NASM
%macro mCPU_ID 0
db 00Fh,0A2h
%endmacro
else
MACRO mCPU_ID
db 00Fh,0A2h
ENDM
endif
ifdef USE_NASM
%macro mRDTSC 0
db 00Fh,031h
%endmacro
else
MACRO mRDTSC
db 00Fh,031h
ENDM
endif
;----------------------------------------------------------------------------
; bool _CPU_check80386(void)
;----------------------------------------------------------------------------
; Determines if we have an i386 processor.
;----------------------------------------------------------------------------
cprocstart _CPU_check80386
enter_c
xor edx,edx ; EDX = 0, not an 80386
mov bx, sp
ifdef USE_NASM
and sp, ~3
else
and sp, not 3
endif
pushfd ; Push original EFLAGS
pop eax ; Get original EFLAGS
mov ecx, eax ; Save original EFLAGS
xor eax, 40000h ; Flip AC bit in EFLAGS
push eax ; Save new EFLAGS value on
; stack
popfd ; Replace current EFLAGS value
pushfd ; Get new EFLAGS
pop eax ; Store new EFLAGS in EAX
xor eax, ecx ; Can't toggle AC bit,
; processor=80386
jnz @@Done ; Jump if not an 80386 processor
inc edx ; We have an 80386
@@Done: push ecx
popfd
mov sp, bx
mov eax, edx
leave_c
ret
cprocend
;----------------------------------------------------------------------------
; bool _CPU_check80486(void)
;----------------------------------------------------------------------------
; Determines if we have an i486 processor.
;----------------------------------------------------------------------------
cprocstart _CPU_check80486
enter_c
; Distinguish between the i486 and Pentium by the ability to set the ID flag
; in the EFLAGS register. If the ID flag is set, then we can use the CPUID
; instruction to determine the final version of the chip. Otherwise we
; simply have an 80486.
; Distinguish between the i486 and Pentium by the ability to set the ID flag
; in the EFLAGS register. If the ID flag is set, then we can use the CPUID
; instruction to determine the final version of the chip. Otherwise we
; simply have an 80486.
pushfd ; Get original EFLAGS
pop eax
mov ecx, eax
xor eax, 200000h ; Flip ID bit in EFLAGS
push eax ; Save new EFLAGS value on stack
popfd ; Replace current EFLAGS value
pushfd ; Get new EFLAGS
pop eax ; Store new EFLAGS in EAX
xor eax, ecx ; Can not toggle ID bit,
jnz @@1 ; Processor=80486
mov eax,1 ; We dont have a Pentium
jmp @@Done
@@1: mov eax,0 ; We have Pentium or later
@@Done: leave_c
ret
cprocend
;----------------------------------------------------------------------------
; bool _CPU_checkClone(void)
;----------------------------------------------------------------------------
; Checks if the i386 or i486 processor is a clone or genuine Intel.
;----------------------------------------------------------------------------
cprocstart _CPU_checkClone
enter_c
mov ax,5555h ; Check to make sure this is a 32-bit processor
xor dx,dx
mov cx,2h
div cx ; Perform Division
clc
jnz @@NoClone
jmp @@Clone
@@NoClone:
stc
@@Clone:
pushfd
pop eax ; Get the flags
and eax,1
xor eax,1 ; EAX=0 is probably Intel, EAX=1 is a Clone
leave_c
ret
cprocend
;----------------------------------------------------------------------------
; bool _CPU_haveCPUID(void)
;----------------------------------------------------------------------------
; Determines if we have support for the CPUID instruction.
;----------------------------------------------------------------------------
cprocstart _CPU_haveCPUID
enter_c
ifdef flatmodel
pushfd ; Get original EFLAGS
pop eax
mov ecx, eax
xor eax, 200000h ; Flip ID bit in EFLAGS
push eax ; Save new EFLAGS value on stack
popfd ; Replace current EFLAGS value
pushfd ; Get new EFLAGS
pop eax ; Store new EFLAGS in EAX
xor eax, ecx ; Can not toggle ID bit,
jnz @@1 ; Processor=80486
mov eax,0 ; We dont have CPUID support
jmp @@Done
@@1: mov eax,1 ; We have CPUID support
else
mov eax,0 ; CPUID requires 32-bit pmode
endif
@@Done: leave_c
ret
cprocend
;----------------------------------------------------------------------------
; uint _CPU_checkCPUID(void)
;----------------------------------------------------------------------------
; Determines the CPU type using the CPUID instruction.
;----------------------------------------------------------------------------
cprocstart _CPU_checkCPUID
enter_c
xor eax, eax ; Set up for CPUID instruction
mCPU_ID ; Get and save vendor ID
cmp eax, 1 ; Make sure 1 is valid input for CPUID
jl @@Fail ; We dont have the CPUID instruction
xor eax,eax ; Assume vendor is unknown
; Check for GenuineIntel processors
LEA_L esi,intel_id
cmp [DWORD esi], ebx
jne @@NotIntel
cmp [DWORD esi+4], edx
jne @@NotIntel
cmp [DWORD esi+8], ecx
jne @@NotIntel
mov eax,CPU_Intel ; Flag that we have GenuineIntel
jmp @@FoundVendor
; Check for CyrixInstead processors
@@NotIntel:
LEA_L esi,cyrix_id
cmp [DWORD esi], ebx
jne @@NotCyrix
cmp [DWORD esi+4], edx
jne @@NotCyrix
cmp [DWORD esi+8], ecx
jne @@NotCyrix
mov eax,CPU_Cyrix ; Flag that we have CyrixInstead
jmp @@FoundVendor
; Check for AuthenticAMD processors
@@NotCyrix:
LEA_L esi,amd_id
cmp [DWORD esi], ebx
jne @@NotAMD
cmp [DWORD esi+4], edx
jne @@NotAMD
cmp [DWORD esi+8], ecx
jne @@NotAMD
mov eax,CPU_AMD ; Flag that we have AuthenticAMD
jmp @@FoundVendor
; Check for CentaurHauls processors
@@NotAMD:
LEA_L esi,idt_id
cmp [DWORD esi], ebx
jne @@NotIDT
cmp [DWORD esi+4], edx
jne @@NotIDT
cmp [DWORD esi+8], ecx
jne @@NotIDT
mov eax,CPU_IDT ; Flag that we have AuthenticIDT
jmp @@FoundVendor
@@NotIDT:
@@FoundVendor:
push eax
xor eax, eax
inc eax
mCPU_ID ; Get family/model/stepping/features
and eax, 0F00h
shr eax, 8 ; Isolate family
and eax, 0Fh
pop ecx
or eax,ecx ; Combine in the clone flag
@@Done: leave_c
ret
@@Fail: xor eax,eax
jmp @@Done
cprocend
;----------------------------------------------------------------------------
; uint _CPU_getCPUIDModel(void)
;----------------------------------------------------------------------------
; Determines the CPU type using the CPUID instruction.
;----------------------------------------------------------------------------
cprocstart _CPU_getCPUIDModel
enter_c
xor eax, eax ; Set up for CPUID instruction
mCPU_ID ; Get and save vendor ID
cmp eax, 1 ; Make sure 1 is valid input for CPUID
jl @@Fail ; We dont have the CPUID instruction
xor eax, eax
inc eax
mCPU_ID ; Get family/model/stepping/features
and eax, 0F0h
shr eax, 4 ; Isolate model
@@Done: leave_c
ret
@@Fail: xor eax,eax
jmp @@Done
cprocend
;----------------------------------------------------------------------------
; uint _CPU_getCPUIDStepping(void)
;----------------------------------------------------------------------------
; Determines the CPU type using the CPUID instruction.
;----------------------------------------------------------------------------
cprocstart _CPU_getCPUIDStepping
enter_c
xor eax, eax ; Set up for CPUID instruction
mCPU_ID ; Get and save vendor ID
cmp eax, 1 ; Make sure 1 is valid input for CPUID
jl @@Fail ; We dont have the CPUID instruction
xor eax, eax
inc eax
mCPU_ID ; Get family/model/stepping/features
and eax, 00Fh ; Isolate stepping
@@Done: leave_c
ret
@@Fail: xor eax,eax
jmp @@Done
cprocend
;----------------------------------------------------------------------------
; uint _CPU_getCPUIDFeatures(void)
;----------------------------------------------------------------------------
; Determines the CPU type using the CPUID instruction.
;----------------------------------------------------------------------------
cprocstart _CPU_getCPUIDFeatures
enter_c
xor eax, eax ; Set up for CPUID instruction
mCPU_ID ; Get and save vendor ID
cmp eax, 1 ; Make sure 1 is valid input for CPUID
jl @@Fail ; We dont have the CPUID instruction
xor eax, eax
inc eax
mCPU_ID ; Get family/model/stepping/features
mov eax, edx
@@Done: leave_c
ret
@@Fail: xor eax,eax
jmp @@Done
cprocend
;----------------------------------------------------------------------------
; uint _CPU_getCacheSize(void)
;----------------------------------------------------------------------------
; Determines the CPU cache size for Intel processors
;----------------------------------------------------------------------------
cprocstart _CPU_getCacheSize
enter_c
xor eax, eax ; Set up for CPUID instruction
mCPU_ID ; Get and save vendor ID
cmp eax,2 ; Make sure 2 is valid input for CPUID
jl @@Fail ; We dont have the CPUID instruction
mov eax,2
mCPU_ID ; Get cache descriptors
LEA_L esi,cache_id ; Get address of cache ID (-fPIC aware)
shr eax,8
mov [esi+0],eax
mov [esi+3],ebx
mov [esi+7],ecx
mov [esi+11],edx
xor eax,eax
LEA_L esi,cache_id ; Get address of cache ID (-fPIC aware)
mov edi,15
@@ScanLoop:
cmp [BYTE esi],41h
mov eax,128
je @@Done
cmp [BYTE esi],42h
mov eax,256
je @@Done
cmp [BYTE esi],43h
mov eax,512
je @@Done
cmp [BYTE esi],44h
mov eax,1024
je @@Done
cmp [BYTE esi],45h
mov eax,2048
je @@Done
inc esi
dec edi
jnz @@ScanLoop
@@Done: leave_c
ret
@@Fail: xor eax,eax
jmp @@Done
cprocend
;----------------------------------------------------------------------------
; uint _CPU_have3DNow(void)
;----------------------------------------------------------------------------
; Determines the CPU type using the CPUID instruction.
;----------------------------------------------------------------------------
cprocstart _CPU_have3DNow
enter_c
mov eax,80000000h ; Query for extended functions
mCPU_ID ; Get extended function limit
cmp eax,80000001h
jbe @@Fail ; Nope, we dont have function 800000001h
mov eax,80000001h ; Setup extended function 800000001h
mCPU_ID ; and get the information
test edx,80000000h ; Bit 31 is set if 3DNow! present
jz @@Fail ; Nope, we dont have 3DNow support
mov eax,1 ; Yep, we have 3DNow! support!
@@Done: leave_c
ret
@@Fail: xor eax,eax
jmp @@Done
cprocend
;----------------------------------------------------------------------------
; ulong _CPU_quickRDTSC(void)
;----------------------------------------------------------------------------
; Reads the time stamp counter and returns the low order 32-bits
;----------------------------------------------------------------------------
cprocstart _CPU_quickRDTSC
mRDTSC
ret
cprocend
;----------------------------------------------------------------------------
; void _CPU_runBSFLoop(ulong interations)
;----------------------------------------------------------------------------
; Runs a loop of BSF instructions for the specified number of iterations
;----------------------------------------------------------------------------
cprocstart _CPU_runBSFLoop
ARG iterations:ULONG
push _bp
mov _bp,_sp
push _bx
mov edx,[iterations]
mov eax,80000000h
mov ebx,edx
ALIGN 4
@@loop: bsf ecx,eax
dec ebx
jnz @@loop
pop _bx
pop _bp
ret
cprocend
;----------------------------------------------------------------------------
; void _CPU_readTimeStamp(CPU_largeInteger *time);
;----------------------------------------------------------------------------
; Reads the time stamp counter and returns the 64-bit result.
;----------------------------------------------------------------------------
cprocstart _CPU_readTimeStamp
mRDTSC
mov ecx,[esp+4] ; Access directly without stack frame
mov [ecx],eax
mov [ecx+4],edx
ret
cprocend
;----------------------------------------------------------------------------
; ulong _CPU_diffTime64(CPU_largeInteger *t1,CPU_largeInteger *t2,CPU_largeInteger *t)
;----------------------------------------------------------------------------
; Computes the difference between two 64-bit numbers.
;----------------------------------------------------------------------------
cprocstart _CPU_diffTime64
ARG t1:DPTR, t2:DPTR, t:DPTR
enter_c
mov ecx,[t2]
mov eax,[ecx] ; EAX := t2.low
mov ecx,[t1]
sub eax,[ecx]
mov edx,eax ; EDX := low difference
mov ecx,[t2]
mov eax,[ecx+4] ; ECX := t2.high
mov ecx,[t1]
sbb eax,[ecx+4] ; EAX := high difference
mov ebx,[t] ; Store the result
mov [ebx],edx ; Store low part
mov [ebx+4],eax ; Store high part
mov eax,edx ; Return low part
ifndef flatmodel
shld edx,eax,16 ; Return in DX:AX
endif
leave_c
ret
cprocend
;----------------------------------------------------------------------------
; ulong _CPU_calcMicroSec(CPU_largeInteger *count,ulong freq);
;----------------------------------------------------------------------------
; Computes the value in microseconds for the elapsed time with maximum
; precision. The formula we use is:
;
; us = (((diff * 0x100000) / freq) * 1000000) / 0x100000)
;
; The power of two multiple before the first divide allows us to scale the
; 64-bit difference using simple shifts, and then the divide brings the
; final result into the range to fit into a 32-bit integer.
;----------------------------------------------------------------------------
cprocstart _CPU_calcMicroSec
ARG count:DPTR, freq:ULONG
enter_c
mov ecx,[count]
mov eax,[ecx] ; EAX := low part
mov edx,[ecx+4] ; EDX := high part
shld edx,eax,20
shl eax,20 ; diff * 0x100000
div [DWORD freq] ; (diff * 0x100000) / freq
mov ecx,1000000
xor edx,edx
mul ecx ; ((diff * 0x100000) / freq) * 1000000)
shrd eax,edx,20 ; ((diff * 0x100000) / freq) * 1000000) / 0x100000
ifndef flatmodel
shld edx,eax,16 ; Return in DX:AX
endif
leave_c
ret
cprocend
;----------------------------------------------------------------------------
; ulong _CPU_mulDiv(ulong a,ulong b,ulong c);
;----------------------------------------------------------------------------
; Computes the following with 64-bit integer precision:
;
; result = (a * b) / c
;
;----------------------------------------------------------------------------
cprocstart _CPU_mulDiv
ARG a:ULONG, b:ULONG, c:ULONG
enter_c
mov eax,[a]
imul [ULONG b]
idiv [ULONG c]
ifndef flatmodel
shld edx,eax,16 ; Return in DX:AX
endif
leave_c
ret
cprocend
endcodeseg _cpuinfo
END