Leaked source code of windows server 2003
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.

1199 lines
34 KiB

  1. page ,132
  2. ;---------------------------Module-Header-------------------------------;
  3. ; Module Name: MATH.ASM
  4. ;
  5. ; Contains FIXED point math routines.
  6. ;
  7. ; Created: Sun 30-Aug-1987 19:28:30
  8. ; Author: Charles Whitmer [chuckwh]
  9. ;
  10. ; Copyright (c) 1987 Microsoft Corporation
  11. ;-----------------------------------------------------------------------;
  12. ?WIN = 0
  13. ?PLM = 1
  14. ?NODATA = 0
  15. .286
  16. .xlist
  17. include cmacros.inc
  18. include windows.inc
  19. .list
  20. externA __WinFlags
  21. UQUAD struc
  22. uq0 dw ?
  23. uq1 dw ?
  24. uq2 dw ?
  25. uq3 dw ?
  26. UQUAD ends
  27. ; The following two equates are just used as shorthand
  28. ; for the "word ptr" and "byte ptr" overrides.
  29. wptr equ word ptr
  30. bptr equ byte ptr
  31. ; The following structure should be used to access high and low
  32. ; words of a DWORD. This means that "word ptr foo[2]" -> "foo.hi".
  33. LONG struc
  34. lo dw ?
  35. hi dw ?
  36. LONG ends
  37. EAXtoDXAX macro
  38. shld edx,eax,16 ; move HIWORD(eax) to dx
  39. endm
  40. DXAXtoEAX macro
  41. ror eax,16 ; xchg HIWORD(eax) and LOWORD(eax)
  42. shrd eax,edx,16 ; move LOWORD(edx) to HIWORD(eax)
  43. endm
  44. neg32 macro hi, lo
  45. neg lo
  46. adc hi,0 ; carry set unless lo zero
  47. neg hi
  48. endm
  49. ifndef SEGNAME
  50. SEGNAME equ <_TEXT>
  51. endif
  52. createSeg %SEGNAME, CodeSeg, word, public, CODE
  53. sBegin CodeSeg
  54. assumes cs,CodeSeg
  55. assumes ds,nothing
  56. assumes es,nothing
  57. ;---------------------------Public-Routine------------------------------;
  58. ; long muldiv32(long, long, long)
  59. ;
  60. ; multiples two 32 bit values and then divides the result by a third
  61. ; 32 bit value with full 64 bit presision
  62. ;
  63. ; lResult = (lNumber * lNumerator) / lDenominator with correct rounding
  64. ;
  65. ; Entry:
  66. ; lNumber = number to multiply by nNumerator
  67. ; lNumerator = number to multiply by nNumber
  68. ; lDenominator = number to divide the multiplication result by.
  69. ;
  70. ; Returns:
  71. ; DX:AX = result of multiplication and division.
  72. ;
  73. ; Error Returns:
  74. ; none
  75. ; Registers Preserved:
  76. ; DS,ES,SI,DI
  77. ; History:
  78. ; Fri 05-Oct-1990 -by- Rob Williams [Robwi]
  79. ; Behavior consistent with MulDiv16 routine (signed, no int 0 on overflow)
  80. ; Stole muldiv16 psuedocode
  81. ;
  82. ; Wed 14-June-1990 -by- Todd Laney [ToddLa]
  83. ; converted it to 386/286 code. (by checking __WinFlags)
  84. ;
  85. ; Tue 08-May-1990 -by- Rob Williams [Robwi]
  86. ; Wrote it.
  87. ;
  88. ;----------------------------Pseudo-Code--------------------------------;
  89. ; long FAR PASCAL muldiv32(long, long, long)
  90. ; long l;
  91. ; long Numer;
  92. ; long Denom;
  93. ; {
  94. ;
  95. ; Sign = sign of Denom; // Sign will keep track of final sign //
  96. ;
  97. ;
  98. ; if (Denom < 0)
  99. ; {
  100. ; negate Denom; // make sure Denom is positive //
  101. ; }
  102. ;
  103. ; if (l < 0)
  104. ; {
  105. ; negate l; // make sure l is positive //
  106. ; }
  107. ;
  108. ; make Sign reflect any sign change;
  109. ;
  110. ;
  111. ; if (Numer < 0)
  112. ; {
  113. ; negate Numer; // make sure Numer is positive //
  114. ; }
  115. ;
  116. ; make Sign reflect any sign change;
  117. ;
  118. ; Numer *= l;
  119. ; Numer += (Denom/2); // adjust for rounding //
  120. ;
  121. ; if (overflow) // check for overflow, and handle divide by zero //
  122. ; {
  123. ; jump to md5;
  124. ; }
  125. ;
  126. ; result = Numer/Denom;
  127. ;
  128. ; if (overflow) // check again to see if overflow occured //
  129. ; {
  130. ; jump to md5;
  131. ; }
  132. ;
  133. ; if (Sign is negative) // put sign on the result //
  134. ; {
  135. ; negate result;
  136. ; }
  137. ;
  138. ;md6:
  139. ; return(result);
  140. ;
  141. ;md5:
  142. ; DX = 7FFF; // indicate overflow by //
  143. ; AX = 0xFFFF // return largest integer //
  144. ; if (Sign is negative)
  145. ; {
  146. ; DX = 0x8000; // with correct sign //
  147. ; AX = 0x0000;
  148. ; }
  149. ;
  150. ; jump to md6;
  151. ; }
  152. ;-----------------------------------------------------------------------;
  153. assumes ds,nothing
  154. assumes es,nothing
  155. cProc muldiv32,<PUBLIC,FAR,NODATA,NONWIN>,<>
  156. ; ParmD lNumber
  157. ; ParmD lNumerator
  158. ; ParmD lDenominator
  159. cBegin <nogen>
  160. mov ax,__WinFlags
  161. test ax,WF_CPU286+WF_CPU086+WF_CPU186
  162. jz md32_1
  163. jmp NEAR PTR muldiv32_286
  164. md32_1:
  165. errn$ muldiv32_386
  166. cEnd <nogen>
  167. cProc muldiv32_386,<PUBLIC,FAR,NODATA,NONWIN>,<>
  168. ParmD lNumber
  169. ParmD lNumerator
  170. ParmD lDenominator
  171. cBegin
  172. .386
  173. mov ebx,lDenominator ; get the demoninator
  174. mov ecx,ebx ; ECX holds the final sign in hiword
  175. or ebx,ebx ; ensure the denominator is positive
  176. jns md386_1
  177. neg ebx
  178. md386_1:
  179. mov eax,lNumber ; get the long we are multiplying
  180. xor ecx,eax ; make ECX reflect any sign change
  181. or eax,eax ; ensure the long is positive
  182. jns md386_2
  183. neg eax
  184. md386_2:
  185. mov edx,lNumerator ; get the numerator
  186. xor ecx,edx ; make ECX reflect any sign change
  187. or edx,edx ; ensure the numerator is positive
  188. jns md386_3
  189. neg edx
  190. md386_3:
  191. mul edx ; multiply
  192. mov cx,bx ; get half of the demoninator to adjust for rounding
  193. sar ebx,1
  194. add eax,ebx ; adjust for possible rounding error
  195. adc edx,0 ; this is really a long addition
  196. sal ebx,1 ; restore the demoninator
  197. or bx,cx ; fix bottom bit
  198. cmp edx,ebx ; check for overflow
  199. jae md386_5 ; (ae handles /0 case)
  200. div ebx ; divide
  201. or eax,eax ; If sign is set, then overflow occured
  202. js md386_5 ; Overflow.
  203. or ecx,ecx ; put the sign on the result
  204. jns md386_6
  205. neg eax
  206. md386_6:
  207. EAXtoDXAX ; convert eax to dx:ax for 16 bit programs
  208. .286
  209. cEnd
  210. .386
  211. md386_5:
  212. mov eax,7FFFFFFFh ; return the largest integer
  213. or ecx,ecx ; with the correct sign
  214. jns md386_6
  215. not eax
  216. jmp md386_6
  217. .286
  218. cProc muldiv32_286,<PUBLIC,FAR,NODATA,NONWIN>,<di,si>
  219. ParmD lNumber
  220. ParmD lNumerator
  221. ParmD lDenominator
  222. LocalW wSign
  223. cBegin
  224. mov dx,lDenominator.hi ; get the demoninator
  225. mov si,dx ; SI holds the final sign
  226. or dx,dx ; ensure the denominator is positive
  227. jns md286_1
  228. neg32 dx, lDenominator.lo
  229. mov lDenominator.hi, dx
  230. md286_1:
  231. mov ax,lNumber.lo ; get the long we are multiplying
  232. mov dx,lNumber.hi
  233. xor si,dx ; make ECX reflect any sign change
  234. or dx,dx ; ensure the long is positive
  235. jns md286_2
  236. neg32 dx, ax
  237. md286_2:
  238. mov bx,lNumerator.lo ; get the numerator
  239. mov cx,lNumerator.hi ; get the numerator
  240. xor si,cx ; make ECX reflect any sign change
  241. or cx,cx ; ensure the numerator is positive
  242. jns md286_3
  243. neg32 cx, bx
  244. md286_3:
  245. mov wSign, si ; save sign
  246. call dmul ; multiply (result in dx:cx:bx:ax)
  247. mov si, lDenominator.hi
  248. mov di, lDenominator.lo
  249. sar si, 1 ; get half of the demoninator
  250. rcr di, 1 ; to adjust for rounding
  251. add ax, di ; adjust for possible rounding error
  252. adc bx, si
  253. adc cx, 0
  254. adc dx, 0 ; this is really a long addition
  255. sal di, 1 ; restore the demoninator
  256. rcl si, 1
  257. or di, lDenominator.lo ; fix bottom bit
  258. cmp dx, si ; begin overflow check (unsigned for div 0 check)
  259. ja md286_5 ; overflow
  260. jb md286_7 ; no overflow
  261. cmp cx, di
  262. jae md286_5 ; overflow
  263. md286_7:
  264. call qdiv ; DX:AX is quotient
  265. or dx,dx ; If sign is set, then overflow occured
  266. js md286_5 ; Overflow.
  267. mov cx, wSign
  268. or cx,cx ; put the sign on the result
  269. jns md286_6
  270. neg32 dx,ax
  271. md286_6:
  272. cEnd
  273. md286_5:
  274. mov cx, wSign
  275. mov ax, 0FFFFh ; return the largest integer
  276. mov dx, 7FFFh
  277. or cx, cx ; with the correct sign
  278. jns md286_6
  279. not dx
  280. not ax
  281. jmp md286_6
  282. cProc muldivru32,<PUBLIC,FAR,NODATA,NONWIN>,<>
  283. ; ParmD lNumber
  284. ; ParmD lNumerator
  285. ; ParmD lDenominator
  286. cBegin <nogen>
  287. mov ax,__WinFlags
  288. test ax,WF_CPU286+WF_CPU086+WF_CPU186
  289. jz mdru32_1
  290. jmp NEAR PTR muldivru32_286
  291. mdru32_1:
  292. errn$ muldivru32_386
  293. cEnd <nogen>
  294. cProc muldivru32_386,<PUBLIC,FAR,NODATA,NONWIN>,<>
  295. ParmD lNumber
  296. ParmD lNumerator
  297. ParmD lDenominator
  298. cBegin
  299. .386
  300. mov ebx,lDenominator ; get the demoninator
  301. mov ecx,ebx ; ECX holds the final sign in hiword
  302. or ebx,ebx ; ensure the denominator is positive
  303. jns mdru386_1
  304. neg ebx
  305. mdru386_1:
  306. mov eax,lNumber ; get the long we are multiplying
  307. xor ecx,eax ; make ECX reflect any sign change
  308. or eax,eax ; ensure the long is positive
  309. jns mdru386_2
  310. neg eax
  311. mdru386_2:
  312. mov edx,lNumerator ; get the numerator
  313. xor ecx,edx ; make ECX reflect any sign change
  314. or edx,edx ; ensure the numerator is positive
  315. jns mdru386_3
  316. neg edx
  317. mdru386_3:
  318. mul edx ; multiply
  319. mov cx,bx ; get demoninator - 1 to adjust for rounding
  320. sub ebx,1
  321. add eax,ebx ; adjust for possible rounding error
  322. adc edx,0 ; this is really a long addition
  323. add ebx,1 ; restore the demoninator
  324. cmp edx,ebx ; check for overflow
  325. jae mdru386_5 ; (ae handles /0 case)
  326. div ebx ; divide
  327. or eax,eax ; If sign is set, then overflow occured
  328. js mdru386_5 ; Overflow.
  329. or ecx,ecx ; put the sign on the result
  330. jns mdru386_6
  331. neg eax
  332. mdru386_6:
  333. EAXtoDXAX ; convert eax to dx:ax for 16 bit programs
  334. .286
  335. cEnd
  336. .386
  337. mdru386_5:
  338. mov eax,7FFFFFFFh ; return the largest integer
  339. or ecx,ecx ; with the correct sign
  340. jns mdru386_6
  341. not eax
  342. jmp mdru386_6
  343. .286
  344. cProc muldivru32_286,<PUBLIC,FAR,NODATA,NONWIN>,<di,si>
  345. ParmD lNumber
  346. ParmD lNumerator
  347. ParmD lDenominator
  348. LocalW wSign
  349. cBegin
  350. mov dx,lDenominator.hi ; get the demoninator
  351. mov si,dx ; SI holds the final sign
  352. or dx,dx ; ensure the denominator is positive
  353. jns mdru286_1
  354. neg32 dx, lDenominator.lo
  355. mov lDenominator.hi, dx
  356. mdru286_1:
  357. mov ax,lNumber.lo ; get the long we are multiplying
  358. mov dx,lNumber.hi
  359. xor si,dx ; make ECX reflect any sign change
  360. or dx,dx ; ensure the long is positive
  361. jns mdru286_2
  362. neg32 dx, ax
  363. mdru286_2:
  364. mov bx,lNumerator.lo ; get the numerator
  365. mov cx,lNumerator.hi ; get the numerator
  366. xor si,cx ; make ECX reflect any sign change
  367. or cx,cx ; ensure the numerator is positive
  368. jns mdru286_3
  369. neg32 cx, bx
  370. mdru286_3:
  371. mov wSign, si ; save sign
  372. call dmul ; multiply (result in dx:cx:bx:ax)
  373. mov si, lDenominator.hi
  374. mov di, lDenominator.lo
  375. sub di, 1 ; get demoninator - 1
  376. sbb si, 0 ; to adjust for rounding
  377. add ax, di ; adjust for possible rounding error
  378. adc bx, si
  379. adc cx, 0
  380. adc dx, 0 ; this is really a long addition
  381. add di, 1 ; restore the demoninator
  382. adc si, 0
  383. cmp dx, si ; begin overflow check (unsigned for div 0 check)
  384. ja mdru286_5 ; overflow
  385. jb mdru286_7 ; no overflow
  386. cmp cx, di
  387. jae mdru286_5 ; overflow
  388. mdru286_7:
  389. call qdiv ; DX:AX is quotient
  390. or dx,dx ; If sign is set, then overflow occured
  391. js mdru286_5 ; Overflow.
  392. mov cx, wSign
  393. or cx,cx ; put the sign on the result
  394. jns mdru286_6
  395. neg32 dx,ax
  396. mdru286_6:
  397. cEnd
  398. mdru286_5:
  399. mov cx, wSign
  400. mov ax, 0FFFFh ; return the largest integer
  401. mov dx, 7FFFh
  402. or cx, cx ; with the correct sign
  403. jns mdru286_6
  404. not dx
  405. not ax
  406. jmp mdru286_6
  407. cProc muldivrd32,<PUBLIC,FAR,NODATA,NONWIN>,<>
  408. ; ParmD lNumber
  409. ; ParmD lNumerator
  410. ; ParmD lDenominator
  411. cBegin <nogen>
  412. mov ax,__WinFlags
  413. test ax,WF_CPU286+WF_CPU086+WF_CPU186
  414. jz mdrd32_1
  415. jmp NEAR PTR muldivrd32_286
  416. mdrd32_1:
  417. errn$ muldivrd32_386
  418. cEnd <nogen>
  419. cProc muldivrd32_386,<PUBLIC,FAR,NODATA,NONWIN>,<>
  420. ParmD lNumber
  421. ParmD lNumerator
  422. ParmD lDenominator
  423. cBegin
  424. .386
  425. mov ebx,lDenominator ; get the demoninator
  426. mov ecx,ebx ; ECX holds the final sign in hiword
  427. or ebx,ebx ; ensure the denominator is positive
  428. jns mdrd386_1
  429. neg ebx
  430. mdrd386_1:
  431. mov eax,lNumber ; get the long we are multiplying
  432. xor ecx,eax ; make ECX reflect any sign change
  433. or eax,eax ; ensure the long is positive
  434. jns mdrd386_2
  435. neg eax
  436. mdrd386_2:
  437. mov edx,lNumerator ; get the numerator
  438. xor ecx,edx ; make ECX reflect any sign change
  439. or edx,edx ; ensure the numerator is positive
  440. jns mdrd386_3
  441. neg edx
  442. mdrd386_3:
  443. mul edx ; multiply
  444. div ebx ; divide
  445. or eax,eax ; If sign is set, then overflow occured
  446. js mdrd386_5 ; Overflow.
  447. or ecx,ecx ; put the sign on the result
  448. jns mdrd386_6
  449. neg eax
  450. mdrd386_6:
  451. EAXtoDXAX ; convert eax to dx:ax for 16 bit programs
  452. .286
  453. cEnd
  454. .386
  455. mdrd386_5:
  456. mov eax,7FFFFFFFh ; return the largest integer
  457. or ecx,ecx ; with the correct sign
  458. jns mdrd386_6
  459. not eax
  460. jmp mdrd386_6
  461. .286
  462. cProc muldivrd32_286,<PUBLIC,FAR,NODATA,NONWIN>,<di,si>
  463. ParmD lNumber
  464. ParmD lNumerator
  465. ParmD lDenominator
  466. LocalW wSign
  467. cBegin
  468. mov dx,lDenominator.hi ; get the demoninator
  469. mov si,dx ; SI holds the final sign
  470. or dx,dx ; ensure the denominator is positive
  471. jns mdrd286_1
  472. neg32 dx, lDenominator.lo
  473. mov lDenominator.hi, dx
  474. mdrd286_1:
  475. mov ax,lNumber.lo ; get the long we are multiplying
  476. mov dx,lNumber.hi
  477. xor si,dx ; make ECX reflect any sign change
  478. or dx,dx ; ensure the long is positive
  479. jns mdrd286_2
  480. neg32 dx, ax
  481. mdrd286_2:
  482. mov bx,lNumerator.lo ; get the numerator
  483. mov cx,lNumerator.hi ; get the numerator
  484. xor si,cx ; make ECX reflect any sign change
  485. or cx,cx ; ensure the numerator is positive
  486. jns mdrd286_3
  487. neg32 cx, bx
  488. mdrd286_3:
  489. mov wSign, si ; save sign
  490. call dmul ; multiply (result in dx:cx:bx:ax)
  491. mov si, lDenominator.hi
  492. mov di, lDenominator.lo
  493. cmp dx, si ; begin overflow check (unsigned for div 0 check)
  494. ja mdrd286_5 ; overflow
  495. jb mdrd286_7 ; no overflow
  496. cmp cx, di
  497. jae mdrd286_5 ; overflow
  498. mdrd286_7:
  499. call qdiv ; DX:AX is quotient
  500. or dx,dx ; If sign is set, then overflow occured
  501. js mdrd286_5 ; Overflow.
  502. mov cx, wSign
  503. or cx,cx ; put the sign on the result
  504. jns mdrd286_6
  505. neg32 dx,ax
  506. mdrd286_6:
  507. cEnd
  508. mdrd286_5:
  509. mov cx, wSign
  510. mov ax, 0FFFFh ; return the largest integer
  511. mov dx, 7FFFh
  512. or cx, cx ; with the correct sign
  513. jns mdrd286_6
  514. not dx
  515. not ax
  516. jmp mdrd286_6
  517. ;---------------------------Public-Routine------------------------------;
  518. ; idmul
  519. ;
  520. ; This is an extended precision multiply routine, intended to emulate
  521. ; 80386 imul instruction.
  522. ;
  523. ; Entry:
  524. ; DX:AX = LONG
  525. ; CX:BX = LONG
  526. ; Returns:
  527. ; DX:CX:BX:AX = QUAD product
  528. ; Registers Destroyed:
  529. ; none
  530. ; History:
  531. ; Tue 26-Jan-1988 23:47:02 -by- Charles Whitmer [chuckwh]
  532. ; Wrote it.
  533. ;-----------------------------------------------------------------------;
  534. assumes ds,nothing
  535. assumes es,nothing
  536. cProc idmul,<PUBLIC,NEAR>,<si,di>
  537. localQ qTemp
  538. cBegin
  539. ; put one argument in safe registers
  540. mov si,dx
  541. mov di,ax
  542. ; do the low order unsigned product
  543. mul bx
  544. mov qTemp.uq0,ax
  545. mov qTemp.uq1,dx
  546. ; do the high order signed product
  547. mov ax,si
  548. imul cx
  549. mov qTemp.uq2,ax
  550. mov qTemp.uq3,dx
  551. ; do a mixed product
  552. mov ax,si
  553. cwd
  554. and dx,bx
  555. sub qTemp.uq2,dx ; adjust for sign bit
  556. sbb qTemp.uq3,0
  557. mul bx
  558. add qTemp.uq1,ax
  559. adc qTemp.uq2,dx
  560. adc qTemp.uq3,0
  561. ; do the other mixed product
  562. mov ax,cx
  563. cwd
  564. and dx,di
  565. sub qTemp.uq2,dx
  566. sbb qTemp.uq3,0
  567. mul di
  568. ; pick up the answer
  569. mov bx,ax
  570. mov cx,dx
  571. xor dx,dx
  572. mov ax,qTemp.uq0
  573. add bx,qTemp.uq1
  574. adc cx,qTemp.uq2
  575. adc dx,qTemp.uq3
  576. cEnd
  577. ;---------------------------Public-Routine------------------------------;
  578. ; dmul
  579. ;
  580. ; This is an extended precision multiply routine, intended to emulate
  581. ; 80386 mul instruction.
  582. ;
  583. ; Entry:
  584. ; DX:AX = LONG
  585. ; CX:BX = LONG
  586. ; Returns:
  587. ; DX:CX:BX:AX = QUAD product
  588. ; Registers Destroyed:
  589. ; none
  590. ; History:
  591. ; Tue 02-Feb-1988 10:50:44 -by- Charles Whitmer [chuckwh]
  592. ; Copied from idmul and modified.
  593. ;-----------------------------------------------------------------------;
  594. assumes ds,nothing
  595. assumes es,nothing
  596. cProc dmul,<PUBLIC,NEAR>,<si,di>
  597. localQ qTemp
  598. cBegin
  599. ; put one argument in safe registers
  600. mov si,dx
  601. mov di,ax
  602. ; do the low order product
  603. mul bx
  604. mov qTemp.uq0,ax
  605. mov qTemp.uq1,dx
  606. ; do the high order product
  607. mov ax,si
  608. mul cx
  609. mov qTemp.uq2,ax
  610. mov qTemp.uq3,dx
  611. ; do a mixed product
  612. mov ax,si
  613. mul bx
  614. add qTemp.uq1,ax
  615. adc qTemp.uq2,dx
  616. adc qTemp.uq3,0
  617. ; do the other mixed product
  618. mov ax,cx
  619. mul di
  620. ; pick up the answer
  621. mov bx,ax
  622. mov cx,dx
  623. xor dx,dx
  624. mov ax,qTemp.uq0
  625. add bx,qTemp.uq1
  626. adc cx,qTemp.uq2
  627. adc dx,qTemp.uq3
  628. cEnd
  629. ;---------------------------Public-Routine------------------------------;
  630. ; iqdiv
  631. ;
  632. ; This is an extended precision divide routine which is intended to
  633. ; emulate the 80386 64 bit/32 bit IDIV instruction. We don't have the
  634. ; 32 bit registers to work with, but we pack the arguments and results
  635. ; into what registers we do have. We will divide two signed numbers
  636. ; and return the quotient and remainder. We will do INT 0 for overflow,
  637. ; just like the 80386 microcode. This should ease conversion later.
  638. ;
  639. ; This routine just keeps track of the signs and calls qdiv to do the
  640. ; real work.
  641. ;
  642. ; Entry:
  643. ; DX:CX:BX:AX = QUAD Numerator
  644. ; SI:DI = LONG Denominator
  645. ; Returns:
  646. ; DX:AX = quotient
  647. ; CX:BX = remainder
  648. ; Registers Destroyed:
  649. ; DI,SI
  650. ; History:
  651. ; Tue 26-Jan-1988 02:49:19 -by- Charles Whitmer [chuckwh]
  652. ; Wrote it.
  653. ;-----------------------------------------------------------------------;
  654. WIMP equ 1
  655. IQDIV_RESULT_SIGN equ 1
  656. IQDIV_REM_SIGN equ 2
  657. assumes ds,nothing
  658. assumes es,nothing
  659. cProc iqdiv,<PUBLIC,NEAR>
  660. localB flags
  661. cBegin
  662. mov flags,0
  663. ; take the absolute value of the denominator
  664. or si,si
  665. jns denominator_is_cool
  666. xor flags,IQDIV_RESULT_SIGN
  667. neg di
  668. adc si,0
  669. neg si
  670. denominator_is_cool:
  671. ; take the absolute value of the denominator
  672. or dx,dx
  673. jns numerator_is_cool
  674. xor flags,IQDIV_RESULT_SIGN + IQDIV_REM_SIGN
  675. not ax
  676. not bx
  677. not cx
  678. not dx
  679. add ax,1
  680. adc bx,0
  681. adc cx,0
  682. adc dx,0
  683. numerator_is_cool:
  684. ; do the unsigned division
  685. call qdiv
  686. ifdef WIMP
  687. jo iqdiv_exit
  688. endif
  689. ; check for overflow
  690. or dx,dx
  691. jns have_a_bit_to_spare
  692. ifdef WIMP
  693. mov ax,8000h
  694. dec ah
  695. jmp short iqdiv_exit
  696. else
  697. int 0 ; You're toast, Jack!
  698. endif
  699. have_a_bit_to_spare:
  700. ; negate the result, if required
  701. test flags,IQDIV_RESULT_SIGN
  702. jz result_is_done
  703. neg ax
  704. adc dx,0
  705. neg dx
  706. result_is_done:
  707. ; negate the remainder, if required
  708. test flags,IQDIV_REM_SIGN
  709. jz remainder_is_done
  710. neg bx
  711. adc cx,0
  712. neg cx
  713. remainder_is_done:
  714. iqdiv_exit:
  715. cEnd
  716. ;---------------------------Public-Routine------------------------------;
  717. ; qdiv
  718. ;
  719. ; This is an extended precision divide routine which is intended to
  720. ; emulate the 80386 64 bit/32 bit DIV instruction. We don't have the
  721. ; 32 bit registers to work with, but we pack the arguments and results
  722. ; into what registers we do have. We will divide two unsigned numbers
  723. ; and return the quotient and remainder. We will do INT 0 for overflow,
  724. ; just like the 80386 microcode. This should ease conversion later.
  725. ;
  726. ; Entry:
  727. ; DX:CX:BX:AX = UQUAD Numerator
  728. ; SI:DI = ULONG Denominator
  729. ; Returns:
  730. ; DX:AX = quotient
  731. ; CX:BX = remainder
  732. ; Registers Destroyed:
  733. ; none
  734. ; History:
  735. ; Tue 26-Jan-1988 00:02:09 -by- Charles Whitmer [chuckwh]
  736. ; Wrote it.
  737. ;-----------------------------------------------------------------------;
  738. assumes ds,nothing
  739. assumes es,nothing
  740. cProc qdiv,<PUBLIC,NEAR>,<si,di>
  741. localQ uqNumerator
  742. localD ulDenominator
  743. localD ulQuotient
  744. localW cShift
  745. cBegin
  746. ; stuff the quad word into local memory
  747. mov uqNumerator.uq0,ax
  748. mov uqNumerator.uq1,bx
  749. mov uqNumerator.uq2,cx
  750. mov uqNumerator.uq3,dx
  751. ; check for overflow
  752. qdiv_restart:
  753. cmp si,dx
  754. ja qdiv_no_overflow
  755. jb qdiv_overflow
  756. cmp di,cx
  757. ja qdiv_no_overflow
  758. qdiv_overflow:
  759. ifdef WIMP
  760. mov ax,8000h
  761. dec ah
  762. jmp qdiv_exit
  763. else
  764. int 0 ; You're toast, Jack!
  765. jmp qdiv_restart
  766. endif
  767. qdiv_no_overflow:
  768. ; check for a zero Numerator
  769. or ax,bx
  770. or ax,cx
  771. or ax,dx
  772. jz qdiv_exit_relay ; quotient = remainder = 0
  773. ; handle the special case when the denominator lives in the low word
  774. or si,si
  775. jnz not_that_special
  776. ; calculate (DX=0):CX:BX:uqNumerator.uq0 / (SI=0):DI
  777. cmp di,1 ; separate out the trivial case
  778. jz div_by_one
  779. xchg dx,cx ; CX = remainder.hi = 0
  780. mov ax,bx
  781. div di
  782. mov bx,ax ; BX = quotient.hi
  783. mov ax,uqNumerator.uq0
  784. div di ; AX = quotient.lo
  785. xchg bx,dx ; DX = quotient.hi, BX = remainder.lo
  786. ifdef WIMP
  787. or ax,ax ; clear OF
  788. endif
  789. qdiv_exit_relay:
  790. jmp qdiv_exit
  791. ; calculate (DX=0):(CX=0):BX:uqNumerator.uq0 / (SI=0):(DI=1)
  792. div_by_one:
  793. xchg dx,bx ; DX = quotient.hi, BX = remainder.lo = 0
  794. mov ax,uqNumerator.uq0 ; AX = quotient.lo
  795. jmp qdiv_exit
  796. not_that_special:
  797. ; handle the special case when the denominator lives in the high word
  798. or di,di
  799. jnz not_this_special_either
  800. ; calculate DX:CX:BX:uqNumerator.uq0 / SI:(DI=0)
  801. cmp si,1 ; separate out the trivial case
  802. jz div_by_10000h
  803. mov ax,cx
  804. div si
  805. mov cx,ax ; CX = quotient.hi
  806. mov ax,bx
  807. div si ; AX = quotient.lo
  808. xchg cx,dx ; DX = quotient.hi, CX = remainder.hi
  809. mov bx,uqNumerator.uq0 ; BX = remainder.lo
  810. ifdef WIMP
  811. or ax,ax ; clear OF
  812. endif
  813. jmp qdiv_exit
  814. ; calculate (DX=0):CX:BX:uqNumerator.uq0 / (SI=1):(DI=0)
  815. div_by_10000h:
  816. xchg cx,dx ; DX = quotient.hi, CX = remainder.hi = 0
  817. mov ax,bx ; AX = quotient.lo
  818. mov bx,uqNumerator.uq0 ; BX = remainder.lo
  819. jmp qdiv_exit
  820. not_this_special_either:
  821. ; normalize the denominator
  822. mov dx,si
  823. mov ax,di
  824. call ulNormalize ; DX:AX = normalized denominator
  825. mov cShift,cx ; CX < 16
  826. mov ulDenominator.lo,ax
  827. mov ulDenominator.hi,dx
  828. ; shift the Numerator by the same amount
  829. jcxz numerator_is_shifted
  830. mov si,-1
  831. shl si,cl
  832. not si ; SI = mask
  833. mov bx,uqNumerator.uq3
  834. shl bx,cl
  835. mov ax,uqNumerator.uq2
  836. rol ax,cl
  837. mov di,si
  838. and di,ax
  839. or bx,di
  840. mov uqNumerator.uq3,bx
  841. xor ax,di
  842. mov bx,uqNumerator.uq1
  843. rol bx,cl
  844. mov di,si
  845. and di,bx
  846. or ax,di
  847. mov uqNumerator.uq2,ax
  848. xor bx,di
  849. mov ax,uqNumerator.uq0
  850. rol ax,cl
  851. mov di,si
  852. and di,ax
  853. or bx,di
  854. mov uqNumerator.uq1,bx
  855. xor ax,di
  856. mov uqNumerator.uq0,ax
  857. numerator_is_shifted:
  858. ; set up registers for division
  859. mov dx,uqNumerator.uq3
  860. mov ax,uqNumerator.uq2
  861. mov di,uqNumerator.uq1
  862. mov cx,ulDenominator.hi
  863. mov bx,ulDenominator.lo
  864. ; check for case when Denominator has only 16 bits
  865. or bx,bx
  866. jnz must_do_long_division
  867. div cx
  868. mov si,ax
  869. mov ax,uqNumerator.uq1
  870. div cx
  871. xchg si,dx ; DX:AX = quotient
  872. mov di,uqNumerator.uq0 ; SI:DI = remainder (shifted)
  873. jmp short unshift_remainder
  874. must_do_long_division:
  875. ; do the long division, part IZ@NL@%
  876. cmp dx,cx ; we only know that DX:AX < CX:BX!
  877. jb first_division_is_safe
  878. mov ulQuotient.hi,0 ; i.e. 10000h, our guess is too big
  879. mov si,ax
  880. sub si,bx ; ... remainder is negative
  881. jmp short first_adjuster
  882. first_division_is_safe:
  883. div cx
  884. mov ulQuotient.hi,ax
  885. mov si,dx
  886. mul bx ; fix remainder for low order term
  887. sub di,ax
  888. sbb si,dx
  889. jnc first_adjuster_done ; The remainder is UNSIGNED! We have
  890. first_adjuster: ; to use the carry flag to keep track
  891. dec ulQuotient.hi ; of the sign. The adjuster loop
  892. add di,bx ; watches for a change to the carry
  893. adc si,cx ; flag which would indicate a sign
  894. jnc first_adjuster ; change IF we had more bits to keep
  895. first_adjuster_done: ; a sign in.
  896. ; do the long division, part II
  897. mov dx,si
  898. mov ax,di
  899. mov di,uqNumerator.uq0
  900. cmp dx,cx ; we only know that DX:AX < CX:BX!
  901. jb second_division_is_safe
  902. mov ulQuotient.lo,0 ; i.e. 10000h, our guess is too big
  903. mov si,ax
  904. sub si,bx ; ... remainder is negative
  905. jmp short second_adjuster
  906. second_division_is_safe:
  907. div cx
  908. mov ulQuotient.lo,ax
  909. mov si,dx
  910. mul bx ; fix remainder for low order term
  911. sub di,ax
  912. sbb si,dx
  913. jnc second_adjuster_done
  914. second_adjuster:
  915. dec ulQuotient.lo
  916. add di,bx
  917. adc si,cx
  918. jnc second_adjuster
  919. second_adjuster_done:
  920. mov ax,ulQuotient.lo
  921. mov dx,ulQuotient.hi
  922. ; unshift the remainder in SI:DI
  923. unshift_remainder:
  924. mov cx,cShift
  925. jcxz remainder_unshifted
  926. mov bx,-1
  927. shr bx,cl
  928. not bx
  929. shr di,cl
  930. ror si,cl
  931. and bx,si
  932. or di,bx
  933. xor si,bx
  934. remainder_unshifted:
  935. mov cx,si
  936. mov bx,di
  937. ifdef WIMP
  938. or ax,ax ; clear OF
  939. endif
  940. qdiv_exit:
  941. cEnd
  942. ;---------------------------Public-Routine------------------------------;
  943. ; ulNormalize
  944. ;
  945. ; Normalizes a ULONG so that the highest order bit is 1. Returns the
  946. ; number of shifts done. Also returns ZF=1 if the ULONG was zero.
  947. ;
  948. ; Entry:
  949. ; DX:AX = ULONG
  950. ; Returns:
  951. ; DX:AX = normalized ULONG
  952. ; CX = shift count
  953. ; ZF = 1 if the ULONG is zero, 0 otherwise
  954. ; Registers Destroyed:
  955. ; none
  956. ; History:
  957. ; Mon 25-Jan-1988 22:07:03 -by- Charles Whitmer [chuckwh]
  958. ; Wrote it.
  959. ;-----------------------------------------------------------------------;
  960. assumes ds,nothing
  961. assumes es,nothing
  962. cProc ulNormalize,<PUBLIC,NEAR>
  963. cBegin
  964. ; shift by words
  965. xor cx,cx
  966. or dx,dx
  967. js ulNormalize_exit
  968. jnz top_word_ok
  969. xchg ax,dx
  970. or dx,dx
  971. jz ulNormalize_exit ; the zero exit
  972. mov cl,16
  973. js ulNormalize_exit
  974. top_word_ok:
  975. ; shift by bytes
  976. or dh,dh
  977. jnz top_byte_ok
  978. xchg dh,dl
  979. xchg dl,ah
  980. xchg ah,al
  981. add cl,8
  982. or dh,dh
  983. js ulNormalize_exit
  984. top_byte_ok:
  985. ; do the rest by bits
  986. inc cx
  987. add ax,ax
  988. adc dx,dx
  989. js ulNormalize_exit
  990. inc cx
  991. add ax,ax
  992. adc dx,dx
  993. js ulNormalize_exit
  994. inc cx
  995. add ax,ax
  996. adc dx,dx
  997. js ulNormalize_exit
  998. inc cx
  999. add ax,ax
  1000. adc dx,dx
  1001. js ulNormalize_exit
  1002. inc cx
  1003. add ax,ax
  1004. adc dx,dx
  1005. js ulNormalize_exit
  1006. inc cx
  1007. add ax,ax
  1008. adc dx,dx
  1009. js ulNormalize_exit
  1010. inc cx
  1011. add ax,ax
  1012. adc dx,dx
  1013. ulNormalize_exit:
  1014. cEnd
  1015. sEnd CodeSeg
  1016. end
  1017.