Annotation of coherent/d/conf/tboot/cpuid.m, revision 1.1.1.1

1.1       root        1: / 
                      2: / FLG_PIQL      Pre-fetch instruction queue length, 0 => 4-byte, 1 => 6-byte
                      3: / FLG_08                Intel 808x
                      4: / FLG_NEC       NEC V20 or V30
                      5: / FLG_18                Intel 8018x
                      6: / FLG_28                Intel 8028x
                      7: / FLG_38                Intel 8038x
                      8: / 
                      9: / FLG_87                Intel 8087
                     10: / FLG_287       Intel 80287
                     11: / FLG_387       Intel 80387
                     12: / 
                     13: / FLG_1167      Weitek 1167
                     14: / 
                     15: / $FLG_CERR     Faulty CPU
                     16: / $FLG_NERR     Faulty NDP switch setting
                     17: 
                     18: FLG     record  $RSVD:6,$FLG_NERR:1,$FLG_CERR:1,$FLG_WTK:1,$FLG_NDP:3,$FLG_CPU:4
                     19: 
                     20: / CPU-related flags
                     21: 
                     22: FLG_PIQL equ    0001b shl $FLG_CPU
                     23: FLG_08  equ     0000b shl $FLG_CPU
                     24: FLG_NEC  equ    0010b shl $FLG_CPU
                     25: FLG_18  equ     0100b shl $FLG_CPU
                     26: FLG_28  equ     0110b shl $FLG_CPU
                     27: FLG_38  equ     1000b shl $FLG_CPU
                     28: 
                     29: FLG_8088 equ    FLG_08
                     30: FLG_8086 equ    FLG_08 or FLG_PIQL
                     31: FLG_V20  equ    FLG_NEC
                     32: FLG_V30  equ    FLG_NEC or FLG_PIQL
                     33: FLG_80188 equ   FLG_18
                     34: FLG_80186 equ   FLG_18 or FLG_PIQL
                     35: FLG_80286 equ   FLG_28 or FLG_PIQL
                     36: FLG_80386 equ   FLG_38 or FLG_PIQL
                     37: 
                     38: / NDP-related flags
                     39: 
                     40: FLG_NDPX equ    000b shl $FLG_NDP      / Not present
                     41: FLG_NDPU equ    001b shl $FLG_NDP      / Untested
                     42: FLG_87  equ     010b shl $FLG_NDP
                     43: FLG_287  equ    011b shl $FLG_NDP
                     44: FLG_387  equ    100b shl $FLG_NDP
                     45: 
                     46: FLG_1167 equ    mask $FLG_WTK
                     47: 
                     48: CPUID   proc    near           / Start CPUID procedure
                     49: 
                     50: / This procedure determines the type of CPU and NDP (if any) in use.
                     51: / 
                     52: / The possibilities include:
                     53: / 
                     54: / Intel 8086
                     55: / Intel 8088
                     56: / NEC   V20
                     57: / NEC   V30
                     58: / Intel 80186
                     59: / Intel 80188
                     60: / Intel 80286
                     61: / Intel 80386
                     62: / Intel 8087
                     63: / Intel 80287
                     64: / Intel 80387
                     65: / 
                     66: /    Also checked is whether or not the CPU allows interrupts after
                     67: / changing the SS segment register.  If the CPU does, it is faulty and
                     68: / should be replaced.
                     69: / 
                     70: /    Further, if an NDP is installed, non-AT machines should have a
                     71: / system board switch set correspondingly.  Such a discrepancy is
                     72: / reported upon.
                     73: / 
                     74: /    On exit, BX contains flag settings (as defined in $FLG record) which
                     75: / the caller can check.  For example, to test for an Intel 80286, use
                     76: / 
                     77: /       and     bx,mask $FLAG_CPU
                     78: / 
                     79: /       cmp     bx,FLG_80286
                     80: /       je      ITSA286
                     81: / 
                     82: 
                     83:        push    ax / Save registers
                     84:        push    cx
                     85:        push    di
                     86:        push    ds
                     87:        push    es
                     88: 
                     89: / Test for 80286/386 -- these CPUs execute PUSH SP by first storing SP on stack,
                     90: / then decrementing it.  Earlier CPUs first decrement then store.
                     91: 
                     92:         push    sp             / Only 286 pushes pre-push SP
                     93:         pop     ax             / Get it back
                     94: 
                     95:         cmp     ax,sp          / Check for same
                     96:         jne     CHECK_18x      / They aren't, try next class
                     97: 
                     98:         call    DIST_286or386  / Distinguish a 286 from 386
                     99:         jmp     short CHECK_PIQL / Join common code
                    100: 
                    101: / Test for 80186/80188 -- 18x and later CPUs mask shift/rotate operations
                    102: / mod 32/ earlier CPUs use all 8 bits of CL.
                    103: 
                    104: CHECK_18x:
                    105:         mov     bx,FLG_18      / Assume it's an 8018x
                    106:         mov     cl,32+1        / 18x masks shift counts mod 32
                    107:                                / Note we can't use just 32 in CL
                    108:         mov     al,0FFh        / Start with all bits set
                    109: 
                    110:         shl     al,cl          / Shift one position if 18x
                    111:         jnz     CHECK_PIQL     / Some bits still on, so it's a 18x or later/
                    112:                                / check PIQL
                    113: 
                    114:         mov     bx,FLG_NEC     / Assume it's an NEC V-series CPU
                    115:         call    CHECK_NEC      / See if it's an NEC chip
                    116:         jcxz    CHECK_PIQL     / Good guess, check PIQL
                    117: 
                    118:         mov     bx,FLG_08      / It's an 808x
                    119: /      subttl  Check Length Of Pre-fetch Instruction Queue
                    120: /      page
                    121: 
                    122: / Check the length of the pre-fetch instruction queue (PIQ).
                    123: / 
                    124: / xxxx6 CPUs have a PIQ length of 6 bytes,
                    125: / xxxx8 CPUs   "     "     "      4   "
                    126: / 
                    127: / Self-modifying code is used to distinguish the two PIQ lengths.
                    128: / 
                    129: / To overcome write pipelining in 286/386 chips, the largest value
                    130: / over several executions of the subroutine is used.
                    131: 
                    132: CHECK_PIQL:
                    133:         call    PIQL_SUB       / Handled via subroutine
                    134: 
                    135:         cmp     cx,PIQL        / Use the larger of the two
                    136:         jbe     CHECK_PIQL1    / CX is smaller
                    137: 
                    138:         mov     PIQL,cx        / Save to report on later
                    139: CHECK_PIQL1:
                    140:         dec     PIQL_CNT       / One fewer times through the loop
                    141:         jnz     CHECK_PIQL     / Jump if
                    142: 
                    143:         cmp     PIQL,4         / Check PIQL
                    144:         jbe     CHECK_ERR      / Jump if xxxx8
                    145: 
                    146:         or      bx,FLG_PIQL    / PIQ length is 5 or longer
                    147: /      subttl  Check For Allowing Interrupts After POP SS
                    148: /      page
                    149: 
                    150: / Test for faulty chip (allows interrupts after change to SS register)
                    151: 
                    152: CHECK_ERR:
                    153:         call    ERR_SUB        / Handled via subroutine
                    154:         jcxz    CHECK_NDP      / If CX is 0, the DEC was executed,
                    155:                                / and the CPU is OK
                    156:         or      bx,mask $FLG_CERR / It's a faulty chip
                    157: /      subttl  Check For Numeric Data Processor
                    158: /      page
                    159: CHECK_NDP:
                    160:         call    NDP_SUB        / Handled via subroutine
                    161: 
                    162:         pop    es / Restore registers
                    163:         pop    ds
                    164:         pop    di
                    165:         pop    cx
                    166:         pop    ax
                    167: 
                    168:         ret                    / Return to caller
                    169: 
                    170: CPUID   endp                   / End CPUID procedure
                    171: /      subttl  Distinguish A 286 From 386
                    172: /      page
                    173: DIST_286or386 proc   near
                    174: 
                    175: / The test for 286 vs. 386 is done by attempting to set flag bits in
                    176: / the high-order nibble of the flag word.  If that's successful, it's a
                    177: / 386/ otherwise it's a 286.
                    178: 
                    179: 
                    180:         push   ax              / Save register
                    181: 
                    182:         pushf                  / Save flags for a moment
                    183: 
                    184:         mov     ax,0F000h      / Try to set high bits in flag register
                    185: 
                    186:         push    ax             / Move into flag register
                    187:         popf
                    188: 
                    189:         pushf                  / Get flags back into AX
                    190:         pop     ax
                    191: 
                    192:         popf                   / Restore original flags
                    193: 
                    194:         test    ax,0F000h      / Any bits set?
                    195:         jz      ITSA286        / No, so it's a 286
                    196: 
                    197:         or      bx,FLG_38      / It's a 38x
                    198:         jmp     short DIST_286or386_EXIT / Join common exit code
                    199: ITSA286:
                    200:         or      bx,FLG_28      / It's a 28x
                    201: DIST_286or386_EXIT:
                    202:         pop ax         / Restore
                    203: 
                    204:         ret                    / Return to caller
                    205: 
                    206: DIST_286or386 endp             / End DIST_286or386 procedure
                    207: /      subttl  Check For NEC V20/V30
                    208: /      page
                    209: CHECK_NEC proc  near
                    210: /    The NEC V20/V30 CPUs are very compatible with the Intel 8086/8088.
                    211: / The only point of "incompatiblity" is that they do not contain a bug
                    212: / found in the Intel CPUs.  Specifically, the NEC CPUs correctly restart
                    213: / an interrupted multi-prefix string instruction at the start of the
                    214: / instruction.  The Intel CPUs incorrectly restart it in the middle of
                    215: / the instruction.  This routine tests for that situation by executing
                    216: / such an instruction for a sufficiently long period of time for a timer
                    217: / interrupt to occur.  If at the end of the instruction, CX is zero,
                    218: / it must be an NEC CPU/ if not, it's an Intel CPU.
                    219: / 
                    220: /    Note that we're counting on the timer interrupt to do its thing
                    221: / every 18.2 times per second.
                    222: / 
                    223: /    Here's a worst case analysis:  An Intel 8086/8088 executes 65535
                    224: / iterations of LODSB ES:[SI] in 2+9+13*65535 = 851,966 clock ticks.  If
                    225: / the Intel 8086/8088 is running at 15 MHz, each clock tick is 66.67
                    226: / nanoseconds, hence the entire operation takes 56.8 milliseconds.  If the
                    227: / timer is running at normal speed, it interrupts the CPU every 55
                    228: / millseconds and so should interrupt the repeated string instruction at
                    229: / least once.
                    230: 
                    231:         mov     cx,0FFFFh      / Move a lot of data
                    232:         sti                    / Ensure timer enabled
                    233: 
                    234: /  Execute multi-prefix instruction.  Note that the value of ES as
                    235: /  well as the direction flag setting is irrelevant.
                    236: 
                    237:         push    ax             / Save registers
                    238:         push    si
                    239:      rep lods   byte ptr es:[si]
                    240:         pop     si             / Restore
                    241:         pop     ax
                    242: 
                    243: / On exit, if CX is zero, it's an NEC CPU, otherwise it's an Intel CPU
                    244: 
                    245:         ret                    / Return to caller
                    246: 
                    247: CHECK_NEC endp
                    248: /      subttl  Pre-fetch Instruction Queue Subroutine
                    249: /      page
                    250: PIQL_SUB proc   near
                    251: /    This subroutine attempts to discern the length of the CPU's
                    252: / pre-fetch instruction queue (PIQ).
                    253: / 
                    254: /    It stores a new instruction into the instruction stream
                    255: / following the STOSB.  The loop proceeds backwards from the end
                    256: / of the stream to the beginning.  At the point the inserted
                    257: / instruction is not executed, we have found the last byte in
                    258: / the PIQ.  The value in CX at that time is then the PIQ length.
                    259: 
                    260:         push   ax / Save registers
                    261:         push   bx
                    262:         push   dx
                    263:         push   si
                    264:         push   di
                    265: 
                    266: @REP    equ     64             / Maximum length of PIQ
                    267:                                / we can handle
                    268: 
                    269:         std                    / Store backwards
                    270: 
                    271:         mov     cx,@REP        / Loop counter
                    272:         lea     di,LAB_NOP+@REP-1 / ES:DI ==> PIQL last byte
                    273:                                / in fill area
                    274: 
                    275:         mov     al,ds:LAB_INC  / Change to INC BX
                    276:         mov     ah,ds:LAB_NOP  / Save a NOP here to restore
                    277:         mov     si,1           / Divisor
                    278:         xor     dx,dx          / Zero high-order word for divide
                    279:         cli                    / Ensure interrupts are disabled, otherwise
                    280:                                / a timer tick could disturb the PIQ filling
                    281:         even                   / Ensure word alignment for LAB_FILL
                    282:         nop
                    283: PIQL_SUB_NEXT:
                    284:         xor     bx,bx          / Initialize flag
                    285:         div     si             / Take up some time and
                    286:                                / refill the queue
                    287:         stosb                  / Change the instruction
                    288: 
                    289: / The PIQ begins filling here
                    290: 
                    291: LAB_NOP  label  byte
                    292:         rept    @REP
                    293:         nop                    // Fill byte
                    294:         endm
                    295: 
                    296:         mov     es:[di+1],ah   / Restore the NOP
                    297: 
                    298:         and     bx,bx          / Did we execute it?
                    299:         loopnz  PIQL_SUB_NEXT  / Go around again if we did
                    300:                                / and loop not finished
                    301:         inc     cx             / Count in last byte
                    302: 
                    303:         sti                    / Restore interrupts
                    304:         cld                    / Restore direction flag
                    305: 
                    306:         pop    di / Restore
                    307:         pop    si
                    308:         pop    dx
                    309:         pop    bx
                    310:         pop    ax
                    311: 
                    312: / At the end, CX has the length of the PIQ
                    313: 
                    314:         ret                    / Return to caller
                    315: 
                    316: LAB_INC  label  byte
                    317:         inc     bx             / Increment counter
                    318: 
                    319: PIQL_SUB endp                  / End PIQL_SUB procedure
                    320: /       subttl  Check For Faulty Interrupts
                    321: /       page
                    322: ERR_SUB  proc   near
                    323: / Test for faulty chip (allows interrupts after change to SS register).
                    324: / Setup a handler for INT 01h (single-step interrupt) and turn on that
                    325: / flag just before executing a POP SS.  If the CPU allows a single-step
                    326: / interrupt after the POP SS, it's faulty.
                    327: / 
                    328: / On exit:
                    329: / 
                    330: / CX    =       1 if CPU is faulty
                    331: /       =       0 if OK
                    332: 
                    333: 
                    334: 
                    335:         push   ax      / Save registers
                    336:         push   ds
                    337: 
                    338:         xor     ax,ax          / Prepare to address interrupt vector segment
                    339:         mov     ds,ax          / DS points to segment 0
                    340: 
                    341:         cli                    / Nobody move while we swap
                    342: 
                    343:         lea     ax,INT01       / Point to our own handler
                    344:         xchg    ax,INT01_OFF   / Get and swap offset
                    345:         mov     OLDINT01_OFF,ax / Save to restore later
                    346: 
                    347:         mov     ax,cs          / Our handler's segment
                    348:         xchg    ax,INT01_SEG   / Get and swap segment
                    349:         mov     OLDINT01_SEG,ax / Save to restore later
                    350: 
                    351: / Note we continue with interrupts disabled to avoid an external interrupt
                    352: / occurring during this test.
                    353: 
                    354:         mov     cx,1           / Initialize a register
                    355:         push    ss             / Save SS to store back into itself
                    356: 
                    357:         pushf                  / Move flags
                    358:         pop     ax             / ...into AX
                    359:         or      ax,mask $TF    / Set trap flag
                    360:         push    ax             / Place onto stack
                    361:         POPFF                  / ...and then into effect
                    362:                                / Some CPUs effect the trap flag immediately,
                    363:                                /   some wait one instruction.
                    364:         nop                    / Allow interrupt to take effect
                    365: POST_NOP:
                    366:         pop     ss             / Change the stack segment register (to itself)
                    367:         dec     cx             / Normal CPUs execute this instruction before
                    368:                                / recognizing the single-step interrupt
                    369:         hlt                    / We never get here
                    370: INT01:
                    371: 
                    372: / Note IF=TF=0
                    373: 
                    374: / If we're stopped at or before POST_NOP, continue on
                    375: 
                    376:         push    bp             / Prepare to address the stack
                    377:         mov     bp,sp          / Hello, Mr. Stack
                    378: 
                    379:         cmp     [bp].ARG_OFF,offset cs:POST_NOP / Check offset
                    380:         pop     bp             / Restore
                    381:         ja      INT01_DONE     / We're done
                    382: 
                    383:         iret                   / Return to caller
                    384: INT01_DONE:
                    385: 
                    386: / Restore old INT 01h handler
                    387: 
                    388:         push    es             / Save for a moment
                    389:         les     ax,OLDINT01_VEC / ES:AX ==> old INT 01h handler
                    390:         mov     INT01_OFF,ax   / Restore offset
                    391:         mov     INT01_SEG,es   / ...and segment
                    392:         pop     es             / Restore
                    393: 
                    394:         sti                    / Allow interrupts again (IF=1)
                    395: 
                    396:         add     sp,3*2         / Strip IP, CS, and Flags from stack
                    397: 
                    398:         pop    ds      / Restore
                    399:         pop    ax
                    400: 
                    401:         ret                    / Return to caller
                    402: 
                    403: ERR_SUB  endp                  / End ERR_SUB procedure
                    404: /       subttl  Check For Numeric Data Processor
                    405: /       page
                    406: NDP_SUB  proc   near
                    407: /    Test for a Numeric Data Processor -- Intel 8087, 80287, or 80387.
                    408: / An 8087 allows FDISI, an 80287/80387 ignores it.  The 80287 and 80387
                    409: / can be distinguished through their different treatment of the infinity
                    410: / closure setting.
                    411: / 
                    412: /    In general, the technique used is passive -- it leaves the NDP in
                    413: / the same state in which it is found.
                    414: / 
                    415: /    Unfortunately, some IBM PC/ATs and 3270/ATs without an NDP don't
                    416: / handle floating-point instructions correctly.  In particular, when
                    417: / no-WAIT NDP instruction is executed on those systems, they wipe out
                    418: / the memory location and all bytes following in the same segment.  To
                    419: / overcome this bug, Dan Lewis has suggested a technique which computes
                    420: / the segment and offset of the location into which the store is made
                    421: / such that the offset is in the last paragraph of the segment.  This
                    422: / way, the wipe out is harmless.
                    423: / 
                    424: / On exit:
                    425: / 
                    426: / BX    =       $FLG_NDP & $FLG_NERR bits set as appropriate.
                    427: 
                    428:         push   ax      / Save registers
                    429:         push   cx
                    430:         push   di
                    431: 
                    432:         call    CHECK_1167     / See if there's a Weitek 1167 in the system
                    433: 
                    434: / Because some IBM PC/ATs and 3270/ATs without an NDP don't handle
                    435: / floating-point instructions correctly, we check for a 286 explicitly
                    436: / and rely upon the equipment flags to tell us if there's an NDP installed.
                    437: / This behavior is also present on some 386s.
                    438: 
                    439:         test    LCL_FLAGS,@LCL_REAL / Use real code or not?
                    440:         jnz     NDP_SUB1       / It's real
                    441: 
                    442:         mov     ax,bx          / Copy CPUID bits for destructive testing
                    443:         and     ax,(mask $FLG_CPU) and not FLG_PIQL / Isolate CPU bits
                    444: 
                    445:         cmp     ax,FLG_28      / Izit a 28x?
                    446:         je      NDP_SUB0       / Yes, skip NDP instructions
                    447: 
                    448:         cmp     ax,FLG_38      / Izit a 38x?
                    449:         jne     NDP_SUB1       / Not this time
                    450: NDP_SUB0:
                    451:         test    LCL_FLAGS,@LCL_I11H / Skip INT 11h test?
                    452:         jnz     NDP_SUB_UN     / Yes
                    453: 
                    454:         int     11h            / Get equipment flags into AX
                    455: 
                    456:         test    ax,mask $I11_NDP / Check NDP-installed bit
                    457:         jz      NDP_SUB_EXIT0  / Not installed
                    458: 
                    459:         call    DIST_287or387  / Distinguish a 287 from a 387
                    460: NDP_SUB_EXIT0:
                    461:         jmp     NDP_SUB_EXIT   / Join common exit code
                    462: 
                    463: NDP_SUB_UN:
                    464:         or      bx,FLG_NDPU    / Mark as untested
                    465: 
                    466:         jmp     NDP_SUB_EXIT   / Join common exit code
                    467: 
                    468: NDP_SUB1:
                    469:         push    es             / Save for a moment
                    470: 
                    471:         lea     di,NDP_ENV+(size NDP_ENV)-1 / Offset of end of environment
                    472:         call    MAX_OFFSET     / Return with ES:DI ==> NDP_ENV and DI largest
                    473:         sub     di,(size NDP_ENV)-1 / Back off to start of NDP_ENV
                    474: 
                    475:         cli                    / Protect FNSTENV
                    476:         fnstenv es:[di]        / If NDP present, save current environment,
                    477:                                / otherwise, this instruction is ignored
                    478:         sti                    / Allow interrupts
                    479: 
                    480:         pop     es             / Restore
                    481: 
                    482:         mov     cx,50/7        / Cycle this many times
                    483:         loop    $              / Wait for result to be stored
                    484: 
                    485:         fninit                 / Initialize processor to known state
                    486:         jmp     short $+2      / Wait for initialization
                    487: 
                    488:         push    es             / Save for a moment
                    489: 
                    490:         lea     di,NDP_CW+(size NDP_CW)-1 / Offset of end of control word
                    491:         call    MAX_OFFSET     / Return with ES:DI ==> NDP_CW and DI largest
                    492:         sub     di,(size NDP_CW)-1 / Back off to start of control word
                    493: 
                    494:         fnstcw  es:[di]        / Save control word
                    495: 
                    496:         pop     es             / Restore
                    497: 
                    498:         jmp     short $+2      / Wait for result to be stored
                    499:         jmp     short $+2
                    500: 
                    501:         and     NDP_CW,not mask $IC / Turn off infinity control in case of 387
                    502: 
                    503:         cmp     NDP_CW_HI,03h  / Check for NDP initial control word
                    504:         jne     NDP_SUB_NONE   / No NDP installed
                    505: 
                    506:         test    LCL_FLAGS,@LCL_I11H / Skip INT 11h test?
                    507:         jnz     NDP_SUB2       / Yes
                    508: 
                    509:         int     11h            / Get equipment flags into AX
                    510: 
                    511:         test    ax,mask $I11_NDP / Check NDP-installed bit
                    512:         jnz     NDP_SUB2       / It's correctly set
                    513: 
                    514:         or      bx,mask $FLG_NERR / Mark as in error
                    515: NDP_SUB2:
                    516:         and     NDP_CW,not mask $IEM / Enable interrupts (IEM=0, 8087 only)
                    517:         fldcw   NDP_CW         / Reload control word
                    518:         fdisi                  / Disable interrupts (IEM=1) on 8087,
                    519:                                / ignored by 80287/80387
                    520:         fstcw   NDP_CW         / Save control word
                    521:         fldenv  NDP_ENV        / Restore original NDP environment
                    522:                                / No need to wait for environment to be loaded
                    523: 
                    524:         test    NDP_CW,mask $IEM / Check Interrupt Enable Mask (8087 only)
                    525:         jnz     NDP_SUB_8087   / It changed, hence NDP is an 8087
                    526: 
                    527:         call    DIST_287or387  / Distinguish a 287 from a 387
                    528: 
                    529:         jmp     short NDP_SUB_EXIT / Exit with flags in BX
                    530: NDP_SUB_8087:
                    531:         or      bx,FLG_87      / NDP is an 8087
                    532: 
                    533:         jmp     short NDP_SUB_EXIT / Join common exit code
                    534: NDP_SUB_NONE:
                    535:         test    LCL_FLAGS,@LCL_I11H / Skip INT 11h test?
                    536:         jnz     NDP_SUB_EXIT   / Yes
                    537: 
                    538:         int     11h            / Get equipment flags into AX
                    539: 
                    540:         test    ax,mask $I11_NDP / Check NDP-installed bit
                    541:         jz      NDP_SUB_EXIT   / It's correctly set
                    542: 
                    543:         or      bx,mask $FLG_NERR / Mark as in error
                    544: NDP_SUB_EXIT:
                    545:         pop    di      / Restore
                    546:         pop    cx
                    547:         pop    ax
                    548: 
                    549:         ret                    / Return to caller
                    550: 
                    551: NDP_SUB  endp                  / End NDP_SUB procedure
                    552: /       subttl  Check For Weitek 1167
                    553: /       page
                    554: CHECK_1167 proc near
                    555: / See if there's a Weitek 1167 in the system.
                    556: / 
                    557: / To determine that, clear EAX, call INT 11h, and test bit 24
                    558: / in EAX.  If set, the coprocessor is present/ if not, then
                    559: / it's not.  Obviously, we must be running on a 386.
                    560: 
                    561:         test    LCL_FLAGS,@LCL_I11H / Skip INT 11h test?
                    562:         jnz     CHECK_1167_EXIT / Yes, 1167 untested
                    563: 
                    564:         test    bx,FLG_38      / Are we on a 38x?
                    565:         jz      CHECK_1167_EXIT / No, thus no 1167
                    566: 
                    567:         db      66h            / Use EAX
                    568:         push    ax             / Save for a moment
                    569: 
                    570:         db      66h            / Use EAX
                    571:         xor     ax,ax          / Clear entire register
                    572: 
                    573:         int     11h            / Get equipment flags
                    574: 
                    575:         db      66h            / Use EAX
                    576:         test    ax,0000h
                    577:         dw      0100h          / Test bit 24
                    578:         jz      CHECK_1167_EXIT0 / Not present
                    579: 
                    580:         or      bx,FLG_1167    / Mark as present
                    581: CHECK_1167_EXIT0:
                    582:         db      66h            / Use EAX
                    583:         pop     ax             / Restore
                    584: CHECK_1167_EXIT:
                    585:         ret                    / Return to caller
                    586: 
                    587: CHECK_1167 endp                / End CHECK_1167 procedure
                    588: /      subttl  Distinguish A 287 From 387
                    589: /      page
                    590: DIST_287or387 proc near
                    591: /    Distinguish a 287 from a 387.
                    592: / 
                    593: /    Both the 80287 and 80387 are initialized with the infinity closure
                    594: / bit set to one.  However, only the 80287 is sensitive to the value of
                    595: / this bit.  Ordinarily when this bit is set to one, the chip uses
                    596: / projective closure/ when it is cleared to zero, the chip uses affine
                    597: / closure.  Thus the 80287 is initialized to use projective closure, but
                    598: / that state can be changed through the infinity closure bit in the
                    599: / control word.  On the other hand, the 80387 is initialized to use
                    600: / affine closure and remains in that state independent of the setting of
                    601: / the infinity closure bit.  The two NDPs can be distinguished by
                    602: / executing code which is sensitive to the setting of the infinity
                    603: / closure bit.
                    604: 
                    605:    The algorithm used is based upon one published by Intel on how to
                    606: detect the 80387.
                    607: 
                    608: On exit:
                    609: 
                    610: BX      =       $FLG_NDP bits set as appropriate.
                    611: 
                    612: |
                    613: 
                    614: .287
                    615:         fstenv  NDP_ENV        / Save current environment
                    616:         finit                  / Initialize processor to known state
                    617:                                / The 80287 is using projective
                    618:                                / closure for arithmetic, the 80387 is
                    619:                                / using affine closure
                    620: 
                    621:         fld1                   / Generate infinity
                    622:         fldz                   /  by dividing zero into one
                    623:         fdiv                   / ST0 = +infinity
                    624:         fld     st(0)          / Copy it
                    625:         fchs                   / ST0 = -infinity, ST1 = +infinity
                    626:         fcompp                 / Compare them and pop both from stack
                    627:         fstsw   ax             / Get status word
                    628:         fldenv  NDP_ENV        / Restore original NDP environment
                    629:         sahf                   / Copy into flags
                    630:         jz      DIST_287or387_PROJ / Jump if the two are equal
                    631:                                / (projective closure)
                    632: 
                    633:         or      bx,FLG_387     / NDP is an 80387
                    634: 
                    635:         jmp     short DIST_287or387_EXIT / Join common exit code
                    636: 
                    637: DIST_287or387_PROJ:
                    638:         or      bx,FLG_287     / NDP is an 80287
                    639: DIST_287or387_EXIT:
                    640:         ret                    / Return to caller
                    641: .8087
                    642: DIST_287or387 endp             / End DIST_287or387 procedure
                    643: /      subttl  Calculate Maximum Offset
                    644: /      page
                    645: MAX_OFFSET proc  near
                    646: 
                    647: / On entry:
                    648: / 
                    649: / ES:DI         ==>     memory offset
                    650: / 
                    651: / On exit:
                    652: / 
                    653: / ES:DI         ==>     same location but with DI as large as possible
                    654: 
                    655: 
                    656: MAXOFF  equ     0FFF0h
                    657: 
                    658:         push   ax      / Save registers
                    659:         push   cx
                    660: 
                    661:         push    di
                    662: 
                    663:         mov     cl,4           / Shift amount
                    664:         shr     di,cl          / Isolate para of control word
                    665:         mov     ax,es          / Get current segment
                    666:         add     ax,di          / Add in segment of control word
                    667:         sub     ax,MAXOFF shr 4 / Less a lot of paras
                    668:         mov     es,ax          / Save as segment of control word
                    669: 
                    670:         pop     di             / Restore
                    671: 
                    672:         or      di,MAXOFF      / Include paras subtracted out above
                    673: 
                    674:         pop    cx      / Restore
                    675:         pop    ax
                    676: 
                    677:         ret                    / Return to caller
                    678: 
                    679: MAX_OFFSET endp                / End MAX_OFFSET procedure
                    680: 
                    681: CODE    ends                   / End CODE segment
                    682: 
                    683:         if1
                    684: %OUT Pass 1 complete
                    685:         else
                    686: %OUT Pass 2 complete
                    687:         endif
                    688: 
                    689:         end     INITIAL        / End CPUID module

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.