page	75, 110

;--------------------------------------------------------------------
;
;	SYSID.ASM
;
;	Version 4.4
;
;	Two subprograms used by SYSID.PAS:
;
;		CPUID		- identifies host CPU and NDP (if
;					any)
;		DISKREAD	- reads absolute sectors from disk
;
;	Steve Grant
;	Long Beach, CA
;	January 13, 1989
;
;--------------------------------------------------------------------

.286P
.8087

	public	CPUID, DISKREAD

CODE	segment	byte

;	Conditional jumps are all coded with the SHORT qualifier in
;	order to minimize the size of the .OBJ file output of Turbo
;	Assembler.

;--------------------------------------------------------------------

CPUID	proc	near

assume	cs:CODE, ds:DATA, es:nothing, ss:nothing

;	On entry:
;
;		BP
;	SP =>	near return address
;		offset  of a cpu_info_t record
;		segment "  "     "        "
;
;	On exit, the cpu_info_t record has been filled in as follows:
;
;		byte	= CPU type
;		word	= Machine Status Word
;		6 bytes	= Global Descriptor Table
;		6 bytes	= Interrupt Descriptor Table
;		boolean	= segment register change/interrupt flag
;		byte	= NDP type
;		word	= NDP control word

mCPU	equ	byte ptr [bx]
mMSW	equ	word ptr [bx + 1]
mGDT	equ	byte ptr [bx + 3]
mIDT	equ	byte ptr [bx + 9]
mchkint	equ	byte ptr [bx + 15]
mNDP	equ	byte ptr [bx + 16]
mNDPCW	equ	word ptr [bx + 17]

f8088	equ	0
f8086	equ	1
fV20	equ	2
fV30	equ	3
f80188	equ	4
f80186	equ	5
f80286	equ	6
f80386	equ	7
funk	=	0FFH

false	equ	0
true	equ	1

	push	bp
	mov	bp,sp
	push	ds
	lds	bx,[bp + 4]
	call	cpu
	call	chkint
	call	ndp
	pop	ds
	pop	bp
	ret	4

;--------------------------------------------------------------------

cpu:

; interrupt of multi-prefix string instruction

	sti
	mov	cx,0FFFFH
rep	lods	byte ptr es:[si]
	jcxz	short cpu_02
	call	piq
	cmp	dx,4
	jne	short cpu_01
	mov	mCPU,f8088
	ret
cpu_01:
	cmp	dx,6
	jne	short cpu_08
	mov	mCPU,f8086
	ret
cpu_02:

; number of bits in displacement register used by shift

	mov	al,0FFH
	mov	cl,20H
	shl	al,cl
	or	al,al
	jnz	short cpu_04
	call	piq
	cmp	dx,4
	jne	short cpu_03
	mov	mCPU,fV20
	ret
cpu_03:
	cmp	dx,6
	jne	short cpu_08
	mov	mCPU,fV30
	ret
cpu_04:

; order of write/decrement by PUSH SP

	push	sp
	pop	ax
	cmp	ax,sp
	je	short cpu_06
	call	piq
	cmp	dx,4
	jne	short cpu_05
	mov	mCPU,f80188
	ret
cpu_05:
	cmp	dx,6
	jne	short cpu_08
	mov	mCPU,f80186
	ret
cpu_06:
	smsw	mMSW
	sgdt	mGDT
	sidt	mIDT

; try to alter flag register bits 15-12

	pushf
	pop	ax
	mov	cx,ax
	xor	cx,0F000H
	push	cx
	popf
	pushf
	pop	cx
	cmp	ax,cx
	jne	short cpu_07
	mov	mCPU,f80286
	ret
cpu_07:
	mov	mCPU,f80386
	ret
cpu_08:
	mov	mCPU,funk
	ret

;--------------------------------------------------------------------

piq:

; 	On exit:
;
;		DX	= length of prefetch instruction queue
;
;	This subroutine uses self-modifying code, but can
;	nevertheless be run repeatedly in the course of the calling
;	program.

count	=	7
opincdx	equ	42H			; inc dx opcode
opnop	equ	90H			; nop opcode

	mov	al,opincdx
	mov	cx,count
	push	cx
	push	cs
	pop	es
	mov	di,offset piq_01 - 1
	push	di
	std
	rep stosb
	mov	al,opnop
	pop	di
	pop	cx
	xor	dx,dx
	cli
	rep stosb
	rept	count
	inc	dx
	endm
piq_01:
	sti
	ret

;--------------------------------------------------------------------

chkint:

; save old INT 01H vector

	push	bx
	mov	ax,3501H
	int	21H
	mov	old_int01_ofs,bx
	mov	old_int01_seg,es
	pop	bx

