Metropoli BBS
VIEWER: cpu.asm MODE: TEXT (ASCII)
;****************************************************************************
;*
;*							  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
[ RETURN TO DIRECTORY ]