Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

902 lines
22 KiB

  1. title "Processor type and stepping detection"
  2. ;++
  3. ;
  4. ; Copyright (c) 1989 Microsoft Corporation
  5. ;
  6. ; Module Name:
  7. ;
  8. ; cpu.asm
  9. ;
  10. ; Abstract:
  11. ;
  12. ; This module implements the assembley code necessary to determine
  13. ; cpu type and stepping information.
  14. ;
  15. ; Author:
  16. ;
  17. ; Shie-Lin Tzong (shielint) 28-Oct-1991.
  18. ; Some of the code is extracted from Cruiser (mainly,
  19. ; the code to determine 386 stepping.)
  20. ;
  21. ; Environment:
  22. ;
  23. ; 80x86 Real Mode.
  24. ;
  25. ; Revision History:
  26. ;
  27. ;
  28. ;--
  29. .xlist
  30. include cpu.inc
  31. .list
  32. ;
  33. ; constant for i386 32-bit multiplication test
  34. ;
  35. MULTIPLIER equ 00000081h
  36. MULTIPLICAND equ 0417a000h
  37. RESULT_HIGH equ 00000002h
  38. RESULT_LOW equ 0fe7a000h
  39. ;
  40. ; Constants for Floating Point test
  41. ;
  42. REALLONG_LOW equ 00000000
  43. REALLONG_HIGH equ 3FE00000h
  44. PSEUDO_DENORMAL_LOW equ 00000000h
  45. PSEUDO_DENORMAL_MID equ 80000000h
  46. PSEUDO_DENORMAL_HIGH equ 0000h
  47. .386p
  48. _TEXT SEGMENT PARA USE16 PUBLIC 'CODE'
  49. ASSUME CS: _TEXT, DS:NOTHING, SS:NOTHING
  50. ;++
  51. ;
  52. ; USHORT
  53. ; HwGetProcessorType (
  54. ; VOID
  55. ; )
  56. ;
  57. ; Routine Description:
  58. ;
  59. ; This function determines type of processor (80486, 80386, 80286,
  60. ; and even 8086/8088). it relies on Intel-approved code that takes
  61. ; advantage of the documented behavior of the high nibble of the flag
  62. ; word in the REAL MODE of the various processors.
  63. ;
  64. ; For completeness, the code also checks for 8088/8086. But, it won't
  65. ; work.
  66. ;
  67. ; Arguments:
  68. ;
  69. ; None.
  70. ;
  71. ; Return Value:
  72. ;
  73. ; (ax) = x86h or 0 if unrecongnized processor.
  74. ;
  75. ;--
  76. .8086
  77. public _HwGetProcessorType
  78. _HwGetProcessorType proc near
  79. pushf ; save entry flags
  80. ;
  81. ; The MSB (bit 15) is always a one on the 8086 and 8088 and a zero on
  82. ; the 286, 386 and 486.
  83. ;
  84. pushf
  85. pop ax
  86. and ax, NOT 08000h ; clear bit 15 of flags
  87. push ax
  88. popf ; try to put that in the flags
  89. pushf
  90. pop ax ; look at what really went into flags
  91. test ax,08000h ; Was high bit set ?
  92. jnz short x_86 ; if nz, still set, goto x_86
  93. ;
  94. ; Bit 14 (NT flag) and bits 13/12 (IOPL bit field) are always zero on
  95. ; the 286, but can be set on the 386 and 486.
  96. ;
  97. or ax,07000h ; Try to set the NT/IOPL bits
  98. push ax
  99. popf ; Put in to the flags
  100. sti ; (for VDMM/IOPL0)
  101. pushf
  102. pop ax ; look at actual flags
  103. test ax,07000h ; Any high bits set ?
  104. jz short x_286 ; if z, no, goto x_286
  105. .386p
  106. ;
  107. ; The Alignment Check bit in flag can be set on 486 and is always zero
  108. ; on 386.
  109. ;
  110. mov eax,cr0 ; test for 486 processor
  111. push eax ; save CR0 value
  112. and eax,not CR0_AM ; disable alignment check
  113. mov cr0,eax
  114. db ADDRESS_OVERRIDE
  115. pushfd ; save original EFLAGS
  116. db ADDRESS_OVERRIDE
  117. pushfd ; try to set alignment check
  118. or dword ptr [esp],EFLAGS_AC ; bit in EFLAGS
  119. db ADDRESS_OVERRIDE
  120. popfd
  121. db ADDRESS_OVERRIDE
  122. pushfd ; copy new flags into ECX
  123. pop ecx ; [ecx] = new flags word
  124. db ADDRESS_OVERRIDE
  125. popfd ; restore original EFLAGS
  126. pop eax ; restore original CR0 value
  127. mov cr0,eax
  128. and ecx, EFLAGS_AC ; did AC bit get set?
  129. jz short x_386 ; if z, no, goto x_386
  130. mov eax, 4h ; if nz, we have a 486 processor
  131. .286p
  132. jmp short hpt99
  133. x_286:
  134. mov ax, 2h ; Return 286 processor type.
  135. jmp short hpt99
  136. x_86:
  137. mov ax, 0h ; Return 86h for 8088/8086 CPU type.
  138. jmp short hpt99
  139. x_386:
  140. mov ax, 3h ; Return 386 processor type.
  141. hpt99:
  142. popf ; restore flags
  143. ret
  144. _HwGetProcessorType endp
  145. .386p
  146. ;++
  147. ;
  148. ; USHORT
  149. ; HwGetCpuStepping (
  150. ; UHSORT CpuType
  151. ; )
  152. ;
  153. ; Routine Description:
  154. ;
  155. ; This function determines cpu stepping for the specified CPU type.
  156. ;
  157. ; Currently, this routine only determine stepping for 386 and 486.
  158. ;
  159. ; Arguments:
  160. ;
  161. ; CpuType - The Cpu type which its stepping information will be returned.
  162. ; The input value MUST be either 386 or 486.
  163. ;
  164. ; Return Value:
  165. ;
  166. ; [ax] - Cpu stepping. For example, [ax] = D0h for D0 stepping.
  167. ;
  168. ;--
  169. HgcsCpuType equ [esp + 2]
  170. public _HwGetCpuStepping
  171. _HwGetCpuStepping proc
  172. mov ax, HgcsCpuType ; [ax] = CpuType
  173. cmp ax, 3h ; Is cpu = 386?
  174. jz short Hgcs00 ; if z, yes, go Hgcs00
  175. call Get486Stepping ; else, check for 486 stepping
  176. jmp short Hgcs90 ; [ax] = Stepping information
  177. Hgcs00:
  178. call Get386Stepping ; [ax] = Stepping information
  179. Hgcs90:
  180. ret
  181. _HwGetCpuStepping endp
  182. ;++
  183. ;
  184. ; USHORT
  185. ; Get386Stepping (
  186. ; VOID
  187. ; )
  188. ;
  189. ; Routine Description:
  190. ;
  191. ; This function determines cpu stepping for i386 CPU stepping.
  192. ;
  193. ; Arguments:
  194. ;
  195. ; None.
  196. ;
  197. ; Return Value:
  198. ;
  199. ; [ax] - Cpu stepping. For example, [ax] = D0h for D0 stepping.
  200. ; [ax] = 0 means bad CPU and stepping is not important.
  201. ;
  202. ;--
  203. public Get386Stepping
  204. Get386Stepping proc
  205. call MultiplyTest ; Perform mutiplication test
  206. jnc short G3s00 ; if nc, muttest is ok
  207. mov ax, 0
  208. ret
  209. G3s00:
  210. call Check386B0 ; Check for B0 stepping
  211. jnc short G3s05 ; if nc, it's B1/later
  212. mov ax, 0B0h ; It is B0/earlier stepping
  213. ret
  214. G3s05:
  215. call Check386D1 ; Check for D1 stepping
  216. jc short G3s10 ; if c, it is NOT D1
  217. mov ax, 0D1h ; It is D1/later stepping
  218. ret
  219. G3s10:
  220. mov ax, 0B1h ; assume it is B1 stepping
  221. ret
  222. Get386Stepping endp
  223. ;++
  224. ;
  225. ; USHORT
  226. ; Get486Stepping (
  227. ; VOID
  228. ; )
  229. ;
  230. ; Routine Description:
  231. ;
  232. ; This function determines cpu stepping for i486 CPU type.
  233. ;
  234. ; Arguments:
  235. ;
  236. ; None.
  237. ;
  238. ; Return Value:
  239. ;
  240. ; [ax] - Cpu stepping. For example, [ax] = D0h for D0 stepping.
  241. ;
  242. ;--
  243. public Get486Stepping
  244. Get486Stepping proc
  245. call Check486AStepping ; Check for A stepping
  246. jnc short G4s00 ; if nc, it is NOT A stepping
  247. mov ax, 0A0h ; set to A stepping
  248. ret
  249. G4s00: call Check486BStepping ; Check for B stepping
  250. jnc short G4s10 ; if nc, it is NOT a B stepping
  251. mov ax, 0B0h ; set to B stepping
  252. ret
  253. ;
  254. ; Before we test for 486 C/D step, we need to make sure NPX is present.
  255. ; Because the test uses FP instruction to do the detection.
  256. ;
  257. G4s10: call _IsNpxPresent ; Check if cpu has coprocessor support?
  258. cmp ax, 0
  259. jz short G4s15 ; it is actually 486sx
  260. call Check486CStepping ; Check for C stepping
  261. jnc short G4s20 ; if nc, it is NOT a C stepping
  262. G4s15:
  263. mov ax, 0C0h ; set to C stepping
  264. ret
  265. G4s20: mov ax, 0D0h ; Set to D stepping
  266. ret
  267. Get486Stepping endp
  268. ;++
  269. ;
  270. ; BOOLEAN
  271. ; Check486AStepping (
  272. ; VOID
  273. ; )
  274. ;
  275. ; Routine Description:
  276. ;
  277. ; This routine checks for 486 A Stepping.
  278. ;
  279. ; It takes advantage of the fact that on the A-step of the i486
  280. ; processor, the ET bit in CR0 could be set or cleared by software,
  281. ; but was not used by the hardware. On B or C -step, ET bit in CR0
  282. ; is now hardwired to a "1" to force usage of the 386 math coprocessor
  283. ; protocol.
  284. ;
  285. ; Arguments:
  286. ;
  287. ; None.
  288. ;
  289. ; Return Value:
  290. ;
  291. ; Carry Flag clear if B or later stepping.
  292. ; Carry Flag set if A or earlier stepping.
  293. ;
  294. ;--
  295. public Check486AStepping
  296. Check486AStepping proc near
  297. .386p
  298. mov eax, cr0 ; reset ET bit in cr0
  299. and eax, NOT CR0_ET
  300. mov cr0, eax
  301. mov eax, cr0 ; get cr0 back
  302. test eax, CR0_ET ; if ET bit still set?
  303. jnz short cas10 ; if nz, yes, still set, it's NOT A step
  304. stc
  305. ret
  306. cas10: clc
  307. ret
  308. ret
  309. Check486AStepping endp
  310. ;++
  311. ;
  312. ; BOOLEAN
  313. ; Check486BStepping (
  314. ; VOID
  315. ; )
  316. ;
  317. ; Routine Description:
  318. ;
  319. ; This routine checks for 486 B Stepping.
  320. ;
  321. ; On the i486 processor, the "mov to/from DR4/5" instructions were
  322. ; aliased to "mov to/from DR6/7" instructions. However, the i486
  323. ; B or earlier steps generate an Invalid opcode exception when DR4/5
  324. ; are used with "mov to/from special register" instruction.
  325. ;
  326. ; Arguments:
  327. ;
  328. ; None.
  329. ;
  330. ; Return Value:
  331. ;
  332. ; Carry Flag clear if C or later stepping.
  333. ; Carry Flag set if B stepping.
  334. ;
  335. ;--
  336. public Check486BStepping
  337. Check486BStepping proc
  338. push ds
  339. push bx
  340. xor ax,ax
  341. mov ds,ax ; (DS) = 0 (real mode IDT)
  342. mov bx,6*4
  343. push dword ptr [bx] ; save old int 6 vector
  344. mov word ptr [bx].VectorOffset,offset Temporary486Int6
  345. mov [bx].VectorSegment,cs ; set vector to new int 6 handler
  346. c4bs50: db 0fh, 21h, 0e0h ; mov eax, DR4
  347. nop
  348. nop
  349. nop
  350. nop
  351. nop
  352. clc ; it is C step
  353. jmp short c4bs70
  354. c4bs60: stc ; it's B step
  355. c4bs70: pop dword ptr [bx] ; restore old int 6 vector
  356. pop bx
  357. pop ds
  358. ret
  359. ret
  360. Check486BStepping endp
  361. ;++
  362. ;
  363. ; BOOLEAN
  364. ; Temporary486Int6 (
  365. ; VOID
  366. ; )
  367. ;
  368. ; Routine Description:
  369. ;
  370. ; Temporary int 6 handler - assumes the cause of the exception was the
  371. ; attempted execution of an mov to/from DR4/5 instruction.
  372. ;
  373. ; Arguments:
  374. ;
  375. ; None.
  376. ;
  377. ; Return Value:
  378. ;
  379. ; none.
  380. ;
  381. ;--
  382. Temporary486Int6 proc
  383. mov word ptr [esp].IretIp,offset c4bs60 ; set IP to stc instruction
  384. iret
  385. Temporary486Int6 endp
  386. ;++
  387. ;
  388. ; BOOLEAN
  389. ; Check486CStepping (
  390. ; VOID
  391. ; )
  392. ;
  393. ; Routine Description:
  394. ;
  395. ; This routine checks for 486 C Stepping.
  396. ;
  397. ; This routine takes advantage of the fact that FSCALE produces
  398. ; wrong result with Denormal or Pseudo-denormal operand on 486
  399. ; C and earlier steps.
  400. ;
  401. ; If the value contained in ST(1), second location in the floating
  402. ; point stack, is between 1 and 11, and the value in ST, top of the
  403. ; floating point stack, is either a pseudo-denormal number or a
  404. ; denormal number with the underflow exception unmasked, the FSCALE
  405. ; instruction produces an incorrect result.
  406. ;
  407. ; Arguments:
  408. ;
  409. ; None.
  410. ;
  411. ; Return Value:
  412. ;
  413. ; Carry Flag clear if D or later stepping.
  414. ; Carry Flag set if C stepping.
  415. ;
  416. ;--
  417. FpControl equ [ebp - 2]
  418. RealLongSt1 equ [ebp - 10]
  419. PseudoDenormal equ [ebp - 20]
  420. FscaleResult equ [ebp - 30]
  421. public Check486CStepping
  422. Check486CStepping proc
  423. push ebp
  424. mov ebp, esp
  425. sub esp, 30 ; Allocate space for temp real variables
  426. ;
  427. ; Initialize the local FP variables to predefined values.
  428. ; RealLongSt1 = 1.0 * (2 ** -1) = 0.5 in normalized double precision FP form
  429. ; PseudoDenormal = a unsupported format by IEEE.
  430. ; Sign bit = 0
  431. ; Exponent = 000000000000000B
  432. ; Significand = 100000...0B
  433. ; FscaleResult = The result of FSCALE instruction. Depending on 486 step,
  434. ; the value will be different:
  435. ; Under C and earlier steps, 486 returns the original value
  436. ; in ST as the result. The correct returned value should be
  437. ; original significand and an exponent of 0...01.
  438. ;
  439. mov dword ptr RealLongSt1, REALLONG_LOW
  440. mov dword ptr RealLongSt1 + 4, REALLONG_HIGH
  441. mov dword ptr PseudoDenormal, PSEUDO_DENORMAL_LOW
  442. mov dword ptr PseudoDenormal + 4, PSEUDO_DENORMAL_MID
  443. mov word ptr PseudoDenormal + 8, PSEUDO_DENORMAL_HIGH
  444. .387
  445. fnstcw FpControl ; Get FP control word
  446. fwait
  447. or word ptr FpControl, 0FFh ; Mask all the FP exceptions
  448. fldcw FpControl ; Set FP control
  449. fld qword ptr RealLongSt1 ; 0 < ST(1) = RealLongSt1 < 1
  450. fld tbyte ptr PseudoDenormal; Denormalized operand. Note, i486
  451. ; won't report denormal exception
  452. ; on 'FLD' instruction.
  453. ; ST(0) = Extended Denormalized operand
  454. fscale ; try to trigger 486Cx errata
  455. fstp tbyte ptr FscaleResult ; Store ST(0) in FscaleResult
  456. cmp word ptr FscaleResult + 8, PSEUDO_DENORMAL_HIGH
  457. ; Is Exponent changed?
  458. jz short c4ds00 ; if z, no, it is C step
  459. clc
  460. jmp short c4ds10
  461. c4ds00: stc
  462. c4ds10: mov esp, ebp
  463. pop ebp
  464. ret
  465. Check486CStepping endp
  466. ;++
  467. ;
  468. ; BOOLEAN
  469. ; Check386B0 (
  470. ; VOID
  471. ; )
  472. ;
  473. ; Routine Description:
  474. ;
  475. ; This routine checks for 386 B0 or earlier stepping.
  476. ;
  477. ; It takes advantage of the fact that the bit INSERT and
  478. ; EXTRACT instructions that existed in B0 and earlier versions of the
  479. ; 386 were removed in the B1 stepping. When executed on the B1, INSERT
  480. ; and EXTRACT cause an int 6 (invalid opcode) exception. This routine
  481. ; can therefore discriminate between B1/later 386s and B0/earlier 386s.
  482. ; It is intended to be used in sequence with other checks to determine
  483. ; processor stepping by exercising specific bugs found in specific
  484. ; steppings of the 386.
  485. ;
  486. ; Arguments:
  487. ;
  488. ; None.
  489. ;
  490. ; Return Value:
  491. ;
  492. ; Carry Flag clear if B1 or later stepping
  493. ; Carry Flag set if B0 or prior
  494. ;
  495. ;--
  496. ASSUME ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
  497. Check386B0 proc
  498. push ds
  499. push bx
  500. xor ax,ax
  501. mov ds,ax ; (DS) = 0 (real mode IDT)
  502. mov bx,6*4
  503. push dword ptr [bx] ; save old int 6 vector
  504. mov word ptr [bx].VectorOffset,offset TemporaryInt6
  505. mov [bx].VectorSegment,cs ; set vector to new int 6 handler
  506. ;
  507. ; Attempt execution of Extract Bit String instruction. Execution on
  508. ; B0 or earlier with length (CL) = 0 will return 0 into the destination
  509. ; (CX in this case). Execution on B1 or later will fail either due to
  510. ; taking the invalid opcode trap, or if the opcode is valid, we don't
  511. ; expect CX will be zeroed by any new instruction supported by newer
  512. ; steppings. The dummy int 6 handler will clears the Carry Flag and
  513. ; returns execution to the appropriate label. If the instruction
  514. ; actually executes, CX will *probably* remain unchanged in any new
  515. ; stepping that uses the opcode for something else. The nops are meant
  516. ; to handle newer steppings with an unknown instruction length.
  517. ;
  518. xor ax,ax
  519. mov dx,ax
  520. mov cx,0ff00h ; Extract length (CL) == 0, (CX) != 0
  521. b1c50: db 0fh, 0a6h, 0cah ; xbts cx,dx,ax,cl
  522. nop
  523. nop
  524. nop
  525. nop
  526. nop
  527. stc ; assume B0
  528. jcxz short b1c70 ; jmp if B0
  529. b1c60: clc
  530. b1c70: pop dword ptr [bx] ; restore old int 6 vector
  531. pop bx
  532. pop ds
  533. ret
  534. Check386B0 endp
  535. ;++
  536. ;
  537. ; BOOLEAN
  538. ; TemporaryInt6 (
  539. ; VOID
  540. ; )
  541. ;
  542. ; Routine Description:
  543. ;
  544. ; Temporary int 6 handler - assumes the cause of the exception was the
  545. ; attempted execution of an XTBS instruction.
  546. ;
  547. ; Arguments:
  548. ;
  549. ; None.
  550. ;
  551. ; Return Value:
  552. ;
  553. ; none.
  554. ;
  555. ;--
  556. TemporaryInt6 proc
  557. mov word ptr [esp].IretIp,offset b1c60 ; set IP to clc instruction
  558. iret
  559. TemporaryInt6 endp
  560. ;++
  561. ;
  562. ; BOOLEAN
  563. ; Check386D1 (
  564. ; VOID
  565. ; )
  566. ;
  567. ; Routine Description:
  568. ;
  569. ; This routine checks for 386 D1 Stepping.
  570. ;
  571. ; It takes advantage of the fact that on pre-D1 386, if a REPeated
  572. ; MOVS instruction is executed when single-stepping is enabled,
  573. ; a single step trap is taken every TWO moves steps, but should
  574. ; occuu each move step.
  575. ;
  576. ; NOTE: This routine cannot distinguish between a D0 stepping and a D1
  577. ; stepping. If a need arises to make this distinction, this routine
  578. ; will need modification. D0 steppings will be recognized as D1.
  579. ;
  580. ; Arguments:
  581. ;
  582. ; None.
  583. ;
  584. ; Return Value:
  585. ;
  586. ; Carry Flag clear if D1 or later stepping
  587. ; Carry Flag set if B1 or prior
  588. ;
  589. ;--
  590. assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
  591. Check386D1 proc
  592. push ds
  593. push bx
  594. xor ax,ax
  595. mov ds,ax ; (DS) = 0 (real mode IDT)
  596. mov bx,1*4
  597. push dword ptr [bx] ; save old int 1 vector
  598. mov word ptr [bx].VectorOffset,offset TemporaryInt1
  599. mov word ptr [bx].VectorSegment,cs ; set vector to new int 1 handler
  600. ;
  601. ; Attempt execution of rep movsb instruction with the Trace Flag set.
  602. ; Execution on B1 or earlier with length (CX) > 1 will trace over two
  603. ; iterations before accepting the trace trap. Execution on D1 or later
  604. ; will accept the trace trap after a single iteration. The dummy int 1
  605. ; handler will return execution to the instruction following the movsb
  606. ; instruction. Examination of (CX) will reveal the stepping.
  607. ;
  608. sub sp,4 ; make room for target of movsb
  609. xor si,si ; (ds:si) = 0:0
  610. push ss ; (es:di) = ss:sp-4
  611. pop es
  612. mov di,sp
  613. mov cx,2 ; 2 iterations
  614. pushf
  615. or word ptr [esp], EFLAGS_TF
  616. popf ; cause a single step trap
  617. rep movsb
  618. d1c60: add sp,4 ; clean off stack
  619. pop dword ptr [bx] ; restore old int 1 vector
  620. stc ; assume B1
  621. jcxz short d1cx ; jmp if <= B1
  622. clc ; else clear carry to indicate >= D1
  623. d1cx:
  624. pop bx
  625. pop ds
  626. ret
  627. Check386D1 endp
  628. ;++
  629. ;
  630. ; BOOLEAN
  631. ; TemporaryInt1 (
  632. ; VOID
  633. ; )
  634. ;
  635. ; Routine Description:
  636. ;
  637. ; Temporary int 1 handler - assumes the cause of the exception was
  638. ; trace trap at the above rep movs instruction.
  639. ;
  640. ; Arguments:
  641. ;
  642. ; (esp)->eip of trapped instruction
  643. ; cs of trapped instruction
  644. ; eflags of trapped instruction
  645. ;
  646. ;--
  647. TemporaryInt1 proc
  648. and word ptr [esp].IretFlags,not EFLAGS_TF ; clear caller's Trace Flag
  649. mov word ptr [esp].IretIp,offset d1c60 ; set IP to next instruction
  650. iret
  651. TemporaryInt1 endp
  652. ;++
  653. ;
  654. ; BOOLEAN
  655. ; MultiplyTest (
  656. ; VOID
  657. ; )
  658. ;
  659. ; Routine Description:
  660. ;
  661. ; This routine checks the 386 32-bit multiply instruction.
  662. ; The reason for this check is because some of the i386 fail to
  663. ; perform this instruction.
  664. ;
  665. ; Arguments:
  666. ;
  667. ; None.
  668. ;
  669. ; Return Value:
  670. ;
  671. ; Carry Flag clear on success
  672. ; Carry Flag set on failure
  673. ;
  674. ;--
  675. ;
  676. assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
  677. MultiplyTest proc
  678. xor cx,cx ; 64K times is a nice round number
  679. mlt00: push cx
  680. call Multiply ; does this chip's multiply work?
  681. pop cx
  682. jc short mltx ; if c, No, exit
  683. loop mlt00 ; if nc, YEs, loop to try again
  684. clc
  685. mltx:
  686. ret
  687. MultiplyTest endp
  688. ;++
  689. ;
  690. ; BOOLEAN
  691. ; Multiply (
  692. ; VOID
  693. ; )
  694. ;
  695. ; Routine Description:
  696. ;
  697. ; This routine performs 32-bit multiplication test which is known to
  698. ; fail on bad 386s.
  699. ;
  700. ; Note, the supplied pattern values must be used for consistent results.
  701. ;
  702. ; Arguments:
  703. ;
  704. ; None.
  705. ;
  706. ; Return Value:
  707. ;
  708. ; Carry Flag clear on success.
  709. ; Carry Flag set on failure.
  710. ;
  711. ;--
  712. Multiply proc
  713. mov ecx, MULTIPLIER
  714. mov eax, MULTIPLICAND
  715. mul ecx
  716. cmp edx, RESULT_HIGH ; Q: high order answer OK ?
  717. stc ; assume failure
  718. jnz short mlpx ; N: exit with error
  719. cmp eax, RESULT_LOW ; Q: low order answer OK ?
  720. stc ; assume failure
  721. jnz short mlpx ; N: exit with error
  722. clc ; indicate success
  723. mlpx:
  724. ret
  725. Multiply endp
  726. ;++
  727. ;
  728. ; BOOLEAN
  729. ; IsNpxPresent(
  730. ; VOID
  731. ; );
  732. ;
  733. ; Routine Description:
  734. ;
  735. ; This routine determines if there is any Numeric coprocessor
  736. ; present. If yes, the ET bit in CR0 will be set; otherwise
  737. ; it will be reset.
  738. ;
  739. ; Note that we do NOT determine its type (287, 387).
  740. ; This code is extracted from Intel book.
  741. ;
  742. ; Arguments:
  743. ;
  744. ; None.
  745. ;
  746. ; Return:
  747. ;
  748. ; TRUE - If NPX is present. Else a value of FALSE is returned.
  749. ;
  750. ;--
  751. public _IsNpxPresent
  752. _IsNpxPresent proc near
  753. push bp ; Save caller's bp
  754. .386p
  755. mov eax, cr0
  756. and eax, NOT CR0_ET ; Assume no NPX
  757. mov edx, 0
  758. .287
  759. fninit ; Initialize NPX
  760. mov cx, 5A5Ah ; Put non-zero value
  761. push cx ; into the memory we are going to use
  762. mov bp, sp
  763. fnstsw word ptr [bp] ; Retrieve status - must use non-wait
  764. cmp byte ptr [bp], 0 ; All bits cleared by fninit?
  765. jne Inp10
  766. or eax, CR0_ET
  767. mov edx, 1
  768. Inp10:
  769. mov cr0, eax
  770. pop ax ; clear scratch value
  771. pop bp ; Restore caller's bp
  772. mov eax, edx
  773. ret
  774. _IsNpxPresent endp
  775. _TEXT ENDS
  776. END