; redirect INT 01H vector

	push	ds
	mov	ax,2501H
	mov	dx,seg new_int01
	mov	ds,dx
	mov	dx,offset new_int01
	int	21H
	pop	ds

; set TF and change SS -- did we trap on following instruction?

	pushf
	pop	ax
	or	ah,01H			; set TF
	push	ax
	popf
	push	ss			; CPU may wait one
					; instruction before
					; recognizing single step
					; interrupt
	pop	ss
chkint_01:				; shouldn't ever trap here

; restore old INT 01H vector

	push	ds
	mov	ax,2501H
	lds	dx,old_int01
	int	21H
	pop	ds
	ret

;--------------------------------------------------------------------

new_int01:

;	INT 01H handler (single step)
;
;	On entry:
;
;	SP =>	IP
;		CS
;		flags

	sti
	pop	ax			; IP
	cmp	ax,offset chkint_01
	jb	short new_int01_03
	je	short new_int01_01
	mov	mchkint,false
	jmp	short new_int01_02
new_int01_01:
	mov	mchkint,true
new_int01_02:
	pop	cx			; CS
	pop	dx			; flags
	and	dh,0FEH			; turn off TF
	push	dx			; flags
	push	cx			; CS
new_int01_03:
	push	ax			; IP
	iret

;--------------------------------------------------------------------

ndp:

fnone	equ	0
f8087	equ	1
f80287	equ	2
f80387	equ	3
funk	=	0FFH

	mov	word ptr ndp_cw,0000H
	cli

; The next three 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.

					;	CPU	NDP
	fnsave	ndp_save		;	 14	221
	mov	cx,(221-6-1)/17+1	;	  4
	loop	$			;  17*CX-12
					;   17*CX+6

	fninit				;	  8	  8
	mov	cx,(8-0-1)/17+1		;	  4
	loop	$			;  17*CX-12
					;     17*CX

	fnstcw	ndp_cw			;	 14	 24
	mov	cx,(24-2-1)/17+1	;	  4
	loop	$			;  17*CX-12
					;   17*CX+2

	sti
	mov	ax,ndp_cw
	cmp	ax,0000H
	jne	short ndp_01
	mov	mNDP,fnone
	ret
ndp_01:
	cmp	ax,03FFH
	jne	short ndp_02
	mov	mNDP,f8087
	jmp	short ndp_04
ndp_02:

.287

	cmp	ax,037FH
	jne	short ndp_05
	fld1
	fldz
	fdiv
	fld1
	fchs
	fldz
	fdiv
	fcom
	fstsw	ndp_sw
	mov	ax,ndp_sw
	and	ah,41H			; C3, C0
	cmp	ah,40H			; ST(0) = ST(1)
	jne	short ndp_03
	mov	mNDP,f80287
	jmp	short ndp_04
ndp_03:
	cmp	ah,01H			; ST(0) < ST(1)
	jne	short ndp_05
	mov	mNDP,f80387
ndp_04:

.8087

	frstor	ndp_save
	fstcw	mNDPCW
	ret
ndp_05:
	mov	mNDP,funk
	ret

CPUID	endp

;--------------------------------------------------------------------

DISKREAD	proc	near

assume cs:CODE, ds:nothing, es:nothing

;	On entry:
;
;		BP
;	SP =>	near return address
;		offset  of disk buffer
;		segment "   "     "
;		number of sectors to read
;		starting logical sector number
;		drive number (0=A, 1=B, etc.)
;
;	On exit:
;
;		AX	= function result
;			00	- function successful
;			01..FF	- DOS INT 25H error result

	drive			equ	[bp + 12]
	starting_sector		equ	[bp + 10]
	number_of_sectors	equ	[bp + 8]
	buffer			equ	[bp + 4]

	push	bp
	mov	bp,sp
	mov	al,drive
	mov	dx,starting_sector
	mov	cx,number_of_sectors
	push	ds
	lds	bx,buffer
	int	25H
	inc	sp			; fix broken stack
	inc	sp
	pop	ds
	jc	short diskread_01
	xor	ax,ax

diskread_01:

	pop	bp
	ret	10

DISKREAD	endp

CODE	ends

;--------------------------------------------------------------------

DATA	segment	byte

; storage for CPUID

; redirected INT 01H vector

old_int01	label	dword
old_int01_ofs	dw	?
old_int01_seg	dw	?

; storage for NDPID

; 80x87 control word after initialization, status word after divide by zero

ndp_cw		dw	?
ndp_save	db	94 dup (?)
ndp_sw  	dw	?

DATA	ends

	end
