[comp.sys.ibm.pc] How do you detect a V20?

toma@killer.UUCP (04/21/87)

Is there a way to detect a NEC V20 or V30 CPU in an IBM or compatible PC
through software???

ADVthanksNCE
Tom
UUCP:           ihnp4\
                      \killer!toma
  infoswx!convex!dj3b1/
Tom Armistead

psfales@ihlpe.ATT.COM (Peter Fales) (04/25/87)

In article <785@killer.UUCP>, toma@killer.UUCP (Tom Armistead) writes:
> Is there a way to detect a NEC V20 or V30 CPU in an IBM or compatible PC
> through software???

I have an assembly language program (that came originally from netnews)
that does exactly that.  The V20/V30 are so compatible that software
differentiation is difficult.  The trick is to use a bug in the Intel
chips that was fixed in the NEC chips.  Namely: If a hardware interrupt
occurs during a string instruction prefaced by a repeat instruction, when
control is returned to the instruction the Intel chips "forget" about
the repeat instruction.  The NECs do not.

The program CPUID.ASM/CPUID.COM is available on some BBS systems or
I can mail or post copies if anyone is interested.  It can figure out
whether you have an 8088, 8086, 80188, 80186, V20, V30, or 80286, and
whether the math coprocessor is installed.
-- 
Peter Fales		UUCP:	...ihnp4!ihlpe!psfales
			work:	(312) 979-7784
				AT&T Information Systems, IW 1Z-243
				1100 E. Warrenville Rd., IL 60566

campbell@maynard.BSW.COM (Larry Campbell) (04/27/87)

In article <1710@ihlpe.ATT.COM> psfales@ihlpe.ATT.COM (Peter Fales) writes:
>In article <785@killer.UUCP>, toma@killer.UUCP (Tom Armistead) writes:
>> Is there a way to detect a NEC V20 or V30 CPU in an IBM or compatible PC
>> through software???
>
>I have an assembly language program (that came originally from netnews)
>that does exactly that.  The V20/V30 are so compatible that software
>differentiation is difficult.  The trick is to use a bug in the Intel
>chips that was fixed in the NEC chips.  ...

There's a much easier method that I got from my local NEC sales engineer.
(Why is it that computer people would rather go pawing around the networks
than just telephoning the manufacturer?)  Anyway, here's the code:

| Use undocumented Intel instruction, opcode D6, to determine if this
| is an Intel chip (D6 copies carry flag throughout AL) or an NEC V20
| (something else happens)

	xor	ax,ax		| ax <= 0
	stc			| set carry flag
	.byte	0xD6		| magic instruction
	cmpb	al,*0xFF	| which chip?
	je	cpu8088		| if FF, this is 8088
	...			| not FF, this is V20

cpu8088: ...

I don't know if this also works for the V30, but I would guess that it
does.  The NEC rep told me that on Intel chips, the test works on the
8088, 8086, 80188, and 80186.
-- 
Larry Campbell                                The Boston Software Works, Inc.
Internet: campbell@maynard.BSW.COM          120 Fulton Street, Boston MA 02109
uucp: {alliant,think,wjh12}!maynard!campbell        +1 617 367 6846

psfales@ihlpe.ATT.COM (Peter Fales) (04/27/87)

I have had enough requests for the CPUID program that it seems to
be worth posting.   This is a program that can detect the type of
processor running on an IBM PC or compatible.  It can distinguish among
the 8088, 8086, 80188, 80186, V20, V30, and 80286, as well as identify
the presence of the numeric coprocessor.

Following this introductory note are two items:  the MASM source code
and the uuencoded binary for CPUID.COM.  The source code is not
necessary if you just want to run the program, but it is well commented
and explains the tricks used to identify the processor type.

I didn't write the program, I only pulled it off the net myself.


Enjoy.

Peter Fales		UUCP:	...ihnp4!ihlpe!psfales
			work:	(312) 979-7784
				AT&T Information Systems, IW 1Z-243
				1100 E. Warrenville Rd., IL 60566

