🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.os.msdos.programmer
Article #1652

Re: Detecting an 80486

#1652
From: Andrew Rossmann
Date: Mon, 23 Jul 1990 22:31
637 lines
19276 bytes
In article <26a858b9@ralf> Ralf.Brown@B.GP.CS.CMU.EDU writes:
>In article <1990Jul20.230335.22816@ddsw1.MCS.COM>, andyross@ddsw1.MCS.COM (Andrew Rossmann) wrote:
>}In article <315@bally.Bally.COM> pete@bally.UUCP (exilied in my own office) writes:
>}>In article <1990Jul19.025150.6150@looking.on.ca> brad@looking.on.ca (Brad Templeton) writes:
>}  In my Infoplus program, the 286/386/486 are detected by trapping the

>Unfortunately, your program will fail miserably on my system (and about a
>million others), because I am running QEMM-386.  QEMM runs in protected mode
>with DOS in virtual-86 mode.  Any invalid opcode exception that your
>program generated invokes the QEMM exception handler, NOT YOURS.  And the
>QEMM exception handler gives the choice of terminating or rebooting.  I
>expect similar behavior from 386^Max, CEMM, AllCharge386, MICEMM, ....
>--

  Since my program is grabbing the interrupt, there is no way QEMM (or
whatever) can get it. I've run Infoplus under QEMM 5 and MICEMM and
EMM386.SYS, and it works fine. What QEMM and other DON'T like is doing a
386 protected instruction!! (Which I found out the hard way.)
  The only time it doesn't seem to work right is running under Windows 3
enhanced mode.

  Several people have asked to see the code I use. Here it is. It is
written for use under Turbo Pascal, but should be adaptable. I'll even
throw in the other two routines used by Infoplus. Someone might find them
useful, too.

page    75, 110

;--------------------------------------------------------------------
;
;       INFOPLUS.ASM
;
;       Version 1.10
;
;       Three subprograms used by INFOPLUS.PAS:
;
;               CPUID           - identifies host CPU and NDP (if
;                                       any)
;               DISKREAD        - reads absolute sectors from disk
;               LONGCALL        - calls a routine using a CALL FAR
;
;       Originally by:
;       Steve Grant
;       Long Beach, CA
;       January 13, 1989
;
;       mods by Andrew Rossmann (7/20/90)
;       286/386/486 detection adapted from code by Robert Collins.
;--------------------------------------------------------------------

.286P
.8087

        public  CPUID, DISKREAD, LONGCALL

;-----------------------------------------------------------------------------
; 80486 instruction macro -- because MASM 5.1 doesn't support the 80486!
;-----------------------------------------------------------------------------
;uncomment next 3 lines if not using TASM 2.0
;XADD    macro
;        db      0fh,0C0h,0D2h           ; 80486 instruction macro
;ENDM

CODE    segment byte

;       Conditional jumps are all coded with the SHORT qualifier in
;       order to minimize the size of the .OBJ file output of Turbo
;       Assembler.

;--------------------------------------------------------------------

CPUID   proc    near

assume  cs:CODE, ds:DATA, es:nothing, ss:nothing

;       On entry:
;
;               BP
;       SP =>   near return address
;               offset  of a cpu_info_t record
;               segment "  "     "        "
;
;       On exit, the cpu_info_t record has been filled in as follows:
;
;               byte    = CPU type
;               word    = Machine Status Word
;               6 bytes = Global Descriptor Table
;               6 bytes = Interrupt Descriptor Table
;               boolean = segment register change/interrupt flag
;               byte    = NDP type
;               word    = NDP control word

mCPU    equ     byte ptr [bx]
mMSW    equ     word ptr [bx + 1]
mGDT    equ     [bx + 3]
mIDT    equ     [bx + 9]
mchkint equ     byte ptr [bx + 15]
mNDP    equ     byte ptr [bx + 16]
mNDPCW  equ     word ptr [bx + 17]

f8088   equ     0
f8086   equ     1
fV20    equ     2
fV30    equ     3
f80188  equ     4
f80186  equ     5
f80286  equ     6
f80386  equ     7
f80486  equ     8
funk    =       0FFH

false   equ     0
true    equ     1

        push    bp
        mov     bp,sp
        push    ds
        lds     bx,[bp + 4]
        call    cpu
        call    chkint
        call    ndp
        pop     ds
        pop     bp
        ret     4

;--------------------------------------------------------------------

cpu:

; interrupt of multi-prefix string instruction

        mov     mCPU,funk               ;set CPU type to unknown
        sti
        mov     cx,0FFFFH
