| 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 |