| /* i7094_cpu1.c: IBM 7094 CPU complex instructions | |
| Copyright (c) 2003-2008, Robert M. Supnik | |
| Permission is hereby granted, free of charge, to any person obtaining a | |
| copy of this software and associated documentation files (the "Software"), | |
| to deal in the Software without restriction, including without limitation | |
| the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| and/or sell copies of the Software, and to permit persons to whom the | |
| Software is furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in | |
| all copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| Except as contained in this notice, the name of Robert M Supnik shall not be | |
| used in advertising or otherwise to promote the sale, use or other dealings | |
| in this Software without prior written authorization from Robert M Supnik. | |
| */ | |
| #include "i7094_defs.h" | |
| #define FP_HIFRAC(x) ((uint32) ((x) >> FP_N_FR) & FP_FMASK) | |
| #define FP_LOFRAC(x) ((uint32) (x) & FP_FMASK) | |
| #define FP_PACK38(s,e,f) (((s)? AC_S: 0) | ((t_uint64) (f)) | \ | |
| (((t_uint64) ((e) & FP_M_ACCH)) << FP_V_CH)) | |
| #define FP_PACK36(s,e,f) (((s)? SIGN: 0) | ((t_uint64) (f)) | \ | |
| (((t_uint64) ((e) & FP_M_CH)) << FP_V_CH)) | |
| extern t_uint64 AC, MQ, SI, KEYS; | |
| extern uint32 PC; | |
| extern uint32 SLT, SSW; | |
| extern uint32 cpu_model, stop_illop; | |
| extern uint32 ind_ovf, ind_dvc, ind_ioc, ind_mqo; | |
| extern uint32 mode_ttrap, mode_strap, mode_ctrap, mode_ftrap; | |
| extern uint32 mode_storn, mode_multi; | |
| extern uint32 chtr_pend, chtr_inht, chtr_inhi; | |
| extern uint32 ch_flags[NUM_CHAN]; | |
| typedef struct { /* unpacked fp */ | |
| uint32 s; /* sign: 0 +, 1 - */ | |
| int32 ch; /* exponent */ | |
| t_uint64 fr; /* fraction (54b) */ | |
| } UFP; | |
| uint32 op_frnd (void); | |
| t_uint64 fp_fracdiv (t_uint64 dvd, t_uint64 dvr, t_uint64 *rem); | |
| void fp_norm (UFP *op); | |
| void fp_unpack (t_uint64 h, t_uint64 l, t_bool q_ac, UFP *op); | |
| uint32 fp_pack (UFP *op, uint32 mqs, int32 mqch); | |
| extern t_bool fp_trap (uint32 spill); | |
| extern t_bool sel_trap (uint32 va); | |
| extern t_stat ch_op_reset (uint32 ch, t_bool ch7909); | |
| /* Integer add | |
| Sherman: "As the result of an addition or subtraction, if the C(AC) is | |
| zero, the sign of AC is unchanged." */ | |
| void op_add (t_uint64 op) | |
| { | |
| t_uint64 mac = AC & AC_MMASK; /* get magnitudes */ | |
| t_uint64 mop = op & MMASK; | |
| AC = AC & AC_S; /* isolate AC sign */ | |
| if ((AC? 1: 0) ^ ((op & SIGN)? 1: 0)) { /* signs diff? sub */ | |
| if (mac >= mop) /* AC >= MQ */ | |
| AC = AC | (mac - mop); | |
| else AC = (AC ^ AC_S) | (mop - mac); /* <, sign change */ | |
| } | |
| else { | |
| AC = AC | ((mac + mop) & AC_MMASK); /* signs same, add */ | |
| if ((AC ^ mac) & AC_P) /* P change? overflow */ | |
| ind_ovf = 1; | |
| } | |
| return; | |
| } | |
| /* Multiply */ | |
| void op_mpy (t_uint64 ac, t_uint64 sr, uint32 sc) | |
| { | |
| uint32 sign; | |
| if (sc == 0) /* sc = 0? nop */ | |
| return; | |
| sign = ((MQ & SIGN)? 1: 0) ^ ((sr & SIGN)? 1: 0); /* result sign */ | |
| ac = ac & AC_MMASK; /* clear AC sign */ | |
| sr = sr & MMASK; /* mpy magnitude */ | |
| MQ = MQ & MMASK; /* MQ magnitude */ | |
| if (sr && MQ) { /* mpy != 0? */ | |
| while (sc--) { /* for sc */ | |
| if (MQ & 1) /* MQ35? AC += mpy */ | |
| ac = (ac + sr) & AC_MMASK; | |
| MQ = (MQ >> 1) | ((ac & 1) << 34); /* AC'MQ >> 1 */ | |
| ac = ac >> 1; | |
| } | |
| } | |
| else ac = MQ = 0; /* result = 0 */ | |
| if (sign) { /* negative? */ | |
| ac = ac | AC_S; /* insert signs */ | |
| MQ = MQ | SIGN; | |
| } | |
| AC = ac; /* update AC */ | |
| return; | |
| } | |
| /* Divide */ | |
| t_bool op_div (t_uint64 sr, uint32 sc) | |
| { | |
| uint32 signa, signm; | |
| if (sc == 0) /* sc = 0? nop */ | |
| return FALSE; | |
| signa = (AC & AC_S)? 1: 0; /* get signs */ | |
| signm = (sr & SIGN)? 1: 0; | |
| sr = sr & MMASK; /* get dvr magn */ | |
| if ((AC & AC_MMASK) >= sr) /* |AC| >= |sr|? */ | |
| return TRUE; | |
| AC = AC & AC_MMASK; /* AC, MQ magn */ | |
| MQ = MQ & MMASK; | |
| while (sc--) { /* for sc */ | |
| AC = ((AC << 1) & AC_MMASK) | (MQ >> 34); /* AC'MQ << 1 */ | |
| MQ = (MQ << 1) & MMASK; | |
| if (AC >= sr) { /* AC >= dvr? */ | |
| AC = AC - sr; /* AC -= dvr */ | |
| MQ = MQ | 1; /* set quo bit */ | |
| } | |
| } | |
| if (signa ^ signm) /* quo neg? */ | |
| MQ = MQ | SIGN; | |
| if (signa) /* rem neg? */ | |
| AC = AC | AC_S; | |
| return FALSE; /* div ok */ | |
| } | |
| /* Shifts */ | |
| void op_als (uint32 addr) | |
| { | |
| uint32 sc = addr & SCMASK; | |
| if ((sc >= 35)? /* shift >= 35? */ | |
| ((AC & MMASK) != 0): /* test all bits for ovf */ | |
| (((AC & MMASK) >> (35 - sc)) != 0)) /* test only 35-sc bits */ | |
| ind_ovf = 1; | |
| if (sc >= 37) /* sc >= 37? result 0 */ | |
| AC = AC & AC_S; | |
| else AC = (AC & AC_S) | ((AC << sc) & AC_MMASK); /* shift, save sign */ | |
| return; | |
| } | |
| void op_ars (uint32 addr) | |
| { | |
| uint32 sc = addr & SCMASK; | |
| if (sc >= 37) /* sc >= 37? result 0 */ | |
| AC = AC & AC_S; | |
| else AC = (AC & AC_S) | ((AC & AC_MMASK) >> sc); /* shift, save sign */ | |
| return; | |
| } | |
| void op_lls (uint32 addr) | |
| { | |
| uint32 sc; /* get sc */ | |
| AC = AC & AC_MMASK; /* clear AC sign */ | |
| for (sc = addr & SCMASK; sc != 0; sc--) { /* for SC */ | |
| AC = ((AC << 1) & AC_MMASK) | ((MQ >> 34) & 1); /* AC'MQ << 1 */ | |
| MQ = (MQ & SIGN) | ((MQ << 1) & MMASK); /* preserve MQ sign */ | |
| if (AC & AC_P) /* if P, overflow */ | |
| ind_ovf = 1; | |
| } | |
| if (MQ & SIGN) /* set ACS from MQS */ | |
| AC = AC | AC_S; | |
| return; | |
| } | |
| void op_lrs (uint32 addr) | |
| { | |
| uint32 sc = addr & SCMASK; | |
| t_uint64 mac; | |
| MQ = MQ & MMASK; /* get MQ magnitude */ | |
| if (sc != 0) { | |
| mac = AC & AC_MMASK; /* get AC magnitude, */ | |
| AC = AC & AC_S; /* sign */ | |
| if (sc < 35) { /* sc [1,34]? */ | |
| MQ = ((MQ >> sc) | (mac << (35 - sc))) & MMASK; /* MQ has AC'MQ */ | |
| AC = AC | (mac >> sc); /* AC has AC only */ | |
| } | |
| else if (sc < 37) { /* sc [35:36]? */ | |
| MQ = (mac >> (sc - 35)) & MMASK; /* MQ has AC only */ | |
| AC = AC | (mac >> sc); /* AC has <QP> */ | |
| } | |
| else if (sc < 72) /* sc [37:71]? */ | |
| MQ = (mac >> (sc - 35)) & MMASK; /* MQ has AC only */ | |
| else MQ = 0; /* >72? MQ = 0 */ | |
| } | |
| if (AC & AC_S) /* set MQS from ACS */ | |
| MQ = MQ | SIGN; | |
| return; | |
| } | |
| void op_lgl (uint32 addr) | |
| { | |
| uint32 sc; /* get sc */ | |
| for (sc = addr & SCMASK; sc != 0; sc--) { /* for SC */ | |
| AC = (AC & AC_S) | ((AC << 1) & AC_MMASK) | /* AC'MQ << 1 */ | |
| ((MQ >> 35) & 1); /* preserve AC sign */ | |
| MQ = (MQ << 1) & DMASK; | |
| if (AC & AC_P) /* if P, overflow */ | |
| ind_ovf = 1; | |
| } | |
| return; | |
| } | |
| void op_lgr (uint32 addr) | |
| { | |
| uint32 sc = addr & SCMASK; | |
| t_uint64 mac; | |
| if (sc != 0) { | |
| mac = AC & AC_MMASK; /* get AC magnitude, */ | |
| AC = AC & AC_S; /* sign */ | |
| if (sc < 36) { /* sc [1,35]? */ | |
| MQ = ((MQ >> sc) | (mac << (36 - sc))) & DMASK; /* MQ has AC'MQ */ | |
| AC = AC | (mac >> sc); /* AC has AC only */ | |
| } | |
| else if (sc == 36) { /* sc [36]? */ | |
| MQ = mac & DMASK; /* MQ = AC<P,1:35> */ | |
| AC = AC | (mac >> 36); /* AC = AC<Q> */ | |
| } | |
| else if (sc < 73) /* sc [37, 72]? */ | |
| MQ = (mac >> (sc - 36)) & DMASK; /* MQ has AC only */ | |
| else MQ = 0; /* >72, AC,MQ = 0 */ | |
| } | |
| return; | |
| } | |
| /* Plus sense - undefined operations are NOPs */ | |
| t_stat op_pse (uint32 addr) | |
| { | |
| uint32 ch, spill; | |
| switch (addr) { | |
| case 00000: /* CLM */ | |
| if (cpu_model & I_9X) /* 709X only */ | |
| AC = AC & AC_S; | |
| break; | |
| case 00001: /* LBT */ | |
| if ((AC & 1) != 0) | |
| PC = (PC + 1) & AMASK; | |
| break; | |
| case 00002: /* CHS */ | |
| AC = AC ^ AC_S; | |
| break; | |
| case 00003: /* SSP */ | |
| AC = AC & ~AC_S; | |
| break; | |
| case 00004: /* ENK */ | |
| MQ = KEYS; | |
| break; | |
| case 00005: /* IOT */ | |
| if (ind_ioc) | |
| ind_ioc = 0; | |
| else PC = (PC + 1) & AMASK; | |
| break; | |
| case 00006: /* COM */ | |
| AC = AC ^ AC_MMASK; | |
| break; | |
| case 00007: /* ETM */ | |
| if (cpu_model & I_9X) /* 709X only */ | |
| mode_ttrap = 1; | |
| break; | |
| case 00010: /* RND */ | |
| if ((cpu_model & I_9X) && (MQ & B1)) /* 709X only, MQ1 set? */ | |
| op_add ((t_uint64) 1); /* incr AC */ | |
| break; | |
| case 00011: /* FRN */ | |
| if (cpu_model & I_9X) { /* 709X only */ | |
| spill = op_frnd (); | |
| if (spill) | |
| fp_trap (spill); | |
| } | |
| break; | |
| case 00012: /* DCT */ | |
| if (ind_dvc) | |
| ind_dvc = 0; | |
| else PC = (PC + 1) & AMASK; | |
| break; | |
| case 00014: /* RCT */ | |
| chtr_inhi = 1; /* 1 cycle delay */ | |
| chtr_inht = 0; /* clr inhibit trap */ | |
| chtr_pend = 0; /* no trap now */ | |
| break; | |
| case 00016: /* LMTM */ | |
| if (cpu_model & I_94) /* 709X only */ | |
| mode_multi = 0; | |
| break; | |
| case 00140: /* SLF */ | |
| if (cpu_model & I_9X) /* 709X only */ | |
| SLT = 0; | |
| break; | |
| case 00141: case 00142: case 00143: case 00144: /* SLN */ | |
| if (cpu_model & I_9X) /* 709X only */ | |
| SLT = SLT | (1u << (00144 - addr)); | |
| break; | |
| case 00161: case 00162: case 00163: /* SWT */ | |
| case 00164: case 00165: case 00166: | |
| if ((SSW & (1u << (00166 - addr))) != 0) | |
| PC = (PC + 1) & AMASK; | |
| break; | |
| case 01000: case 02000: case 03000: case 04000: /* BTT */ | |
| case 05000: case 06000: case 07000: case 10000: | |
| if (cpu_model & I_9X) { /* 709X only */ | |
| if (sel_trap (PC)) /* sel trap? */ | |
| break; | |
| ch = GET_U_CH (addr); /* get channel */ | |
| if (ch_flags[ch] & CHF_BOT) /* BOT? */ | |
| ch_flags[ch] &= ~CHF_BOT; /* clear */ | |
| else PC = (PC + 1) & AMASK; /* else skip */ | |
| } | |
| break; | |
| case 001350: case 002350: case 003350: case 004350: /* RICx */ | |
| case 005350: case 006350: case 007350: case 010350: | |
| ch = GET_U_CH (addr); /* get channel */ | |
| return ch_op_reset (ch, 1); | |
| case 001352: case 002352: case 003352: case 004352: /* RDCx */ | |
| case 005352: case 006352: case 007352: case 010352: | |
| ch = GET_U_CH (addr); /* get channel */ | |
| return ch_op_reset (ch, 0); | |
| } /* end case */ | |
| return SCPE_OK; | |
| } | |
| /* Minus sense */ | |
| t_stat op_mse (uint32 addr) | |
| { | |
| uint32 t, ch; | |
| switch (addr) { | |
| case 00000: /* CLM */ | |
| if (cpu_model & I_9X) /* 709X only */ | |
| AC = AC & AC_S; | |
| break; | |
| case 00001: /* PBT */ | |
| if ((AC & AC_P) != 0) | |
| PC = (PC + 1) & AMASK; | |
| break; | |
| case 00002: /* EFTM */ | |
| if (cpu_model & I_9X) { /* 709X only */ | |
| mode_ftrap = 1; | |
| ind_mqo = 0; /* clears MQ ovf */ | |
| } | |
| break; | |
| case 00003: /* SSM */ | |
| if (cpu_model & I_9X) /* 709X only */ | |
| AC = AC | AC_S; | |
| break; | |
| case 00004: /* LFTM */ | |
| if (cpu_model & I_9X) /* 709X only */ | |
| mode_ftrap = 0; | |
| break; | |
| case 00005: /* ESTM */ | |
| if (cpu_model & I_9X) /* 709X only */ | |
| mode_strap = 1; | |
| break; | |
| case 00006: /* ECTM */ | |
| if (cpu_model & I_9X) /* 709X only */ | |
| mode_ctrap = 1; | |
| break; | |
| case 00007: /* LTM */ | |
| if (cpu_model & I_9X) /* 709X only */ | |
| mode_ttrap = 0; | |
| break; | |
| case 00010: /* LSNM */ | |
| if (cpu_model & I_9X) /* 709X only */ | |
| mode_storn = 0; | |
| break; | |
| case 00012: /* RTT (704) */ | |
| if (cpu_model & I_9X) /* 709X only */ | |
| sel_trap (PC); | |
| break; | |
| case 00016: /* EMTM */ | |
| mode_multi = 1; | |
| break; | |
| case 00140: /* SLF */ | |
| if (cpu_model & I_9X) /* 709X only */ | |
| SLT = 0; | |
| break; | |
| case 00141: case 00142: case 00143: case 00144: /* SLT */ | |
| if (cpu_model & I_9X) { /* 709X only */ | |
| t = SLT & (1u << (00144 - addr)); | |
| SLT = SLT & ~t; | |
| if (t != 0) | |
| PC = (PC + 1) & AMASK; | |
| } | |
| break; | |
| case 00161: case 00162: case 00163: /* SWT */ | |
| case 00164: case 00165: case 00166: | |
| if ((cpu_model & I_9X) && /* 709X only */ | |
| ((SSW & (1u << (00166 - addr))) != 0)) | |
| PC = (PC + 1) & AMASK; | |
| break; | |
| case 001000: case 002000: case 003000: case 004000: /* ETT */ | |
| case 005000: case 006000: case 007000: case 010000: | |
| if (sel_trap (PC)) /* sel trap? */ | |
| break; | |
| ch = GET_U_CH (addr); /* get channel */ | |
| if (ch_flags[ch] & CHF_EOT) /* EOT? */ | |
| ch_flags[ch] = ch_flags[ch] & ~CHF_EOT; /* clear */ | |
| else PC = (PC + 1) & AMASK; /* else skip */ | |
| break; | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Floating add | |
| Notes: | |
| - AC<Q,P> enter into the initial exponent comparison. If either is set, | |
| the numbers are always swapped. AC<P> gets OR'd into AC<S> during the | |
| swap, and AC<Q,P> are cleared afterwards | |
| - The early end test is actually > 077 if AC <= SR and > 100 if | |
| AC > SR. However, any shift >= 54 will produce a zero fraction, | |
| so the difference can be ignored */ | |
| uint32 op_fad (t_uint64 sr, t_bool norm) | |
| { | |
| UFP op1, op2, t; | |
| int32 mqch, diff; | |
| MQ = 0; /* clear MQ */ | |
| fp_unpack (AC, 0, 1, &op1); /* unpack AC */ | |
| fp_unpack (sr, 0, 0, &op2); /* unpack sr */ | |
| if (op1.ch > op2.ch) { /* AC exp > SR exp? */ | |
| if (AC & AC_P) /* AC P or's with S */ | |
| op1.s = 1; | |
| t = op1; /* swap operands */ | |
| op1 = op2; | |
| op2 = t; | |
| op2.ch = op2.ch & FP_M_CH; /* clear P,Q */ | |
| } | |
| diff = op2.ch - op1.ch; /* exp diff */ | |
| if (diff) { /* any shift? */ | |
| if ((diff < 0) || (diff > 077)) /* diff > 63? */ | |
| op1.fr = 0; | |
| else op1.fr = op1.fr >> diff; /* no, denormalize */ | |
| } | |
| if (op1.s ^ op2.s) { /* subtract? */ | |
| if (op1.fr >= op2.fr) { /* op1 > op2? */ | |
| op2.fr = op1.fr - op2.fr; /* op1 - op2 */ | |
| op2.s = op1.s; /* op2 sign is result */ | |
| } | |
| else op2.fr = op2.fr - op1.fr; /* else op2 - op1 */ | |
| } | |
| else { | |
| op2.fr = op2.fr + op1.fr; /* op2 + op1 */ | |
| if (op2.fr & FP_FCRY) { /* carry? */ | |
| op2.fr = op2.fr >> 1; /* renormalize */ | |
| op2.ch++; /* incr exp */ | |
| } | |
| } | |
| if (norm) { /* normalize? */ | |
| if (op2.fr) { /* non-zero frac? */ | |
| fp_norm (&op2); | |
| mqch = op2.ch - FP_N_FR; | |
| } | |
| else op2.ch = mqch = 0; /* else true zero */ | |
| } | |
| else mqch = op2.ch - FP_N_FR; | |
| return fp_pack (&op2, op2.s, mqch); /* pack AC, MQ */ | |
| } | |
| /* Floating multiply */ | |
| uint32 op_fmp (t_uint64 sr, t_bool norm) | |
| { | |
| UFP op1, op2; | |
| int32 mqch; | |
| uint32 f1h, f2h; | |
| fp_unpack (MQ, 0, 0, &op1); /* unpack MQ */ | |
| fp_unpack (sr, 0, 0, &op2); /* unpack sr */ | |
| op1.s = op1.s ^ op2.s; /* result sign */ | |
| if ((op2.ch == 0) && (op2.fr == 0)) { /* sr a normal 0? */ | |
| AC = op1.s? AC_S: 0; /* result is 0 */ | |
| MQ = op1.s? SIGN: 0; | |
| return 0; | |
| } | |
| f1h = FP_HIFRAC (op1.fr); /* get hi fracs */ | |
| f2h = FP_HIFRAC (op2.fr); | |
| op1.fr = ((t_uint64) f1h) * ((t_uint64) f2h); /* f1h * f2h */ | |
| op1.ch = (op1.ch & FP_M_CH) + op2.ch - FP_BIAS; /* result exponent */ | |
| if (norm) { /* normalize? */ | |
| if (!(op1.fr & FP_FNORM)) { /* not normalized? */ | |
| op1.fr = op1.fr << 1; /* shift frac left 1 */ | |
| op1.ch--; /* decr exp */ | |
| } | |
| if (FP_HIFRAC (op1.fr)) /* hi result non-zero? */ | |
| mqch = op1.ch - FP_N_FR; /* set MQ exp */ | |
| else op1.ch = mqch = 0; /* clear AC, MQ exp */ | |
| } | |
| else mqch = op1.ch - FP_N_FR; /* set MQ exp */ | |
| return fp_pack (&op1, op1.s, mqch); /* pack AC, MQ */ | |
| } | |
| /* Floating divide */ | |
| uint32 op_fdv (t_uint64 sr) | |
| { | |
| UFP op1, op2; | |
| int32 mqch; | |
| uint32 spill, quos; | |
| t_uint64 rem; | |
| fp_unpack (AC, 0, 1, &op1); /* unpack AC */ | |
| fp_unpack (sr, 0, 0, &op2); /* unpack sr */ | |
| quos = op1.s ^ op2.s; /* quotient sign */ | |
| if (op1.fr >= (2 * op2.fr)) { /* |AC| >= 2*|sr|? */ | |
| MQ = quos? SIGN: 0; /* MQ = sign only */ | |
| return TRAP_F_DVC; /* divide check */ | |
| } | |
| if (op1.fr == 0) { /* |AC| == 0? */ | |
| MQ = quos? SIGN: 0; /* MQ = sign only */ | |
| AC = 0; /* AC = +0 */ | |
| return 0; /* done */ | |
| } | |
| op1.ch = op1.ch & FP_M_CH; /* remove AC<Q,P> */ | |
| if (op1.fr >= op2.fr) { /* |AC| >= |sr|? */ | |
| op1.fr = op1.fr >> 1; /* denorm AC */ | |
| op1.ch++; | |
| } | |
| op1.fr = fp_fracdiv (op1.fr, op2.fr, &rem); /* fraction divide */ | |
| op1.fr = op1.fr | (rem << FP_N_FR); /* rem'quo */ | |
| mqch = op1.ch - op2.ch + FP_BIAS; /* quotient exp */ | |
| op1.ch = op1.ch - FP_N_FR; /* remainder exp */ | |
| spill = fp_pack (&op1, quos, mqch); /* pack up */ | |
| return (spill? (spill | TRAP_F_SGL): 0); /* if spill, set SGL */ | |
| } | |
| /* Double floating add | |
| Notes: | |
| - AC<Q,P> enter into the initial exponent comparison. If either is set, | |
| the numbers are always swapped. AC<P> gets OR'd into AC<S> during the | |
| swap, and AC<Q,P> are cleared afterwards | |
| - For most cases, SI ends up with the high order part of the larger number | |
| - The 'early end' cases (smaller number is shifted away) must be tracked | |
| exactly for SI impacts. The early end cases are: | |
| (a) AC > SR, diff > 0100, and AC normalized | |
| (b) AC <= SR, diff > 077, and SR normalized | |
| In case (a), SI is unchanged. In case (b), SI ends up with the SR sign | |
| and characteristic but the MQ (!) fraction */ | |
| uint32 op_dfad (t_uint64 sr, t_uint64 sr1, t_bool norm) | |
| { | |
| UFP op1, op2, t; | |
| int32 mqch, diff; | |
| fp_unpack (AC, MQ, 1, &op1); /* unpack AC'MQ */ | |
| fp_unpack (sr, sr1, 0, &op2); /* unpack sr'sr1 */ | |
| if (op1.ch > op2.ch) { /* AC exp > SR exp? */ | |
| if (((op1.ch - op2.ch) > 0100) && (AC & B9)) ; /* early out */ | |
| else SI = FP_PACK36 (op1.s, op1.ch, FP_HIFRAC (op1.fr)); | |
| if (AC & AC_P) /* AC P or's with S */ | |
| op1.s = 1; | |
| t = op1; /* swap operands */ | |
| op1 = op2; | |
| op2 = t; | |
| op2.ch = op2.ch & FP_M_CH; /* clear P,Q */ | |
| } | |
| else { /* AC <= SR */ | |
| if (((op2.ch - op1.ch) > 077) && (sr & B9)) /* early out */ | |
| SI = FP_PACK36 (op2.s, op2.ch, FP_LOFRAC (MQ)); | |
| else SI = FP_PACK36 (op2.s, op2.ch, FP_HIFRAC (op2.fr)); | |
| } | |
| diff = op2.ch - op1.ch; /* exp diff */ | |
| if (diff) { /* any shift? */ | |
| if ((diff < 0) || (diff > 077)) /* diff > 63? */ | |
| op1.fr = 0; | |
| else op1.fr = op1.fr >> diff; /* no, denormalize */ | |
| } | |
| if (op1.s ^ op2.s) { /* subtract? */ | |
| if (op1.fr >= op2.fr) { /* op1 > op2? */ | |
| op2.fr = op1.fr - op2.fr; /* op1 - op2 */ | |
| op2.s = op1.s; /* op2 sign is result */ | |
| } | |
| else op2.fr = op2.fr - op1.fr; /* op2 - op1 */ | |
| } | |
| else { | |
| op2.fr = op2.fr + op1.fr; /* op2 + op1 */ | |
| if (op2.fr & FP_FCRY) { /* carry? */ | |
| op2.fr = op2.fr >> 1; /* renormalize */ | |
| op2.ch++; /* incr exp */ | |
| } | |
| } | |
| if (norm) { /* normalize? */ | |
| if (op2.fr) { /* non-zero frac? */ | |
| fp_norm (&op2); | |
| mqch = op2.ch - FP_N_FR; | |
| } | |
| else op2.ch = mqch = 0; /* else true zero */ | |
| } | |
| else mqch = op2.ch - FP_N_FR; | |
| return fp_pack (&op2, op2.s, mqch); /* pack AC, MQ */ | |
| } | |
| /* Double floating multiply | |
| Notes (notation is A+B' * C+D', where ' denotes 2^-27): | |
| - The instruction returns 0 if A and C are both zero, because B*D is never | |
| done as part of the algorithm | |
| - For most cases, SI ends up with B*C, with a zero sign and exponent | |
| - For the A+B' both zero 'early end' case SI ends up with A or C, | |
| depending on whether the operation is normalized or not */ | |
| uint32 op_dfmp (t_uint64 sr, t_uint64 sr1, t_bool norm) | |
| { | |
| UFP op1, op2; | |
| int32 mqch; | |
| uint32 f1h, f2h, f1l, f2l; | |
| t_uint64 tx; | |
| fp_unpack (AC, MQ, 1, &op1); /* unpack AC'MQ */ | |
| fp_unpack (sr, sr1, 0, &op2); /* unpack sr'sr1 */ | |
| op1.s = op1.s ^ op2.s; /* result sign */ | |
| f1h = FP_HIFRAC (op1.fr); /* A */ | |
| f1l = FP_LOFRAC (op1.fr); /* B */ | |
| f2h = FP_HIFRAC (op2.fr); /* C */ | |
| f2l = FP_LOFRAC (op2.fr); /* D */ | |
| if (((op1.ch == 0) && (op1.fr == 0)) || /* AC'MQ normal 0? */ | |
| ((op2.ch == 0) && (op2.fr == 0)) || /* sr'sr1 normal 0? */ | |
| ((f1h == 0) && (f2h == 0))) { /* both hi frac zero? */ | |
| AC = op1.s? AC_S: 0; /* result is 0 */ | |
| MQ = op1.s? SIGN: 0; | |
| SI = sr; /* SI has C */ | |
| return 0; | |
| } | |
| op1.ch = (op1.ch & FP_M_CH) + op2.ch - FP_BIAS; /* result exponent */ | |
| if (op1.fr) { /* A'B != 0? */ | |
| op1.fr = ((t_uint64) f1h) * ((t_uint64) f2h); /* A * C */ | |
| tx = ((t_uint64) f1h) * ((t_uint64) f2l); /* A * D */ | |
| op1.fr = op1.fr + (tx >> FP_N_FR); /* add in hi 27b */ | |
| tx = ((t_uint64) f1l) * ((t_uint64) f2h); /* B * C */ | |
| op1.fr = op1.fr + (tx >> FP_N_FR); /* add in hi 27b */ | |
| SI = tx >> FP_N_FR; /* SI keeps B * C */ | |
| } | |
| else { | |
| if (norm) /* early out */ | |
| SI = sr; | |
| else SI = FP_PACK36 (op2.s, op2.ch, 0); | |
| } | |
| if (norm) { /* normalize? */ | |
| if (!(op1.fr & FP_FNORM)) { /* not normalized? */ | |
| op1.fr = op1.fr << 1; /* shift frac left 1 */ | |
| op1.ch--; /* decr exp */ | |
| } | |
| if (FP_HIFRAC (op1.fr)) { /* non-zero? */ | |
| mqch = op1.ch - FP_N_FR; /* set MQ exp */ | |
| } | |
| else op1.ch = mqch = 0; /* clear AC, MQ exp */ | |
| } | |
| else mqch = op1.ch - FP_N_FR; /* set MQ exp */ | |
| return fp_pack (&op1, op1.s, mqch); /* pack AC, MQ */ | |
| } | |
| /* Double floating divide | |
| Notes: | |
| - This is a Taylor series expansion (where ' denotes >> 27): | |
| (A+B') * (C+D')^-1 = (A+B') * C^-1 - (A+B') * D'* C^-2 +... | |
| to two terms, which can be rewritten as terms Q1, Q2: | |
| Q1 = (A+B')/C | |
| Q2' = (R - Q1*D)'/C | |
| - Tracking the sign of Q2' is complicated: | |
| Q1 has the sign of the quotient, s_AC ^ s_SR | |
| D has the sign of the divisor, s_SR | |
| R has the sign of the dividend, s_AC | |
| Q1*D sign is s_AC ^ s_SR ^ s^SR = s^AC | |
| Therefore, R and Q1*D have the same sign, s_AC | |
| Q2' sign is s^AC ^ s_SR, which is the sign of the quotient | |
| - For first divide check, SI is 0 | |
| - For other cases, including second divide check, SI ends up with Q1 | |
| - R-Q1*D is only calculated to the high 27b; using the full 54b | |
| throws off the result | |
| - The second divide must check for divd >= divr, otherwise an extra | |
| bit of quotient would be devloped, throwing off the result | |
| - A late ECO added full post-normalization; single precision divide | |
| does no normalization */ | |
| uint32 op_dfdv (t_uint64 sr, t_uint64 sr1) | |
| { | |
| UFP op1, op2; | |
| int32 mqch; | |
| uint32 csign, ac_s; | |
| t_uint64 f1h, f2h, tr, tq1, tq1d, trmq1d, tq2; | |
| fp_unpack (AC, MQ, 1, &op1); /* unpack AC'MQ */ | |
| fp_unpack (sr, 0, 0, &op2); /* unpack sr only */ | |
| ac_s = op1.s; /* save AC sign */ | |
| op1.s = op1.s ^ op2.s; /* sign of result */ | |
| f1h = FP_HIFRAC (op1.fr); | |
| f2h = FP_HIFRAC (op2.fr); | |
| if (f1h >= (2 * f2h)) { /* |A| >= 2*|C|? */ | |
| SI = 0; /* clear SI */ | |
| return TRAP_F_DVC; /* divide check */ | |
| } | |
| if (f1h == 0) { /* |AC| == 0? */ | |
| SI = MQ = op1.s? SIGN: 0; /* MQ, SI = sign only */ | |
| AC = op1.s? AC_S: 0; /* AC = sign only */ | |
| return 0; /* done */ | |
| } | |
| op1.ch = op1.ch & FP_M_CH; /* remove AC<Q,P> */ | |
| if (f1h >= f2h) { /* |A| >= |C|? */ | |
| op1.fr = op1.fr >> 1; /* denorm AC */ | |
| op1.ch++; | |
| } | |
| op1.ch = op1.ch - op2.ch + FP_BIAS; /* exp of quotient */ | |
| tq1 = fp_fracdiv (op1.fr, op2.fr, &tr); /* |A+B| / |C| */ | |
| tr = tr << FP_N_FR; /* R << 27 */ | |
| tq1d = (tq1 * ((t_uint64) FP_LOFRAC (sr1))) & /* Q1 * D */ | |
| ~((t_uint64) FP_FMASK); /* top 27 bits */ | |
| csign = (tr < tq1d); /* correction sign */ | |
| if (csign) /* |R|<|Q1*D|? compl */ | |
| trmq1d = tq1d - tr; | |
| else trmq1d = tr - tq1d; /* no, subtr ok */ | |
| SI = FP_PACK36 (op1.s, op1.ch, tq1); /* SI has Q1 */ | |
| if (trmq1d >= (2 * op2.fr)) { /* |R-Q1*D| >= 2*|C|? */ | |
| AC = FP_PACK38 (csign ^ ac_s, 0, FP_HIFRAC (trmq1d)); /* AC has R-Q1*D */ | |
| MQ = (csign ^ ac_s)? SIGN: 0; /* MQ = sign only */ | |
| return TRAP_F_DVC; /* divide check */ | |
| } | |
| tq2 = fp_fracdiv (trmq1d, op2.fr, NULL); /* |R-Q1*D| / |C| */ | |
| if (trmq1d >= op2.fr) /* can only gen 27b quo */ | |
| tq2 &= ~((t_uint64) 1); | |
| op1.fr = tq1 << FP_N_FR; /* shift Q1 into place */ | |
| if (csign) /* sub or add Q2 */ | |
| op1.fr = op1.fr - tq2; | |
| else op1.fr = op1.fr + tq2; | |
| fp_norm (&op1); /* normalize */ | |
| if (op1.fr) /* non-zero? */ | |
| mqch = op1.ch - FP_N_FR; | |
| else op1.ch = mqch = 0; /* clear AC, MQ exp */ | |
| return fp_pack (&op1, op1.s, mqch); /* pack AC, MQ */ | |
| } | |
| /* Floating round */ | |
| uint32 op_frnd (void) | |
| { | |
| UFP op; | |
| uint32 spill; | |
| spill = 0; /* no error */ | |
| if (MQ & B9) { /* MQ9 set? */ | |
| fp_unpack (AC, 0, 1, &op); /* unpack AC */ | |
| op.fr = op.fr + ((t_uint64) (1 << FP_N_FR)); /* round up */ | |
| if (op.fr & FP_FCRY) { /* carry out? */ | |
| op.fr = op.fr >> 1; /* renormalize */ | |
| op.ch++; /* incr exp */ | |
| if (op.ch == (FP_M_CH + 1)) /* ovf with QP = 0? */ | |
| spill = TRAP_F_OVF | TRAP_F_AC; | |
| } | |
| AC = FP_PACK38 (op.s, op.ch, FP_HIFRAC (op.fr)); /* pack AC */ | |
| } | |
| return spill; | |
| } | |
| /* Fraction divide - 54/27'0 yielding quotient and remainder */ | |
| t_uint64 fp_fracdiv (t_uint64 dvd, t_uint64 dvr, t_uint64 *rem) | |
| { | |
| dvr = dvr >> FP_N_FR; | |
| if (rem) | |
| *rem = dvd % dvr; | |
| return (dvd / dvr); | |
| } | |
| /* Floating point normalize */ | |
| void fp_norm (UFP *op) | |
| { | |
| op->fr = op->fr & FP_DFMASK; /* mask fraction */ | |
| if (op->fr == 0) /* zero? */ | |
| return; | |
| while ((op->fr & FP_FNORM) == 0) { /* until norm */ | |
| op->fr = op->fr << 1; /* lsh 1 */ | |
| op->ch--; /* decr exp */ | |
| } | |
| return; | |
| } | |
| /* Floating point unpack */ | |
| void fp_unpack (t_uint64 h, t_uint64 l, t_bool q_ac, UFP *op) | |
| { | |
| if (q_ac) { /* AC? */ | |
| op->s = (h & AC_S)? 1: 0; /* get sign */ | |
| op->ch = (uint32) ((h >> FP_V_CH) & FP_M_ACCH); /* get exp */ | |
| } | |
| else { | |
| op->s = (h & SIGN)? 1: 0; /* no, mem */ | |
| op->ch = (uint32) ((h >> FP_V_CH) & FP_M_CH); | |
| } | |
| op->fr = (((t_uint64) FP_LOFRAC (h)) << FP_N_FR) | /* get frac hi */ | |
| ((t_uint64) FP_LOFRAC (l)); /* get frac lo */ | |
| return; | |
| } | |
| /* Floating point pack */ | |
| uint32 fp_pack (UFP *op, uint32 mqs, int32 mqch) | |
| { | |
| uint32 spill; | |
| AC = FP_PACK38 (op->s, op->ch, FP_HIFRAC (op->fr)); /* pack AC */ | |
| MQ = FP_PACK36 (mqs, mqch, FP_LOFRAC (op->fr)); /* pack MQ */ | |
| if (op->ch > FP_M_CH) /* check AC exp */ | |
| spill = TRAP_F_OVF | TRAP_F_AC; | |
| else if (op->ch < 0) | |
| spill = TRAP_F_AC; | |
| else spill = 0; | |
| if (mqch > FP_M_CH) /* check MQ exp */ | |
| spill |= (TRAP_F_OVF | TRAP_F_MQ); | |
| else if (mqch < 0) | |
| spill |= TRAP_F_MQ; | |
| return spill; | |
| } |