rep     lods    byte ptr es:[si]
        jcxz    short cpu_02
        call    piq
        cmp     dx,4
        jg      short cpu_01
        mov     mCPU,f8088
        ret
cpu_01:
        cmp     dx,6
        jne     short cpu_01a
        mov     mCPU,f8086
cpu_01a:
        ret
cpu_02:

; number of bits in displacement register used by shift

        mov     al,0FFH
        mov     cl,20H
        shl     al,cl
        or      al,al
        jnz     short cpu_04
        call    piq
        cmp     dx,4
        jg      short cpu_03
        mov     mCPU,fV20
        ret
cpu_03:
        cmp     dx,6
        jne     short CPUID_done
        mov     mCPU,fV30
        ret
cpu_04:

; order of write/decrement by PUSH SP

        push    sp
        pop     ax
        cmp     ax,sp
        je      short cpu_06
        call    piq
        cmp     dx,4
        jg      short cpu_05
        mov     mCPU,f80188
        ret
cpu_05:
        cmp     dx,6
        jne     short CPUID_done
        mov     mCPU,f80186
        ret


; The following code is adapted from a program by Robert Collins
;since we probably have a 286, 386 or 486, we'll trap the invalid opcode
;handler, and try to determine the cpu type by using various opcodes.
;First, though, grab some tables

cpu_06:
        smsw    mMSW
        sgdt    mGDT
        sidt    mIDT
;-----------------------------------------------------------------------------
; Setup INT6 handler
;-----------------------------------------------------------------------------
        push    bx                      ;save bx
        mov     ax,3506h                ;get INT 6 pointer
        int     21h
        mov     old_int06_seg,es        ;save
        mov     old_int06_ofs,bx
        push    ds                      ;save DS
        mov     dx,seg INT6_handler     ;point to new handler
        mov     ds,dx
        mov     dx,offset INT6_handler
        mov     ax,2506h                ;put into effect
        int     21h
        pop     ds                      ;restore DS
        pop     bx
        mov     mCPU,f80486             ; initialize CPU flag
        xor     cx,cx                   ; initialize semaphore

;-----------------------------------------------------------------------------
; Now, try and determine which CPU we are by executing invalid opcodes.
; The instructions I chose to invoke invalid opcodes, are themselves rather
; benign.  In each case, the chosen instruction modifies the DX register,
; and nothing else.  No system parameters are changed, e.g. protected mode,
; or other CPU dependant features.
;-----------------------------------------------------------------------------
; The 80486 instruction 'XADD' xchanges the registers, then adds them.
;-----------------------------------------------------------------------------
;if not using TASM 2.0, comment out next 2 lines, and uncoment 3rd.
.486
        XADD    DX,DX                   ; 80486
;       XADD    ;DX,DX
.286
        jcxz    CPU_exit
        mov     mCPU,f80386             ; set 80386 semaphore
        xor     cx,cx                   ; CX=0

;-----------------------------------------------------------------------------
; For a description on the effects of the following instructions, look in
; the Intel Programmers Reference Manual's for the 80186, 80286, or 80386.
;-----------------------------------------------------------------------------
.386
        mov     edx,edx                 ; 80386
.286P
        jcxz    CPU_exit
        mov     mCPU,f80286             ; set 80286 semaphore
        xor     cx,cx                   ; CX=0

        smsw    dx                      ; 80286
        jcxz    CPU_exit
        mov     mCPU,funk               ; set unknown CPU

CPU_exit:
        push    ds                      ;save DS
        lds     dx,old_int06            ;get old pointer
        mov     ax,2506h                ;restore it
        int     21h
        pop     ds

;!!!!!!!
;!!! Original 286/386 detection code. Commented out but left in in
;!!! case of problems.
;!!!!!!!
; try to alter flag register bits 15-12
;
;       pushf
;       pop     ax
;       mov     cx,ax
;       xor     cx,0F000H
;       push    cx
;       popf
;       pushf
;       pop     cx
;       cmp     ax,cx
;       jne     short cpu_07
;       mov     mCPU,f80286
;       ret
;cpu_07:
;       mov     mCPU,f80386
;       ret
;cpu_08:
;       mov     mCPU,funk
CPUID_done:
        ret

;--------------------------------------------------------------------

piq:

;       On exit:
;
;               DX      = length of prefetch instruction queue
;
;       This subroutine uses self-modifying code, but can
;       nevertheless be run repeatedly in the course of the calling
;       program.

