|
|
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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.