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