home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PROGRAMS / UTILS / HARDWARE / HARDCHK.ZIP / CPUID.ASM < prev    next >
Encoding:
Assembly Source File  |  1986-02-17  |  20.0 KB  |  678 lines

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