Thread View: comp.os.msdos.programmer
15 messages
15 total messages
Started by Ralf.Brown
Sat, 21 Jul 1990 13:29
Re: Detecting an 80486
Author: Ralf.Brown
Date: Sat, 21 Jul 1990 13:29
Date: Sat, 21 Jul 1990 13:29
25 lines
2084 bytes
2084 bytes
In article <1990Jul20.230335.22816@ddsw1.MCS.COM>, andyross@ddsw1.MCS.COM (Andrew Rossmann) wrote:
}In article <315@bally.Bally.COM> pete@bally.UUCP (exilied in my own office) writes:
}>In article <1990Jul19.025150.6150@looking.on.ca> brad@looking.on.ca (Brad Templeton) writes:
} In my Infoplus program, the 286/386/486 are detected by trapping the
}invalid opcode interrupt, and then trying instructions. For the 486, I use
}XADD DX,DX. This is new on the 486, and exchanges the registers before
}doing the addition.
} For the 386, I now use MOV EDX,EDX, which is 32-bit do-nothing! (It used
}to use an instruction that read some special registers. This caused major
}problems with most EMS emulators!)
} For the 286, it tries the SMSW DX instruction (whatever that does.)
} The code come from a program by Robert Collins, and I just made a few
}changes to fit INFOPLUS (and fix the 386 problem!)
Unfortunately, your program will fail miserably on my system (and about a
million others), because I am running QEMM-386. QEMM runs in protected mode
with DOS in virtual-86 mode. Any invalid opcode exception that your
program generated invokes the QEMM exception handler, NOT YOURS. And the
QEMM exception handler gives the choice of terminating or rebooting. I
expect similar behavior from 386^Max, CEMM, AllCharge386, MICEMM, ....
--
UUCP: {ucbvax,harvard}!cs.cmu.edu!ralf -=- 412-268-3053 (school) -=- FAX: ask
ARPA: ralf@cs.cmu.edu BIT: ralf%cs.cmu.edu@CMUCCVMA FIDO: 1:129/3.1
Disclaimer? | I was gratified to be able to answer promptly, and I did.
What's that? | I said I didn't know. --Mark Twain
Re: Detecting an 80486
Author: David Hinds
Date: Sun, 22 Jul 1990 18:55
Date: Sun, 22 Jul 1990 18:55
23 lines
1757 bytes
1757 bytes
In article <26a858b9@ralf> Ralf.Brown@B.GP.CS.CMU.EDU writes: >In article <1990Jul20.230335.22816@ddsw1.MCS.COM>, andyross@ddsw1.MCS.COM (Andrew Rossmann) wrote: >} For the 286, it tries the SMSW DX instruction (whatever that does.) >} The code come from a program by Robert Collins, and I just made a few >}changes to fit INFOPLUS (and fix the 386 problem!) > >Unfortunately, your program will fail miserably on my system (and about a >million others), because I am running QEMM-386. QEMM runs in protected mode >with DOS in virtual-86 mode. Any invalid opcode exception that your >program generated invokes the QEMM exception handler, NOT YOURS. And the >QEMM exception handler gives the choice of terminating or rebooting. I >expect similar behavior from 386^Max, CEMM, AllCharge386, MICEMM, .... The SMSW instruction will fail if executed on less than a 286, so it is a safe test even in the presence of QEMM. As a bonus, the low-order bit of the MSW (machine status word) is the protected-mode flag. The only way it can be set under DOS is if a 386 is running in virtual-86 mode. So, if the bit is set, you are on a 386. If it isn't, you are either on a 286 or 386 in real mode, and it is safe to try a 386 instruction and trap the interrupt. -David Hinds dhinds@popserver.stanford.edu
Re: Detecting an 80486
Author: Andrew Rossmann
Date: Mon, 23 Jul 1990 22:31
Date: Mon, 23 Jul 1990 22:31
637 lines
19276 bytes
19276 bytes
In article <26a858b9@ralf> Ralf.Brown@B.GP.CS.CMU.EDU writes:
>In article <1990Jul20.230335.22816@ddsw1.MCS.COM>, andyross@ddsw1.MCS.COM (Andrew Rossmann) wrote:
>}In article <315@bally.Bally.COM> pete@bally.UUCP (exilied in my own office) writes:
>}>In article <1990Jul19.025150.6150@looking.on.ca> brad@looking.on.ca (Brad Templeton) writes:
>} In my Infoplus program, the 286/386/486 are detected by trapping the
>Unfortunately, your program will fail miserably on my system (and about a
>million others), because I am running QEMM-386. QEMM runs in protected mode
>with DOS in virtual-86 mode. Any invalid opcode exception that your
>program generated invokes the QEMM exception handler, NOT YOURS. And the
>QEMM exception handler gives the choice of terminating or rebooting. I
>expect similar behavior from 386^Max, CEMM, AllCharge386, MICEMM, ....
>--
Since my program is grabbing the interrupt, there is no way QEMM (or
whatever) can get it. I've run Infoplus under QEMM 5 and MICEMM and
EMM386.SYS, and it works fine. What QEMM and other DON'T like is doing a
386 protected instruction!! (Which I found out the hard way.)
The only time it doesn't seem to work right is running under Windows 3
enhanced mode.
Several people have asked to see the code I use. Here it is. It is
written for use under Turbo Pascal, but should be adaptable. I'll even
throw in the other two routines used by Infoplus. Someone might find them
useful, too.
page 75, 110
;--------------------------------------------------------------------
;
; INFOPLUS.ASM
;
; Version 1.10
;
; Three subprograms used by INFOPLUS.PAS:
;
; CPUID - identifies host CPU and NDP (if
; any)
; DISKREAD - reads absolute sectors from disk
; LONGCALL - calls a routine using a CALL FAR
;
; Originally by:
; Steve Grant
; Long Beach, CA
; January 13, 1989
;
; mods by Andrew Rossmann (7/20/90)
; 286/386/486 detection adapted from code by Robert Collins.
;--------------------------------------------------------------------
.286P
.8087
public CPUID, DISKREAD, LONGCALL
;-----------------------------------------------------------------------------
; 80486 instruction macro -- because MASM 5.1 doesn't support the 80486!
;-----------------------------------------------------------------------------
;uncomment next 3 lines if not using TASM 2.0
;XADD macro
; db 0fh,0C0h,0D2h ; 80486 instruction macro
;ENDM
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 [bx + 3]
mIDT equ [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
f80486 equ 8
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
mov mCPU,funk ;set CPU type to unknown
sti
mov cx,0FFFFH
rep lods byte ptr es:[si]
jcxz short cpu_02
call piq
cmp dx,4
jg short cpu_01
mov mCPU,f8088
ret
cpu_01:
cmp dx,6
jne short cpu_01a
mov mCPU,f8086
cpu_01a:
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
jg short cpu_03
mov mCPU,fV20
ret
cpu_03:
cmp dx,6
jne short CPUID_done
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
jg short cpu_05
mov mCPU,f80188
ret
cpu_05:
cmp dx,6
jne short CPUID_done
mov mCPU,f80186
ret
; The following code is adapted from a program by Robert Collins
;since we probably have a 286, 386 or 486, we'll trap the invalid opcode
;handler, and try to determine the cpu type by using various opcodes.
;First, though, grab some tables
cpu_06:
smsw mMSW
sgdt mGDT
sidt mIDT
;-----------------------------------------------------------------------------
; Setup INT6 handler
;-----------------------------------------------------------------------------
push bx ;save bx
mov ax,3506h ;get INT 6 pointer
int 21h
mov old_int06_seg,es ;save
mov old_int06_ofs,bx
push ds ;save DS
mov dx,seg INT6_handler ;point to new handler
mov ds,dx
mov dx,offset INT6_handler
mov ax,2506h ;put into effect
int 21h
pop ds ;restore DS
pop bx
mov mCPU,f80486 ; initialize CPU flag
xor cx,cx ; initialize semaphore
;-----------------------------------------------------------------------------
; Now, try and determine which CPU we are by executing invalid opcodes.
; The instructions I chose to invoke invalid opcodes, are themselves rather
; benign. In each case, the chosen instruction modifies the DX register,
; and nothing else. No system parameters are changed, e.g. protected mode,
; or other CPU dependant features.
;-----------------------------------------------------------------------------
; The 80486 instruction 'XADD' xchanges the registers, then adds them.
;-----------------------------------------------------------------------------
;if not using TASM 2.0, comment out next 2 lines, and uncoment 3rd.
.486
XADD DX,DX ; 80486
; XADD ;DX,DX
.286
jcxz CPU_exit
mov mCPU,f80386 ; set 80386 semaphore
xor cx,cx ; CX=0
;-----------------------------------------------------------------------------
; For a description on the effects of the following instructions, look in
; the Intel Programmers Reference Manual's for the 80186, 80286, or 80386.
;-----------------------------------------------------------------------------
.386
mov edx,edx ; 80386
.286P
jcxz CPU_exit
mov mCPU,f80286 ; set 80286 semaphore
xor cx,cx ; CX=0
smsw dx ; 80286
jcxz CPU_exit
mov mCPU,funk ; set unknown CPU
CPU_exit:
push ds ;save DS
lds dx,old_int06 ;get old pointer
mov ax,2506h ;restore it
int 21h
pop ds
;!!!!!!!
;!!! Original 286/386 detection code. Commented out but left in in
;!!! case of problems.
;!!!!!!!
; 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
CPUID_done:
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
;-----------------------------------------------------------------------------
; INT6 handler sets a semaphore (CX=FFFF) and adjusts the return address to
; point past the invalid opcode.
;-----------------------------------------------------------------------------
INT6_handler:
enter 0,0 ; create new stack frame
dec cx ; make CX=FFFF
add word ptr ss:[bp][2],3 ; point past invalid opcode
leave
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:DATA, 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 ax,3000h ;get DOS version
int 21h
cmp al,4 ;DOS 4?
jb read3 ;Read assuming DOS 3.x
mov al,drive
mov bx,starting_sector ;copy info into parameter block
mov extd_starting_sector_lo,bx
mov extd_starting_sector_hi,0 ;We're only using lower part
mov bx,number_of_sectors
mov extd_number_of_sectors,bx
les bx,buffer ;get seg:ofs of buffer in ES:BX
mov extd_bufofs,bx ;put into block
mov extd_bufseg,es
mov bx,offset dos4_block ;DS:BX points to block
mov cx,-1 ;-1 means extended read
push ds ;save DS (not really needed, but lets
;me share code with DOS 3 read.)
jmp short readit
read3: mov al,drive
mov dx,starting_sector
mov cx,number_of_sectors
push ds
lds bx,buffer ;get seg:ofs of buffer in DS:BX
readit: 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
;
;LONGCALL will call a routine using a CALL FAR. This is used by XMS
;
;Pascal format: procedure longcall(AXi: word; var addr: longint;
; var AXo, BXo, DXo: word); external;
;
longcall proc near
assume cs:CODE, ds:DATA, es:nothing
AXi equ [bp+20]
addr equ [bp+16]
AXo equ [bp+12]
BXo equ [bp+8]
DXo equ [bp+4]
push bp
mov bp,sp
les di,addr
mov ax,es:[di]
mov word ptr address,ax
mov ax,es:[di+2]
mov word ptr address+2,ax
mov ax,AXi
call dword ptr address
les di,DXo
mov [es:di],dx
les di,BXo
mov [es:di],bx
les di,AXo
mov [es:di],ax
pop bp
ret 18
longcall 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 ?
; redirected INT 6 vector
old_int06 label dword
old_int06_ofs dw ?
old_int06_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 ?
; storage for DISKREAD
; DOS 4.0 extended read parameter block
dos4_block label byte
extd_starting_sector_lo dw ?
extd_starting_sector_hi dw ?
extd_number_of_sectors dw ?
extd_bufofs dw ?
extd_bufseg dw ?
; storage for LONGCALL
address dd ?
DATA ends
end
Andrew Rossmann
andyross@ddsw1.MCS.COM
Re: Detecting an 80486
Author: Robert Collins
Date: Mon, 13 Aug 1990 17:49
Date: Mon, 13 Aug 1990 17:49
69 lines
3913 bytes
3913 bytes
In some article, it was written: +} In my Infoplus program, the 286/386/486 are detected by trapping the +}invalid opcode interrupt, and then trying instructions. For the 486, I use +}XADD DX,DX. This is new on the 486, and exchanges the registers before +}doing the addition. Hmm. Sounds like my algorithm! +} For the 386, I now use MOV EDX,EDX, which is 32-bit do-nothing! (It used +}to use an instruction that read some special registers. This caused major +}problems with most EMS emulators!) Wow, this can't be coincidence! +} For the 286, it tries the SMSW DX instruction (whatever that does.) Ok, about now I got a little hot, until I read the next line. (BTW, "SMSW DX" is the '286 version of "MOV EDX,CR0"). +} The code come from a program by Robert Collins Thanks for the credit. +Unfortunately, your program will fail miserably on my system (and about a +million others), because I am running QEMM-386. QEMM runs in protected mode +with DOS in virtual-86 mode. Any invalid opcode exception that your +program generated invokes the QEMM exception handler, NOT YOURS. And the +QEMM exception handler gives the choice of terminating or rebooting. I +expect similar behavior from 386^Max, CEMM, AllCharge386, MICEMM, .... Ask yourself. Is the the CORRECT behavior for QEMM-386, et al? Or should these programs turn control of invalid opcodes to the user's program? >From the Intel "80386 Programmer's Reference Manual" PN 230985-001 (first printing), on page 2-20 comes the following quote: "Invalid opcodes may be used by some applications to extend the instruction set. In such a case, the invalid opcode exception presents an opportunity to emulate the opcode." But Intel found that software writers were REALLY taking advantage of this "feature" and as a result ran into some problems. Intel found that some programmers were using invalid opcodes that didn't generate an invalid opcode exception. HUH!? Intel has a list of reserved, undocumented opcodes on the '286, '386, and '486 that do something, and obviously won't generate an invalid opcode exception. People were attempting to use these opcodes to emulate CPU extensions. Intel probably got tired of taking so many calls on this subject, and removed the abovementioned quote from the PRM. The following disclaimer replaced it: "Intel reserves the right to enhance future products by using opcodes that are currently defined as invalid. Use of invalid opcodes to extend the instruction set may not be compatible with future Intel products and is therefore discouraged." But, there is plenty of software that uses this technique. My CPU determination subroutine was written for ROM BIOS, and was obviously not intended to run under protected mode. But the makers of these products that put the '386 and '486 in V8086 mode, who don't pass control of the invalid opcode handler back to the user program, are obviously in error. If QEMM fails to do this, it is obviously in error, and will have numerous compatibility problems with software using this technique. 386MAX does NOT use this technique, and passes control to the user program. Currently, VM386 does fail, and I called them this morning to explain to them that they are wrong. I am awaiting a return call from them. I hope this resolves some confusion, and puts to rest any "blame" regarding the use of my algorithm in Virtual 8086 mode. Robert Collins Altos Computer Systems
Re: Detecting an 80486
Author: John R. Levine
Date: Tue, 14 Aug 1990 02:55
Date: Tue, 14 Aug 1990 02:55
22 lines
1732 bytes
1732 bytes
In article <3817@altos86.Altos.COM> rcollins@altos86.UUCP (Robert Collins) writes:
>In some article, it was written:
>+} In my Infoplus program, the 286/386/486 are detected by trapping the
>+}invalid opcode interrupt, and then trying instructions.
>[but there are lots of places where this doesn't work very well, either
>because someone else gets the trap, or because some putatively unused opcodes
>do secret things.]
The Intel 486 Programmer's manual has sample code to tell the difference
among the 8086, 286, 386, and 486 without any trapping at all. There are
lots of differences other than new instructions. For example, on a PUSH SP,
the 8086 pushes the decremented SP but newer processors push the original
value. When you push the flags register with PUSHF, the 8086 always sets
bits 12-15 to one, the 286 always sets them to zero, and the 386 and 486
store some actual flags there. The 486 uses flag bit 18, which the 386
didn't. Intel provides sample code that is straightforward and doesn't
depend on catching traps. Looks good to me.
--
John R. Levine, Segue Software, POB 349, Cambridge MA 02238, +1 617 864 9650
johnl@esegue.segue.boston.ma.us, {ima|spdcc|world}!esegue!johnl
Marlon Brando and Doris Day were born on the same day.
Re: Detecting an 80486
Author: Sidney Markowitz
Date: Tue, 14 Aug 1990 08:38
Date: Tue, 14 Aug 1990 08:38
14 lines
1172 bytes
1172 bytes
I recall seeing a posting recently that contained source code for distinguishing between 80x86, 8086, 8088 cpus without blowing up under QEMM. Unfortunately I didn't save it and it has scrolled off systems I have access to. Can anyone point me to an ftp'able copy, or could offer to e-mail it to me? Was it, indeed QEMM-safe? In regards to the problem of QEMM trapping invalid opcodes used to detect the CPU type -- Does anyone know what Turbo Debugger v 2.0 does in TDH386.SYS to give TD286.EXE access to the 386 registers for hardware breakpoints? Whatever it is, it is compatible with QEMM, and so it must have something that would let one detect that they are running on a virtual 8086. -- sidney markowitz <sidney@saturn.ucsc.edu>
Re: Detecting an 80486
Author: Andrew Rossmann
Date: Tue, 14 Aug 1990 23:19
Date: Tue, 14 Aug 1990 23:19
165 lines
6058 bytes
6058 bytes
In article <3817@altos86.Altos.COM> rcollins@altos86.UUCP (Robert Collins) writes:
>But, there is plenty of software that uses this technique. My CPU
>determination subroutine was written for ROM BIOS, and was obviously
>not intended to run under protected mode. But the makers of these
>products that put the '386 and '486 in V8086 mode, who don't pass control
>of the invalid opcode handler back to the user program, are obviously in
>error. If QEMM fails to do this, it is obviously in error, and will have
>numerous compatibility problems with software using this technique.
QEMM has no problems with the technique. I abandoned the technique in
INFOPLUS when I got hold of a version that does not use invalid opcodes,
yet can still detect a '486. The big reason was because Windows 3 grabs the
interrupt at a higher privilege level. Even if your program directly
modifies (it's copy) of the interrupt table, Win3 reports that your program
has doing something nasty, and aborts it.
>
>I hope this resolves some confusion, and puts to rest any "blame" regarding
>the use of my algorithm in Virtual 8086 mode.
I first replaced the alogorithm because I thought it was locking up
peoples computers, but I've pretty much come to the conclusion that it was
actually the NDP test that was giving problems. (Both tests were done at
the same time before returning to the main program, a situtation corrected
in INFOPLUS 1.25.)
>
>Robert Collins
>Altos Computer Systems
What follows is the new 286/386/486 detection code. This is only that
part, the rest of it is the same as before (for those who have the original
big posting.) After that is the corrected code for the NDP correction.
;!!!!!!!
;!!! Original 286/386 detection code (modified 8/10/90)
;!!! Modified by code supplied by John Levine, apparantly from an Intel
;!!! '486 manual.
;!!!!!!!
pushf ;put flags into CX
pop cx
and cx,0fffh ;mask off upper 4 bits
push cx
popf
pushf
pop ax
and ax,0f000h ;look only at upper 4 bits
cmp ax,0f000h ;88/86 etc.. turn them on
jz badcpu ;not 286/386/486!!!
or cx,0f000h ;force upper 4 bits on
push cx
popf
pushf
pop ax
and ax,0f000h
jz found286 ;bits are zeroed in real mode 286
.386
mov dx,sp ;save current stack position
and sp,not 3 ;dword align to avoid traps
pushfd ;push 32 bit flag
pop eax
mov ecx,eax
xor eax,40000h ;flip AC (alignment check) flag
push eax
popfd
pushfd
pop eax
mov sp,dx
xor eax,ecx ;was bit changed??
test eax,40000h
.286
jz found386 ;if not, is a 386
mov mCPU,f80486 ;must be a 486!!
jmp short CPUID_done
found286:
mov mCPU,f80286
jmp short CPUID_done
found386:
mov mCPU,f80386
jmp short CPUID_done
badcpu:
mov mCPU,funk ;how'd an 8088 get this far?????
CPUID_done:
ret
; Here is the NDP detection code
ndp:
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.
.287
; 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,14h
loop $ ; 17*CX-12
; 17*CX+2
sti
mov ax,ndp_cw ;<< New code
and ax,0f3fh ;<< New code
cmp ax,33fh ;<< New code
je short ndp_01
mov mNDP,fnone
ret
ndp_01:
mov ax,ndp_cw
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
Andrew Rossmann
andyross@ddsw1.MCS.COM
Re: Detecting an 80486
Author: Otto J. Makela
Date: Wed, 15 Aug 1990 00:29
Date: Wed, 15 Aug 1990 00:29
18 lines
1859 bytes
1859 bytes
In article <1990Aug14.025550.17669@esegue.segue.boston.ma.us> johnl@esegue.segue.boston.ma.us (John R. Levine) writes:
The Intel 486 Programmer's manual has sample code to tell the difference
among the 8086, 286, 386, and 486 without any trapping at all. There are
lots of differences other than new instructions. For example, on a PUSH SP,
the 8086 pushes the decremented SP but newer processors push the original
value. When you push the flags register with PUSHF, the 8086 always sets
bits 12-15 to one, the 286 always sets them to zero, and the 386 and 486
store some actual flags there. The 486 uses flag bit 18, which the 386
didn't. Intel provides sample code that is straightforward and doesn't
depend on catching traps. Looks good to me.
Sounds perfect. Does anyone have this online already ? If not, does
anyone who has the 486 programmer's wanna type it in ?
--
* * * Otto J. Makela (otto@jyu.fi, MAKELA_OTTO_@FINJYU.BITNET) * * * * * * *
* Phone: +358 41 613 847, BBS: +358 41 211 562 (CCITT, Bell 2400/1200/300) *
* Mail: Kauppakatu 1 B 18, SF-40100 Jyvaskyla, Finland, EUROPE *
* * * freopen("/dev/null","r",stdflame); * * * * * * * * * * * * * * * * * *
Re: Detecting an 80486
Author: Sidney Markowitz
Date: Wed, 15 Aug 1990 07:46
Date: Wed, 15 Aug 1990 07:46
125 lines
5295 bytes
5295 bytes
In article <1990Aug14.025550.17669@esegue.segue.boston.ma.us> johnl@esegue.segue.boston.ma.us (John R. Levine) writes:
>In article <3817@altos86.Altos.COM> rcollins@altos86.UUCP (Robert Collins) writes:
>>In some article, it was written:
>>+} In my Infoplus program, the 286/386/486 are detected by trapping the
>>+}invalid opcode interrupt, and then trying instructions.
>>[but there are lots of places where this doesn't work very well, either
>>because someone else gets the trap, or because some putatively unused opcodes
>>do secret things.]
>
>The Intel 486 Programmer's manual has sample code to tell the difference
>among the 8086, 286, 386, and 486 without any trapping at all.
;; Program to distinguish between 8086/80286/80386/80486
;; It does not trap illegal opcodes, and so works under virtual 8086
;; systems such as QEMM which cause other methods to crash.
;;
;; Notes: 1) It is possible for a monitor program that is handling a virtual
;; 8086 on an 80386 or 80486 to trap the instructions that access the
;; flags registers and make this code not work. But that would be a lot
;; of work to no purpose, so don't expect a problem in real life systems.
;; 2) This was only tested on an 80386 and an 80486. However, the code
;; for 8086 and 80286 comes from an Intel manual and looks simple
;;
;; Acknowledgements:
;; This program gets its output code from a PC Magazine program that uses
;; invalid opcode trapping and doesn't work under QEMM
;; The code to distinguish 8086 fro 80286 from 80386/486 is adapted from
;; page 22-2 of the Untel 80486 Programmers Manual
;; The code to distiguish between 80386 and 80486 is adapted from pg 3-23
;; of the same manual. Note that the comments in the manual describe
;; the return value backwards.
;; The comments are my own
;;
;; Sidney Markowitz <sidney@saturn.ucsc.edu> or <sidney@ai.mit.edu>
.MODEL TINY
DOS equ 21H
DosPrint equ 09H
DosExit equ 4CH
.DATA
CPUMSG DB 0DH,0AH,'CPU is an 80$'
I86 DB '86$'
I286 DB '286$'
I386 DB '386$'
I486 DB '486$'
CRLF DB 0DH,0AH,'$'
.CODE
ORG 100H
.386p ;; allow 386 style assembler instructions
prog:
mov ah,DosPrint
mov dx,offset CPUMSG
int DOS
;; Test for 8086 by trying to set flags high nibble to 0
;; On an 8086, the nibble will remain all ones
mov dx,offset I86
pushf
pop bx
and bh,0Fh
push bx
popf
pushf
pop ax
and ah,0F0h
cmp ah,0F0h
je TELLCPU ; print out that this is an 8086
;; Test for 80286 by trying to set flags high nibble to all ones.
;; On an 80286, the nibble will remain all zeroes
mov dx,offset I286
or bh,0F0h
push bx
popf
pushf
pop ax
test ah,0F0h
jz TELLCPU
;; 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
;;
;; at this point ax = 0 on a 386
;; ax = 1 on a 486
mov dx,offset I386
test ax,ax
jz TELLCPU
mov dx,offset I486
;; output a message with the result, then exit
TELLCPU:
mov ah,DosPrint
int DOS
mov ah,DosPrint
mov dx,offset CRLF
int DOS
mov al,0
mov ah,DosExit
int DOS
end prog
;;;; end of program ;;;;;;;;
Re: Detecting an 80486
Author: Robert Collins
Date: Wed, 15 Aug 1990 20:48
Date: Wed, 15 Aug 1990 20:48
76 lines
4541 bytes
4541 bytes
In article <1990Aug14.025550.17669@esegue.segue.boston.ma.us> johnl@esegue.segue.boston.ma.us (John R. Levine) writes: > The Intel 486 Programmer's manual has sample code to tell the difference > among the 8086, 286, 386, and 486 without any trapping at all. The code being referenced is on page 22-12 of the Intel "i486 Microprocessor Programmer's Reference Manual" (PRM) PN 240486. The code on this page does NOT detect the difference between '386 and '486. > There are > lots of differences other than new instructions. For example, on a PUSH SP, > the 8086 pushes the decremented SP but newer processors push the original > value. Which is exactly how my subroutine detects an 8086/80186. Since the 8086 has no capability to trap invalid opcodes, you must do this first before using opcodes not in the CPU. > When you push the flags register with PUSHF, the 8086 always sets > bits 12-15 to one, the 286 always sets them to zero, and the 386 and 486 > store some actual flags there. The 486 uses flag bit 18, which the 386 > didn't. Intel provides sample code that is straightforward and doesn't > depend on catching traps. Looks good to me. I looked for documentation to verify this and found that the '286 manual makes no mention of any flags differences with the 8086. That's not to say they don't exist. However, in the '386 PRM the difference of the flags register of the '386 vs. 8086 IS documented; but in differences with the '286 no flags difference is documented. > Sounds perfect. Does anyone have this online already ? If not, does > anyone who has the 486 programmer's wanna type it in ? You peaked my curiocity. I typed it in. If I am following this discussion correctly, the intension is to have a program that detects the differences between CPU's that will work in real mode, protected mode, and virtual 8086 mode. The algorithm that Intel provides makes no attempt to detect the '486. So, with that in mind, I took their same approach, and applied it to bit 18 of EFLAGS in an attempt to detect the '486. This approach won't work in real mode, but does in protected mode. Bit18 gets latched on the '486 only in protected mode, and is always cleared in real mode. On the '386, bit18 is always cleared. Therefore, this technique WON'T work to detect the '486, and is therefore less than "perfect." Furthermore, it might be noted, that the algorithm Intel provided is documented as follows: "This code is intended for application programs executing in real-address mode" (P. 22-11). Now, back to my original point of mode-independant CPU detection. As I pointed out in a previous posing, I believe that the manufacturers of QEMM, et. al. who don't pass control of INT6 to the user-program are in error, and in jeopordy of being "incompatible" with software that was written in accordance with the original '386 PRM. There is still a lot of code out there that hasn't been changed. (Hopefully this won't sound like "my code is better than your's...but.) The code I wrote is totally mode-independant. It works in real mode. It works in protected mode (with some obvious changes to the interrupt trapping mechanism). And it works in VM86 mode -- provided the host kernal passes control to the INT6 handler defined in the V86 task. I think it should be obvious that software that doesn't follow this convention is circumventing the whole idea of V86 tasks. Imagine if INT0, INT1, INT3, or INT4 weren't passed to the V86 task! In fact, I would go so far as to say that ALL CPU-generated interrupts that are defined on the 8086 and 80186 should be passed to the V86 task... and guess what? That list includes INT6 -- invalid opcode. -- "Worship the Lord your God, and serve him only." Mat. 4:10 Robert Collins UUCP: ...!sun!altos86!rcollins HOME: (408) 225-8002 WORK: (408) 432-6200 x4356
Re: Detecting an 80486
Author: John R. Levine
Date: Thu, 16 Aug 1990 17:08
Date: Thu, 16 Aug 1990 17:08
29 lines
2124 bytes
2124 bytes
In article <3827@altos86.Altos.COM> you write:
>
>In article <1990Aug14.025550.17669@esegue.segue.boston.ma.us> [I wrote]:
>> The Intel 486 Programmer's manual has sample code to tell the difference
>> among the 8086, 286, 386, and 486 without any trapping at all.
>
>The code being referenced is on page 22-12 of the Intel "i486 Microprocessor
>Programmer's Reference Manual" (PRM) PN 240486. The code on this page does
>NOT detect the difference between '386 and '486.
Actually, the code to tell a 386 from a 486 is on page 3-42.
>> The 486 uses flag bit 18, which the 386 didn't.
>Bit18 gets latched on the '486 only in protected mode, and is always cleared
>in real mode. On the '386, bit18 is always cleared. Therefore, this
>technique WON'T work to detect the '486, ...
The manual says that the trap provoked by bit 18, alignment check, won't
happen in real mode, but nowhere that I can see do they say anything about
bit 18 being forced to zero. (To make alignment checks happen,you have to
set the enable bit, bit 18 in CR0, the processor must be in user mode at CPL
3, and you must set flag bit 18.) Has someone tried this and found that you
can't set bit 18 in real mode on a 486? (I only have a 386, not a 486, so I
can't try it myself.) If flag bit 18 really doesn't work, it might work to
try wiggling bit 18 in CR0 which is unimplemented on the 386.
Regards,
John Levine, johnl@esegue.segue.boston.ma.us, {spdcc|ima|world}!esegue!johnl
Re: Detecting an 80486
Author: Robert Collins
Date: Mon, 20 Aug 1990 16:09
Date: Mon, 20 Aug 1990 16:09
29 lines
2027 bytes
2027 bytes
Whether trapping invalid opcodes, or looking for differences in the flags register, either algorithm has its pitfalls. The algorithm trapping invalid opcodes can fail in V86 mode if the kernal doesn't pass control to the to the applications invalid opcode handler. Reportedly, QEMM and WIN386 don't do this. As a result, both of these programs are in joepordy of not being 100% "PC" compatible. On the other hand, looking for differences in the flags register has its problems. Though Intel supplied the code, it doesn't appear to be DOCUMENTED anywhere in Intel literature the default state of these flag bits. So the problem with this technique is that it relies on undocumented features of the chip. In is commonly believed that C&T and some other chip manufacturers are working on a '386 and/or '486 clone chip. If the polarity of these flag bits aren't documented, then there is no guarantee that clone chips, or even Intel chips will not change their polarity. I can certainly see advantages to both techniques. For now, I feel safest using a technique that relies on documented features of the chip. In the future, that opinion might change. After I get a chance to evaluate some of the clone chips, I might change to the Intel algorithm. -- "Worship the Lord your God, and serve him only." Mat. 4:10 Robert Collins UUCP: ...!sun!altos86!rcollins HOME: (408) 225-8002 WORK: (408) 432-6200 x4356
Re: Detecting an 80486
Author: Robert Collins
Date: Mon, 20 Aug 1990 16:26
Date: Mon, 20 Aug 1990 16:26
46 lines
2892 bytes
2892 bytes
In article <9008161307.AA11621@esegue.segue.boston.ma.us> johnl@esegue.segue.boston.ma.us (John R. Levine) writes: > >can't try it myself.) If flag bit 18 really doesn't work, it might work to >try wiggling bit 18 in CR0 which is unimplemented on the 386. > You can't access CR0 in CPL3. So setting any bit in CR0 in V86 mode will be trapped. I have tried setting bit18 in EFLAGS, and found it not to be latched in real mode. I found this astonishing, and therefore posted my results. Since that posting I did further testing, and found my first conclusion to be wrong. ----------------------------------- Continuation of a posting that I didn't get a chance to finish: ----------------------------------- As I said in another posting, trapping invalid opcodes, and setting undefined bits in EFLAGS both have their pitfalls, and both are seemingly valid techniques for determining CPU type. However, setting undefined bits in EFLAGS relies on (apparently) undocumented default settings for these bits in EFLAGS. Deep down inside, I still feel more comfortable trapping invalid opcodes because this method relies on documented differences in the CPUs. Another consideration is upward compatibility, and expandability. What about the '586? Will the '586 behave in the same manner -- that isn't documented? Since Intel published the CPU detection code themselves, that is a good chance that the '586 will behave in the same manner. But this conclusion assumes that the guy that wrote the code has good connections in the CPU department. If have found that Intel isn't like this at ALL. One hand doens't know, nor have access to, what the other hand is doing. So, just because Intel published the code, and the code relies on undocumented features of the chip, doesn't guarantee the code will work on the '586. Chosing which algorithm to use now becomes a consideration of the '586. Which probability is greater: the '586 having another new non-intrusive opcode, or the '586 having another new bit in EFLAGS? -- "Worship the Lord your God, and serve him only." Mat. 4:10 Robert Collins UUCP: ...!sun!altos86!rcollins HOME: (408) 225-8002 WORK: (408) 432-6200 x4356
Re: Detecting an 80486
Author: John R. Levine
Date: Tue, 21 Aug 1990 17:47
Date: Tue, 21 Aug 1990 17:47
33 lines
2648 bytes
2648 bytes
In article <3839@altos86.Altos.COM> you write:
>Deep down inside, I still feel more comfortable trapping invalid opcodes
>because this method relies on documented differences in the CPUs.
Do keep in mind that every Intel CPU appears to have secret, undocumented
opcodes. The 8086 has POP CS (useful, huh?) The 286 has the infamous
LOADALL. Early 386 chips had bit insert and extract instructions which they
removed when they noticed that you could do the same thing faster via double
register shifts. I expect that the 386 and 486 also have something like
LOADALL to aid testing. History suggests that when clone CPUs come out, the
vendors won't leave well enough alone and will add new instructions of their
own, viz. the V20 and V30 which have some instruction that puts you into Z80
mode. Expecting undocumented opcodes to fail seems to me a fragile way to
test CPU types, independent of the difficulty of persuading operating
systems to give you control back after the trap, since they might do
something else already.
Even worse, I can easily imagine that 386 operating systems will for upward
compatibility purposes simulate the new 486 instructions, albeit very
slowly, so that any test that depended on BSWAP not working would fail.
I can cite lots of cases of this in the past, such as the Microvax which
only implements about 80% of the Vax instruction set in hardware with the
obscure instructions simulated by the operating system, or early versions of
VM/370 which when run on a 360/67 would simulate the new 370 instructions so
they could debug new 370 software. For that matter, consider the way that
most 386 operating systems simulate a missing 387.
The 486 manual explicitly documents the flag peculiarities that their sample
code uses to test CPU types. They're not totally foolish, I expect that
they'll try not to break the test code in the future chips.
Regards,
John Levine, johnl@esegue.segue.boston.ma.us, {spdcc|ima|world}!esegue!johnl
Re: Re: Detecting an 80486
Author: Andrew Rossmann
Date: Tue, 21 Aug 1990 22:34
Date: Tue, 21 Aug 1990 22:34
507 lines
13566 bytes
13566 bytes
In article <3839@altos86.Altos.COM> rcollins@altos86.UUCP (Robert Collins) writes:
>In article <9008161307.AA11621@esegue.segue.boston.ma.us> johnl@esegue.segue.boston.ma.us (John R. Levine) writes:
>>
>>can't try it myself.) If flag bit 18 really doesn't work, it might work to
>>try wiggling bit 18 in CR0 which is unimplemented on the 386.
>>
>
>You can't access CR0 in CPL3. So setting any bit in CR0 in V86 mode will
>be trapped. I have tried setting bit18 in EFLAGS, and found it not to
>be latched in real mode. I found this astonishing, and therefore posted
>my results. Since that posting I did further testing, and found my first
>conclusion to be wrong.
I just took a 'peek' at Nortons SI, version 5 (the new one). It detects
the '486 by fiddling with bit 18! So, apparantly, they must think it's OK,
although I should think they have probably tested it, too.
What follows is the latest version of the CPU/NDP code from INFOPLUS.
I've slightly tweaked the '486 detection, and further changed the NDP
detection. I hope to get that problem fixed soon, if this doesn't do it.
Some people have said that the 386 w/ AMI BIOS and no 387 is giving the
most problems.
Andrew Rossmann
andyross@ddsw1.MCS.COM
P.S.> Version 1.3 of INFOPLUS is still under development.
;--------------------------------------------------------------------
;
; INFOPLUS.ASM
;
; Version 1.30
;
; Four subprograms used by INFOPLUS.PAS:
;
; CPUID - identifies host CPU and NDP (if
; any)
; DISKREAD - reads absolute sectors from disk
; LONGCALL - calls a routine using a CALL FAR
; ATIINFO - for accessing ATI VGAWonder cards
;
; Originally by:
; Steve Grant
; Long Beach, CA
; January 13, 1989
;
; mods by Andrew Rossmann (8/19/90)
;--------------------------------------------------------------------
.286P
.8087
public CPUID, DISKREAD, LONGCALL, ATIINFO, AltIntr
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 " " " "
; also, the test type byte should be 'C', 'N' or 'W' to execute the
; CPU or NDP or Weitek tests.
;
; 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
; byte = Weitek presence
; byte = test type (C, N, or W)
mCPU equ byte ptr [bx]
mMSW equ word ptr [bx + 1]
mGDT equ [bx + 3]
mIDT equ [bx + 9]
mchkint equ byte ptr [bx + 15]
mNDP equ byte ptr [bx + 16]
mNDPCW equ word ptr [bx + 17]
mWeitek equ byte ptr [bx + 19]
mtest equ byte ptr [bx + 20]
f8088 equ 0
f8086 equ 1
fV20 equ 2
fV30 equ 3
f80188 equ 4
f80186 equ 5
f80286 equ 6
f80386 equ 7
f80486 equ 8
funk = 0FFH
false equ 0
true equ 1
push bp
mov bp,sp
push ds
lds bx,[bp + 4]
cmp mtest, 'C'
jnz skipcpu
call cpu
call chkint
skipcpu:
cmp mtest, 'N'
jnz skipndp
call ndp
skipndp:
cmp mtest, 'W'
jnz skipweitek
call weitek
skipweitek:
pop ds
pop bp
ret 4
;--------------------------------------------------------------------
cpu:
; interrupt of multi-prefix string instruction
mov mCPU,funk ;set CPU type to unknown
sti
mov cx,0FFFFH
rep lods byte ptr es:[si]
jcxz short cpu_02
call piq
cmp dx,4
jg short cpu_01
mov mCPU,f8088
ret
cpu_01:
cmp dx,6
jne short cpu_01a
mov mCPU,f8086
cpu_01a:
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
jg short cpu_03
mov mCPU,fV20
ret
cpu_03:
cmp dx,6
je cpu_03a
jmp CPUID_done
cpu_03a:
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
jg short cpu_05
mov mCPU,f80188
ret
cpu_05:
cmp dx,6
jne short CPUID_done
mov mCPU,f80186
ret
;First, grab some tables
cpu_06:
smsw mMSW
sgdt mGDT
sidt mIDT
;!!!!!!!
;!!! Original 286/386 detection code (modified 8/10/90)
;!!! Modified by code supplied by John Levine, apparantly from an Intel
;!!! '486 manual.
;!!!!!!!
pushf ;put flags into CX
pop cx
and cx,0fffh ;mask off upper 4 bits
push cx
popf
pushf
pop ax
and ax,0f000h ;look only at upper 4 bits
cmp ax,0f000h ;88/86 etc.. turn them on
jz badcpu ;not 286/386/486!!!
or cx,0f000h ;force upper 4 bits on
push cx
popf
pushf
pop ax
and ax,0f000h
jz found286 ;bits are zeroed in real mode 286
;
;since we probably have have a 386 or 486 by now, we need to do some 32-bit
;work. Detect the 486 by seeing if the Alignment Check flag is settable. This
;flag only exists on the '486.
;
.386
and esp,0FFFFh ;use only 64K stack
mov edx,esp ;save current stack position
and esp,0FFFCh ;dword align to avoid traps
pushfd ;push 32 bit flag
pop eax
mov ecx,eax ;save current flags
xor eax,40000h ;flip AC (alignment check) flag
push eax
popfd
pushfd
pop eax
xor eax,ecx ;eliminate all but AC bit
push ecx ;restore flags
popfd
mov esp,edx ;restore stack position
test eax,40000h ;is bit set?
.286
jz found386 ;if not, is a 386
mov mCPU,f80486 ;must be a 486!!
jmp short CPUID_done
found286:
mov mCPU,f80286
jmp short CPUID_done
found386:
mov mCPU,f80386
jmp short CPUID_done
badcpu:
mov mCPU,funk ;how'd an 8088 get this far?????
CPUID_done:
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 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.
.287
cli ;no interrupts during this test
;
; if there are problems, try uncommenting the following lines
; xor ax,ax
; out 0f0h,al
finit ;initialize NDP
mov cx,2
loop $
fstcw ndp_cw ;store status word in ndp_cw
mov cx,14h
loop $
sti
mov ax,ndp_cw ;check for valid status word
cmp ah,3 ;is NDP present?
je short ndp_01 ;if 3, must be there
mov mNDP,fnone
jmp short ndp_done
ndp_01:
cmp ax,03FFH ;check if 8087
jne short ndp_02
mov mNDP,f8087
jmp short ndp_04
ndp_02:
.287
cmp ax,037FH ;check if 286/387/486
jne short ndp_05 ;must be garbage
;detect 287 or 387
fld1 ;Load +1.0 onto NDP stack
fldz ;Load +0.0 onto NDP stack
fdiv ;do +1/0
fld1 ;Load +1.0 onto NDP stack
fchs ;Change to -1.0
fldz ;Load +0.0 onto NDP stack
fdiv ;do -1/0
fcom ;compare
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
fstcw mNDPCW ;save status for INFOPLUS
ret
ndp_05:
mov mNDP,funk
ndp_done:
ret
; This checks to see if the BIOS reports a Weitek math coprocessor. This should
; only be called if a 386 or 486 is found.
; NOTE!! This may not work with all computers!!
fnoWeitek equ 0
fWeitek equ 1
fWeitek_real equ 81h
weitek:
.386
xor eax,eax ;zero everything
int 11h ;do equipment check
test eax,01000000h ;check bit 24, set if Weitek present
je no_weitek
mov mWeitek,fWeitek
test eax,0800000h ;check bit 23, set if Weitek can be
je weitek_done ; addressed in real mode
mov mWeitek,fWeitek_real
jmp short weitek_done
no_weitek:
mov mWeitek,fnoWeitek
weitek_done:
ret
.286
CPUID 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_sw dw ?
DATA ends
end
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads