|
|
1.1 root 1: //////////
2: / rts87.s
3: / cc386 80x87 runtime support.
4: //////////
5:
6: .text
7:
8: .globl _fwait
9: .globl _cfcc
10: .globl _tstcc
11: .globl _tstccp
12: .globl _dp87
13: .globl _fdcvt
14: .globl _vdcvt
15: .globl _ldcvt
16: .globl _pdcvt
17: .globl _udcvt
18: .globl _idcvt
19: .globl _dfcvt
20: .globl _dvcvt
21: .globl _dlcvt
22: .globl _ducvt
23: .globl _dicvt
24: .globl _dpcvt
25: .globl ldexp
26: .globl frexp
27: .globl modf
28:
29: / The following should be unnecessary but...
30: .globl _fvcvt
31: .globl _flcvt
32: .globl _fucvt
33: .globl _ficvt
34:
35: cwchop .word 0x0F3F / chop control word
36: two .word 2 / constant 2
37:
38: RASIZE = 4 / size of a return address
39: EBPSIZE = 4 / size of saved %ebp
40:
41: //////////
42: / _fwait Force NDP synchronization.
43: /
44: / This is here in case the user wants to explicitly force an fwait.
45: //////////
46:
47: _fwait:
48: fwait
49: ret
50:
51: //////////
52: / _tstcc() Test %st and copy NDP condition codes.
53: / _tstccp() Test %st and pop and copy NDP condition codes.
54: / _cfcc() Copy NDP condition codes.
55: /
56: / _tstcc() tests %st against 0 and then does a _cfcc().
57: / _tstccp() tests %st against 0, pops it, and then does a _cfcc.
58: / _cfcc() copies the C0 and C3 status flags from the NDP
59: / into the Cf and Zf flags of the 80x86.
60: / This uses the otherwise useless opcode that copies the flags
61: / from %ah in 8080 format.
62: /
63: / Input:
64: / %st for _tstcc() and _tstccp(), NDP status word for _cfcc().
65: / Output:
66: / 80x86 flags Cf and Zf.
67: / NDP stack popped for _tstccp().
68: //////////
69:
70: _tstcc:
71: ftst / Test %st against 0
72: jmp _cfcc / and set the condition codes.
73: _tstccp:
74: ftst / Test st against 0,
75: fstp %st / pop the 80x87 stack,
76: / and fall through...
77: _cfcc:
78: fstsw %ax / Store the 80x87 status word to %eax
79: fwait / and wait for it to finish up.
80: sahf / Load Cf and Zf correctly.
81: ret
82:
83: //////////
84: / _dp87() Double push from NDP stacktop to 80x86 stack.
85: /
86: / Push a double from the NDP stacktop %st to the system stack.
87: / There is some magic to keep the return address in one piece.
88: /
89: / Input:
90: / Double in %st.
91: / Outputs:
92: / NDP stack popped.
93: / Double on 80x86 stack.
94: //////////
95:
96: _dp87:
97: pop %eax / Return address to %eax.
98: subl %esp, $8 / Claim a double on stack and
99: fstpl (%esp) / pop 80x87 to memory.
100: fwait / Make sure pop is completed.
101: ijmp %eax / Return to the caller.
102:
103: //////////
104: / _dicvt() Convert integer to double.
105: / _ducvt() Convert unsigned integer to double.
106: / _dpcvt() Convert pointer to double.
107: / _dlcvt() Convert long integer to double.
108: / _dvcvt() Convert unsigned long integer to double.
109: / _dfcvt() Convert float to double.
110: /
111: / Widen to double.
112: / Some simple widens can be compiled inline,
113: / but the unsigned widens and the signed
114: / widens when the object lacks an lvalue cannot be.
115: /
116: / Input:
117: / Operand passed in standard C fashion on 80x86 stack.
118: / Output:
119: / Result in %st.
120: //////////
121:
122: IARG = RASIZE
123:
124: _dicvt:
125: _ficvt:
126: _dlcvt:
127: _flcvt:
128: fildl IARG(%esp) / Load and convert the integer.
129: ret
130:
131: UARG = RASIZE+EBPSIZE
132: INT64 = -8 / Auto int64 offset.
133:
134: _ducvt:
135: _dpcvt:
136: _fucvt:
137: _dvcvt:
138: _fvcvt:
139: push %ebp
140: movl %ebp, %esp
141: subl %esp, $8 / Claim a 64 bit integer.
142:
143: movl %eax, UARG(%ebp) / Grab unsigned integer
144: movl INT64(%ebp), %eax / and store in 64 bit integer low half.
145: movl INT64+4(%ebp), $0 / Zero-extend to high half.
146: fildll INT64(%ebp) / Load and convert to double.
147:
148: movl %esp, %ebp
149: pop %ebp
150: ret
151:
152: DARG = RASIZE+EBPSIZE
153: FLOAT = -4 / Auto float offset.
154:
155: _dfcvt:
156: push %ebp
157: movl %ebp, %esp
158: push %eax / Claim a float.
159:
160: fldl DARG(%ebp) / Grab double and
161: fstps FLOAT(%ebp) / round to a float.
162: flds FLOAT(%ebp) / Load result.
163:
164: movl %esp, %ebp
165: pop %ebp
166: ret
167:
168: //////////
169: / _idcvt() Convert double to integer.
170: / _udcvt() Convert double to unsigned integer.
171: / _pdcvt() Convert double to pointer.
172: / _ldcvt() Convert double to long integer.
173: / _vdcvt() Convert double to unsigned long integer.
174: / _fdcvt() Convert double to float.
175: /
176: / Shrinks from double.
177: / The task is a little harder than would be expected.
178: / Shift the 80x87 into chop mode for integer conversions
179: / so that conversions work the right way.
180: /
181: / Input:
182: / Double argument on the 80x86 stack.
183: / Output:
184: / Result in %eax for [iulv]dcvt(), %st for _fdcvt().
185: //////////
186:
187: SAVECW = -6 / Auto saved control word offset.
188: INT = -4 / Auto int32 offset.
189:
190: _idcvt:
191: _ldcvt:
192: push %ebp
193: movl %ebp, %esp
194: subl %esp, $6 / Claim a word and a dword.
195:
196: fstcw SAVECW(%ebp) / Save old control word.
197: fldcw cwchop / Load chopping control word.
198: fldl DARG(%ebp) / Load up double and
199: fistpl INT(%ebp) / convert to integer and
200: fldcw SAVECW(%ebp) / put control word back.
201: movl %eax, INT(%ebp) / Result to %eax.
202:
203: movl %esp, %ebp
204: pop %ebp
205: ret
206:
207: SAVECW = -10 / Auto saved control word offset.
208: INT64 = -8 / Auto int64 offset.
209:
210: _pdcvt:
211: _udcvt:
212: _vdcvt:
213: push %ebp
214: movl %ebp, %esp
215: subl %esp, $10 / Claim a word and a 64-bit int.
216:
217: fstcw SAVECW(%ebp) / Save old control word.
218: fldcw cwchop / Load chopping control word.
219: fldl DARG(%ebp) / Load up double and
220: fistpll INT64(%ebp) / convert to integer and
221: fldcw SAVECW(%ebp) / put control word back.
222: movl %eax, INT64(%ebp) / Unsigned long result to %eax.
223: cmpl INT64+4(%ebp), $0 / If hi result dword is zero,
224: je ?0 / return lo result dword.
225: movl %eax, $-1 / Overflow, return UINTMAX.
226:
227: ?0:
228: movl %esp, %ebp
229: pop %ebp
230: ret
231:
232: FLOAT = -4 / Auto float offset.
233:
234: _fdcvt:
235: push %ebp
236: movl %ebp, %esp
237: push %eax / Claim a float.
238:
239: fldl DARG(%ebp) / Load the double and
240: fstps FLOAT(%ebp) / convert it to a float.
241: flds FLOAT(%ebp) / Load the float.
242:
243: movl %esp, %ebp
244: pop %ebp
245: ret
246:
247: //////////
248: / double
249: / ldexp(fraction, exponent) double fraction; int exponent;
250: /
251: / Assemble a double precision floating point number
252: / from the given fraction and integer exponent.
253: //////////
254:
255: FRAC = RASIZE+EBPSIZE
256: EXP = FRAC+8
257:
258: ldexp:
259: push %ebp
260: movl %ebp, %esp
261:
262: fildl EXP(%ebp) / Grab exponent
263: fldl FRAC(%ebp) / and fraction.
264: fscale / Put it all together
265: fstp %st(1) / and pop stack.
266:
267: pop %ebp
268: ret
269:
270: //////////
271: / double
272: / frexp(value, expp) double value; int *expp;
273: /
274: / Split a double float into its fraction and its exponent.
275: / The "fxtract" instruction almost does the job.
276: / The IEEE standard says that the significand is not from 0.5 to 1
277: / but from 1 to 2.
278: / The extracted significand and exponent must be adjusted if nonzero.
279: //////////
280:
281: VALUE = RASIZE+EBPSIZE
282: EXPP = VALUE+8
283:
284: frexp:
285: push %ebp
286: movl %ebp, %esp
287:
288: fldl VALUE(%ebp) / Load the value.
289: fxtract / Significand to %st, unbiased exp to %st1.
290: call _tstcc / Check if the significand is zero.
291: je frexp0 / Jump if it is.
292: filds two / %st=2.0, %st1=sig, %st2=exp.
293: fdivrp %st(1), %st / %st=sig/2.0, %st1=exp.
294: fld1 / %st=1.0, %st1=sig/2.0, %st2=exp.
295: faddp %st(2), %st / %st=sig/2.0, %st1=exp+1.0.
296:
297: frexp0: / Significand in %st, exponent in %st1.
298: fxch / Exponent to %st, significand to %st1.
299: movl %eax, EXPP(%ebp) / Save the exponent
300: fistpl (%eax) / through the supplied pointer and pop.
301: fwait / Make sure it is good.
302:
303: pop %ebp
304: ret
305:
306: //////////
307: / double
308: / modf(value, intpart) double value; double *intpart;
309: /
310: / Split a double (another way!).
311: //////////
312:
313: VALUE = RASIZE+EBPSIZE
314: INTPART = VALUE+8
315: SAVECW = -2 / Auto saved control word offset.
316:
317: modf:
318: push %ebp
319: movl %ebp, %esp
320: push %eax / Claim one dword (only 2 bytes used).
321:
322: fstcw SAVECW(%ebp) / Save the old control word and
323: fldcw cwchop / load chopping control word.
324: fldl VALUE(%ebp) / Pick up argument.
325: fld %st / %st=arg, %st1=arg.
326: frndint / %st=intpart, %st1=arg.
327: fsubr %st(1), %st / %st=intpart, %st1=fracpart.
328: movl %eax, INTPART(%ebp) / Load integer pointer.
329: fstpl (%eax) / Store intpart through it and pop.
330: fldcw SAVECW(%ebp) / Put old control word back.
331:
332: movl %esp, %ebp
333: pop %ebp
334: ret
335:
336: / end of rts87.s
337:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.