Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | /* pdp11_fp.c: PDP-11 floating point simulator (32b version)\r |
2 | \r | |
3 | Copyright (c) 1993-2005, Robert M Supnik\r | |
4 | \r | |
5 | Permission is hereby granted, free of charge, to any person obtaining a\r | |
6 | copy of this software and associated documentation files (the "Software"),\r | |
7 | to deal in the Software without restriction, including without limitation\r | |
8 | the rights to use, copy, modify, merge, publish, distribute, sublicense,\r | |
9 | and/or sell copies of the Software, and to permit persons to whom the\r | |
10 | Software is furnished to do so, subject to the following conditions:\r | |
11 | \r | |
12 | The above copyright notice and this permission notice shall be included in\r | |
13 | all copies or substantial portions of the Software.\r | |
14 | \r | |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r | |
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r | |
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r | |
18 | ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r | |
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r | |
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r | |
21 | \r | |
22 | Except as contained in this notice, the name of Robert M Supnik shall not be\r | |
23 | used in advertising or otherwise to promote the sale, use or other dealings\r | |
24 | in this Software without prior written authorization from Robert M Supnik.\r | |
25 | \r | |
26 | 22-Sep-05 RMS Fixed declarations (from Sterling Garwood)\r | |
27 | 04-Oct-04 RMS Added FIS instructions\r | |
28 | 19-Jan-03 RMS Changed mode definitions for Apple Dev Kit conflict\r | |
29 | 08-Oct-02 RMS Fixed macro definitions\r | |
30 | 05-Jun-98 RMS Fixed implementation specific shift bugs\r | |
31 | 20-Apr-98 RMS Fixed bug in MODf integer truncation\r | |
32 | 17-Apr-98 RMS Fixed bug in STCfi range check\r | |
33 | 16-Apr-98 RMS Fixed bugs in STEXP, STCfi, round/pack\r | |
34 | 09-Apr-98 RMS Fixed bug in LDEXP\r | |
35 | 04-Apr-98 RMS Fixed bug in MODf condition codes\r | |
36 | \r | |
37 | This module simulates the PDP-11 floating point unit (FP11 series).\r | |
38 | It is called from the instruction decoder for opcodes 170000:177777.\r | |
39 | \r | |
40 | The floating point unit recognizes three instruction formats:\r | |
41 | \r | |
42 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ no operand\r | |
43 | | 1 1 1 1| 0 0 0 0 0 0| opcode | 170000:\r | |
44 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 170077\r | |
45 | \r | |
46 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ one operand\r | |
47 | | 1 1 1 1| 0 0 0| opcode | dest spec | 170100:\r | |
48 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 170777\r | |
49 | \r | |
50 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ register + operand\r | |
51 | | 1 1 1 1| opcode | fac | dest spec | 171000:\r | |
52 | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 177777\r | |
53 | \r | |
54 | The instruction space is further extended through use of the floating\r | |
55 | point status register (FPS) mode bits. Three mode bits affect how\r | |
56 | instructions are interpreted:\r | |
57 | \r | |
58 | FPS_D if 0, floating registers are single precision\r | |
59 | if 1, floating registers are double precision\r | |
60 | \r | |
61 | FPS_L if 0, integer operands are word\r | |
62 | if 1, integer operands are longword\r | |
63 | \r | |
64 | FPS_T if 0, floating operations are rounded\r | |
65 | if 1, floating operations are truncated\r | |
66 | \r | |
67 | FPS also contains the condition codes for the floating point unit,\r | |
68 | and exception enable bits for individual error conditions. Exceptions\r | |
69 | cause a trap through 0244, unless the individual exception, or all\r | |
70 | exceptions, are disabled. Illegal address mode, undefined variable,\r | |
71 | and divide by zero abort the current instruction; all other exceptions\r | |
72 | permit the instruction to complete. (Aborts are implemented as traps\r | |
73 | that request an "interrupt" trap. If an interrupt is pending, it is\r | |
74 | serviced; if not, trap_req is updated and processing continues.)\r | |
75 | \r | |
76 | Floating point specifiers are similar to integer specifiers, with\r | |
77 | the length of the operand being up to 8 bytes. In two specific cases,\r | |
78 | the floating point unit reads or writes only two bytes, rather than\r | |
79 | the length specified by the operand type:\r | |
80 | \r | |
81 | register for integers, only 16b are accessed; if the\r | |
82 | operand is 32b, these are the high order 16b\r | |
83 | of the operand\r | |
84 | \r | |
85 | immediate for integers or floating point, only 16b are\r | |
86 | accessed; if the operand is 32b or 64b, these\r | |
87 | are the high order 16b of the operand\r | |
88 | */\r | |
89 | \r | |
90 | #include "pdp11_defs.h"\r | |
91 | \r | |
92 | /* Floating point status register */\r | |
93 | \r | |
94 | #define FPS_ER (1u << FPS_V_ER) /* error */\r | |
95 | #define FPS_ID (1u << FPS_V_ID) /* interrupt disable */\r | |
96 | #define FPS_IUV (1u << FPS_V_IUV) /* int on undef var */\r | |
97 | #define FPS_IU (1u << FPS_V_IU) /* int on underflow */\r | |
98 | #define FPS_IV (1u << FPS_V_IV) /* int on overflow */\r | |
99 | #define FPS_IC (1u << FPS_V_IC) /* int on conv error */\r | |
100 | #define FPS_D (1u << FPS_V_D) /* single/double */\r | |
101 | #define FPS_L (1u << FPS_V_L) /* word/long */\r | |
102 | #define FPS_T (1u << FPS_V_T) /* round/truncate */\r | |
103 | #define FPS_N (1u << FPS_V_N)\r | |
104 | #define FPS_Z (1u << FPS_V_Z)\r | |
105 | #define FPS_V (1u << FPS_V_V)\r | |
106 | #define FPS_C (1u << FPS_V_C)\r | |
107 | #define FPS_CC (FPS_N + FPS_Z + FPS_V + FPS_C)\r | |
108 | #define FPS_RW (FPS_ER + FPS_ID + FPS_IUV + FPS_IU + FPS_IV + \\r | |
109 | FPS_IC + FPS_D + FPS_L + FPS_T + FPS_CC)\r | |
110 | \r | |
111 | /* Floating point exception codes */\r | |
112 | \r | |
113 | #define FEC_OP 2 /* illegal op/mode */\r | |
114 | #define FEC_DZRO 4 /* divide by zero */\r | |
115 | #define FEC_ICVT 6 /* conversion error */\r | |
116 | #define FEC_OVFLO 8 /* overflow */\r | |
117 | #define FEC_UNFLO 10 /* underflow */\r | |
118 | #define FEC_UNDFV 12 /* undef variable */\r | |
119 | \r | |
120 | /* Floating point format, all assignments 32b relative */\r | |
121 | \r | |
122 | #define FP_V_SIGN (63 - 32) /* high lw: sign */\r | |
123 | #define FP_V_EXP (55 - 32) /* exponent */\r | |
124 | #define FP_V_HB FP_V_EXP /* hidden bit */\r | |
125 | #define FP_V_F0 (48 - 32) /* fraction 0 */\r | |
126 | #define FP_V_F1 (32 - 32) /* fraction 1 */\r | |
127 | #define FP_V_FROUND (31 - 32) /* f round point */\r | |
128 | #define FP_V_F2 16 /* low lw: fraction 2 */\r | |
129 | #define FP_V_F3 0 /* fraction 3 */\r | |
130 | #define FP_V_DROUND (-1) /* d round point */\r | |
131 | #define FP_M_EXP 0377\r | |
132 | #define FP_SIGN (1u << FP_V_SIGN)\r | |
133 | #define FP_EXP (FP_M_EXP << FP_V_EXP)\r | |
134 | #define FP_HB (1u << FP_V_HB)\r | |
135 | #define FP_FRACH ((1u << FP_V_HB) - 1)\r | |
136 | #define FP_FRACL 0xFFFFFFFF\r | |
137 | #define FP_BIAS 0200 /* exponent bias */\r | |
138 | #define FP_GUARD 3 /* guard bits */\r | |
139 | \r | |
140 | /* Data lengths */\r | |
141 | \r | |
142 | #define WORD 2\r | |
143 | #define LONG 4\r | |
144 | #define QUAD 8\r | |
145 | \r | |
146 | /* Double precision operations on 64b quantities */\r | |
147 | \r | |
148 | #define F_LOAD(qd,ac,ds) \\r | |
149 | ds.h = ac.h; ds.l = (qd)? ac.l: 0\r | |
150 | #define F_LOAD_P(qd,ac,ds) \\r | |
151 | ds->h = ac.h; ds->l = (qd)? ac.l: 0\r | |
152 | #define F_LOAD_FRAC(qd,ac,ds) \\r | |
153 | ds.h = (ac.h & FP_FRACH) | FP_HB; \\r | |
154 | ds.l = (qd)? ac.l: 0\r | |
155 | #define F_STORE(qd,sr,ac) \\r | |
156 | ac.h = sr.h; if ((qd)) ac.l = sr.l\r | |
157 | #define F_STORE_P(qd,sr,ac) \\r | |
158 | ac.h = sr->h; if ((qd)) ac.l = sr->l\r | |
159 | #define F_GET_FRAC_P(sr,ds) \\r | |
160 | ds.l = sr->l; \\r | |
161 | ds.h = (sr->h & FP_FRACH) | FP_HB\r | |
162 | #define F_ADD(s2,s1,ds) \\r | |
163 | ds.l = (s1.l + s2.l) & 0xFFFFFFFF; \\r | |
164 | ds.h = (s1.h + s2.h + (ds.l < s2.l)) & 0xFFFFFFFF\r | |
165 | #define F_SUB(s2,s1,ds) \\r | |
166 | ds.h = (s1.h - s2.h - (s1.l < s2.l)) & 0xFFFFFFFF; \\r | |
167 | ds.l = (s1.l - s2.l) & 0xFFFFFFFF\r | |
168 | #define F_LT(x,y) ((x.h < y.h) || ((x.h == y.h) && (x.l < y.l)))\r | |
169 | #define F_LT_AP(x,y) (((x->h & ~FP_SIGN) < (y->h & ~FP_SIGN)) || \\r | |
170 | (((x->h & ~FP_SIGN) == (y->h & ~FP_SIGN)) && (x->l < y->l)))\r | |
171 | #define F_LSH_V(sr,n,ds) \\r | |
172 | ds.h = (((n) >= 32)? (sr.l << ((n) - 32)): \\r | |
173 | (sr.h << (n)) | ((sr.l >> (32 - (n))) & and_mask[n])) \\r | |
174 | & 0xFFFFFFFF; \\r | |
175 | ds.l = ((n) >= 32)? 0: (sr.l << (n)) & 0xFFFFFFFF\r | |
176 | #define F_RSH_V(sr,n,ds) \\r | |
177 | ds.l = (((n) >= 32)? (sr.h >> ((n) - 32)) & and_mask[64 - (n)]: \\r | |
178 | ((sr.l >> (n)) & and_mask[32 - (n)]) | \\r | |
179 | (sr.h << (32 - (n)))) & 0xFFFFFFFF; \\r | |
180 | ds.h = ((n) >= 32)? 0: \\r | |
181 | ((sr.h >> (n)) & and_mask[32 - (n)]) & 0xFFFFFFFF\r | |
182 | \r | |
183 | /* For the constant shift macro, arguments must in the range [2,31] */\r | |
184 | \r | |
185 | #define F_LSH_1(ds) ds.h = ((ds.h << 1) | ((ds.l >> 31) & 1)) & 0xFFFFFFFF; \\r | |
186 | ds.l = (ds.l << 1) & 0xFFFFFFFF\r | |
187 | #define F_RSH_1(ds) ds.l = ((ds.l >> 1) & 0x7FFFFFFF) | ((ds.h & 1) << 31); \\r | |
188 | ds.h = ((ds.h >> 1) & 0x7FFFFFFF)\r | |
189 | #define F_LSH_K(sr,n,ds) \\r | |
190 | ds.h = ((sr.h << (n)) | ((sr.l >> (32 - (n))) & and_mask[n])) \\r | |
191 | & 0xFFFFFFFF; \\r | |
192 | ds.l = (sr.l << (n)) & 0xFFFFFFFF\r | |
193 | #define F_RSH_K(sr,n,ds) \\r | |
194 | ds.l = (((sr.l >> (n)) & and_mask[32 - (n)]) | \\r | |
195 | (sr.h << (32 - (n)))) & 0xFFFFFFFF; \\r | |
196 | ds.h = ((sr.h >> (n)) & and_mask[32 - (n)]) & 0xFFFFFFFF\r | |
197 | #define F_LSH_GUARD(ds) F_LSH_K(ds,FP_GUARD,ds)\r | |
198 | #define F_RSH_GUARD(ds) F_RSH_K(ds,FP_GUARD,ds)\r | |
199 | \r | |
200 | #define GET_BIT(ir,n) (((ir) >> (n)) & 1)\r | |
201 | #define GET_SIGN(ir) GET_BIT((ir), FP_V_SIGN)\r | |
202 | #define GET_EXP(ir) (((ir) >> FP_V_EXP) & FP_M_EXP)\r | |
203 | #define GET_SIGN_L(ir) GET_BIT((ir), 31)\r | |
204 | #define GET_SIGN_W(ir) GET_BIT((ir), 15)\r | |
205 | \r | |
206 | extern jmp_buf save_env;\r | |
207 | extern uint32 cpu_type;\r | |
208 | extern int32 FEC, FEA, FPS;\r | |
209 | extern int32 CPUERR, trap_req;\r | |
210 | extern int32 N, Z, V, C;\r | |
211 | extern int32 R[8];\r | |
212 | extern int32 STKLIM;\r | |
213 | extern int32 cm, isenable, dsenable, MMR0, MMR1;\r | |
214 | extern fpac_t FR[6];\r | |
215 | \r | |
216 | fpac_t zero_fac = { 0, 0 };\r | |
217 | fpac_t one_fac = { 1, 0 };\r | |
218 | fpac_t fround_fac = { (1u << (FP_V_FROUND + 32)), 0 };\r | |
219 | fpac_t fround_guard_fac = { 0, (1u << (FP_V_FROUND + FP_GUARD)) };\r | |
220 | fpac_t dround_guard_fac = { (1u << (FP_V_DROUND + FP_GUARD)), 0 };\r | |
221 | fpac_t fmask_fac = { 0xFFFFFFFF, (1u << (FP_V_HB + FP_GUARD + 1)) - 1 };\r | |
222 | static const uint32 and_mask[33] = { 0,\r | |
223 | 0x1, 0x3, 0x7, 0xF,\r | |
224 | 0x1F, 0x3F, 0x7F, 0xFF,\r | |
225 | 0x1FF, 0x3FF, 0x7FF, 0xFFF,\r | |
226 | 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,\r | |
227 | 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF,\r | |
228 | 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF,\r | |
229 | 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF,\r | |
230 | 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF\r | |
231 | };\r | |
232 | int32 backup_PC;\r | |
233 | int32 fpnotrap (int32 code);\r | |
234 | int32 GeteaFP (int32 spec, int32 len);\r | |
235 | \r | |
236 | uint32 ReadI (int32 addr, int32 spec, int32 len);\r | |
237 | void ReadFP (fpac_t *fac, int32 addr, int32 spec, int32 len);\r | |
238 | void WriteI (int32 data, int32 addr, int32 spec, int32 len);\r | |
239 | void WriteFP (fpac_t *data, int32 addr, int32 spec, int32 len);\r | |
240 | int32 setfcc (int32 old_status, int32 result_high, int32 newV);\r | |
241 | int32 addfp11 (fpac_t *src1, fpac_t *src2);\r | |
242 | int32 mulfp11 (fpac_t *src1, fpac_t *src2);\r | |
243 | int32 divfp11 (fpac_t *src1, fpac_t *src2);\r | |
244 | int32 modfp11 (fpac_t *src1, fpac_t *src2, fpac_t *frac);\r | |
245 | void frac_mulfp11 (fpac_t *src1, fpac_t *src2);\r | |
246 | int32 roundfp11 (fpac_t *src);\r | |
247 | int32 round_and_pack (fpac_t *fac, int32 exp, fpac_t *frac, int r);\r | |
248 | \r | |
249 | extern int32 GeteaW (int32 spec);\r | |
250 | extern int32 ReadW (int32 addr);\r | |
251 | extern void WriteW (int32 data, int32 addr);\r | |
252 | extern void set_stack_trap (int32 adr);\r | |
253 | \r | |
254 | /* Set up for instruction decode and execution */\r | |
255 | \r | |
256 | void fp11 (int32 IR)\r | |
257 | {\r | |
258 | int32 dst, ea, ac, dstspec;\r | |
259 | int32 i, qdouble, lenf, leni;\r | |
260 | int32 newV, exp, sign;\r | |
261 | fpac_t fac, fsrc, modfrac;\r | |
262 | static const uint32 i_limit[2][2] = {\r | |
263 | { 0x80000000, 0x80010000 },\r | |
264 | { 0x80000000, 0x80000001 }\r | |
265 | };\r | |
266 | \r | |
267 | backup_PC = PC; /* save PC for FEA */\r | |
268 | ac = (IR >> 6) & 03; /* fac is IR<7:6> */\r | |
269 | dstspec = IR & 077;\r | |
270 | qdouble = FPS & FPS_D;\r | |
271 | lenf = qdouble? QUAD: LONG;\r | |
272 | switch ((IR >> 8) & 017) { /* decode IR<11:8> */\r | |
273 | \r | |
274 | case 000:\r | |
275 | switch (ac) { /* decode IR<7:6> */\r | |
276 | \r | |
277 | case 0: /* specials */\r | |
278 | if (IR == 0170000) { /* CFCC */\r | |
279 | N = (FPS >> PSW_V_N) & 1;\r | |
280 | Z = (FPS >> PSW_V_Z) & 1;\r | |
281 | V = (FPS >> PSW_V_V) & 1;\r | |
282 | C = (FPS >> PSW_V_C) & 1;\r | |
283 | }\r | |
284 | else if (IR == 0170001) /* SETF */\r | |
285 | FPS = FPS & ~FPS_D;\r | |
286 | else if (IR == 0170002) /* SETI */\r | |
287 | FPS = FPS & ~FPS_L;\r | |
288 | else if (IR == 0170011) /* SETD */\r | |
289 | FPS = FPS | FPS_D;\r | |
290 | else if (IR == 0170012) /* SETL */\r | |
291 | FPS = FPS | FPS_L;\r | |
292 | else fpnotrap (FEC_OP);\r | |
293 | break;\r | |
294 | \r | |
295 | case 1: /* LDFPS */\r | |
296 | dst = (dstspec <= 07)? R[dstspec]: ReadW (GeteaW (dstspec));\r | |
297 | FPS = dst & FPS_RW;\r | |
298 | break;\r | |
299 | \r | |
300 | case 2: /* STFPS */\r | |
301 | FPS = FPS & FPS_RW;\r | |
302 | if (dstspec <= 07) R[dstspec] = FPS;\r | |
303 | else WriteW (FPS, GeteaW (dstspec));\r | |
304 | break;\r | |
305 | \r | |
306 | case 3: /* STST */\r | |
307 | if (dstspec <= 07) R[dstspec] = FEC;\r | |
308 | else WriteI ((FEC << 16) | FEA, GeteaFP (dstspec, LONG),\r | |
309 | dstspec, LONG);\r | |
310 | break;\r | |
311 | } /* end switch <7:6> */\r | |
312 | break; /* end case 0 */\r | |
313 | \r | |
314 | case 001:\r | |
315 | switch (ac) { /* decode IR<7:6> */\r | |
316 | \r | |
317 | case 0: /* CLRf */\r | |
318 | WriteFP (&zero_fac, GeteaFP (dstspec, lenf), dstspec, lenf);\r | |
319 | FPS = (FPS & ~FPS_CC) | FPS_Z;\r | |
320 | break;\r | |
321 | \r | |
322 | case 1: /* TSTf */\r | |
323 | ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);\r | |
324 | FPS = setfcc (FPS, fsrc.h, 0);\r | |
325 | break;\r | |
326 | \r | |
327 | case 2: /* ABSf */\r | |
328 | ReadFP (&fsrc, ea = GeteaFP (dstspec, lenf), dstspec, lenf);\r | |
329 | if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac;\r | |
330 | else fsrc.h = fsrc.h & ~FP_SIGN;\r | |
331 | WriteFP (&fsrc, ea, dstspec, lenf);\r | |
332 | FPS = setfcc (FPS, fsrc.h, 0);\r | |
333 | break;\r | |
334 | \r | |
335 | case 3: /* NEGf */\r | |
336 | ReadFP (&fsrc, ea = GeteaFP (dstspec, lenf), dstspec, lenf);\r | |
337 | if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac;\r | |
338 | else fsrc.h = fsrc.h ^ FP_SIGN;\r | |
339 | WriteFP (&fsrc, ea, dstspec, lenf);\r | |
340 | FPS = setfcc (FPS, fsrc.h, 0);\r | |
341 | break;\r | |
342 | } /* end switch <7:6> */\r | |
343 | break; /* end case 1 */\r | |
344 | \r | |
345 | case 005: /* LDf */\r | |
346 | ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);\r | |
347 | F_STORE (qdouble, fsrc, FR[ac]);\r | |
348 | FPS = setfcc (FPS, fsrc.h, 0);\r | |
349 | break;\r | |
350 | \r | |
351 | case 010: /* STf */\r | |
352 | F_LOAD (qdouble, FR[ac], fac);\r | |
353 | WriteFP (&fac, GeteaFP (dstspec, lenf), dstspec, lenf);\r | |
354 | break;\r | |
355 | \r | |
356 | case 017: /* LDCff' */\r | |
357 | ReadFP (&fsrc, GeteaFP (dstspec, 12 - lenf), dstspec, 12 - lenf);\r | |
358 | if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac;\r | |
359 | if ((FPS & (FPS_D + FPS_T)) == 0) newV = roundfp11 (&fsrc);\r | |
360 | else newV = 0;\r | |
361 | F_STORE (qdouble, fsrc, FR[ac]);\r | |
362 | FPS = setfcc (FPS, fsrc.h, newV);\r | |
363 | break;\r | |
364 | \r | |
365 | case 014: /* STCff' */\r | |
366 | F_LOAD (qdouble, FR[ac], fac);\r | |
367 | if (GET_EXP (fac.h) == 0) fac = zero_fac;\r | |
368 | if ((FPS & (FPS_D + FPS_T)) == FPS_D) newV = roundfp11 (&fac);\r | |
369 | else newV = 0;\r | |
370 | WriteFP (&fac, GeteaFP (dstspec, 12 - lenf), dstspec, 12 - lenf);\r | |
371 | FPS = setfcc (FPS, fac.h, newV);\r | |
372 | break;\r | |
373 | \r | |
374 | case 007: /* CMPf */\r | |
375 | ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);\r | |
376 | F_LOAD (qdouble, FR[ac], fac);\r | |
377 | if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac;\r | |
378 | if (GET_EXP (fac.h) == 0) fac = zero_fac;\r | |
379 | if ((fsrc.h == fac.h) && (fsrc.l == fac.l)) { /* equal? */\r | |
380 | FPS = (FPS & ~FPS_CC) | FPS_Z;\r | |
381 | if ((fsrc.h | fsrc.l) == 0) { /* zero? */\r | |
382 | F_STORE (qdouble, zero_fac, FR[ac]);\r | |
383 | }\r | |
384 | break;\r | |
385 | }\r | |
386 | FPS = (FPS & ~FPS_CC) | ((fsrc.h >> (FP_V_SIGN - PSW_V_N)) & FPS_N);\r | |
387 | if ((GET_SIGN (fsrc.h ^ fac.h) == 0) && (fac.h != 0) &&\r | |
388 | F_LT (fsrc, fac)) FPS = FPS ^ FPS_N;\r | |
389 | break;\r | |
390 | \r | |
391 | case 015: /* LDEXP */\r | |
392 | dst = (dstspec <= 07)? R[dstspec]: ReadW (GeteaW (dstspec));\r | |
393 | F_LOAD (qdouble, FR[ac], fac);\r | |
394 | fac.h = (fac.h & ~FP_EXP) | (((dst + FP_BIAS) & FP_M_EXP) << FP_V_EXP);\r | |
395 | newV = 0;\r | |
396 | if ((dst > 0177) && (dst <= 0177600)) {\r | |
397 | if (dst < 0100000) {\r | |
398 | if (fpnotrap (FEC_OVFLO)) fac = zero_fac;\r | |
399 | newV = FPS_V;\r | |
400 | }\r | |
401 | else {\r | |
402 | if (fpnotrap (FEC_UNFLO)) fac = zero_fac;\r | |
403 | }\r | |
404 | }\r | |
405 | F_STORE (qdouble, fac, FR[ac]);\r | |
406 | FPS = setfcc (FPS, fac.h, newV);\r | |
407 | break; \r | |
408 | \r | |
409 | case 012: /* STEXP */\r | |
410 | dst = (GET_EXP (FR[ac].h) - FP_BIAS) & 0177777;\r | |
411 | N = GET_SIGN_W (dst);\r | |
412 | Z = (dst == 0);\r | |
413 | V = 0;\r | |
414 | C = 0;\r | |
415 | FPS = (FPS & ~FPS_CC) | (N << PSW_V_N) | (Z << PSW_V_Z);\r | |
416 | if (dstspec <= 07) R[dstspec] = dst;\r | |
417 | else WriteW (dst, GeteaW (dstspec));\r | |
418 | break;\r | |
419 | \r | |
420 | case 016: /* LDCif */\r | |
421 | leni = FPS & FPS_L? LONG: WORD;\r | |
422 | if (dstspec <= 07) fac.l = R[dstspec] << 16;\r | |
423 | else fac.l = ReadI (GeteaFP (dstspec, leni), dstspec, leni);\r | |
424 | fac.h = 0;\r | |
425 | if (fac.l) {\r | |
426 | if (sign = GET_SIGN_L (fac.l)) fac.l = (fac.l ^ 0xFFFFFFFF) + 1;\r | |
427 | for (i = 0; GET_SIGN_L (fac.l) == 0; i++) fac.l = fac.l << 1;\r | |
428 | exp = ((FPS & FPS_L)? FP_BIAS + 32: FP_BIAS + 16) - i;\r | |
429 | fac.h = (sign << FP_V_SIGN) | (exp << FP_V_EXP) |\r | |
430 | ((fac.l >> (31 - FP_V_HB)) & FP_FRACH);\r | |
431 | fac.l = (fac.l << (FP_V_HB + 1)) & FP_FRACL;\r | |
432 | if ((FPS & (FPS_D + FPS_T)) == 0) roundfp11 (&fac);\r | |
433 | }\r | |
434 | F_STORE (qdouble, fac, FR[ac]);\r | |
435 | FPS = setfcc (FPS, fac.h, 0);\r | |
436 | break;\r | |
437 | \r | |
438 | case 013: /* STCfi */\r | |
439 | sign = GET_SIGN (FR[ac].h); /* get sign, */\r | |
440 | exp = GET_EXP (FR[ac].h); /* exponent, */\r | |
441 | F_LOAD_FRAC (qdouble, FR[ac], fac); /* fraction */\r | |
442 | if (FPS & FPS_L) {\r | |
443 | leni = LONG;\r | |
444 | i = FP_BIAS + 32;\r | |
445 | }\r | |
446 | else {\r | |
447 | leni = WORD;\r | |
448 | i = FP_BIAS + 16;\r | |
449 | }\r | |
450 | C = 0;\r | |
451 | if (exp <= FP_BIAS) dst = 0;\r | |
452 | else if (exp > i) {\r | |
453 | dst = 0;\r | |
454 | C = 1;\r | |
455 | }\r | |
456 | else {\r | |
457 | F_RSH_V (fac, FP_V_HB + 1 + i - exp, fsrc);\r | |
458 | if (leni == WORD) fsrc.l = fsrc.l & ~0177777;\r | |
459 | if (fsrc.l >= i_limit[leni == LONG][sign]) {\r | |
460 | dst = 0;\r | |
461 | C = 1;\r | |
462 | } \r | |
463 | else {\r | |
464 | dst = fsrc.l;\r | |
465 | if (sign) dst = -dst;\r | |
466 | }\r | |
467 | }\r | |
468 | N = GET_SIGN_L (dst);\r | |
469 | Z = (dst == 0);\r | |
470 | V = 0;\r | |
471 | if (C) fpnotrap (FEC_ICVT);\r | |
472 | FPS = (FPS & ~FPS_CC) | (N << PSW_V_N) |\r | |
473 | (Z << PSW_V_Z) | (C << PSW_V_C);\r | |
474 | if (dstspec <= 07) R[dstspec] = (dst >> 16) & 0177777;\r | |
475 | else WriteI (dst, GeteaFP (dstspec, leni), dstspec, leni);\r | |
476 | break;\r | |
477 | \r | |
478 | case 002: /* MULf */\r | |
479 | ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);\r | |
480 | F_LOAD (qdouble, FR[ac], fac);\r | |
481 | newV = mulfp11 (&fac, &fsrc);\r | |
482 | F_STORE (qdouble, fac, FR[ac]);\r | |
483 | FPS = setfcc (FPS, fac.h, newV);\r | |
484 | break;\r | |
485 | \r | |
486 | case 003: /* MODf */\r | |
487 | ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);\r | |
488 | F_LOAD (qdouble, FR[ac], fac);\r | |
489 | newV = modfp11 (&fac, &fsrc, &modfrac);\r | |
490 | F_STORE (qdouble, fac, FR[ac | 1]);\r | |
491 | F_STORE (qdouble, modfrac, FR[ac]);\r | |
492 | FPS = setfcc (FPS, modfrac.h, newV);\r | |
493 | break;\r | |
494 | \r | |
495 | case 004: /* ADDf */\r | |
496 | ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);\r | |
497 | F_LOAD (qdouble, FR[ac], fac);\r | |
498 | newV = addfp11 (&fac, &fsrc);\r | |
499 | F_STORE (qdouble, fac, FR[ac]);\r | |
500 | FPS = setfcc (FPS, fac.h, newV);\r | |
501 | break;\r | |
502 | \r | |
503 | case 006: /* SUBf */\r | |
504 | ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);\r | |
505 | F_LOAD (qdouble, FR[ac], fac);\r | |
506 | if (GET_EXP (fsrc.h) != 0) fsrc.h = fsrc.h ^ FP_SIGN;\r | |
507 | newV = addfp11 (&fac, &fsrc);\r | |
508 | F_STORE (qdouble, fac, FR[ac]);\r | |
509 | FPS = setfcc (FPS, fac.h, newV);\r | |
510 | break;\r | |
511 | \r | |
512 | case 011: /* DIVf */\r | |
513 | ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);\r | |
514 | F_LOAD (qdouble, FR[ac], fac);\r | |
515 | if (GET_EXP (fsrc.h) == 0) { /* divide by zero? */\r | |
516 | fpnotrap (FEC_DZRO);\r | |
517 | ABORT (TRAP_INT);\r | |
518 | }\r | |
519 | newV = divfp11 (&fac, &fsrc);\r | |
520 | F_STORE (qdouble, fac, FR[ac]);\r | |
521 | FPS = setfcc (FPS, fac.h, newV);\r | |
522 | break;\r | |
523 | } /* end switch fop */\r | |
524 | \r | |
525 | return;\r | |
526 | }\r | |
527 | \r | |
528 | /* Effective address calculation for fp operands\r | |
529 | \r | |
530 | Inputs:\r | |
531 | spec = specifier\r | |
532 | len = length\r | |
533 | Outputs:\r | |
534 | VA = virtual address\r | |
535 | \r | |
536 | Warnings:\r | |
537 | - Do not call this routine for integer mode 0 operands\r | |
538 | - Do not call this routine more than once per instruction\r | |
539 | */\r | |
540 | \r | |
541 | int32 GeteaFP (int32 spec, int32 len)\r | |
542 | {\r | |
543 | int32 adr, reg, ds;\r | |
544 | \r | |
545 | reg = spec & 07; /* reg number */\r | |
546 | ds = (reg == 7)? isenable: dsenable; /* dspace if not PC */\r | |
547 | switch (spec >> 3) { /* case on spec */\r | |
548 | \r | |
549 | case 0: /* floating AC */\r | |
550 | if (reg >= 06) {\r | |
551 | fpnotrap (FEC_OP);\r | |
552 | ABORT (TRAP_INT);\r | |
553 | }\r | |
554 | return 0;\r | |
555 | \r | |
556 | case 1: /* (R) */\r | |
557 | return (R[reg] | ds);\r | |
558 | \r | |
559 | case 2: /* (R)+ */\r | |
560 | if (reg == 7) len = 2;\r | |
561 | R[reg] = ((adr = R[reg]) + len) & 0177777;\r | |
562 | if (update_MM) MMR1 = (len << 3) | reg;\r | |
563 | return (adr | ds);\r | |
564 | \r | |
565 | case 3: /* @(R)+ */\r | |
566 | R[reg] = ((adr = R[reg]) + 2) & 0177777;\r | |
567 | if (update_MM) MMR1 = 020 | reg;\r | |
568 | adr = ReadW (adr | ds);\r | |
569 | return (adr | dsenable);\r | |
570 | \r | |
571 | case 4: /* -(R) */\r | |
572 | adr = R[reg] = (R[reg] - len) & 0177777;\r | |
573 | if (update_MM) MMR1 = (((-len) & 037) << 3) | reg;\r | |
574 | if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y)))\r | |
575 | set_stack_trap (adr);\r | |
576 | return (adr | ds);\r | |
577 | \r | |
578 | case 5: /* @-(R) */\r | |
579 | adr = R[reg] = (R[reg] - 2) & 0177777;\r | |
580 | if (update_MM) MMR1 = 0360 | reg;\r | |
581 | if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y)))\r | |
582 | set_stack_trap (adr);\r | |
583 | adr = ReadW (adr | ds);\r | |
584 | return (adr | dsenable);\r | |
585 | \r | |
586 | case 6: /* d(r) */\r | |
587 | adr = ReadW (PC | isenable);\r | |
588 | PC = (PC + 2) & 0177777;\r | |
589 | return (((R[reg] + adr) & 0177777) | dsenable);\r | |
590 | \r | |
591 | case 7: /* @d(R) */\r | |
592 | adr = ReadW (PC | isenable);\r | |
593 | PC = (PC + 2) & 0177777;\r | |
594 | adr = ReadW (((R[reg] + adr) & 0177777) | dsenable);\r | |
595 | return (adr | dsenable);\r | |
596 | } /* end switch */\r | |
597 | \r | |
598 | return 0;\r | |
599 | }\r | |
600 | \r | |
601 | /* Read integer operand\r | |
602 | \r | |
603 | Inputs:\r | |
604 | VA = virtual address, VA<18:16> = mode, I/D space\r | |
605 | spec = specifier\r | |
606 | len = length (2/4 bytes)\r | |
607 | Outputs:\r | |
608 | data = data read from memory or I/O space\r | |
609 | */\r | |
610 | \r | |
611 | uint32 ReadI (int32 VA, int32 spec, int32 len)\r | |
612 | {\r | |
613 | if ((len == WORD) || (spec == 027)) return (ReadW (VA) << 16);\r | |
614 | return ((ReadW (VA) << 16) |\r | |
615 | ReadW ((VA & ~0177777) | ((VA + 2) & 0177777)));\r | |
616 | }\r | |
617 | \r | |
618 | /* Read floating operand\r | |
619 | \r | |
620 | Inputs:\r | |
621 | fptr = pointer to output\r | |
622 | VA = virtual address, VA<18:16> = mode, I/D space\r | |
623 | spec = specifier\r | |
624 | len = length (4/8 bytes)\r | |
625 | */\r | |
626 | \r | |
627 | void ReadFP (fpac_t *fptr, int32 VA, int32 spec, int32 len)\r | |
628 | {\r | |
629 | int32 exta;\r | |
630 | \r | |
631 | if (spec <= 07) {\r | |
632 | F_LOAD_P (len == QUAD, FR[spec], fptr);\r | |
633 | return;\r | |
634 | }\r | |
635 | if (spec == 027) {\r | |
636 | fptr->h = (ReadW (VA) << FP_V_F0);\r | |
637 | fptr->l = 0;\r | |
638 | }\r | |
639 | else {\r | |
640 | exta = VA & ~0177777;\r | |
641 | fptr->h = (ReadW (VA) << FP_V_F0) |\r | |
642 | (ReadW (exta | ((VA + 2) & 0177777)) << FP_V_F1);\r | |
643 | if (len == QUAD) fptr->l = \r | |
644 | (ReadW (exta | ((VA + 4) & 0177777)) << FP_V_F2) |\r | |
645 | (ReadW (exta | ((VA + 6) & 0177777)) << FP_V_F3);\r | |
646 | else fptr->l = 0;\r | |
647 | }\r | |
648 | if ((GET_SIGN (fptr->h) != 0) && (GET_EXP (fptr->h) == 0) &&\r | |
649 | (fpnotrap (FEC_UNDFV) == 0)) ABORT (TRAP_INT);\r | |
650 | return;\r | |
651 | }\r | |
652 | \r | |
653 | /* Write integer result\r | |
654 | \r | |
655 | Inputs:\r | |
656 | data = data to be written\r | |
657 | VA = virtual address, VA<18:16> = mode, I/D space\r | |
658 | spec = specifier\r | |
659 | len = length\r | |
660 | Outputs: none\r | |
661 | */\r | |
662 | \r | |
663 | void WriteI (int32 data, int32 VA, int32 spec, int32 len)\r | |
664 | {\r | |
665 | WriteW ((data >> 16) & 0177777, VA);\r | |
666 | if ((len == WORD) || (spec == 027)) return;\r | |
667 | WriteW (data & 0177777, (VA & ~0177777) | ((VA + 2) & 0177777));\r | |
668 | return;\r | |
669 | }\r | |
670 | \r | |
671 | /* Write floating result\r | |
672 | \r | |
673 | Inputs:\r | |
674 | fptr = pointer to data to be written\r | |
675 | VA = virtual address, VA<18:16> = mode, I/D space\r | |
676 | spec = specifier\r | |
677 | len = length\r | |
678 | Outputs: none\r | |
679 | */\r | |
680 | \r | |
681 | void WriteFP (fpac_t *fptr, int32 VA, int32 spec, int32 len)\r | |
682 | {\r | |
683 | int32 exta;\r | |
684 | \r | |
685 | if (spec <= 07) {\r | |
686 | F_STORE_P (len == QUAD, fptr, FR[spec]);\r | |
687 | return;\r | |
688 | }\r | |
689 | WriteW ((fptr->h >> FP_V_F0) & 0177777, VA);\r | |
690 | if (spec == 027) return;\r | |
691 | exta = VA & ~0177777;\r | |
692 | WriteW ((fptr->h >> FP_V_F1) & 0177777, exta | ((VA + 2) & 0177777));\r | |
693 | if (len == LONG) return;\r | |
694 | WriteW ((fptr->l >> FP_V_F2) & 0177777, exta | ((VA + 4) & 0177777));\r | |
695 | WriteW ((fptr->l >> FP_V_F3) & 0177777, exta | ((VA + 6) & 0177777));\r | |
696 | return;\r | |
697 | }\r | |
698 | \r | |
699 | /* FIS instructions */\r | |
700 | \r | |
701 | t_stat fis11 (int32 IR)\r | |
702 | {\r | |
703 | int32 reg, exta;\r | |
704 | fpac_t fac, fsrc;\r | |
705 | \r | |
706 | reg = IR & 07; /* isolate reg */\r | |
707 | if (reg == 7) exta = isenable; /* choose I,D */\r | |
708 | else exta = dsenable;\r | |
709 | if (IR & 000740) { /* defined? */\r | |
710 | if (CPUT (CPUT_03)) ReadW (exta | R[reg]); /* 11/03 reads word */\r | |
711 | ABORT (TRAP_ILL);\r | |
712 | }\r | |
713 | FEC = 0; /* no errors */\r | |
714 | FPS = FPS_IU|FPS_IV; /* trap ovf,unf */\r | |
715 | \r | |
716 | fsrc.h = (ReadW (exta | R[reg]) << FP_V_F0) |\r | |
717 | (ReadW (exta | ((R[reg] + 2) & 0177777)) << FP_V_F1);\r | |
718 | fsrc.l = 0;\r | |
719 | fac.h = (ReadW (exta | ((R[reg] + 4) & 0177777)) << FP_V_F0) |\r | |
720 | (ReadW (exta | ((R[reg] + 6) & 0177777)) << FP_V_F1);\r | |
721 | fac.l = 0;\r | |
722 | if (GET_SIGN (fsrc.h) && (GET_EXP (fsrc.h) == 0)) /* clean 0's */\r | |
723 | fsrc.h = fsrc.l = 0;\r | |
724 | if (GET_SIGN (fac.h) && (GET_EXP (fac.l) == 0))\r | |
725 | fac.h = fac.l = 0;\r | |
726 | \r | |
727 | N = Z = V = C = 0; /* clear cc's */\r | |
728 | switch ((IR >> 3) & 3) { /* case IR<5:3> */\r | |
729 | \r | |
730 | case 0: /* FAD */\r | |
731 | addfp11 (&fac, &fsrc);\r | |
732 | break;\r | |
733 | \r | |
734 | case 1: /* FSUB */\r | |
735 | if (fsrc.h != 0) fsrc.h = fsrc.h ^ FP_SIGN; /* invert sign */\r | |
736 | addfp11 (&fac, &fsrc);\r | |
737 | break;\r | |
738 | \r | |
739 | case 2: /* FMUL */\r | |
740 | mulfp11 (&fac, &fsrc);\r | |
741 | break;\r | |
742 | \r | |
743 | case 3: /* FDIV */\r | |
744 | if (fsrc.h == 0) { /* div by 0? */\r | |
745 | V = N = C = 1; /* set cc's */\r | |
746 | setTRAP (TRAP_FPE); /* set trap */\r | |
747 | return SCPE_OK;\r | |
748 | }\r | |
749 | else divfp11 (&fac, &fsrc);\r | |
750 | break;\r | |
751 | }\r | |
752 | \r | |
753 | if (FEC == 0) { /* no err? */\r | |
754 | WriteW ((fac.h >> FP_V_F0) & 0177777, exta | ((R[reg] + 4) & 0177777));\r | |
755 | WriteW ((fac.h >> FP_V_F1) & 0177777, exta | ((R[reg] + 6) & 0177777));\r | |
756 | R[reg] = (R[reg] + 4) & 0177777; /* pop stack */\r | |
757 | N = (GET_SIGN (fac.h) != 0); /* set N,Z */\r | |
758 | Z = (fac.h == 0);\r | |
759 | }\r | |
760 | else if (FEC == FEC_OVFLO) V = 1; /* ovf? trap set */\r | |
761 | else if (FEC == FEC_UNFLO) V = N = 1; /* unf? trap set */\r | |
762 | else return SCPE_IERR; /* what??? */\r | |
763 | return SCPE_OK;\r | |
764 | }\r | |
765 | \r | |
766 | /* Floating point add\r | |
767 | \r | |
768 | Inputs:\r | |
769 | facp = pointer to src1 (output)\r | |
770 | fsrcp = pointer to src2\r | |
771 | Outputs:\r | |
772 | ovflo = overflow variable\r | |
773 | */\r | |
774 | \r | |
775 | int32 addfp11 (fpac_t *facp, fpac_t *fsrcp)\r | |
776 | {\r | |
777 | int32 facexp, fsrcexp, ediff;\r | |
778 | fpac_t facfrac, fsrcfrac;\r | |
779 | \r | |
780 | if (F_LT_AP (facp, fsrcp)) { /* if !fac! < !fsrc! */\r | |
781 | facfrac = *facp;\r | |
782 | *facp = *fsrcp; /* swap operands */\r | |
783 | *fsrcp = facfrac;\r | |
784 | }\r | |
785 | facexp = GET_EXP (facp->h); /* get exponents */\r | |
786 | fsrcexp = GET_EXP (fsrcp->h);\r | |
787 | if (facexp == 0) { /* fac = 0? */\r | |
788 | *facp = fsrcexp? *fsrcp: zero_fac; /* result fsrc or 0 */\r | |
789 | return 0;\r | |
790 | }\r | |
791 | if (fsrcexp == 0) return 0; /* fsrc = 0? no op */\r | |
792 | ediff = facexp - fsrcexp; /* exponent diff */\r | |
793 | if (ediff >= 60) return 0; /* too big? no op */\r | |
794 | F_GET_FRAC_P (facp, facfrac); /* get fractions */\r | |
795 | F_GET_FRAC_P (fsrcp, fsrcfrac);\r | |
796 | F_LSH_GUARD (facfrac); /* guard fractions */\r | |
797 | F_LSH_GUARD (fsrcfrac);\r | |
798 | if (GET_SIGN (facp->h) != GET_SIGN (fsrcp->h)) { /* signs different? */\r | |
799 | if (ediff) { F_RSH_V (fsrcfrac, ediff, fsrcfrac); } /* sub, shf fsrc */\r | |
800 | F_SUB (fsrcfrac, facfrac, facfrac); /* sub fsrc from fac */\r | |
801 | if ((facfrac.h | facfrac.l) == 0) { /* result zero? */\r | |
802 | *facp = zero_fac; /* no overflow */\r | |
803 | return 0;\r | |
804 | }\r | |
805 | if (ediff <= 1) { /* big normalize? */\r | |
806 | if ((facfrac.h & (0x00FFFFFF << FP_GUARD)) == 0) {\r | |
807 | F_LSH_K (facfrac, 24, facfrac);\r | |
808 | facexp = facexp - 24;\r | |
809 | }\r | |
810 | if ((facfrac.h & (0x00FFF000 << FP_GUARD)) == 0) {\r | |
811 | F_LSH_K (facfrac, 12, facfrac);\r | |
812 | facexp = facexp - 12;\r | |
813 | }\r | |
814 | if ((facfrac.h & (0x00FC0000 << FP_GUARD)) == 0) {\r | |
815 | F_LSH_K (facfrac, 6, facfrac);\r | |
816 | facexp = facexp - 6;\r | |
817 | }\r | |
818 | }\r | |
819 | while (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) {\r | |
820 | F_LSH_1 (facfrac);\r | |
821 | facexp = facexp - 1;\r | |
822 | }\r | |
823 | }\r | |
824 | else {\r | |
825 | if (ediff) {\r | |
826 | F_RSH_V (fsrcfrac, ediff, fsrcfrac); /* add, shf fsrc */\r | |
827 | }\r | |
828 | F_ADD (fsrcfrac, facfrac, facfrac); /* add fsrc to fac */\r | |
829 | if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD + 1)) {\r | |
830 | F_RSH_1 (facfrac); /* carry out, shift */\r | |
831 | facexp = facexp + 1;\r | |
832 | }\r | |
833 | }\r | |
834 | return round_and_pack (facp, facexp, &facfrac, 1);\r | |
835 | }\r | |
836 | \r | |
837 | /* Floating point multiply\r | |
838 | \r | |
839 | Inputs:\r | |
840 | facp = pointer to src1 (output)\r | |
841 | fsrcp = pointer to src2\r | |
842 | Outputs:\r | |
843 | ovflo = overflow indicator\r | |
844 | */\r | |
845 | \r | |
846 | int32 mulfp11 (fpac_t *facp, fpac_t *fsrcp)\r | |
847 | {\r | |
848 | int32 facexp, fsrcexp;\r | |
849 | fpac_t facfrac, fsrcfrac;\r | |
850 | \r | |
851 | facexp = GET_EXP (facp->h); /* get exponents */\r | |
852 | fsrcexp = GET_EXP (fsrcp->h);\r | |
853 | if ((facexp == 0) || (fsrcexp == 0)) { /* test for zero */\r | |
854 | *facp = zero_fac;\r | |
855 | return 0;\r | |
856 | }\r | |
857 | F_GET_FRAC_P (facp, facfrac); /* get fractions */\r | |
858 | F_GET_FRAC_P (fsrcp, fsrcfrac);\r | |
859 | facexp = facexp + fsrcexp - FP_BIAS; /* calculate exp */\r | |
860 | facp->h = facp->h ^ fsrcp->h; /* calculate sign */\r | |
861 | frac_mulfp11 (&facfrac, &fsrcfrac); /* multiply fracs */\r | |
862 | \r | |
863 | /* Multiplying two numbers in the range [.5,1) produces a result in the\r | |
864 | range [.25,1). Therefore, at most one bit of normalization is required\r | |
865 | to bring the result back to the range [.5,1).\r | |
866 | */\r | |
867 | \r | |
868 | if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) {\r | |
869 | F_LSH_1 (facfrac);\r | |
870 | facexp = facexp - 1;\r | |
871 | }\r | |
872 | return round_and_pack (facp, facexp, &facfrac, 1);\r | |
873 | }\r | |
874 | \r | |
875 | /* Floating point mod\r | |
876 | \r | |
877 | Inputs:\r | |
878 | facp = pointer to src1 (integer result)\r | |
879 | fsrcp = pointer to src2\r | |
880 | fracp = pointer to fractional result\r | |
881 | Outputs:\r | |
882 | ovflo = overflow indicator\r | |
883 | \r | |
884 | See notes on multiply for initial operation\r | |
885 | */\r | |
886 | \r | |
887 | int32 modfp11 (fpac_t *facp, fpac_t *fsrcp, fpac_t *fracp)\r | |
888 | {\r | |
889 | int32 facexp, fsrcexp;\r | |
890 | fpac_t facfrac, fsrcfrac, fmask;\r | |
891 | \r | |
892 | facexp = GET_EXP (facp->h); /* get exponents */\r | |
893 | fsrcexp = GET_EXP (fsrcp->h);\r | |
894 | if ((facexp == 0) || (fsrcexp == 0)) { /* test for zero */\r | |
895 | *fracp = zero_fac;\r | |
896 | *facp = zero_fac;\r | |
897 | return 0;\r | |
898 | }\r | |
899 | F_GET_FRAC_P (facp, facfrac); /* get fractions */\r | |
900 | F_GET_FRAC_P (fsrcp, fsrcfrac);\r | |
901 | facexp = facexp + fsrcexp - FP_BIAS; /* calculate exp */\r | |
902 | fracp->h = facp->h = facp->h ^ fsrcp->h; /* calculate sign */\r | |
903 | frac_mulfp11 (&facfrac, &fsrcfrac); /* multiply fracs */\r | |
904 | \r | |
905 | /* Multiplying two numbers in the range [.5,1) produces a result in the\r | |
906 | range [.25,1). Therefore, at most one bit of normalization is required\r | |
907 | to bring the result back to the range [.5,1).\r | |
908 | */\r | |
909 | \r | |
910 | if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) {\r | |
911 | F_LSH_1 (facfrac);\r | |
912 | facexp = facexp - 1;\r | |
913 | }\r | |
914 | \r | |
915 | /* There are three major cases of MODf:\r | |
916 | \r | |
917 | 1. Exp <= FP_BIAS (all fraction). Return 0 as integer, product as\r | |
918 | fraction. Underflow can occur.\r | |
919 | 2. Exp > FP_BIAS + #fraction bits (all integer). Return product as\r | |
920 | integer, 0 as fraction. Overflow can occur.\r | |
921 | 3. FP_BIAS < exp <= FP_BIAS + #fraction bits. Separate integer and\r | |
922 | fraction and return both. Neither overflow nor underflow can occur.\r | |
923 | */\r | |
924 | \r | |
925 | if (facexp <= FP_BIAS) { /* case 1 */\r | |
926 | *facp = zero_fac;\r | |
927 | return round_and_pack (fracp, facexp, &facfrac, 1);\r | |
928 | }\r | |
929 | if (facexp > ((FPS & FPS_D)? FP_BIAS + 56: FP_BIAS + 24)) {\r | |
930 | *fracp = zero_fac; /* case 2 */\r | |
931 | return round_and_pack (facp, facexp, &facfrac, 0);\r | |
932 | }\r | |
933 | F_RSH_V (fmask_fac, facexp - FP_BIAS, fmask); /* shift mask */\r | |
934 | fsrcfrac.l = facfrac.l & fmask.l; /* extract fraction */\r | |
935 | fsrcfrac.h = facfrac.h & fmask.h;\r | |
936 | if ((fsrcfrac.h | fsrcfrac.l) == 0) *fracp = zero_fac;\r | |
937 | else {\r | |
938 | F_LSH_V (fsrcfrac, facexp - FP_BIAS, fsrcfrac);\r | |
939 | fsrcexp = FP_BIAS;\r | |
940 | if ((fsrcfrac.h & (0x00FFFFFF << FP_GUARD)) == 0) {\r | |
941 | F_LSH_K (fsrcfrac, 24, fsrcfrac);\r | |
942 | fsrcexp = fsrcexp - 24;\r | |
943 | }\r | |
944 | if ((fsrcfrac.h & (0x00FFF000 << FP_GUARD)) == 0) {\r | |
945 | F_LSH_K (fsrcfrac, 12, fsrcfrac);\r | |
946 | fsrcexp = fsrcexp - 12;\r | |
947 | }\r | |
948 | if ((fsrcfrac.h & (0x00FC0000 << FP_GUARD)) == 0) {\r | |
949 | F_LSH_K (fsrcfrac, 6, fsrcfrac);\r | |
950 | fsrcexp = fsrcexp - 6;\r | |
951 | }\r | |
952 | while (GET_BIT (fsrcfrac.h, FP_V_HB + FP_GUARD) == 0) {\r | |
953 | F_LSH_1 (fsrcfrac);\r | |
954 | fsrcexp = fsrcexp - 1;\r | |
955 | }\r | |
956 | round_and_pack (fracp, fsrcexp, &fsrcfrac, 1);\r | |
957 | }\r | |
958 | facfrac.l = facfrac.l & ~fmask.l;\r | |
959 | facfrac.h = facfrac.h & ~fmask.h;\r | |
960 | return round_and_pack (facp, facexp, &facfrac, 0);\r | |
961 | }\r | |
962 | \r | |
963 | /* Fraction multiply\r | |
964 | \r | |
965 | Inputs:\r | |
966 | f1p = pointer to multiplier (output)\r | |
967 | f2p = pointer to multiplicand fraction\r | |
968 | \r | |
969 | Note: the inputs are unguarded; the output is guarded.\r | |
970 | \r | |
971 | This routine performs a classic shift-and-add multiply. The low\r | |
972 | order bit of the multiplier is tested; if 1, the multiplicand is\r | |
973 | added into the high part of the double precision result. The\r | |
974 | result and the multiplier are both shifted right 1.\r | |
975 | \r | |
976 | For the 24b x 24b case, this routine develops 48b of result.\r | |
977 | For the 56b x 56b case, this routine only develops the top 64b\r | |
978 | of the the result. Because the inputs are normalized fractions,\r | |
979 | the interesting part of the result is the high 56+guard bits.\r | |
980 | Everything shifted off to the right, beyond 64b, plays no part\r | |
981 | in rounding or the result.\r | |
982 | \r | |
983 | There are many possible optimizations in this routine: scanning\r | |
984 | for groups of zeroes, particularly in the 56b x 56b case; using\r | |
985 | "extended multiply" capability if available in the hardware.\r | |
986 | */\r | |
987 | \r | |
988 | void frac_mulfp11 (fpac_t *f1p, fpac_t *f2p)\r | |
989 | {\r | |
990 | fpac_t result, mpy, mpc;\r | |
991 | int32 i;\r | |
992 | \r | |
993 | result = zero_fac; /* clear result */\r | |
994 | mpy = *f1p; /* get operands */\r | |
995 | mpc = *f2p;\r | |
996 | F_LSH_GUARD (mpc); /* guard multipicand */\r | |
997 | if ((mpy.l | mpc.l) == 0) { /* 24b x 24b? */\r | |
998 | for (i = 0; i < 24; i++) {\r | |
999 | if (mpy.h & 1) result.h = result.h + mpc.h;\r | |
1000 | F_RSH_1 (result);\r | |
1001 | mpy.h = mpy.h >> 1;\r | |
1002 | }\r | |
1003 | }\r | |
1004 | else {\r | |
1005 | if (mpy.l != 0) { /* 24b x 56b? */\r | |
1006 | for (i = 0; i < 32; i++) {\r | |
1007 | if (mpy.l & 1) {\r | |
1008 | F_ADD (mpc, result, result);\r | |
1009 | }\r | |
1010 | F_RSH_1 (result);\r | |
1011 | mpy.l = mpy.l >> 1;\r | |
1012 | }\r | |
1013 | }\r | |
1014 | for (i = 0; i < 24; i++) {\r | |
1015 | if (mpy.h & 1) {\r | |
1016 | F_ADD (mpc, result, result);\r | |
1017 | }\r | |
1018 | F_RSH_1 (result);\r | |
1019 | mpy.h = mpy.h >> 1;\r | |
1020 | }\r | |
1021 | }\r | |
1022 | *f1p = result;\r | |
1023 | return;\r | |
1024 | }\r | |
1025 | \r | |
1026 | /* Floating point divide\r | |
1027 | \r | |
1028 | Inputs:\r | |
1029 | facp = pointer to dividend (output)\r | |
1030 | fsrcp = pointer to divisor\r | |
1031 | Outputs:\r | |
1032 | ovflo = overflow indicator\r | |
1033 | \r | |
1034 | Source operand must be checked for zero by caller!\r | |
1035 | */\r | |
1036 | \r | |
1037 | int32 divfp11 (fpac_t *facp, fpac_t *fsrcp)\r | |
1038 | {\r | |
1039 | int32 facexp, fsrcexp, i, count, qd;\r | |
1040 | fpac_t facfrac, fsrcfrac, quo;\r | |
1041 | \r | |
1042 | fsrcexp = GET_EXP (fsrcp->h); /* get divisor exp */\r | |
1043 | facexp = GET_EXP (facp->h); /* get dividend exp */\r | |
1044 | if (facexp == 0) { /* test for zero */\r | |
1045 | *facp = zero_fac; /* result zero */\r | |
1046 | return 0;\r | |
1047 | }\r | |
1048 | F_GET_FRAC_P (facp, facfrac); /* get fractions */\r | |
1049 | F_GET_FRAC_P (fsrcp, fsrcfrac);\r | |
1050 | F_LSH_GUARD (facfrac); /* guard fractions */\r | |
1051 | F_LSH_GUARD (fsrcfrac);\r | |
1052 | facexp = facexp - fsrcexp + FP_BIAS + 1; /* calculate exp */\r | |
1053 | facp->h = facp->h ^ fsrcp->h; /* calculate sign */\r | |
1054 | qd = FPS & FPS_D;\r | |
1055 | count = FP_V_HB + FP_GUARD + (qd? 33: 1); /* count = 56b/24b */\r | |
1056 | \r | |
1057 | quo = zero_fac;\r | |
1058 | for (i = count; (i > 0) && ((facfrac.h | facfrac.l) != 0); i--) {\r | |
1059 | F_LSH_1 (quo); /* shift quotient */\r | |
1060 | if (!F_LT (facfrac, fsrcfrac)) { /* divd >= divr? */\r | |
1061 | F_SUB (fsrcfrac, facfrac, facfrac); /* divd - divr */\r | |
1062 | if (qd) quo.l = quo.l | 1; /* double or single? */\r | |
1063 | else quo.h = quo.h | 1;\r | |
1064 | }\r | |
1065 | F_LSH_1 (facfrac); /* shift divd */\r | |
1066 | }\r | |
1067 | if (i > 0) { /* early exit? */\r | |
1068 | F_LSH_V (quo, i, quo);\r | |
1069 | }\r | |
1070 | \r | |
1071 | /* Dividing two numbers in the range [.5,1) produces a result in the\r | |
1072 | range [.5,2). Therefore, at most one bit of normalization is required\r | |
1073 | to bring the result back to the range [.5,1). The choice of counts\r | |
1074 | and quotient bit positions makes this work correctly.\r | |
1075 | */\r | |
1076 | \r | |
1077 | if (GET_BIT (quo.h, FP_V_HB + FP_GUARD) == 0) {\r | |
1078 | F_LSH_1 (quo);\r | |
1079 | facexp = facexp - 1;\r | |
1080 | }\r | |
1081 | return round_and_pack (facp, facexp, &quo, 1);\r | |
1082 | }\r | |
1083 | \r | |
1084 | /* Update floating condition codes\r | |
1085 | Note that FC is only set by STCfi via the integer condition codes\r | |
1086 | \r | |
1087 | Inputs:\r | |
1088 | oldst = current status\r | |
1089 | result = high result\r | |
1090 | newV = new V\r | |
1091 | Outputs:\r | |
1092 | newst = new status\r | |
1093 | */\r | |
1094 | \r | |
1095 | int32 setfcc (int32 oldst, int32 result, int32 newV)\r | |
1096 | {\r | |
1097 | oldst = (oldst & ~FPS_CC) | newV;\r | |
1098 | if (GET_SIGN (result)) oldst = oldst | FPS_N;\r | |
1099 | if (GET_EXP (result) == 0) oldst = oldst | FPS_Z;\r | |
1100 | return oldst;\r | |
1101 | }\r | |
1102 | \r | |
1103 | /* Round (in place) floating point number to f_floating\r | |
1104 | \r | |
1105 | Inputs:\r | |
1106 | fptr = pointer to floating number\r | |
1107 | Outputs:\r | |
1108 | ovflow = overflow\r | |
1109 | */\r | |
1110 | \r | |
1111 | int32 roundfp11 (fpac_t *fptr)\r | |
1112 | {\r | |
1113 | fpac_t outf;\r | |
1114 | \r | |
1115 | outf = *fptr; /* get argument */\r | |
1116 | F_ADD (fround_fac, outf, outf); /* round */\r | |
1117 | if (GET_SIGN (outf.h ^ fptr->h)) { /* flipped sign? */\r | |
1118 | outf.h = (outf.h ^ FP_SIGN) & 0xFFFFFFFF; /* restore sign */\r | |
1119 | if (fpnotrap (FEC_OVFLO)) *fptr = zero_fac; /* if no int, clear */\r | |
1120 | else *fptr = outf; /* return rounded */\r | |
1121 | return FPS_V; /* overflow */\r | |
1122 | }\r | |
1123 | *fptr = outf; /* round was ok */\r | |
1124 | return 0; /* no overflow */\r | |
1125 | }\r | |
1126 | \r | |
1127 | /* Round result of calculation, test overflow, pack\r | |
1128 | \r | |
1129 | Input:\r | |
1130 | facp = pointer to result, sign in place\r | |
1131 | exp = result exponent, right justified\r | |
1132 | fracp = pointer to result fraction, right justified with\r | |
1133 | guard bits\r | |
1134 | r = round (1) or truncate (0)\r | |
1135 | Outputs:\r | |
1136 | ovflo = overflow indicator\r | |
1137 | */\r | |
1138 | \r | |
1139 | int32 round_and_pack (fpac_t *facp, int32 exp, fpac_t *fracp, int r)\r | |
1140 | {\r | |
1141 | fpac_t frac;\r | |
1142 | \r | |
1143 | frac = *fracp; /* get fraction */\r | |
1144 | if (r && ((FPS & FPS_T) == 0)) { \r | |
1145 | if (FPS & FPS_D) {\r | |
1146 | F_ADD (dround_guard_fac, frac, frac);\r | |
1147 | }\r | |
1148 | else {\r | |
1149 | F_ADD (fround_guard_fac, frac, frac);\r | |
1150 | }\r | |
1151 | if (GET_BIT (frac.h, FP_V_HB + FP_GUARD + 1)) {\r | |
1152 | F_RSH_1 (frac);\r | |
1153 | exp = exp + 1;\r | |
1154 | }\r | |
1155 | }\r | |
1156 | F_RSH_GUARD (frac);\r | |
1157 | facp->l = frac.l & FP_FRACL;\r | |
1158 | facp->h = (facp->h & FP_SIGN) | ((exp & FP_M_EXP) << FP_V_EXP) |\r | |
1159 | (frac.h & FP_FRACH);\r | |
1160 | if (exp > 0377) {\r | |
1161 | if (fpnotrap (FEC_OVFLO)) *facp = zero_fac;\r | |
1162 | return FPS_V;\r | |
1163 | }\r | |
1164 | if ((exp <= 0) && (fpnotrap (FEC_UNFLO))) *facp = zero_fac;\r | |
1165 | return 0;\r | |
1166 | }\r | |
1167 | \r | |
1168 | /* Process floating point exception\r | |
1169 | \r | |
1170 | Inputs:\r | |
1171 | code = exception code\r | |
1172 | Outputs:\r | |
1173 | int = FALSE if interrupt enabled, TRUE if disabled\r | |
1174 | */\r | |
1175 | \r | |
1176 | int32 fpnotrap (int32 code)\r | |
1177 | {\r | |
1178 | static const int32 test_code[] = { 0, 0, 0, FPS_IC, FPS_IV, FPS_IU, FPS_IUV };\r | |
1179 | \r | |
1180 | if ((code >= FEC_ICVT) && (code <= FEC_UNDFV) &&\r | |
1181 | ((FPS & test_code[code >> 1]) == 0)) return TRUE;\r | |
1182 | FPS = FPS | FPS_ER;\r | |
1183 | FEC = code;\r | |
1184 | FEA = (backup_PC - 2) & 0177777;\r | |
1185 | if ((FPS & FPS_ID) == 0) setTRAP (TRAP_FPE);\r | |
1186 | return FALSE;\r | |
1187 | }\r |