[comp.os.msdos.programmer] Detecting an 80486

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