count   =       7
opincdx equ     42H                     ; inc dx opcode
opnop   equ     90H                     ; nop opcode

        mov     al,opincdx
        mov     cx,count
        push    cx
        push    cs
        pop     es
        mov     di,offset piq_01 - 1
        push    di
        std
        rep stosb
        mov     al,opnop
        pop     di
        pop     cx
        xor     dx,dx
        cli
        rep stosb
        rept    count
        inc     dx
        endm
piq_01:
        sti
        ret

;--------------------------------------------------------------------

chkint:

; save old INT 01H vector

        push    bx
        mov     ax,3501H
        int     21H
        mov     old_int01_ofs,bx
        mov     old_int01_seg,es
        pop     bx

; redirect INT 01H vector

        push    ds
        mov     ax,2501H
        mov     dx,seg new_int01
        mov     ds,dx
        mov     dx,offset new_int01
        int     21H
        pop     ds

; set TF and change SS -- did we trap on following instruction?

        pushf
        pop     ax
        or      ah,01H                  ; set TF
        push    ax
        popf
        push    ss                      ; CPU may wait one
                                        ; instruction before
                                        ; recognizing single step
                                        ; interrupt
        pop     ss
chkint_01:                              ; shouldn't ever trap here

; restore old INT 01H vector

        push    ds
        mov     ax,2501H
        lds     dx,old_int01
        int     21H
        pop     ds
        ret

;--------------------------------------------------------------------

new_int01:

;       INT 01H handler (single step)
;
;       On entry:
;
;       SP =>   IP
;               CS
;               flags

        sti
        pop     ax                      ; IP
        cmp     ax,offset chkint_01
        jb      short new_int01_03
        je      short new_int01_01
        mov     mchkint,false
        jmp     short new_int01_02
new_int01_01:
        mov     mchkint,true
new_int01_02:
        pop     cx                      ; CS
        pop     dx                      ; flags
        and     dh,0FEH                 ; turn off TF
        push    dx                      ; flags
        push    cx                      ; CS
new_int01_03:
        push    ax                      ; IP
        iret

;-----------------------------------------------------------------------------
; INT6 handler sets a semaphore (CX=FFFF) and adjusts the return address to
; point past the invalid opcode.
;-----------------------------------------------------------------------------
INT6_handler:
        enter   0,0                     ; create new stack frame
        dec     cx                      ; make CX=FFFF
        add     word ptr ss:[bp][2],3   ; point past invalid opcode
        leave
        iret

;--------------------------------------------------------------------

ndp:

fnone   equ     0
f8087   equ     1
f80287  equ     2
f80387  equ     3
funk    =       0FFH

        mov     word ptr ndp_cw,0000H
        cli

; The next three 80x87 instructions cannot carry the WAIT prefix,
; because there may not be an 80x87 for which to wait.  The WAIT is
; therefore emulated with a MOV CX,<value>! LOOP $ combination.

                                        ;       CPU     NDP
        fnsave  ndp_save                ;        14     221
        mov     cx,(221-6-1)/17+1       ;         4
        loop    $                       ;  17*CX-12
                                        ;   17*CX+6

        fninit                          ;         8       8
        mov     cx,(8-0-1)/17+1         ;         4
        loop    $                       ;  17*CX-12
                                        ;     17*CX

        fnstcw  ndp_cw                  ;        14      24
        mov     cx,(24-2-1)/17+1        ;         4
        loop    $                       ;  17*CX-12
                                        ;   17*CX+2

        sti
        mov     ax,ndp_cw
        cmp     ax,0000H
        jne     short ndp_01
        mov     mNDP,fnone
        ret
ndp_01:
        cmp     ax,03FFH
        jne     short ndp_02
        mov     mNDP,f8087
        jmp     short ndp_04
ndp_02:

.287

        cmp     ax,037FH
        jne     short ndp_05
        fld1
        fldz
        fdiv
        fld1
        fchs
        fldz
        fdiv
        fcom
        fstsw   ndp_sw
        mov     ax,ndp_sw
        and     ah,41H                  ; C3, C0
        cmp     ah,40H                  ; ST(0) = ST(1)
        jne     short ndp_03
        mov     mNDP,f80287
        jmp     short ndp_04
ndp_03:
        cmp     ah,01H                  ; ST(0) < ST(1)
        jne     short ndp_05
        mov     mNDP,f80387
ndp_04:

.8087

        frstor  ndp_save
        fstcw   mNDPCW
        ret
ndp_05:
        mov     mNDP,funk
        ret

CPUID   endp

;--------------------------------------------------------------------

DISKREAD        proc    near

assume cs:CODE, ds:DATA, es:nothing