==========================cut here=======================================

	title	CPUID - Determine CPU & NDP Type
	page	58,122
	name	CPUID

; :ts=8 /* for MY editor only */
;
;	CPUID uniquely identifies each NEC & Intel CPU & NDP.
;
; Notes on program structure:
;
;	This program uses four segments, two classes, and one group.
;	It demonstrates a useful technique for programmers who generate
;	.COM programs.  In particular, it shows how to use segment
;	classes to re-order segments, and how to eliminate the linker's
;	warning message about the absence of a stack segment.
;
;	The correspondence between segments and classes is as follows:
;
;			Segment		Class
;			-------		-----
;			STACK		prog
;			DATA		data
;			MDATA		data
;			CODE		prog
;
;	The segments apprear in the above order in the program source
;	to avoid forward references in the CODE segment to labels in
;	the DATA/MDATA segments.  However, because the STACK segment
;	appears first in the file, it and all segments in the same
;	class are made contiguous by the linker.  Thus they precede
;	the DATA/MDATA segments in the resulting .COM file because
;	the latter are in a different class.  In this manner, although
;	DATA and MDATA precede CODE in the source file, their order
;	is swapped in the .COM file.  That way there is no need for
;	an initial skip over the data areas to get to the CODE
;	segment.  As a side benefit, declaring a STACK segment (as
;	the first segment in the source) also eliminates the linker's
;	warning about that segment missing.  Finally, all segments
;	are declared to be in the same group so the linker can properly
;	resolve offsets.
;
;	Note that if you re-assemble the code for any reason, it is
;	important to use an assembler later than the IBM version 1.0.
;	That version has a number of bugs including an annoying habit
;	of alphabetizing segment names in the .OBJ file.  If you use
;	IBM MASM 2.0, be sure to specify /S to order the segments
;	properly.
;
;	If the program reports results at variance with your knowledge
;	of the system, please contact the author.
;
; Environments tested in:
;
;			CPU Speed
;	System		 in MHz		CPU		NDP
;	------		---------	---		---
;	IBM PC AT	6		Intel 80286	Intel 80287
;	IBM PC AT	9		Intel 80286	Intel 80287
;	IBM PC AT	6		Intel 80286	none
;	IBM PC AT	8.5		Intel 80286	none
;	IBM PC		4.77		Intel 8088	Intel 8087-3
;	IBM PC		4.77		Intel 8088*	Intel 8087-3
;	IBM PC XT	4.77		Intel 8088	none
;	IBM PC XT	4.77		Intel 8088	Intel 8087-3
;	IBM PC Portable	4.77		NEC V20		none
;	COMPAQ		4.77		Intel 8088	none
;	COMPAQ		4.77		NEC V20		none
;	AT&T PC 6300	8		Intel 8086	Intel 8087-2
;	AT&T PC 6300	8		NEC V30		Intel 8087-2
;	Tandy 2000	8		Intel 80186	none
;
;	* = faulty CPU
;
; Program structure:
;
;	Group PGROUP:
;	Stack   segment STACK, byte-aligned, stack,  class 'prog'
;	Program segment CODE,  byte-aligned, public, class 'prog'
;	Data    segment DATA,  byte-aligned, public, class 'data'
;	Data    segment MDATA, byte-aligned, public, class 'data'
;
; Assembly requirements:
;
;	Use MASM 1.25 or later.
;	With IBM's MASM 2.0 only, use /S to avoid alphabetizing the
;		segment names.
;	Use /r option to generate real NDP code.
;
;	MASM CPUID/r;			to convert .ASM to .OBJ
;	LINK CPUID;			to convert .OBJ to .EXE
;	EXE2BIN CPUID CPUID.COM		to convert .EXE to .COM
;	ERASE CPUID.EXE			to avoid executing .EXE
;
;	Note that the linker doesn't warn about a missing stack segment.
;
; Author:
;
;	Original code by:	Bob Smith	May 1985
;				Qualitas, Inc.
;				8314 Thoreau Dr.
;				Bethesda, MD   20817
;
;	Arthur Zachai suggested the technique to distinguish within the
;	808x and 8018x families by exploiting the difference in the
;	length of their pre-fetch instruction queues.
;
;	Published in PC Tech Journal - April 1986 - Vol 4 No 4

	subttl	Structures, Records, Equates, & Macros
	page
