| ;**************************************************************************** |
| ;* |
| ;* 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 |