brad@looking.on.ca (Brad Templeton) (07/19/90)
For status programs that wish to detect what sort of processor they are running on, does anybody have the scoop on official detection procedure for an 80486? Assuming, for example, that one already knows that one is on a 386 or better. I already have code to detect 8086, 8088, 80186, 80286, 80386, 8087, 80287 and 80387 -- I want to make it complete. -- Brad Templeton, ClariNet Communications Corp. -- Waterloo, Ontario 519/884-7473
dodson@convex.com (Dave Dodson) (07/19/90)
In article <1990Jul19.025150.6150@looking.on.ca> brad@looking.on.ca (Brad Templeton) writes: >For status programs that wish to detect what sort of processor they >are running on, does anybody have the scoop on official detection >procedure for an 80486? Assuming, for example, that one already knows >that one is on a 386 or better. I already have code to detect >8086, 8088, 80186, 80286, 80386, 8087, 80287 and 80387 -- I want to make >it complete. PC Magazine, July 1990, pages 425-426, tells how to separate 80286s, 80386s, 80486s, and lesser chips. It mentions a March 13 column that told how to detect a math coprocessor, and promises a future column to identify math coprocessor types. It glosses over the method of separating the lesser chips: 8088s, 8086s, 80186s, and NEC V20s and V30s. Apparently you know how to do this (or at least some of it); could you share the method or a reference? ---------------------------------------------------------------------- Dave Dodson dodson@convex.COM Convex Computer Corporation Richardson, Texas (214) 497-4234
pete@bally.Bally.COM (exilied in my own office) (07/20/90)
In article <1990Jul19.025150.6150@looking.on.ca> brad@looking.on.ca (Brad Templeton) writes: >For status programs that wish to detect what sort of processor they >are running on, does anybody have the scoop on official detection >procedure for an 80486? Assuming, for example, that one already knows >that one is on a 386 or better. I already have code to detect >8086, 8088, 80186, 80286, 80386, 8087, 80287 and 80387 -- I want to make >it complete. I spoke with an engineer at SCO (whose remains unnamed) who sadly told me that there was no way to tell an 80386 from an 80486. SCO wanted to market a higher-priced version of Xenix for 80486, but they could find no way to detect the difference. Pete Gregory : uucp: uunet!bally!pete | Bally Systems : domain: pete@bally.bally.com ---|--- 5270 Neil Rd, Suite 301: phone: 702-689-2485 | Reno, NV 89502 : FAX: 702-689-2417 | "God so loved the world, that he gave his only begotten son, that whosoever believes in him should not perish, but have everlasting life." John 3:16
johnl@esegue.segue.boston.ma.us (John R. Levine) (07/20/90)
In article <315@bally.Bally.COM> you write: >I spoke with an engineer at SCO (whose remains unnamed) who sadly >told me that there was no way to tell an 80386 from an 80486. Had anyone at SCO bothered to read the Intel i486 Programmer's Reference, they would have found several straightforward ways to tell the difference. The Intel manual even gives sample code. >SCO wanted to market a higher-priced version of Xenix for 80486, but they >could find no way to detect the difference. Sounds like it's just as well they couldn't figure out how. What a crock that would have been. -- John R. Levine, Segue Software, POB 349, Cambridge MA 02238, +1 617 864 9650 johnl@esegue.segue.boston.ma.us, {ima|lotus|spdcc}!esegue!johnl Marlon Brando and Doris Day were born on the same day.
hv@uwasa.fi (Harri Valkama LAKE) (07/20/90)
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: >>For status programs that wish to detect what sort of processor they >>are running on, does anybody have the scoop on official detection >>procedure for an 80486? Assuming, for example, that one already knows >>that one is on a 386 or better. I already have code to detect >>8086, 8088, 80186, 80286, 80386, 8087, 80287 and 80387 -- I want to make >>it complete. > >I spoke with an engineer at SCO (whose remains unnamed) who sadly >told me that there was no way to tell an 80386 from an 80486. I have some assembler source that claims to do the detection but I have not tested it. If somebody wants to check it out please free to ftp it from chyde.uwasa.fi (128.214.12.3). It is in pc/source and called 80486.asm -- == Harri Valkama ============================================================== | harri.valkama@wmac00.uwasa.fi hv@uwasa.fi hv@flame.uwasa.fi hv@nic.funet.fi | | University of Vaasa, PO BOX 700, 65101 VAASA, Finland (fax: +358 61 248465) | = Moderating at chyde.uwasa.fi (128.214.12.3) & nic.funet.fi (128.214.6.100) ==
erik@westworld.esd.sgi.com (Erik Fortune) (07/21/90)
In article <1990Jul20.011633.1686@esegue.segue.boston.ma.us>, johnl@esegue.segue.boston.ma.us (John R. Levine) writes: >In article <315@bally.Bally.COM> you write: >>I spoke with an engineer at SCO (whose remains unnamed) who sadly >>told me that there was no way to tell an 80386 from an 80486. > >Had anyone at SCO bothered to read the Intel i486 Programmer's Reference, >they would have found several straightforward ways to tell the difference. >The Intel manual even gives sample code. > >>SCO wanted to market a higher-priced version of Xenix for 80486, but they >>could find no way to detect the difference. > >Sounds like it's just as well they couldn't figure out how. What a crock >that would have been. Come on! Give the SCO bashing a rest, will you. Now it's unsubstantiated rumors and snide asides. How low will you people go? -- Erik (erik@sgi.com)
brad@looking.on.ca (Brad Templeton) (07/21/90)
Beware of the 486 detection method given in PC Magazine July 1990. I just tried it, and attempts to access the 386 control register will cause you to trap and abort in protected mode DOS systems like DOS-Merge. So far the only two methods I have had suggested are the method of PC Magazine (try to set the cache write through bit in CR0 and see if you did it) and the trap the Int 6 illegal OP trap yourself, and then try to execute a 486 instruction. See if you trap. I don't like either of those methods. Perhaps I will see if a test like PC magazine's will work if you make sure that you don't do it in virtual 8086 mode. You do want to know if you have an 386 in virtual 8086 mode, because you can still use 32 bit registers in that mode. I don't know if there's any special 486 power you would want in virtual 8086 mode or not. -- Brad Templeton, ClariNet Communications Corp. -- Waterloo, Ontario 519/884-7473
andyross@ddsw1.MCS.COM (Andrew Rossmann) (07/21/90)
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: >>For status programs that wish to detect what sort of processor they >>are running on, does anybody have the scoop on official detection >>procedure for an 80486? Assuming, for example, that one already knows >>that one is on a 386 or better. I already have code to detect >>8086, 8088, 80186, 80286, 80386, 8087, 80287 and 80387 -- I want to make >>it complete. >I spoke with an engineer at SCO (whose remains unnamed) who sadly >told me that there was no way to tell an 80386 from an 80486. >SCO wanted to market a higher-priced version of Xenix for 80486, but they >could find no way to detect the difference. >Pete Gregory : uucp: uunet!bally!pete | 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!) If anyone is interested, I can email a copy, or post it. Andrew Rossmann andyross@ddsw1.MCS.COM
roelofs@amelia.nas.nasa.gov (Cave Newt) (07/21/90)
brad@looking.on.ca (Brad Templeton) writes: >>For status programs that wish to detect what sort of processor they >>are running on, does anybody have the scoop on official detection >>procedure for an 80486? Assuming, for example, that one already knows >>that one is on a 386 or better. I already have code to detect >>8086, 8088, 80186, 80286, 80386, 8087, 80287 and 80387 -- I want to make >>it complete. In article <104096@convex.convex.com> dodson@convex.com (Dave Dodson) writes: >PC Magazine, July 1990, pages 425-426, tells how to separate 80286s, 80386s, >80486s, and lesser chips. This discussion came up recently in a related newsgroup whose name escapes me at the moment (egad! there's only about a dozen of them nowadays...), but I suspect it was one of the OS/2 groups. At any rate, the point (which has not been mentioned here so far) was that all the proposed methods so far generate a General Protection fault if the processor is currently in protected mode (including, I believe, PC Magazine's). To the best of my knowledge, nobody has yet discovered how to do this (this is relevant to an msdos group since a number of DOS extenders such as 386^max run DOS in protected mode). I could, of course, be wrong about this. :)
Ralf.Brown@B.GP.CS.CMU.EDU (07/21/90)
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
dhinds@portia.Stanford.EDU (David Hinds) (07/23/90)
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
davidsen@sixhub.UUCP (Wm E. Davidsen Jr) (07/23/90)
In article <315@bally.Bally.COM> pete@bally.UUCP (exilied in my own office) writes: | I spoke with an engineer at SCO (whose remains unnamed) who sadly | told me that there was no way to tell an 80386 from an 80486. | | SCO wanted to market a higher-priced version of Xenix for 80486, but they | could find no way to detect the difference. This is a lot of hogwash. There are several instructions in the 486 not in the 386, and a number of other instructions which have diferent timing between the two processors. Detecting which you have is a reasonable things to do. Oh, and the 486 has some FPU instructions not in the 387, so you could tell that way, too. Either he's incompetent or you misunderstood him. Perhaps he meant that the 486 offers nothing but speed over the 386 from a standpoint of actual capability, and SCO didn't see a way to market a version with extra features which would justify a separate product. -- bill davidsen - davidsen@sixhub.uucp (uunet!crdgw1!sixhub!davidsen) sysop *IX BBS and Public Access UNIX moderator of comp.binaries.ibm.pc and 80386 mailing list "Stupidity, like virtue, is its own reward" -me
andyross@ddsw1.MCS.COM (Andrew Rossmann) (07/24/90)
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
rcollins@altos86.Altos.COM (Robert Collins) (08/14/90)
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
johnl@esegue.segue.boston.ma.us (John R. Levine) (08/14/90)
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.
sidney@saturn.ucsc.edu (Sidney Markowitz ) (08/14/90)
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>
andyross@ddsw1.MCS.COM (Andrew Rossmann) (08/15/90)
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
otto@tukki.jyu.fi (Otto J. Makela) (08/15/90)
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); * * * * * * * * * * * * * * * * * *
sidney@saturn.ucsc.edu (Sidney Markowitz ) (08/15/90)
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 ;;;;;;;;
rcollins@altos86.Altos.COM (Robert Collins) (08/16/90)
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
johnl@esegue.segue.boston.ma.us (John R. Levine) (08/17/90)
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
sidney@saturn.ucsc.edu (Sidney Markowitz ) (08/17/90)
In article <3827@altos86.Altos.COM> rcollins@altos86.UUCP (Robert Collins) writes: > >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. The code to detect the difference between the '386 and '486 is hidden away on page 3-42. It uses bit 18 of EFLAGS. The code that I posted that is derived from that worked in real mode and in virtual 8086 mode (for the latter, at least it worked under QEMM 5.0, which I assume means it was in VM 8086 mode). I don't know why the code you tried didn't work in real mode. You might compare the two to find out why. I have not tried mine out in protected mode. -- sidney markowitz <sidney@saturn.ucsc.edu>
rcollins@altos86.Altos.COM (Robert Collins) (08/20/90)
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
rcollins@altos86.Altos.COM (Robert Collins) (08/20/90)
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
johnl@esegue.segue.boston.ma.us (John R. Levine) (08/22/90)
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
andyross@ddsw1.MCS.COM (Andrew Rossmann) (08/22/90)
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
bright@Data-IO.COM (Walter Bright) (08/23/90)
I don't have an 80486 manual, but perhaps a more reliable way to detect a 486 would be to detect a difference between an 80487 and a 387. Since 486's always have a 487 on board, this could work. Are there any differences between a 387 and a 487? Are FWAITs *never* necessary with a 487?
steveha@microsoft.UUCP (Steve Hastings) (08/24/90)
In article <9008211347.AA05979@esegue.segue.boston.ma.us> johnl@esegue.segue.boston.ma.us (John R. Levine) writes: >History suggests that when clone CPUs come out, the >vendors won't leave well enough alone and will add new instructions of their >own I wish that someone would add a "version" command, that would identify the chip. Even if Intel doesn't want to add such a feature, the clone manufacturers could add it and make a de facto standard. It is not an earthshakingly important issue, but how hard could it be to do this? -- Steve "I don't speak for Microsoft" Hastings ===^=== ::::: uunet!microsoft!steveha steveha@microsoft.uucp ` \\==|
dmurdoch@watstat.uwaterloo.ca (Duncan Murdoch) (08/25/90)
In article <2664@dataio.Data-IO.COM> bright@Data-IO.COM (Walter Bright) writes: >I don't have an 80486 manual, but perhaps a more reliable way to detect >a 486 would be to detect a difference between an 80487 and a 387. Since >486's always have a 487 on board, this could work. > >Are there any differences between a 387 and a 487? Great idea! Yes, there's a difference: the FINIT clears the error pointers in the 486, but not in the 387. That should be a really easy test! >Are FWAITs *never* necessary with a 487? The 486 manual says you need them if you want to be sure that operands to floating point instructions are unchanged when an error interrupt occurs. The example given is FILD count INC count FSQRT where an error occuring during the FILD won't be detected until after count has been incremented, making recovery difficult or impossible. A wait may be necessary after a store to memory. For example, FSAVE state MOV ax,state[6] won't work, because the state won't be completely saved before the MOV reads it. I'm not sure about other write instructions - the manual doesn't seem to discuss the problem for them. I'd guess that the explicit wait is necessary, since most of them take dozens of cycles to complete. The only exception is the FST, which is supposed to take only 6-8 cycles. Duncan Murdoch dmurdoch@watstat.waterloo.edu
johnl@esegue.segue.boston.ma.us (John R. Levine) (08/27/90)
In article <2664@dataio.Data-IO.COM> you write: >Are there any differences between a 387 and a 487? Not many. The instruction sets seem to be the same. There are some minor changes in the state after a reset. There is one new status bit that controls whether exceptions cause an internal interrupt 16 as Intel recommends or wiggle an external pin to be fed back through the external interrupt controller for PC compatibility. This seems hard to test, since a system designer is free to wire up the external interrupt any way he wants. >Are FWAITs *never* necessary with a 487? Funny you should ask. In general, no, but the 387 doesn't need them either. Despite what the 387 manual says, all memory accesses to the 387 are fully interlocked with the 386, and loading and storing 387 data never causes a race condition. You'll notice that no 386 C compiler ever emits an FWAIT. (I confirmed this with an Intel designer in Santa Clara, who said it was he who told all 387 compiler writers to dispense with the waits. It was a surprise to the 387 support people in Oregon.) The only time an FWAIT is useful is to force a pending floating point interrupt, both on the 387 and the 486. Regards, John Levine, johnl@esegue.segue.boston.ma.us, {spdcc|ima|world}!esegue!johnl
NU013809@NDSUVM1.BITNET (Greg Wettstein) (08/27/90)
It is too bad that the original 8086(8) designers did not have omnipotent foresight and forsee how well CMOS would scale into the future. If they would the following instruction would have been set in microcode: LMTW AX For those of us not omnipotent this translates to Load Machine Type Word. Where AX receives: (0 = 8086, 1 = 8088, 2 = 80186, 3 = 80286, 4 = 80386) The only concern then is what happens when the number of processors released exceeds the range of an unsigned 16 bit integer. ;-) As always, Dr. G.W. Wettstein Roger Maris Cancer Center Computing Facility UUCP: uunet!plains!wind!greg INTERNET: greg%wind.uucp@plains.nodak.edu Phone: 701-234-2833 `The truest mark of a man's wisdom is his ability to listen to other men expound their wisdom.'
mikey@quiche.cs.mcgill.ca (Michael GALLOP) (08/28/90)
In article <2664@dataio.Data-IO.COM> bright@Data-IO.COM (Walter Bright) writes: >I don't have an 80486 manual, but perhaps a more reliable way to detect >a 486 would be to detect a difference between an 80487 and a 387. Since >486's always have a 487 on board, this could work. > >Are there any differences between a 387 and a 487? Ok, here's my 2 cents. There is no difference between a 387 and a 487 The 486 chip is supposed to be a new improved 386 with an internal 8k cache. In previous postings people have asked why can't the clone manufacturers put a 'ver' instruction on the chip. Well NOBODY has a working clone of the 386, let alone a 486. There is a company in California theat is reputadely close but, in my book close only counts in horse shoes and hand grenades. There do seem to be, however a few undocumented instructions in the early versions of the chip. But who knows? > >Are FWAITs *never* necessary with a 487? In theory no.....But who knows. Odds are they aren't unless you are into serious vector prcessing, of more than 32k of instructions. So that the cache is full.. but now I'm guessing, I've never run into that problem. -- | mikey@calvin.cs.mcgill.ca | "Life is just a linear Regression to Chaos," | | Mike Gallop | "...Poured through a Klein bottle" | | You think my profs know who I am to disclaim me?!?? | |"Stealing from one author is plagarism....Stealing from many is research" |
jal@acc (John Lauro) (08/28/90)
In article <4562NU013809@NDSUVM1> NU013809@NDSUVM1.BITNET (Greg Wettstein) writes: >It is too bad that the original 8086(8) designers did not have omnipotent >foresight and forsee how well CMOS would scale into the future. If they >would the following instruction would have been set in microcode: > > LMTW AX > >For those of us not omnipotent this translates to Load Machine Type Word. >Where AX receives: (0 = 8086, 1 = 8088, 2 = 80186, 3 = 80286, 4 = 80386) Better yet, AH as cpu, and AL as version of CPU. > >The only concern then is what happens when the number of processors >released exceeds the range of an unsigned 16 bit integer. ;-) > The 386 and I assume 486 do something like that, but only at power up. Check your manuals on conditions after reset. Too bad all 386/486 BIOS makers don't stuff that information somewhere.... An instruction like LMTW would be much better. - John Lauro University of Michigan - Flint
strobl@gmdzi.UUCP (Wolfgang Strobl) (08/28/90)
jal@acc (John Lauro) writes: >> ... >The 386 and I assume 486 do something like that, but only at power up. >Check your manuals on conditions after reset. Too bad all 386/486 >BIOS makers don't stuff that information somewhere.... An instruction >like LMTW would be much better. From page 93, section 9.5, table 6.2 (Register Values after Reset) of "i486(tm) microprocessor, order no. 240440-001, intel): Register Initial Value -------- ------------- ... EDX 0004 + Revision ID The 80386 Programmer's Reference Manual (ISBN 1-55512-022-9) says: Contents of EDX after RESET 31 23 15 7 +-------------------+-----------+-------------+ ! undefined ! DH ! DL ! ! !DEVICE ID ! STEPPING ID ! ! ! 3 ! (UNIQUE) ! +-------------------+-----------+-------------+ I would like to get to the above "Revision ID" without replacing the BIOS of my machine. Are you sure that all BIOS makers throw this information away? I have just started to disassemble a few BIOSes in order to see what they do after reset, but haven't found anything, yet. Wolfgang Strobl #include <std.disclaimer.hpp>
rcollins@altos86.Altos.COM (Robert Collins) (08/28/90)
In article <1990Aug27.222630.26146@caen.engin.umich.edu> jal@acc (John Lauro) writes: >> >>For those of us not omnipotent this translates to Load Machine Type Word. >>Where AX receives: (0 = 8086, 1 = 8088, 2 = 80186, 3 = 80286, 4 = 80386) > >The 386 and I assume 486 do something like that, but only at power up. >Check your manuals on conditions after reset. Too bad all 386/486 >BIOS makers don't stuff that information somewhere.... An instruction Better BIOSs do. MR BIOS does. -- "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