ARG_STR	struc
	dw	?			; caller's bp
ARG_OFF	dw	?			; caller's offset
ARG_SEG	dw	?			;          segment
ARG_FLG	dw	?			;          flags
ARG_STR	ends

; Record to define bits in the CPU's & NDP's flags' registers

CPUFLAGS record	RO:1,NT:1,IOPL:2,OF:1,DF:1,IF:1,TF:1,SF:1,ZF:1,R1:1,AF:1,R2:1,PF:1,R3:1,CF:1

NDPFLAGS record	R4:3,IC:1,RC:2,PC:2,IEM:1,R5:1,PM:1,UM:1,OM:1,ZM:1,DM:1,IM:1

;	FLG_PIQL	Pre-fetch instruction queue length, 0 => 4-byte
;							    1 => 6-byte
;	FLG_08		Intel 808x
;	FLG_NEC		NEC V20 or V30
;	FLG_18		Intel 8018x
;	FLG_28		Intel 8028x
;	FLG_87		Intel 8087
;	FLG_287		Intel 80287
;
;	FLG_CERR	Faulty CPU
;	FLG_NERR	Faulty NDP switch setting

FLG	record	RSVD:9,FLG_NERR:1,FLG_CERR:1,FLG_NDP:2,FLG_CPU:3

; CPU-related flags

FLG_PIQL	equ	001b shl FLG_CPU
FLG_08		equ	000b shl FLG_CPU
FLG_NEC		equ	010b shl FLG_CPU
FLG_18		equ	100b shl FLG_CPU
FLG_28		equ	110b shl FLG_CPU

FLG_8088	equ	FLG_08
FLG_8086	equ	FLG_08 or FLG_PIQL
FLG_V20		equ	FLG_NEC
FLG_v30		equ	FLG_NEC or FLG_PIQL
FLG_80188	equ	FLG_18
FLG_80186	equ	FLG_18 or FLG_PIQL
FLG_80286	equ	FLG_28 or FLG_PIQL

; NDP-related flags

;			00b shl FLG_NDP		Not Present
FLG_87		equ	01b shl FLG_NDP
FLG_287	equ	10b shl FLG_NDP
BEL		equ	07h
LF		equ	0ah
CR		equ	0dh
EOS		equ	'$'

POPFF	macro
	local	L1,L2
	jmp	short L2		; skip over IRET
L1:
	iret				; pop the cs & ip pushed below along
					; with the flags, our original purpose
L2:
	push	cs			; prepare for IRET by pushing cs
	call	L1			; push ip, jump to IRET
	endm				; POPFF macro

TAB	macro	TYP
	push	bx			; save for a moment
	and	bx,mask FLG_&TYP	; isolate flags
	mov	cl,FLG_&TYP		; shift amount
	shr	bx,cl			; shift to low-order
	shl	bx,1			; times two to index table of words
	mov	dx,TYP&MSG_TAB[bx]	; ds:dx => descriptive message
	pop	bx			; restore
	mov	ah,09h			; function code to display string
	int	21h			; request dos service
	endm				; TAB macro
	page
INT_VEC	segment at 0			; start INT_VEC segment
		dd	?		; pointer to INT 00h
INT01_OFF	dw	?		; pointer to INT 01h
INT01_SEG	dw	?
INT_VEC	ends				; end INT_VEC segment

PGROUP	group	STACK,CODE,DATA,MDATA

; The following segment both positions class 'prog' segments lower in
; memory than others so the first byte of the resulting .COM file is
; in the CODE segment, as well as satisfies the LINKer's need to have
; a stack segment.

STACK	segment	byte stack 'prog'	; start STACK segment
STACK	ends				; end STACK segment

