;**************************************************************************** ;* ;* The MegaToolbox ;* ;* Copyright (C) 1993 Kendall Bennett. ;* All rights reserved. ;* ;* Filename: $RCSfile: cpu.asm $ ;* Version: $Revision: 1.4 $ ;* ;* Language: 8086 Assembler ;* Environment: IBM PC (MS DOS) ;* ;* Description: Autodetection routine to determine the type of CPU installed ;* in the system. ;* ;* $Id: cpu.asm 1.4 1992/09/09 09:57:57 kjb Exp $ ;* ;* Revision History: ;* ----------------- ;* ;* $Log: cpu.asm $ ;* Revision 1.4 1992/09/09 09:57:57 kjb ;* Fixed to only check for emulation on > 80286 machines. ;* ;* Revision 1.3 1992/09/08 09:36:25 kjb ;* Added code to check for 80x87 emulation software. ;* ;* Revision 1.2 1992/04/13 18:53:43 kjb ;* Added support for detecting the numeric co-processor ;* ;* Revision 1.1 91/11/28 01:52:21 kjb ;* Initial revision ;* ;**************************************************************************** IDEAL INCLUDE "model.mac" ; Memory model macros header cpu ; Set up memory model ;**************************************************************************** ; ; Equates used by queryCPU routine. ; ;**************************************************************************** ; Central Processing Unit type codes CPU86 = 0 ; 8086/88 type processor CPU186 = 1 ; 80186 type processor CPU286 = 2 ; 80286 type processor CPU286p = 3 ; 80286 type processor in protected mode CPU386 = 4 ; 80386 type processor CPU386p = 5 ; 80386 type processor in protected mode CPU486 = 6 ; 80486 type processor CPU486p = 7 ; 80486 type processor in protected mode ; Floating Point Unit type codes FPUNONE = 0 ; No coprocessor present FPU87 = 1 ; 8087 coprocessor FPU287 = 2 ; 80287 coprocessor FPU387 = 3 ; 80387 coprocessor FPU487 = 4 ; 80487 coprocessor begcodeseg cpu ; Start of code segment P386 ; Enable all '386 instructions P387 ; Enable all '387 instructions ; Save the type of CPU detected so we can determine the co-processor ; type correctly. This means that we MUST call queryCpu() BEFORE calling ; queryFpu() to obtain correct results. cpu dw CPU86 ;---------------------------------------------------------------------------- ; cpu_type queryCpu(void) ;---------------------------------------------------------------------------- ; Determine type of processor present. ;---------------------------------------------------------------------------- procstart _queryCpu push bp ; We MUST save bp for initialization code... mov ax,CPU86 ; Default to 8086/8088 processor push sp pop bx ; BX holds the value of SP or SP-2 cmp bx,sp ; 88/86/186 pushes the value of SP-2 je @@Check286 ; Must be a 286/386/486 type processor mov cl,32 ; 186 uses count mod 32 = 0 shl bx,cl ; 86 shifts 32 bits left so ax = 0 jz @@Done ; zero: shifted out all bits so 86/88 mov ax,CPU186 ; nonzero: no shift, so 186 jz @@Done @@Check286: ; First check for 386/486 in 32 bit mode pushf ; Test for 16 or 32 operand size: mov bx,sp ; pushed 2 or 4 bytes of flags popf inc bx inc bx cmp bx,sp ; did pushf change sp by 2? jnz @@Check486 ; 32 bit push, so it is a 386/486 sub sp,6 ; Is it a 286/386/486 in 16 bit mode? mov bp,sp sgdt [QWORD ptr bp] ; 80286/386/486 specific instrucion add sp,4 ; Get global descriptor table pop bx inc bh ; Third word of GDT = -1 for 286 jnz @@Check486 ; We have a 386/486 mov ax,CPU286 ; We have a 286 jmp @@TestPROT @@Check486: ; Distinguish an 80386 from an 80486. Bit 18 (40000H) of EFLAGS register ; is used only in the 486. This code flips it and tests if anything happened. mov edx,esp ; Save stack pointer and esp,not 3 ; Align stack pointer to prevent a fault ; when we set the AC flag on a 486 pushfd ; Copy the EFLAGS register pop eax ; into register eax mov ecx,eax ; Save the original EFLAGS value xor eax,40000H ; Flip the AC flag bit push eax ; Try to put the modified value back popfd ; into the EFLAGS register pushfd ; Copy the EFLAGS register again pop eax ; into eax xor eax,ecx ; Compare the old and new AC bits shr eax,18 ; Shift and mask to get the AC comparison bit and eax,1 ; in the low order position of eax push ecx popfd ; Restore EFLAGS that were saved on entry mov esp,edx ; And restore stack pointer to saved value mov bx,ax ; and move into bx ; At this point ax = 0 for a 386, or ax = 1 for a 486 mov ax,CPU386 ; Assume a 386 test bx,bx jz @@TestPROT ; We have a 386 mov ax,CPU486 ; We have a 486 @@TestPROT: smsw cx ; protected? machine status -> cx ror cx,1 ; protection bit -> carry flag jnc @@Done ; Real mode if no carry inc ax ; Protected: return value + 1 @@Done: mov [cpu],ax ; Save CPU type in code segment variable pop bp ; Restore bp ret ; We are done procend _queryCpu ndp_cw dw ? ndp_sw dw ? ;---------------------------------------------------------------------------- ; fpu_type queryFpu(void) ;---------------------------------------------------------------------------- ; Determine type of floating point coprocessor present in the system. ; The idea is to determine whether or not the floating-point control word ; can be successfully read. If it cannot, then no coprocessor exists. ; If it can the correct coprocessor is then determined depending on the ; main CPU id. ;---------------------------------------------------------------------------- procstart _queryFpu push bp mov bx,FPUNONE ; Default to no FPU present ; The next two 80x87 instructions cannot carry the WAIT prefix, ; because there may not be an 80x87 for which to wait. The WAIT is ; therefore emulated with a MOV CX,<value> LOOP $ combination. mov [ndp_cw],0 ; Clear the control word in memory cli ; Interrupts must be off during test fninit ; reset NDP status word mov cx,2 ; Wait for co-pro to complete operation loop $ fnstcw [ndp_cw] ; Obtain the processor control word mov cx,14h ; Wait for co-pro to complete operation loop $ sti ; Re-enable interrupts ; We check to see that the precison control bits of the control word ; indicate 64 bit internal precision (bits 8 & 9 set) which is the default ; set up by the fninit instruction above. We also test that the exception ; masks are properly set. mov ax,[ndp_cw] ; AX := NDP control word and ax,033fh ; Mask out the precision control bits etc. cmp ax,033fh ; is the NDP present? jne @@Done ; No, we are all done... (must be a 3) ; Determine the type of NDP from the main CPU type mov bx,FPU87 ; Start with the 8087 NDP mov ax,[cpu] ; Get current cpu type cmp ax,CPU286 ; >= 80286 type processor? jge @@80286 ; Yes, check for 287/387/487 jmp @@Done ; No, we are done ; Now that we know we have a possible co-processor and the processor is ; at least an 80286, we can check to se if coprocessor emulation software ; is installed in the system. Some emulators such as FRANKIE.387 emulate ; the co-processor so well that the above checks believe a co-pro is ; actually out there. @@80286: smsw ax ; AX := machine status word test al,4 ; Check the EM bit status jnz @@Done ; Software emulation installed on INT 7! cmp ax,CPU386 ; Do we have a 386 or above? jge @@80386 ; Yes, check for it mov bx,FPU287 ; We have a 80287 co-pro jmp @@Done @@80386: cmp ax,CPU486 ; Do we have a 486 or above? jge @@80486 ; Yes, check for it ; The i386 processor can work with either an 80287 or 80387 co processor ; so we must check for that here. The 387 says that +inf <> -inf while ; the 287 says that they are the same. fld1 ; Load +1.0 onto NDP stack fldz ; Load +0.0 onto NDP stack fdiv ; do +1/0 (create +inf) fld1 ; Load +1.0 onto NDP stack fchs ; Change to -1.0 fldz ; Load +0.0 onto NDP stack fdiv ; do -1/0 (create -inf) fcompp ; compare and pop values from stack fstsw [ndp_sw] ; Get the status word from the co pro mov ax,[ndp_sw] ; AX := Status word and ah,41h ; Mask out C3 and C0 condition codes cmp ah,40h ; C3 = 1, C0 = 0 means ST(0) == ST(1) mov bx,FPU287 ; Set up for a 287 co pro je @@Done ; Yes, we were correct mov bx,FPU387 ; No, it was an 80387 jmp @@Done @@80486: mov bx,FPU487 ; We must have a 487 co pro. @@Done: mov ax,bx ; Return FPU type in AX pop bp ; Restore bp ret ; We are done procend _queryFpu endcodeseg cpu END ; End of module