home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / pc / source / cpu.zoo / cpu.1 next >
Internet Message Format  |  1989-08-18  |  24KB

  1. From santra!kth!sunic!mcvax!uunet!ginosko!gem.mps.ohio-state.edu!csd4.csd.uwm.edu!mailrus!jarvis.csri.toronto.edu!utgpu!utzoo!mnetor!tmsoft!mshiels Fri Aug 18 10:31:28 EET DST 1989
  2. Article 7094 of comp.sys.ibm.pc:
  3. Path: chyde!santra!kth!sunic!mcvax!uunet!ginosko!gem.mps.ohio-state.edu!csd4.csd.uwm.edu!mailrus!jarvis.csri.toronto.edu!utgpu!utzoo!mnetor!tmsoft!mshiels
  4. >From: mshiels@tmsoft.uucp (Michael A. Shiels)
  5. Newsgroups: comp.sys.ibm.pc
  6. Subject: CPU Identification 1 of 4
  7. Message-ID: <1989Aug16.110451.272@tmsoft.uucp>
  8. Date: 16 Aug 89 11:04:51 GMT
  9. Reply-To: mshiels@tmsoft.UUCP (Michael A. Shiels)
  10. Followup-To: comp.sys.ibm.pc
  11. Organization: MaS Network Software and Consulting
  12. Lines: 627
  13.  
  14.  
  15.     title    CPUID - Determine CPU & NDP Type
  16.     page    58,122
  17.     name    CPUID
  18.      
  19. ;
  20. ;    CPUID uniquely identifies each NEC & Intel CPU & NDP.
  21. ;
  22. ; Notes on program structure:
  23. ;
  24. ;    This program uses four segments, two classes, and one group.
  25. ;    It demonstrates a useful technique for programmers who generate
  26. ;    .COM programs.  In particular, it shows how to use segment
  27. ;    classes to re-order segments, and how to eliminate the linker's
  28. ;    warning message about the absence of a stack segment.
  29. ;
  30. ;    The correspondence between segments and classes is as follows:
  31. ;
  32. ;            Segment        Class
  33. ;            -------        -----
  34. ;            STACK        prog
  35. ;            DATA        data
  36. ;            MDATA        data
  37. ;            CODE        prog
  38. ;
  39. ;    The segments apprear in the above order in the program source
  40. ;    to avoid forward references in the CODE segment to labels in
  41. ;    the DATA/MDATA segments.  However, because the STACK segment
  42. ;    appears first in the file, it and all segments in the same
  43. ;    class are made contiguous by the linker.  Thus they precede
  44. ;    the DATA/MDATA segments in the resulting .COM file because
  45. ;    the latter are in a different class.  In this manner, although
  46. ;    DATA and MDATA precede CODE in the source file, their order
  47. ;    is swapped in the .COM file.  That way there is no need for
  48. ;    an initial skip over the data areas to get to the CODE
  49. ;    segment.  As a side benefit, declaring a STACK segment (as
  50. ;    the first segment in the source) also eliminates the linker's
  51. ;    warning about that segment missing.  Finally, all segments
  52. ;    are declared to be in the same group so the linker can properly
  53. ;    resolve offsets.
  54. ;
  55. ;    Note that if you re-assemble the code for any reason, it is
  56. ;    important to use an assembler later than the IBM version 1.0.
  57. ;    That version has a number of bugs including an annoying habit
  58. ;    of alphabetizing segment names in the .OBJ file.  If you use
  59. ;    IBM MASM 2.0, be sure to specify /S to order the segments
  60. ;    properly.
  61. ;
  62. ;    If the program reports results at variance with your knowledge
  63. ;    of the system, please contact the author.
  64. ;
  65. ; Environments tested in:
  66. ;
  67. ;            CPU Speed
  68. ;    System         in MHz        CPU        NDP
  69. ;    ------        ---------    ---        ---
  70. ;    IBM PC AT    6        Intel 80286    Intel 80287
  71. ;    IBM PC AT    9        Intel 80286    Intel 80287
  72. ;    IBM PC AT    6        Intel 80286    none
  73. ;    IBM PC AT    8.5        Intel 80286    none
  74. ;    IBM PC        4.77        Intel 8088    Intel 8087-3
  75. ;    IBM PC        4.77        Intel 8088*    Intel 8087-3
  76. ;    IBM PC XT    4.77        Intel 8088    none
  77. ;    IBM PC XT    4.77        Intel 8088    Intel 8087-3
  78. ;    IBM PC Portable    4.77        NEC V20        none
  79. ;    COMPAQ        4.77        Intel 8088    none
  80. ;    COMPAQ        4.77        NEC V20        none
  81. ;    AT&T PC 6300    8        Intel 8086    Intel 8087-2
  82. ;    AT&T PC 6300    8        NEC V30        Intel 8087-2
  83. ;    Tandy 2000    8        Intel 80186    none
  84. ;
  85. ;    * = faulty CPU
  86. ;
  87. ; Program structure:
  88. ;
  89. ;    Group PGROUP:
  90. ;    Stack   segment STACK, byte-aligned, stack,  class 'prog'
  91. ;    Program segment CODE,  byte-aligned, public, class 'prog'
  92. ;    Data    segment DATA,  byte-aligned, public, class 'data'
  93. ;    Data    segment MDATA, byte-aligned, public, class 'data'
  94. ;
  95. ; Assembly requirements:
  96. ;
  97. ;    Use MASM 1.25 or later.
  98. ;    With IBM's MASM 2.0 only, use /S to avoid alphabetizing the
  99. ;        segment names.
  100. ;    Use /r option to generate real NDP code.
  101. ;
  102. ;    MASM CPUID/r;            to convert .ASM to .OBJ
  103. ;    LINK CPUID;            to convert .OBJ to .EXE
  104. ;    EXE2BIN CPUID CPUID.COM        to convert .EXE to .COM
  105. ;    ERASE CPUID.EXE            to avoid executing .EXE
  106. ;
  107. ;    Note that the linker doesn't warn about a missing stack segment.
  108. ;
  109. ; Author:
  110. ;
  111. ;    Original code by:    Bob Smith    May 1985
  112. ;                Qualitas, Inc.
  113. ;                8314 Thoreau Dr.
  114. ;                Bethesda, MD   20817
  115. ;
  116. ;    Arthur Zachai suggested the technique to distinguish within the
  117. ;    808x and 8018x families by exploiting the difference in the
  118. ;    length of their pre-fetch instruction queues.
  119. ;
  120. ;    Published in PC Tech Journal - April 1986 - Vol 4 No 4
  121.      
  122.     subttl    Structures, Records, Equates, & Macros
  123.     page
  124. ARG_STR    struc
  125.     dw    ?            ; caller's bp
  126. ARG_OFF    dw    ?            ; caller's offset
  127. ARG_SEG    dw    ?            ;          segment
  128. ARG_FLG    dw    ?            ;          flags
  129. ARG_STR    ends
  130.      
  131. ; Record to define bits in the CPU's & NDP's flags' registers
  132.      
  133. CPUFLAGS record RO:1,NT:1,IOPL:2,OF:1,DF:1,IF:1,TF:1,SF:1,ZF:1,R1:1,AF:1,R2:1,PF:1,R3:1,CF:1
  134.      
  135. NDPFLAGS record R4:3,IC:1,RC:2,PC:2,IEM:1,R5:1,PM:1,UM:1,OM:1,ZM:1,DM:1,IM:1
  136.      
  137. ;    FLG_PIQL    Pre-fetch instruction queue length, 0 => 4-byte
  138. ;                                1 => 6-byte
  139. ;    FLG_08        Intel 808x
  140. ;    FLG_NEC        NEC V20 or V30
  141. ;    FLG_18        Intel 8018x
  142. ;    FLG_28        Intel 8028x
  143. ;    FLG_87        Intel 8087
  144. ;    FLG_287        Intel 80287
  145. ;
  146. ;    FLG_CERR    Faulty CPU
  147. ;    FLG_NERR    Faulty NDP switch setting
  148.      
  149. FLG    record    RSVD:9,FLG_NERR:1,FLG_CERR:1,FLG_NDP:2,FLG_CPU:3
  150.      
  151. ; CPU-related flags
  152.      
  153. FLG_PIQL    equ    001b shl FLG_CPU
  154. FLG_08        equ    000b shl FLG_CPU
  155. FLG_NEC        equ    010b shl FLG_CPU
  156. FLG_18        equ    100b shl FLG_CPU
  157. FLG_28        equ    110b shl FLG_CPU
  158.      
  159. FLG_8088    equ    FLG_08
  160. FLG_8086    equ    FLG_08 or FLG_PIQL
  161. FLG_V20        equ    FLG_NEC
  162. FLG_v30        equ    FLG_NEC or FLG_PIQL
  163. FLG_80188    equ    FLG_18
  164. FLG_80186    equ    FLG_18 or FLG_PIQL
  165. FLG_80286    equ    FLG_28 or FLG_PIQL
  166.      
  167. ; NDP-related flags
  168.      
  169. ;            00b shl FLG_NDP        Not Present
  170. FLG_87        equ    01b shl FLG_NDP
  171. FLG_287    equ    10b shl FLG_NDP
  172. BEL        equ    07h
  173. LF        equ    0ah
  174. CR        equ    0dh
  175. EOS        equ    '$'
  176.      
  177. POPFF    macro
  178.     local    L1,L2
  179.     jmp    short L2        ; skip over IRET
  180. L1:
  181.     iret                ; pop the cs & ip pushed below along
  182.                     ; with the flags, our original purpose
  183. L2:
  184.     push    cs            ; prepare for IRET by pushing cs
  185.     call    L1            ; push ip, jump to IRET
  186.     endm                ; POPFF macro
  187.      
  188. TAB    macro    TYP
  189.     push    bx            ; save for a moment
  190.     and    bx,mask FLG_&TYP    ; isolate flags
  191.     mov    cl,FLG_&TYP        ; shift amount
  192.     shr    bx,cl            ; shift to low-order
  193.     shl    bx,1            ; times two to index table of words
  194.     mov    dx,TYP&MSG_TAB[bx]    ; ds:dx => descriptive message
  195.     pop    bx            ; restore
  196.     mov    ah,09h            ; function code to display string
  197.     int    21h            ; request dos service
  198.     endm                ; TAB macro
  199.     page
  200. INT_VEC    segment at 0            ; start INT_VEC segment
  201.         dd    ?        ; pointer to INT 00h
  202. INT01_OFF    dw    ?        ; pointer to INT 01h
  203. INT01_SEG    dw    ?
  204. INT_VEC    ends                ; end INT_VEC segment
  205.      
  206. PGROUP    group    STACK,CODE,DATA,MDATA
  207.      
  208. ; The following segment both positions class 'prog' segments lower in
  209. ; memory than others so the first byte of the resulting .COM file is
  210. ; in the CODE segment, as well as satisfies the LINKer's need to have
  211. ; a stack segment.
  212.      
  213. STACK    segment    byte stack 'prog'    ; start STACK segment
  214. STACK    ends                ; end STACK segment
  215.      
  216. I11_REC    record I11_PRN:2,I11_RSV1:2,I11_COM:3,I11_RSV2:1,I11_DISK:2,I11_VID:2,I11_RSV3:2,I11_NDP:1,I11_IPL:1
  217.      
  218. DATA    segment    byte public 'data'    ; start DATA segment
  219.     assume    ds:PGROUP
  220.      
  221. OLDINT01_VEC    label    dword        ; save area for original INT 01h handler
  222. OLDINT01_OFF    dw    ?
  223. OLDINT01_SEG    dw    ?
  224.      
  225. NDP_CW        label    word        ; save area for NDP control word
  226.         db    ?
  227. NDP_CW_HI    db    0        ; high byte of control word
  228. NDP_ENV        dw    7 dup(?)    ; save area for NDP environment
  229.      
  230. DATA    ends
  231.     subttl    Message Data Area
  232.     page
  233. MDATA    segment    byte public 'data'    ; start MDATA segment
  234.     assume    ds:PGROUP
  235.      
  236. MSG_START    db    'CPUID -- Version 1.0'
  237.         db    CR,LF,CR,LF,EOS
  238. MSG_8088    db    'CPU is an Intel 8088.'
  239.         db    CR,LF,EOS
  240. MSG_8086    db    'CPU is an Intel 8086.'
  241.         db    CR,LF,EOS
  242. MSG_V20        db    'CPU is an NEC V20.'
  243.         db    CR,LF,EOS
  244. MSG_V30        db    'CPU is an NEC V30.'
  245.         db    CR,LF,EOS
  246. MSG_80188    db    'CPU is an Intel 80188.'
  247.         db    CR,LF,EOS
  248. MSG_80186    db    'CPU is an Intel 80186.'
  249.         db    CR,LF,EOS
  250. MSG_UNK        db    'CPU is a maverick -- 80288??.'
  251.         db    CR,LF,EOS
  252. MSG_80286    db    'CPU is an Intel 80286.'
  253.         db    CR,LF,EOS
  254.      
  255. CPUMSG_TAB    label    word
  256.     dw    PGROUP:MSG_8088        ; 000 = Intel 8088
  257.     dw    PGROUP:MSG_8086        ; 001 = Intel 8086
  258.     dw    PGROUP:MSG_V20        ; 010 = NEC V20
  259.     dw    PGROUP:MSG_V30        ; 011 = NEC V30
  260.     dw    PGROUP:MSG_80188    ; 100 = Intel 80188
  261.     dw    PGROUP:MSG_80186    ; 101 = Intel 80186
  262.     dw    PGROUP:MSG_UNK        ; 110 = ?
  263.     dw    PGROUP:MSG_80286    ; 111 = Intel 80286
  264.      
  265. NDPMSG_TAB    label    word
  266.     dw    PGROUP:MSG_NDPX        ; 00 = No NDP
  267.     dw    PGROUP:MSG_8087        ; 01 = Intel 8087
  268.     dw    PGROUP:MSG_80287    ; 10 = Intel 80287
  269.      
  270. MSG_NDPX    db    'NDP is not present.'
  271.         db    CR,LF,EOS
  272. MSG_8087    db    'NDP is an Intel 8087.'
  273.         db    CR,LF,EOS
  274. MSG_80287    db    'NDP is an Intel 80287.'
  275.         db    CR,LF,EOS
  276.      
  277. CERRMSG_TAB    label    word
  278.     dw    PGROUP:MSG_CPUOK    ; 0 = CPU healthy
  279.     dw    PGROUP:MSG_CPUBAD    ; 1 = CPU faulty
  280.      
  281. MSG_CPUOK    db    'CPU appears to be healthy.'
  282.         db    CR,LF,EOS
  283. MSG_CPUBAD    label    byte
  284.         db    BEL,'*** CPU incorrectly allows interrupts '
  285.         db    'after a change to SS ***',CR,LF
  286.         db    'It should be replaced with a more recent '
  287.         db    'version as it could crash the',CR,LF
  288.         db    'system at seemingly random times.',CR,LF,EOS
  289.      
  290. NERRMSG_TAB    label    word
  291.     dw    PGROUP:MSG_NDPSWOK    ; 0 = NDP switch set correctly
  292.     dw    PGROUP:MSG_NDPSWERR    ; 1 = NDP switch set incorrectly
  293.      
  294. MSG_NDPSWOK    db    EOS        ; no message
  295. MSG_NDPSWERR    label    byte
  296.         db    '*** Although there is an NDP installed '
  297.         db    'on this sytem, the corresponding',CR,LF
  298.         db    'system board switch is not properly set.  '
  299.         db    'To correct this, flip switch 2 of',CR,LF
  300.         db    'switch block 1 on the system board.',CR,LF,EOS
  301.      
  302. MDATA    ends                ; end MDATA segment
  303.     subttl    Main Routine
  304.     page
  305. CODE    segment    byte public 'prog'    ; start CODE segment
  306.     assume    cs:PGROUP,ds:PGROUP,es:PGROUP
  307.     org    100h            ; skip over PSP
  308.      
  309. INITIAL    proc    near
  310.     mov    dx,offset ds:MSG_START    ; starting message
  311.     mov    ah,09h            ; function code to display string
  312.     int    21h            ; request DOS service
  313.      
  314.     call    CPUID            ; check the CPU's identity
  315.      
  316.     TAB    CPU            ; display CPU results
  317.     TAB    NDP            ; display NDP results
  318.     TAB    CERR            ; display CPU ERR results
  319.     TAB    NERR            ; display NDP ERR results
  320.      
  321.     ret                ; return to DOS
  322. INITIAL    endp                ; end INITIAL procedure
  323.     subttl    CPUID Procedure
  324.     page
  325. CPUID    proc    near            ; start CPUID procedure
  326.     assume    cs:PGROUP,ds:PGROUP,es:PGROUP
  327.      
  328. ; This procedure determines the type of CPU and NDP (if any) in use.
  329. ;
  330. ; The possibilities include:
  331. ;
  332. ;        Intel 8086
  333. ;        Intel 8088
  334. ;        NEC V20
  335. ;        NEC V30
  336. ;        Intel 80186
  337. ;        Intel 80188
  338. ;        Intel 80286
  339. ;        Intel 8087
  340. ;        Intel 80287
  341. ;
  342. ; Also checked is whether or not the CPU allows interrupts after
  343. ; changing the SS register segment.  If the CPU does, it is faulty
  344. ; and should be replaced.
  345. ;
  346. ; Further, if an NDP is installed, non-AT machines should have a
  347. ; system board switch set.  Such a discrepancy is reported.
  348. ;
  349. ; On exit, BX contains flag settings (as defined in FLG record) which
  350. ; the caller can check.  For example, to test for an Intel 80286, use
  351. ;
  352. ;        and    bx,mask FLAG_CPU
  353. ;        cmp    bx,FLG_80286
  354. ;        je    ITSA286
  355.      
  356.     irp    XX,<ax,cx,di,ds,es>    ; save registers
  357.     push    XX
  358.     endm
  359.      
  360. ; test for 80286 -- this CPU executes PUSH SP by first storing SP on
  361. ; stack, then decrementing it.  earlier CPU's decrement, THEN store.
  362.      
  363.     mov    bx,FLG_28        ; assume it's a 286
  364.     push    sp            ; only 286 pushes pre-push SP
  365.     pop    ax            ; get it back
  366.     cmp    ax,sp            ; check for same
  367.     je    CHECK_PIQL        ; they are, so it's a 286
  368.      
  369. ; test for 80186/80188 -- 18xx and 286 CPU's mask shift/rotate
  370. ; operations mod 32; earlier CPUs use all 8 bits of CL.
  371.      
  372.     mov    bx,FLG_18        ; assume it's an 8018x
  373.     mov    cl,32+1            ; 18x masks shift counts mod 32
  374.                     ; note we can't use just 32 in CL
  375.     mov    al,0ffh            ; start with all bits set
  376.      
  377.     shl    al,cl            ; shift one position if 18x
  378.     jnz    CHECK_PIQL        ; some bits still on,
  379.                     ; so its a 18x, check PIQL
  380.      
  381. ; test for V20
  382.      
  383.     mov    bx,FLG_NEC        ; assume it's an NEC V-series CPU
  384.     call    CHECK_NEC        ; see if it's an NEC chip
  385.     jcxz    CHECK_PIQL        ; good guess, check PIQL
  386.      
  387.     mov    bx,FLG_08        ; it's an 808x
  388.     subttl    Check Length of Pre-Fetch Instruction Queue
  389.     page
  390. ; Check the length of the pre-fetch instruction queue (PIQ).
  391. ;
  392. ; xxxx6 CPUs have a PIQ length of 6 bytes,
  393. ; xxxx8 CPUs have a PIQ length of 4 bytes
  394. ;
  395. ; Self-modifying code is used to distinguish the two PIQ lengths.
  396.      
  397. CHECK_PIQL:
  398.     call    PIQL_SUB        ; handle via subroutine
  399.     jcxz    CHECK_ERR        ; if CX is 0, INC was not executed,
  400.                     ; hence PIQ length is 4
  401.     or    bx,FLG_PIQL        ; PIQ length is 6
  402.     subttl    Check for Allowing Interrupts After POP SS
  403.     page
  404. ; Test for faulty chip (allows interrupts after change to SS register)
  405.      
  406. CHECK_ERR:
  407.     xor    ax,ax            ; prepare to address
  408.                     ; interrupt vector segment
  409.     mov    ds,ax            ; DS points to segment 0
  410.     assume    ds:INT_VEC        ; tell the assembler
  411.      
  412.     cli                ; nobody move while we swap
  413.      
  414.     mov    ax,offset cs:INT01    ; point to our own handler
  415.     xchg    ax,INT01_OFF        ; get and swap offset
  416.     mov    OLDINT01_OFF,ax        ; save to restore later
  417.      
  418.     mov    ax,cs            ; our handler's segment
  419.     xchg    ax,INT01_SEG        ; get and swap segment
  420.     mov    OLDINT01_SEG,ax        ; save to restore later
  421.      
  422. ; note we continue with interrupts disabled to avoid
  423. ; an external interrupt occuring during this test
  424.      
  425.     mov    cx,1            ; initialize a register
  426.     push    ss            ; save ss to store back into itself
  427.     pushf                ; move flags
  428.     pop    ax            ; ... into ax
  429.     or    ax,mask TF        ; set trap flag
  430.     push    ax            ; place onto stack
  431.     POPFF                ; ... and then into effect
  432.                     ; some CPUs effect the trap flag
  433.                     ; immediately, some
  434.                     ; wait one instruction
  435.     nop                ; allow interrupt to take effect
  436.      
  437. POST_NOP:
  438.     pop    ss            ; change the stack segment register
  439.                     ; (to itself)
  440.     dec    cx            ; normal cpu's execute this instruction
  441.                     ; before recognizing the single-step
  442.                     ; interrupt
  443.     hlt                ; we never get here
  444.      
  445. INT01:
  446.      
  447. ; Note: IF=TF=0
  448. ; If we're stopped at or before POST_NOP, continue on
  449.      
  450.     push    bp            ; prepare to address the stack
  451.     mov    bp,sp            ; hello, Mr. stack
  452.      
  453.     cmp    [bp].ARG_OFF,offset cs:POST_NOP    ; check offset
  454.     pop    bp            ; restore
  455.     ja    INTO1_DONE        ; we're done
  456.      
  457.     iret                ; return to caller
  458.      
  459. INTO1_DONE:
  460.      
  461. ; restore old INT 01h handler
  462.      
  463.     les    ax,OLDINT01_VEC    ; ES:AX ==> old INT 01h handler
  464.     assume    es:nothing        ; tell the assembler
  465.     mov    INT01_OFF,ax        ; restore offset
  466.     mov    INT01_SEG,es        ; ... and segment
  467.     sti                ; allow interrupts again (IF=1)
  468.      
  469.     add    sp,3*2            ; strip ip, cs, and flags from stack
  470.      
  471.     push    cs            ; setup ds for code below
  472.     pop    ds
  473.     assume    ds:PGROUP        ; tell the assembler
  474.      
  475.     jcxz    CHECK_NDP        ; if cx is 0, the dec cx was executed,
  476.                     ; and the cpu is ok
  477.     or    bx,mask FLG_CERR    ; it's a faulty chip
  478.     subttl    Check For Numeric Data Processor
  479.     page
  480. ; Test for a Numeric Data Processor -- Intel 8087 or 80287.  The
  481. ; technique used is passive -- it leaves the NDP in the same state in
  482. ; which it is found.
  483.      
  484. CHECK_NDP:
  485.     cli                ; protect FNSTENV
  486.     fnstenv NDP_ENV            ; if NDP present, save
  487.                     ; current environment,
  488.                     ; otherwise, this instruction
  489.                     ; is ignored
  490.     mov    cx,50/7            ; cycle this many times
  491.     loop    $            ; wait for result to be stored
  492.     sti                ; allow interrupts
  493.     fninit                ; initialize processor to known state
  494.     jmp    short $+2        ; wait for initialization
  495.      
  496.     fnstcw    NDP_CW            ; save control word
  497.     jmp    short $+2        ; wait for result to be stored
  498.     jmp    short $+2
  499.     cmp    NDP_CW_HI,03h        ; check for NDP initial control word
  500.     jne    CPUID_EXIT        ; no NDP installed
  501.     int    11h            ; get equipment flags into ax
  502.     test    ax,mask I11_NDP        ; check NDP-installed bit
  503.     jnz    CHECK_NDP1        ; it's correctly set
  504.     or    bx,mask FLG_NERR    ; mark as in error
  505. CHECK_NDP1:
  506.     and    NDP_CW,not mask IEM    ; enable interrupts
  507.                     ; (IEM=0, 8087 only)
  508.     fldcw    NDP_CW            ; reload control word
  509.     fdisi                ; disable interrupts (IEM=1) on 8087,
  510.                     ; ignored by 80287
  511.     fstcw    NDP_CW            ; save control word
  512.     fldenv    NDP_ENV            ; restore original NDP environment
  513.                     ; no need to wait
  514.                     ; for environment to be loaded
  515.     test    NDP_CW,mask IEM        ; check interrupt enable mask
  516.                     ; (8087 only)
  517.     jnz    CPUID_8087        ; it changed, hence NDP is an 8087
  518.     or    bx,FLG_287        ; NDP is an 80287
  519.     jmp    short CPUID_EXIT    ; exit with falgs in BX
  520. CPUID_8087:
  521.     or    bx,FLG_87        ; NDP is an 8087
  522. CPUID_EXIT:
  523.     irp    XX,<es,ds,di,cx,ax>    ; restore registers
  524.     pop    XX
  525.     endm
  526.     assume    ds:nothing,es:nothing
  527.     ret                ; return to caller
  528. CPUID    endp                ; end CPUID procedure
  529.     subttl    Check For NEC V20/V30
  530.     page
  531. CHECK_NEC    proc    near
  532.      
  533. ; The NEC V20/V30 are very compatible with the Intel 8086/8088.
  534. ; The only point of "incompatibility" is that they do not contain
  535. ; a bug found in the Intel CPU's.  Specifically, the NEC CPU's
  536. ; correctly restart an interrupted multi-prefix string instruction
  537. ; at the start of the instruction.  The Intel CPU's incorrectly
  538. ; restart in the middle of the instruction.  This routine tests
  539. ; for that situation by executing such an instruction for a
  540. ; sufficiently long period of time for a timer interrupt to occur.
  541. ; If at the end of the instruction, CX is zero, it must be an NEC
  542. ; CPU; if not, it's an Intel CPU.
  543. ;
  544. ; Note that we're counting on the timer interrupt to do its thing
  545. ; every 18.2 times per second.
  546. ;
  547. ; Here's a worst case analysis: An Intel 8088/8086 executes 65535
  548. ; iterations of LODSB ES[SI] in 2+9+13*65535 = 851,966 clock ticks.
  549. ; If the Intel 8088/8086 is running at 10 MHz, each clock tick is
  550. ; 100 nanoseconds, hence the entire operation takes 85 milliseconds.
  551. ; If the timer is running at normal speed, it interrupts the CPU every
  552. ; 55ms and so should interrupt the repeated string instruction at least
  553. ; once.
  554.      
  555.     mov    cx,0ffffh        ; move a lot of data
  556.     sti                ; ensure timer enabled
  557.      
  558. ; execute multi-prefix instruction.  note that the value of ES as
  559. ; well as the direction flag setting is irrelevant.
  560.      
  561.     push    ax            ; save registers
  562.     push    si
  563.     rep    lods    byte ptr es:[si]
  564.     pop    si            ; restore
  565.     pop    ax
  566.      
  567. ; on exit: if cx is zero, it's an NEC CPU, otherwise it's an Intel CPU
  568.      
  569.     ret                ; return to caller
  570. CHECK_NEC    endp
  571.     subttl    Pre-Fetch Instruction Queue Subroutine
  572.     page
  573. PIQL_SUB    proc    near
  574.      
  575. ; This subroutine discerns the length of the CPU's pre-fetch
  576. ; instruction queue (PIQ).
  577. ;
  578. ; The technique used is to first ensure that the PIQ is full, then
  579. ; change an instruction which should be in a 6-byte PIQ but not in a
  580. ; 4-byte PIQ.  Then, if the original instruction is executed, the PIQ
  581. ; is 6-bytes long; if the new instruction is executed, PIQ length is 4.
  582. ;
  583. ; We ensure the PIQ is full be executing an instruction which takes
  584. ; long enough so that the Bus Interface Unit (BIU) can fill the PIQ
  585. ; while the instruction is executing.
  586. ;
  587. ; Specifically, for all byt the last STOSB, we're simple marking time
  588. ; waiting for the BIU to fill the PIQ.  The last STOSB actually changes
  589. ; the instruction.  By that time, the orignial instruction should be in
  590. ; a six-byte PIQ byt not a four-byte PIQ.
  591.      
  592.     assume    cs:PGROUP,es:PGROUP
  593. @REP    equ    3            ; repeat the store this many times
  594.     std                ; store backwards
  595.     mov    di,offset es:LAB_INC+@REP-1    ; change the instructions
  596.                     ; at ES:DI
  597.                     ; and preceding
  598.     mov    al,ds:LAB_STI        ; change to a sti
  599.     mov    cx,@REP            ; give the BIU time
  600.                     ; to pre-fetch instructions
  601.     cli                ; ensure interrupts are disabled,
  602.                     ; otherwise a timer tick
  603.                     ; could change the PIQ filling
  604.     rep    stosb            ; change the instruction
  605.                     ; during execution of this instruction
  606.                     ; the BIU is refilling the PIQ.  The
  607.                     ; current instruction is no longer
  608.                     ; in the PIQ.
  609.                     ; Note at end, CX is 0.
  610.      
  611. ; The PIQ begins filling here
  612.      
  613.     cld                ; restore direction flag
  614.     nop                ; PIQ fillers
  615.     nop
  616.     nop
  617.      
  618. ; The following instruction is beyond a four-byte-PIQ CPU's reach,
  619. ; but within that of a six-byte-PIQ CPU.
  620.      
  621. LAB_INC        label    byte
  622.     inc    cx            ; executed only if PIQ length is 6
  623.      
  624. LAB_STI    label     byte
  625.     rept    @REP-1
  626.     sti                ; restore interrupts
  627.     endm
  628.     ret                ; return to caller
  629.     assume    ds:nothing,es:nothing
  630. PIQL_SUB    endp            ; end PIQL_SUB procedure
  631.      
  632. CODE    ends                ; end code segment
  633.      
  634.     if1
  635. %OUT    Pass 1 Complete
  636.     else
  637. %OUT    Pass 2 Complete
  638.     endif
  639.      
  640.     end    INITIAL            ; end CPUID module
  641.  
  642.  
  643.