I11_REC	record	I11_PRN:2,I11_RSV1:2,I11_COM:3,I11_RSV2:1,I11_DISK:2,I11_VID:2,I11_RSV3:2,I11_NDP:1,I11_IPL:1

DATA	segment	byte public 'data'	; start DATA segment
	assume	ds:PGROUP

OLDINT01_VEC	label	dword		; save area for original INT 01h handler
OLDINT01_OFF	dw	?
OLDINT01_SEG	dw	?

NDP_CW		label	word		; save area for NDP control word
		db	?
NDP_CW_HI	db	0		; high byte of control word
NDP_ENV		dw	7 dup(?)	; save area for NDP environment

DATA	ends
	subttl	Message Data Area
	page
MDATA	segment	byte public 'data'	; start MDATA segment
	assume	ds:PGROUP

MSG_START	db	'CPUID -- Version 1.0'
		db	CR,LF,CR,LF,EOS
MSG_8088	db	'CPU is an Intel 8088.'
		db	CR,LF,EOS
MSG_8086	db	'CPU is an Intel 8086.'
		db	CR,LF,EOS
MSG_V20		db	'CPU is an NEC V20.'
		db	CR,LF,EOS
MSG_V30		db	'CPU is an NEC V30.'
		db	CR,LF,EOS
MSG_80188	db	'CPU is an Intel 80188.'
		db	CR,LF,EOS
MSG_80186	db	'CPU is an Intel 80186.'
		db	CR,LF,EOS
MSG_UNK		db	'CPU is a maverick -- 80288??.'
		db	CR,LF,EOS
MSG_80286	db	'CPU is an Intel 80286.'
		db	CR,LF,EOS

CPUMSG_TAB	label	word
	dw	PGROUP:MSG_8088		; 000 = Intel 8088
	dw	PGROUP:MSG_8086		; 001 = Intel 8086
	dw	PGROUP:MSG_V20		; 010 = NEC V20
	dw	PGROUP:MSG_V30		; 011 = NEC V30
	dw	PGROUP:MSG_80188	; 100 = Intel 80188
	dw	PGROUP:MSG_80186	; 101 = Intel 80186
	dw	PGROUP:MSG_UNK		; 110 = ?
	dw	PGROUP:MSG_80286	; 111 = Intel 80286

NDPMSG_TAB	label	word
	dw	PGROUP:MSG_NDPX		; 00 = No NDP
	dw	PGROUP:MSG_8087		; 01 = Intel 8087
	dw	PGROUP:MSG_80287	; 10 = Intel 80287

MSG_NDPX	db	'NDP is not present.'
		db	CR,LF,EOS
MSG_8087	db	'NDP is an Intel 8087.'
		db	CR,LF,EOS
MSG_80287	db	'NDP is an Intel 80287.'
		db	CR,LF,EOS

CERRMSG_TAB	label	word
	dw	PGROUP:MSG_CPUOK	; 0 = CPU healthy
	dw	PGROUP:MSG_CPUBAD	; 1 = CPU faulty

MSG_CPUOK	db	'CPU appears to be healthy.'
		db	CR,LF,EOS
MSG_CPUBAD	label	byte
		db	BEL,'*** CPU incorrectly allows interrupts '
		db	'after a change to SS ***',CR,LF
		db	'It should be replaced with a more recent '
		db	'version as it could crash the',CR,LF
		db	'system at seemingly random times.',CR,LF,EOS

NERRMSG_TAB	label	word
	dw	PGROUP:MSG_NDPSWOK	; 0 = NDP switch set correctly
	dw	PGROUP:MSG_NDPSWERR	; 1 = NDP switch set incorrectly

MSG_NDPSWOK	db	EOS		; no message
MSG_NDPSWERR	label	byte
		db	'*** Although there is an NDP installed '
		db	'on this sytem, the corresponding',CR,LF
		db	'system board switch is not properly set.  '
		db	'To correct this, flip switch 2 of',CR,LF
		db	'switch block 1 on the system board.',CR,LF,EOS

MDATA	ends				; end MDATA segment
	subttl	Main Routine
	page
CODE	segment	byte public 'prog'	; start CODE segment
	assume	cs:PGROUP,ds:PGROUP,es:PGROUP
	org	100h			; skip over PSP

INITIAL	proc	near
	mov	dx,offset ds:MSG_START	; starting message
	mov	ah,09h			; function code to display string
	int	21h			; request DOS service

	call	CPUID			; check the CPU's identity

	TAB	CPU			; display CPU results
	TAB	NDP			; display NDP results
	TAB	CERR			; display CPU ERR results
	TAB	NERR			; display NDP ERR results

	ret				; return to DOS
INITIAL	endp				; end INITIAL procedure
	subttl	CPUID Procedure
	page
CPUID	proc	near			; start CPUID procedure
	assume	cs:PGROUP,ds:PGROUP,es:PGROUP

; This procedure determines the type of CPU and NDP (if any) in use.
;
; The possibilities include:
;
;		Intel 8086
;		Intel 8088
;		NEC V20
;		NEC V30
;		Intel 80186
;		Intel 80188
;		Intel 80286
;		Intel 8087
;		Intel 80287
;
; Also checked is whether or not the CPU allows interrupts after
; changing the SS register segment.  If the CPU does, it is faulty
; and should be replaced.
;
; Further, if an NDP is installed, non-AT machines should have a
; system board switch set.  Such a discrepancy is reported.
;
; On exit, BX contains flag settings (as defined in FLG record) which
; the caller can check.  For example, to test for an Intel 80286, use
;
;		and	bx,mask FLAG_CPU
;		cmp	bx,FLG_80286
;		je	ITSA286

	irp	XX,<ax,cx,di,ds,es>	; save registers
	push	XX
	endm

; test for 80286 -- this CPU executes PUSH SP by first storing SP on
; stack, then decrementing it.  earlier CPU's decrement, THEN store.

	mov	bx,FLG_28		; assume it's a 286
	push	sp			; only 286 pushes pre-push SP
	pop	ax			; get it back
	cmp	ax,sp			; check for same
	je	CHECK_PIQL		; they are, so it's a 286

; test for 80186/80188 -- 18xx and 286 CPU's mask shift/rotate
; operations mod 32; earlier CPUs use all 8 bits of CL.

	mov	bx,FLG_18		; assume it's an 8018x
	mov	cl,32+1			; 18x masks shift counts mod 32
					; note we can't use just 32 in CL
	mov	al,0ffh			; start with all bits set

	shl	al,cl			; shift one position if 18x
	jnz	CHECK_PIQL		; some bits still on,
					; so its a 18x, check PIQL

; test for V20

	mov	bx,FLG_NEC		; assume it's an NEC V-series CPU
	call	CHECK_NEC		; see if it's an NEC chip
	jcxz	CHECK_PIQL		; good guess, check PIQL

	mov	bx,FLG_08		; it's an 808x
	subttl	Check Length of Pre-Fetch Instruction Queue
	page
; Check the length of the pre-fetch instruction queue (PIQ).
;
; xxxx6 CPUs have a PIQ length of 6 bytes,
; xxxx8 CPUs have a PIQ length of 4 bytes
;
; Self-modifying code is used to distinguish the two PIQ lengths.

CHECK_PIQL:
	call	PIQL_SUB		; handle via subroutine
	jcxz	CHECK_ERR		; if CX is 0, INC was not executed,
					; hence PIQ length is 4
	or	bx,FLG_PIQL		; PIQ length is 6
	subttl	Check for Allowing Interrupts After POP SS
	page
; Test for faulty chip (allows interrupts after change to SS register)

CHECK_ERR:
	xor	ax,ax			; prepare to address
					; interrupt vector segment
	mov	ds,ax			; DS points to segment 0
	assume	ds:INT_VEC		; tell the assembler

	cli				; nobody move while we swap

	mov	ax,offset cs:INT01	; point to our own handler
	xchg	ax,INT01_OFF		; get and swap offset
	mov	OLDINT01_OFF,ax		; save to restore later

	mov	ax,cs			; our handler's segment
	xchg	ax,INT01_SEG		; get and swap segment
	mov	OLDINT01_SEG,ax		; save to restore later

