qlfloat.pas 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. {
  2. Conversion code from various number formats to QL Float format.
  3. Code ported from the C68/QL-GCC libc implementation available at:
  4. http://morloch.hd.free.fr/qdos/qdosgcc.html
  5. The QL wiki claims the original of these sources are by
  6. Dave Walker, and they are in the Public Domain.
  7. https://qlwiki.qlforum.co.uk/doku.php?id=qlwiki:c68
  8. **********************************************************************}
  9. unit qlfloat;
  10. interface
  11. uses
  12. qdos;
  13. function longint_to_qlfp(qlf: Pqlfloat; val: longint): Pqlfloat;
  14. function double_to_qlfp(qlf: Pqlfloat; val: Pdouble): Pqlfloat;
  15. implementation
  16. function longint_to_qlfp(qlf: Pqlfloat; val: longint): Pqlfloat; assembler; nostackframe;
  17. asm
  18. { pointer to qlfloat is in a0 }
  19. { val is in d0 }
  20. movem.l d2-d4/a0,-(sp) { save register variables and a0 }
  21. moveq.l #0,d2 { sign value }
  22. move.l d2,d3 { shift value }
  23. tst.l d0 { zero or -ve ? }
  24. beq @zeroval { zero }
  25. bpl @plusval { +ve }
  26. { i is negative here. set the sign value then make i positive }
  27. moveq #1,d2 { boolean to say -ve }
  28. not.l d0 { i has all bits reversed }
  29. bne @plusval { i was not -1, so can continue }
  30. { i was -1, so cannot go into following loop, as it now is zero }
  31. moveq #0,d2 { pretend i was positive }
  32. move.l #$80000000,d1 { set d1 correctly }
  33. move.w #31,d3 { shift value }
  34. bra @outloop { continue }
  35. @plusval:
  36. move.l d0,d1 { save a copy of the original i }
  37. { check for shortcuts with shifts }
  38. and.l #$ffffff00,d0 { shift by 23 ? }
  39. bne @bigger23 { no cheat available }
  40. move.w #23,d3 { shift value is 23 }
  41. lsl.l d3,d1 { shift copy of i }
  42. bra @nbigger { continue }
  43. { check for 15 bit shortcut shift }
  44. @bigger23:
  45. move.l d1,d0 { restore i }
  46. and.l #$ffff0000,d0 { shift by 15 ? }
  47. bne @nbigger { no cheat available }
  48. move.w #15,d3 { shift value is 15 }
  49. lsl.l d3,d1 { shift copy of i }
  50. { no shortcuts available }
  51. @nbigger:
  52. move.l d1,d0 { restore i }
  53. and.l #$40000000,d0 { if(!(i & 0x40000000)) }
  54. bne @outloop { bit is set, no more shifts }
  55. lsl.l #1,d1 { shift copy of i }
  56. addq.l #1,d3 { increment shift count }
  57. bra @nbigger { ensures i is restored }
  58. { finished shifts - copy into qlfloat }
  59. { correct shifted i is in d1, d0 contains i & 0x40000000 }
  60. @outloop:
  61. move.w #$81f,d4
  62. sub.w d3,d4 { set exponent correctly }
  63. move.w d4,(a0)+ { copy into exponent }
  64. { difference here between positive and negative numbers
  65. ; negative should just be shifted until first zero, so as we
  66. ; have 2s complemented and shifted until first one, we must now
  67. ; re-complement what is left }
  68. tst.b d2
  69. beq @setmant { positive value here - just copy it }
  70. { negative value, xor it with -1 shifted by same amount as in shift (d3)
  71. ; to convert it back to -ve representation }
  72. moveq.l #-1,d2 { set d2 to all $FFs }
  73. lsl.l d3,d2 { shift it by shift (d3 ) }
  74. eor.l d2,d1 { not the value by xoring }
  75. { negative value restored by above }
  76. @setmant:
  77. move.l d1,(a0) { copy into mantissa }
  78. bra @fin
  79. { quick exit if zero }
  80. @zeroval:
  81. move.w d2,(a0)+ { zero exponent }
  82. move.l d2,(a0) { zero mantissa }
  83. @fin:
  84. movem.l (sp)+,d2-d4/a0 { reset register variables and return value }
  85. move.l a0,d0 { copy return value into d0 as well }
  86. end;
  87. function double_to_qlfp(qlf: Pqlfloat; val: Pdouble): Pqlfloat; assembler; nostackframe;
  88. asm
  89. {----------------------------- IEEE -----------------------------------
  90. ; routine to convert IEEE double precision (8 byte) floating point
  91. ; to a QLFLOAT_t.
  92. }
  93. { pointer to qlfloat is in a0 }
  94. move.l (a1),d0 { high long of IEEE double }
  95. { SNG - avoid loading low part for now so we can treat D1 as temporary }
  96. add.l d0,d0 { Put sign bit in carry }
  97. lsr.l #1,d0 { put zero where sign was }
  98. bne @notzero { not zero }
  99. move.l 4(a1),d1 { Test low bits too (probably zero!) }
  100. bne @notzero
  101. { here the double was a signed zero - set the QLFLOAT_t and return }
  102. move.w d1,(a0)+ { We know that D1 is 0 at this point }
  103. bra @positive
  104. { was not zero - do manipulations }
  105. @notzero:
  106. move.l d0,d1 { set non-signed high part copy }
  107. { We are going to lose least significant byte so we
  108. ; can afford to over-write it. We can thus take
  109. ; advantage that the shift size when specified in
  110. ; a register is modulo 64 }
  111. move.b #20,d0 { shift amount for exponent }
  112. lsr.l d0,d0 { get exponent - tricky but it works! }
  113. add.w #$402,d0 { adjust to QLFLOAT_t exponent }
  114. move.w d0,(a0)+ { set QLFLOAT_t exponent }
  115. { now deal with mantissa }
  116. and.l #$fffff,d1 { get top 20 mantissa bits }
  117. or.l #$100000,d1 { add implied bit }
  118. moveq #10,d0 { shift amount ;; save another 2 code bytes }
  119. lsl.l d0,d1 { shift top 21 bits into place }
  120. move.l 4(a1),d0 { get less significant bits }
  121. { We are going to lose least significant byte so we
  122. ; can afford to over-write it. We can thus take
  123. ; advantage that the shift size when specified in
  124. ; a register is modulo 64 }
  125. move.b #22,d0 { amount to shift down low long: not MOVEQ! }
  126. lsr.l d0,d0 { position low 10 bits of mantissa }
  127. or.l d0,d1 { D1 now positive mantissa }
  128. @lowzer:
  129. tst.b (a1) { Top byte of IEEE argument }
  130. bpl @positive { No need to negate if positive }
  131. neg.l d1 { Mantissa in D1 now }
  132. @positive:
  133. move.l d1,(a0) { put mantissa in QLFLOAT_t }
  134. subq.l #2,a0 { correct for return address }
  135. move.l a0,d0 { set return value as original QLFLOAT_t address }
  136. end;
  137. end.