;       On entry:
;
;               BP
;       SP =>   near return address
;               offset  of disk buffer
;               segment "   "     "
;               number of sectors to read
;               starting logical sector number
;               drive number (0=A, 1=B, etc.)
;
;       On exit:
;
;               AX      = function result
;                       00      - function successful
;                       01..FF  - DOS INT 25H error result

        drive                   equ     [bp + 12]
        starting_sector         equ     [bp + 10]
        number_of_sectors       equ     [bp + 8]
        buffer                  equ     [bp + 4]

        push    bp
        mov     bp,sp
        mov     ax,3000h                ;get DOS version
        int     21h
        cmp     al,4                    ;DOS 4?
        jb      read3                   ;Read assuming DOS 3.x
        mov     al,drive
        mov     bx,starting_sector      ;copy info into parameter block
        mov     extd_starting_sector_lo,bx
        mov     extd_starting_sector_hi,0       ;We're only using lower part
        mov     bx,number_of_sectors
        mov     extd_number_of_sectors,bx
        les     bx,buffer               ;get seg:ofs of buffer in ES:BX
        mov     extd_bufofs,bx          ;put into block
        mov     extd_bufseg,es
        mov     bx,offset dos4_block    ;DS:BX points to block
        mov     cx,-1                   ;-1 means extended read
        push    ds                      ;save DS (not really needed, but lets
                                        ;me share code with DOS 3 read.)
        jmp     short readit

read3:  mov     al,drive
        mov     dx,starting_sector
        mov     cx,number_of_sectors
        push    ds
        lds     bx,buffer               ;get seg:ofs of buffer in DS:BX
readit: int     25H
        inc     sp                      ; fix broken stack
        inc     sp
        pop     ds
        jc      short diskread_01
        xor     ax,ax

diskread_01:

        pop     bp
        ret     10

DISKREAD        endp

;
;LONGCALL will call a routine using a CALL FAR. This is used by XMS
;
;Pascal format: procedure longcall(AXi: word; var addr: longint;
;                                  var AXo, BXo, DXo: word); external;
;

longcall        proc    near
        assume  cs:CODE, ds:DATA, es:nothing

AXi     equ     [bp+20]
addr    equ     [bp+16]
AXo     equ     [bp+12]
BXo     equ     [bp+8]
DXo     equ     [bp+4]

        push    bp
        mov     bp,sp
        les     di,addr
        mov     ax,es:[di]
        mov     word ptr address,ax
        mov     ax,es:[di+2]
        mov     word ptr address+2,ax
        mov     ax,AXi
        call    dword ptr address
        les     di,DXo
        mov     [es:di],dx
        les     di,BXo
        mov     [es:di],bx
        les     di,AXo
        mov     [es:di],ax
        pop     bp
        ret     18
longcall endp

code    ends

;--------------------------------------------------------------------

DATA    segment byte

; storage for CPUID

; redirected INT 01H vector

old_int01       label   dword
old_int01_ofs   dw      ?
old_int01_seg   dw      ?

; redirected INT 6 vector
old_int06       label   dword
old_int06_ofs   dw      ?
old_int06_seg   dw      ?

; storage for NDPID

; 80x87 control word after initialization, status word after divide by zero

ndp_cw          dw      ?
ndp_save        db      94 dup (?)
ndp_sw          dw      ?

; storage for DISKREAD

; DOS 4.0 extended read parameter block
dos4_block                      label   byte
extd_starting_sector_lo         dw      ?
extd_starting_sector_hi         dw      ?
extd_number_of_sectors          dw      ?
extd_bufofs                     dw      ?
extd_bufseg                     dw      ?

; storage for LONGCALL

address dd      ?

DATA    ends

        end

  Andrew Rossmann
  andyross@ddsw1.MCS.COM
Technical Details
Message-ID: <1990Jul23.223141.1776@ddsw1.MCS.COM>
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!tut.cis.ohio-state.edu!rutgers!mcdchg!ddsw1!andyross
References: <1990Jul20.230335.22816@ddsw1.MCS.COM> <26a858b9@ralf>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!tut.cis.ohio-state.edu!rutgers!mcdchg!ddsw1!andyross
From: andyross@ddsw1.MCS.COM (Andrew Rossmann)
Newsgroups: comp.os.msdos.programmer
Subject: Re: Detecting an 80486
Message-ID: <1990Jul23.223141.1776@ddsw1.MCS.COM>
Date: 23 Jul 90 22:31:41 GMT
References: <1990Jul20.230335.22816@ddsw1.MCS.COM> <26a858b9@ralf>
Reply-To: andyross@ddsw1.MCS.COM (Andrew Rossmann)
Organization: ddsw1.MCS.COM Contributor, Wheeling, IL
Lines: 637