; note we continue with interrupts disabled to avoid
; an external interrupt occuring during this test

	mov	cx,1			; initialize a register
	push	ss			; save ss to store back into itself
	pushf				; move flags
	pop	ax			; ... into ax
	or	ax,mask TF		; set trap flag
	push	ax			; place onto stack
	POPFF				; ... and then into effect
					; some CPUs effect the trap flag
					; immediately, some
					; wait one instruction
	nop				; allow interrupt to take effect

POST_NOP:
	pop	ss			; change the stack segment register
					; (to itself)
	dec	cx			; normal cpu's execute this instruction
					; before recognizing the single-step
					; interrupt
	hlt				; we never get here

INT01:

; Note: IF=TF=0
; If we're stopped at or before POST_NOP, continue on

	push	bp			; prepare to address the stack
	mov	bp,sp			; hello, Mr. stack

	cmp	[bp].ARG_OFF,offset cs:POST_NOP	; check offset
	pop	bp			; restore
	ja	INTO1_DONE		; we're done

	iret				; return to caller

INTO1_DONE:

; restore old INT 01h handler

	les	ax,OLDINT01_VEC	; ES:AX ==> old INT 01h handler
	assume	es:nothing		; tell the assembler
	mov	INT01_OFF,ax		; restore offset
	mov	INT01_SEG,es		; ... and segment
	sti				; allow interrupts again (IF=1)

	add	sp,3*2			; strip ip, cs, and flags from stack

	push	cs			; setup ds for code below
	pop	ds
	assume	ds:PGROUP		; tell the assembler

	jcxz	CHECK_NDP		; if cx is 0, the dec cx was executed,
					; and the cpu is ok
	or	bx,mask FLG_CERR	; it's a faulty chip
	subttl	Check For Numeric Data Processor
	page
; Test for a Numeric Data Processor -- Intel 8087 or 80287.  The
; technique used is passive -- it leaves the NDP in the same state in
; which it is found.

CHECK_NDP:
	cli				; protect FNSTENV
	fnstenv NDP_ENV			; if NDP present, save
					; current environment,
					; otherwise, this instruction
					; is ignored
	mov	cx,50/7			; cycle this many times
	loop	$			; wait for result to be stored
	sti				; allow interrupts
	fninit				; initialize processor to known state
	jmp	short $+2		; wait for initialization

	fnstcw	NDP_CW			; save control word
	jmp	short $+2		; wait for result to be stored
	jmp	short $+2
	cmp	NDP_CW_HI,03h		; check for NDP initial control word
	jne	CPUID_EXIT		; no NDP installed
	int	11h			; get equipment flags into ax
	test	ax,mask I11_NDP		; check NDP-installed bit
	jnz	CHECK_NDP1		; it's correctly set
	or	bx,mask FLG_NERR	; mark as in error
CHECK_NDP1:
	and	NDP_CW,not mask IEM	; enable interrupts
					; (IEM=0, 8087 only)
	fldcw	NDP_CW			; reload control word
	fdisi				; disable interrupts (IEM=1) on 8087,
					; ignored by 80287
	fstcw	NDP_CW			; save control word
	fldenv	NDP_ENV			; restore original NDP environment
					; no need to wait
					; for environment to be loaded
	test	NDP_CW,mask IEM		; check interrupt enable mask
					; (8087 only)
	jnz	CPUID_8087		; it changed, hence NDP is an 8087
	or	bx,FLG_287		; NDP is an 80287
	jmp	short CPUID_EXIT	; exit with falgs in BX
CPUID_8087:
	or	bx,FLG_87		; NDP is an 8087
CPUID_EXIT:
	irp	XX,<es,ds,di,cx,ax>	; restore registers
	pop	XX
	endm
	assume	ds:nothing,es:nothing
	ret				; return to caller
CPUID	endp				; end CPUID procedure
	subttl	Check For NEC V20/V30
	page
CHECK_NEC	proc	near

; The NEC V20/V30 are very compatible with the Intel 8086/8088.
; The only point of "incompatibility" is that they do not contain
; a bug found in the Intel CPU's.  Specifically, the NEC CPU's
; correctly restart an interrupted multi-prefix string instruction
; at the start of the instruction.  The Intel CPU's incorrectly
; restart in the middle of the instruction.  This routine tests
; for that situation by executing such an instruction for a
; sufficiently long period of time for a timer interrupt to occur.
; If at the end of the instruction, CX is zero, it must be an NEC
; CPU; if not, it's an Intel CPU.
;
; Note that we're counting on the timer interrupt to do its thing
; every 18.2 times per second.
;
; Here's a worst case analysis: An Intel 8088/8086 executes 65535
; iterations of LODSB ES[SI] in 2+9+13*65535 = 851,966 clock ticks.
; If the Intel 8088/8086 is running at 10 MHz, each clock tick is
; 100 nanoseconds, hence the entire operation takes 85 milliseconds.
; If the timer is running at normal speed, it interrupts the CPU every
; 55ms and so should interrupt the repeated string instruction at least
; once.

	mov	cx,0ffffh		; move a lot of data
	sti				; ensure timer enabled

; execute multi-prefix instruction.  note that the value of ES as
; well as the direction flag setting is irrelevant.

	push	ax			; save registers
	push	si
	rep	lods	byte ptr es:[si]
	pop	si			; restore
	pop	ax

; on exit: if cx is zero, it's an NEC CPU, otherwise it's an Intel CPU

	ret				; return to caller
CHECK_NEC	endp
	subttl	Pre-Fetch Instruction Queue Subroutine
	page
PIQL_SUB	proc	near

; This subroutine discerns the length of the CPU's pre-fetch
; instruction queue (PIQ).
;
; The technique used is to first ensure that the PIQ is full, then
; change an instruction which should be in a 6-byte PIQ but not in a
; 4-byte PIQ.  Then, if the original instruction is executed, the PIQ
; is 6-bytes long; if the new instruction is executed, PIQ length is 4.
;
; We ensure the PIQ is full be executing an instruction which takes
; long enough so that the Bus Interface Unit (BIU) can fill the PIQ
; while the instruction is executing.
;
; Specifically, for all byt the last STOSB, we're simple marking time
; waiting for the BIU to fill the PIQ.  The last STOSB actually changes
; the instruction.  By that time, the orignial instruction should be in
; a six-byte PIQ byt not a four-byte PIQ.

	assume	cs:PGROUP,es:PGROUP
@REP	equ	3			; repeat the store this many times
	std				; store backwards
	mov	di,offset es:LAB_INC+@REP-1	; change the instructions
					; at ES:DI
					; and preceding
	mov	al,ds:LAB_STI		; change to a sti
	mov	cx,@REP			; give the BIU time
					; to pre-fetch instructions
	cli				; ensure interrupts are disabled,
					; otherwise a timer tick
					; could change the PIQ filling
	rep	stosb			; change the instruction
					; during execution of this instruction
					; the BIU is refilling the PIQ.  The
					; current instruction is no longer
					; in the PIQ.
					; Note at end, CX is 0.

; The PIQ begins filling here

	cld				; restore direction flag
	nop				; PIQ fillers
	nop
	nop

; The following instruction is beyond a four-byte-PIQ CPU's reach,
; but within that of a six-byte-PIQ CPU.

LAB_INC		label	byte
	inc	cx			; executed only if PIQ length is 6

LAB_STI	label 	byte
	rept	@REP-1
	sti				; restore interrupts
	endm
	ret				; return to caller
	assume	ds:nothing,es:nothing
PIQL_SUB	endp			; end PIQL_SUB procedure

CODE	ends				; end code segment

	if1
%OUT	Pass 1 Complete
	else
%OUT	Pass 2 Complete
	endif

	end	INITIAL			; end CPUID module



begin 644 cpuid.com
MNFD"M G-(>A1 %.!XP< L0#3Z]'CBY=' UNT"<TA4X'C& "Q ]/KT>.+EU<#
M6[0)S2%3@>,@ +$%T^O1XXN7I -;M G-(5.!XT  L0;3Z]'CBY=R!%NT"<TA
MPU!15QX&NP8 5%@[Q'06NP0 L2&P_]+@=0N[ @#HN@#C [L  .B^ .,$@<L!
M #/ CMCZN+<!AP8$ "ZC50*,R(<&!@ NHU<"N0$ %IQ8#0 !4.L!SP[H^_^0
M%TGT58OL@7X"M %==P'/+L0&50*C! ",!@8 ^X/$!@X?XP2!RR  ^MDV6P*Y
M!P#B_OO;X^L V3Y9 NL ZP" /EH" W4US1&I @!U!('+0 "!)ED"?_^;V2Y9
M IO;X9O9/ED"F]DF6P+W!ED"@ !U!H'+$ #K!('+"  ''U]96,.Y___[4%;S
M)JQ>6,/]OU,"H%("N0, ^O.J_)"0D$'[^\,                         
M $-054E$("TM(%9E<G-I;VX@,2XP#0H-"B1#4%4@:7,@86X@26YT96P@.# X
M."X-"B1#4%4@:7,@86X@26YT96P@.# X-BX-"B1#4%4@:7,@86X@3D5#(%8R
M,"X-"B1#4%4@:7,@86X@3D5#(%8S,"X-"B1#4%4@:7,@86X@26YT96P@.# Q
M.#@N#0HD0U!5(&ES(&%N($EN=&5L(#@P,3@V+@T*)$-052!I<R!A(&UA=F5R
M:6-K("TM(#@P,C@X/S\N#0HD0U!5(&ES(&%N($EN=&5L(#@P,C@V+@T*)(("
MF@*R L<"W +U @X#+@-= W,#BP-.1% @:7,@;F]T('!R97-E;G0N#0HD3D10
M(&ES(&%N($EN=&5L(#@P.#<N#0HD3D10(&ES(&%N($EN=&5L(#@P,C@W+@T*
M)*@#Q0-#4%4@87!P96%R<R!T;R!B92!H96%L=&AY+@T*) <J*BH@0U!5(&EN
M8V]R<F5C=&QY(&%L;&]W<R!I;G1E<G)U<'1S(&%F=&5R(&$@8VAA;F=E('1O
M(%-3("HJ*@T*270@<VAO=6QD(&)E(')E<&QA8V5D('=I=&@@82!M;W)E(')E
M8V5N="!V97)S:6]N(&%S(&ET(&-O=6QD(&-R87-H('1H90T*<WES=&5M(&%T
M('-E96UI;F=L>2!R86YD;VT@=&EM97,N#0HD=@1W!"0J*BH@06QT:&]U9V@@
M=&AE<F4@:7,@86X@3D10(&EN<W1A;&QE9"!O;B!T:&ES('-Y=&5M+"!T:&4@
M8V]R<F5S<&]N9&EN9PT*<WES=&5M(&)O87)D('-W:71C:"!I<R!N;W0@<')O
M<&5R;'D@<V5T+B @5&\@8V]R<F5C="!T:&ES+"!F;&EP('-W:71C:" R(&]F
M#0IS=VET8V@@8FQO8VL@,2!O;B!T:&4@<WES=&5M(&)O87)D+@T*)" @("  
M+BXN+BXN+BXN+BXN+BXN+BXN+BXN+BXN "X@+BXN("XN+BTN+BXN #$R,#  
;($XX,4X@(" @(" @("  +BXN+BXN+BXN+BXN
 
end
-- 
Peter Fales		UUCP:	...ihnp4!ihlpe!psfales
			work:	(312) 979-7784
				AT&T Information Systems, IW 1Z-243
				1100 E. Warrenville Rd., IL 60566