/* vax_cis.c: VAX CIS instructions Copyright (c) 2004-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. On a full VAX, this module simulates the VAX commercial instruction set (CIS). On a subset VAX, this module implements the emulated instruction fault. 16-Oct-08 RMS Fixed bug in ASHP left overflow calc (Word/NibbleLShift) Fixed bug in DIVx (LntDstr calculation) 28-May-08 RMS Inlined physical memory routines 16-May-06 RMS Fixed bug in length calculation (Tim Stark) 03-May-06 RMS Fixed MOVTC, MOVTUC to preserve cc's through page faults Fixed MOVTUC to stop on translated == escape Fixed CVTPL to set registers before destination reg write Fixed CVTPL to set correct cc bit on overflow Fixed EDITPC to preserve cc's through page faults Fixed EDITPC EO$BLANK_ZERO count, cc test Fixed EDITPC EO$INSERT to insert fill instead of blank Fixed EDITPC EO$LOAD_PLUS/MINUS to skip character (Tim Stark) 12-Apr-04 RMS Cloned from pdp11_cis.c and vax_cpu1.c Zero length decimal strings require either zero bytes (trailing) or one byte (separate, packed). CIS instructions can run for a very long time, so they are interruptible and restartable. In the simulator, string instructions (and EDITPC) are interruptible by faults, but decimal instructions run to completion. The code is unoptimized. */ #include "vax_defs.h" #if defined (FULL_VAX) /* Decimal string structure */ #define DSTRLNT 4 #define DSTRMAX (DSTRLNT - 1) #define MAXDVAL 429496730 /* 2^32 / 10 */ #define C_SPACE 0x20 /* ASCII chars */ #define C_PLUS 0x2B #define C_MINUS 0x2D #define C_ZERO 0x30 #define C_NINE 0x39 typedef struct { uint32 sign; uint32 val[DSTRLNT]; } DSTR; static DSTR Dstr_zero = { 0, {0, 0, 0, 0} }; static DSTR Dstr_one = { 0, {0x10, 0, 0, 0} }; int32 ReadDstr (int32 lnt, int32 addr, DSTR *dec, int32 acc); int32 WriteDstr (int32 lnt, int32 addr, DSTR *dec, int32 v, int32 acc); int32 SetCCDstr (int32 lnt, DSTR *src, int32 pslv); int32 AddDstr (DSTR *src1, DSTR *src2, DSTR *dst, int32 cin); void SubDstr (DSTR *src1, DSTR *src2, DSTR *dst); int32 CmpDstr (DSTR *src1, DSTR *src2); int32 TestDstr (DSTR *dsrc); void ProbeDstr (int32 lnt, int32 addr, int32 acc); int32 LntDstr (DSTR *dsrc, int32 nz); uint32 NibbleLshift (DSTR *dsrc, int32 sc, uint32 cin); uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin); int32 WordLshift (DSTR *dsrc, int32 sc); void WordRshift (DSTR *dsrc, int32 sc); void CreateTable (DSTR *dsrc, DSTR mtable[10]); int32 do_crc_4b (int32 crc, int32 tbl, int32 acc); int32 edit_read_src (int32 inc, int32 acc); void edit_adv_src (int32 inc); int32 edit_read_sign (int32 acc); extern int32 eval_int (void); /* CIS emulator */ int32 op_cis (int32 *op, int32 cc, int32 opc, int32 acc) { int32 i, j, c, t, pop, rpt, V; int32 match, fill, sign, shift; int32 ldivd, ldivr; int32 lenl, lenp; uint32 nc, d, result; t_stat r; DSTR accum, src1, src2, dst; DSTR mptable[10]; switch (opc) { /* case on opcode */ /* MOVTC Operands if PSL = 0: op[0:1] = source string descriptor op[2] = fill character op[3] = table address op[4:5] = destination string descriptor Registers if PSL = 1: R[0] = delta-PC/fill/source string length R[1] = source string address R[2] = number of bytes remaining to move R[3] = table address R[4] = saved cc's/destination string length R[5] = destination string address Condition codes: NZC = set from op[0]:op[4] V = 0 Registers: R0 = src length remaining, or 0 R1 = addr of end of source string + 1 R2 = 0 R3 = table address R4 = 0 R5 = addr of end of dest string + 1 */ case MOVTC: if (PSL & PSL_FPD) { /* FPD set? */ SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ fill = STR_GETCHR (R[0]); /* get fill */ R[2] = R[2] & STR_LNMASK; /* remaining move */ cc = (R[4] >> 16) & CC_MASK; /* restore cc's */ } else { CC_CMP_W (op[0], op[4]); /* set cc's */ R[0] = STR_PACK (op[2], op[0]); /* src len, fill */ R[1] = op[1]; /* src addr */ fill = op[2]; /* set fill */ R[3] = op[3]; /* table addr */ R[4] = op[4] | ((cc & CC_MASK) << 16); /* dst len + cc's */ R[5] = op[5]; /* dst addr */ R[2] = (op[0] < op[4])? op[0]: op[4]; /* remaining move */ PSL = PSL | PSL_FPD; /* set FPD */ } if (R[2]) { /* move to do? */ int32 mvl; mvl = R[0] & STR_LNMASK; /* orig move len */ if (mvl >= (R[4] & STR_LNMASK)) mvl = R[4] & STR_LNMASK; if (((uint32) R[1]) < ((uint32) R[5])) { /* backward? */ while (R[2]) { /* loop thru char */ t = Read ((R[1] + R[2] - 1) & LMASK, L_BYTE, RA); c = Read ((R[3] + t) & LMASK, L_BYTE, RA); Write ((R[5] + R[2] - 1) & LMASK, c, L_BYTE, WA); R[2] = (R[2] - 1) & STR_LNMASK; } R[1] = (R[1] + mvl) & LMASK; /* adv src, dst */ R[5] = (R[5] + mvl) & LMASK; } else { /* forward */ while (R[2]) { /* loop thru char */ t = Read (R[1], L_BYTE, RA); /* read src */ c = Read ((R[3] + t) & LMASK, L_BYTE, RA); Write (R[5], c, L_BYTE, WA); /* write dst */ R[1] = (R[1] + 1) & LMASK; /* adv src, dst */ R[2] = (R[2] - 1) & STR_LNMASK; R[5] = (R[5] + 1) & LMASK; } } /* update lengths */ R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - mvl) & STR_LNMASK); R[4] = (R[4] & ~STR_LNMASK) | ((R[4] - mvl) & STR_LNMASK); } while (R[4] & STR_LNMASK) { /* fill if needed */ Write (R[5], fill, L_BYTE, WA); R[4] = (R[4] & ~STR_LNMASK) | ((R[4] - 1) & STR_LNMASK); R[5] = (R[5] + 1) & LMASK; /* adv dst */ } R[0] = R[0] & STR_LNMASK; /* mask off state */ R[4] = 0; PSL = PSL & ~PSL_FPD; return cc; /* MOVTUC Operands: op[0:1] = source string descriptor op[2] = escape character op[3] = table address op[4:5] = destination string descriptor Registers if PSL = 1: R[0] = delta-PC/match/source string length R[1] = source string address R[2] = saved condition codes R[3] = table address R[4] = destination string length R[5] = destination string address Condition codes: NZC = set from op[0]:op[4] V = 1 if match to escape character Registers: R0 = src length remaining, or 0 R1 = addr of end of source string + 1 R2 = 0 R3 = table address R4 = dst length remaining, or 0 R5 = addr of end of dest string + 1 */ case MOVTUC: if (PSL & PSL_FPD) { /* FPD set? */ SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ fill = STR_GETCHR (R[0]); /* get match */ R[4] = R[4] & STR_LNMASK; cc = R[2] & CC_MASK; /* restore cc's */ } else { CC_CMP_W (op[0], op[4]); /* set cc's */ R[0] = STR_PACK (op[2], op[0]); /* src len, fill */ R[1] = op[1]; /* src addr */ fill = op[2]; /* set match */ R[3] = op[3]; /* table addr */ R[4] = op[4]; /* dst len */ R[5] = op[5]; /* dst addr */ R[2] = cc; /* save cc's */ PSL = PSL | PSL_FPD; /* set FPD */ } while ((R[0] & STR_LNMASK) && R[4]) { /* while src & dst */ t = Read (R[1], L_BYTE, RA); /* read src */ c = Read ((R[3] + t) & LMASK, L_BYTE, RA); /* translate */ if (c == fill) { /* stop char? */ cc = cc | CC_V; /* set V, done */ break; } Write (R[5], c, L_BYTE, WA); /* write dst */ R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - 1) & STR_LNMASK); R[1] = (R[1] + 1) & LMASK; R[4] = (R[4] - 1) & STR_LNMASK; /* adv src, dst */ R[5] = (R[5] + 1) & LMASK; } R[0] = R[0] & STR_LNMASK; /* mask off state */ R[2] = 0; PSL = PSL & ~PSL_FPD; return cc; /* MATCHC Operands: op[0:1] = substring descriptor op[2:3] = string descriptor Registers if PSL = 1: R[0] = delta-PC/match/substring length R[1] = substring address R[2] = source string length R[3] = source string address Condition codes: NZ = set from R0 VC = 0 Registers: R0 = if match, 0, else, op[0] R1 = if match, op[0] + op[1], else, op[1] R2 = if match, src bytes remaining, else, 0 R3 = if match, end of substr, else, op[2] + op[3] Notes: - If the string is zero length, and the substring is not, the outer loop exits immediately, and the result is "no match" - If the substring is zero length, the inner loop always exits immediately, and the result is a "match" - If the string is zero length, and the substring is as well, the outer loop executes, the inner loop exits immediately, and the result is a match, but the result is the length of the string (zero) - This instruction can potentially run a very long time - worst case execution on a real VAX-11/780 was more than 30 minutes. Accordingly, the instruction tests for interrupts and stops if one is found. */ case MATCHC: if (PSL & PSL_FPD) { /* FPD? */ SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ R[2] = R[2] & STR_LNMASK; } else { R[0] = STR_PACK (0, op[0]); /* srclen + FPD data */ R[1] = op[1]; /* save operands */ R[2] = op[2]; R[3] = op[3]; PSL = PSL | PSL_FPD; /* set FPD */ } for (match = 0; R[2] >= (R[0] & STR_LNMASK); ) { for (i = 0, match = 1; match && (i < (R[0] & STR_LNMASK)); i++) { c = Read ((R[1] + i) & LMASK, L_BYTE, RA); t = Read ((R[3] + i) & LMASK, L_BYTE, RA); match = (c == t); /* continue if match */ } /* end for substring */ if (match) /* exit if match */ break; R[2] = (R[2] - 1) & STR_LNMASK; /* decr src length */ R[3] = (R[3] + 1) & LMASK; /* next string char */ if (i >= sim_interval) { /* done with interval? */ sim_interval = 0; if ((r = sim_process_event ())) { /* presumably WRU */ PC = fault_PC; /* backup up PC */ ABORT (r); /* abort flushes IB */ } SET_IRQL; /* update interrupts */ if (trpirq) /* pending? stop */ ABORT (ABORT_INTR); } else sim_interval = sim_interval - i; } /* end for string */ R[0] = R[0] & STR_LNMASK; if (match) { /* if match */ R[1] = (R[1] + R[0]) & LMASK; R[2] = (R[2] - R[0]) & STR_LNMASK; R[3] = (R[3] + R[0]) & LMASK; R[0] = 0; } else { /* if no match */ R[3] = (R[3] + R[2]) & LMASK; R[2] = 0; } PSL = PSL & ~PSL_FPD; CC_IIZZ_L (R[0]); /* set cc's */ return cc; /* CRC Operands: op[0] = table address op[1] = initial CRC op[2:3] = source string descriptor Registers if PSL = 1: R[0] = result R[1] = table address R[2] = delta-PC/0/source string length R[3] = source string address Condition codes: NZ = set from result VC = 0 Registers: R0 = result R1 = 0 R2 = 0 R3 = addr + 1 of last byte in source string */ case CRC: if (PSL & PSL_FPD) { /* FPD? */ SETPC (fault_PC + STR_GETDPC (R[2])); /* reset PC */ } else { R[2] = STR_PACK (0, op[2]); /* srclen + FPD data */ R[0] = op[1]; /* save operands */ R[1] = op[0]; R[3] = op[3]; PSL = PSL | PSL_FPD; /* set FPD */ } while ((R[2] & STR_LNMASK) != 0) { /* loop thru chars */ c = Read (R[3], L_BYTE, RA); /* get char */ t = R[0] ^ c; /* XOR to CRC */ t = do_crc_4b (t, R[1], acc); /* do 4b shift */ R[0] = do_crc_4b (t, R[1], acc); /* do 4b shift */ R[3] = (R[3] + 1) & LMASK; R[2] = R[2] - 1; } R[1] = 0; R[2] = 0; PSL = PSL & ~PSL_FPD; CC_IIZZ_L (R[0]); /* set cc's */ return cc; /* MOVP Operands: op[0] = length op[1] = source string address op[2] = dest string address Condition codes: NZ = set from result V = 0 C = unchanged Registers: R0 = 0 R1 = addr of source string R2 = 0 R3 = addr of dest string */ case MOVP: if ((PSL & PSL_FPD) || (op[0] > 31)) RSVD_OPND_FAULT; ReadDstr (op[0], op[1], &dst, acc); /* read source */ cc = WriteDstr (op[0], op[2], &dst, 0, acc) | /* write dest */ (cc & CC_C); /* preserve C */ R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[2]; return cc; /* ADDP4, ADDP6, SUBP4, SUBP6 Operands: op[0:1] = src1 string descriptor op[2:3] = src2 string descriptor (ADDP6, SUBP6 only) op[4:5] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of src1 string R2 = 0 R3 = addr of src2 string (ADDP6, SUBP6 only) R4 = 0 R5 = addr of dest string */ case ADDP4: case SUBP4: op[4] = op[2]; /* copy dst */ op[5] = op[3]; case ADDP6: case SUBP6: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31) || (op[4] > 31)) RSVD_OPND_FAULT; ReadDstr (op[0], op[1], &src1, acc); /* get src1 */ ReadDstr (op[2], op[3], &src2, acc); /* get src2 */ if (opc & 2) /* sub? invert sign */ src1.sign = src1.sign ^ 1; if (src1.sign ^ src2.sign) { /* opp signs? sub */ if (CmpDstr (&src1, &src2) < 0) { /* src1 < src2? */ SubDstr (&src1, &src2, &dst); /* src2 - src1 */ dst.sign = src2.sign; /* sign = src2 */ } else { SubDstr (&src2, &src1, &dst); /* src1 - src2 */ dst.sign = src1.sign; /* sign = src1 */ } V = 0; /* can't carry */ } else { /* addition */ V = AddDstr (&src1, &src2, &dst, 0); /* add magnitudes */ dst.sign = src1.sign; /* set result sign */ } cc = WriteDstr (op[4], op[5], &dst, V, acc); /* store result */ R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[3]; if (opc & 1) { /* ADDP6, SUBP6? */ R[4] = 0; R[5] = op[5]; } return cc; /* MULP Operands: op[0:1] = src1 string descriptor op[2:3] = src2 string descriptor op[4:5] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of src1 string R2 = 0 R3 = addr of src2 string R4 = 0 R5 = addr of dest string */ case MULP: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31) || (op[4] > 31)) RSVD_OPND_FAULT; dst = Dstr_zero; /* clear result */ if (ReadDstr (op[0], op[1], &src1, acc) && /* read src1, src2 */ ReadDstr (op[2], op[3], &src2, acc)) { /* if both > 0 */ dst.sign = src1.sign ^ src2.sign; /* sign of result */ accum = Dstr_zero; /* clear accum */ NibbleRshift (&src1, 1, 0); /* shift out sign */ CreateTable (&src1, mptable); /* create *1, *2, ... */ for (i = 1; i < (DSTRLNT * 8); i++) { /* 31 iterations */ d = (src2.val[i / 8] >> ((i % 8) * 4)) & 0xF; if (d > 0) /* add in digit*mpcnd */ AddDstr (&mptable[d], &accum, &accum, 0); nc = NibbleRshift (&accum, 1, 0); /* ac right 4 */ NibbleRshift (&dst, 1, nc); /* result right 4 */ } V = TestDstr (&accum) != 0; /* if ovflo, set V */ } else V = 0; /* result = 0 */ cc = WriteDstr (op[4], op[5], &dst, V, acc); /* store result */ R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[3]; R[4] = 0; R[5] = op[5]; return cc; /* DIVP Operands: op[0:1] = src1 string descriptor op[2:3] = src2 string descriptor op[4:5] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of src1 string R2 = 0 R3 = addr of src2 string R4 = 0 R5 = addr of dest string */ case DIVP: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31) || (op[4] > 31)) RSVD_OPND_FAULT; ldivr = ReadDstr (op[0], op[1], &src1, acc); /* get divisor */ if (ldivr == 0) { /* divisor = 0? */ SET_TRAP (TRAP_FLTDIV); /* dec div trap */ return cc; } ldivr = LntDstr (&src1, ldivr); /* get exact length */ ldivd = ReadDstr (op[2], op[3], &src2, acc); /* get dividend */ ldivd = LntDstr (&src2, ldivd); /* get exact length */ dst = Dstr_zero; /* clear dest */ NibbleRshift (&src1, 1, 0); /* right justify ops */ NibbleRshift (&src2, 1, 0); if ((t = ldivd - ldivr) >= 0) { /* any divide to do? */ dst.sign = src1.sign ^ src2.sign; /* calculate sign */ WordLshift (&src1, t / 8); /* align divr to divd */ NibbleLshift (&src1, t % 8, 0); CreateTable (&src1, mptable); /* create *1, *2, ... */ for (i = 0; i <= t; i++) { /* divide loop */ for (d = 9; d > 0; d--) { /* find digit */ if (CmpDstr (&src2, &mptable[d]) >= 0) { SubDstr (&mptable[d], &src2, &src2); dst.val[0] = dst.val[0] | d; break; } /* end if */ } /* end for */ NibbleLshift (&src2, 1, 0); /* shift dividend */ NibbleLshift (&dst, 1, 0); /* shift quotient */ } /* end divide loop */ } /* end if */ cc = WriteDstr (op[4], op[5], &dst, 0, acc); /* store result */ R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[3]; R[4] = 0; R[5] = op[5]; return cc; /* CMPP3, CMPP4 Operands (CMPP3): op[0] = string length op[1], op[2] = string lengths Operands (CMPP4): op[0:1] = string1 descriptor op[2:3] = string2 descriptor Condition codes: NZ = set from comparison VC = 0 Registers: R0 = 0 R1 = addr of src1 string R2 = 0 R3 = addr of src2 string */ case CMPP3: op[3] = op[2]; /* reposition ops */ op[2] = op[0]; case CMPP4: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31)) RSVD_OPND_FAULT; ReadDstr (op[0], op[1], &src1, acc); /* get src1 */ ReadDstr (op[2], op[3], &src2, acc); /* get src2 */ cc = 0; if (src1.sign != src2.sign) cc = (src1.sign)? CC_N: 0; else { t = CmpDstr (&src1, &src2); /* compare strings */ if (t < 0) cc = (src1.sign? 0: CC_N); else if (t > 0) cc = (src1.sign? CC_N: 0); else cc = CC_Z; } R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[3]; return cc ; /* ASHP Operands: op[0] = shift count op[1:2] = source string descriptor op[3] = round digit op[4:5] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of src1 string R2 = 0 R3 = addr of src2 string */ case ASHP: if ((PSL & PSL_FPD) || (op[1] > 31) || (op[4] > 31)) RSVD_OPND_FAULT; ReadDstr (op[1], op[2], &src1, acc); /* get source */ V = 0; /* init V */ shift = op[0]; /* get shift count */ if (shift & BSIGN) { /* right shift? */ shift = BMASK + 1 - shift; /* !shift! */ WordRshift (&src1, shift / 8); /* do word shifts */ NibbleRshift (&src1, shift % 8, 0); /* do nibble shifts */ t = op[3] & 0xF; /* get round nibble */ if ((t + (src1.val[0] & 0xF)) > 9) /* rounding needed? */ AddDstr (&src1, &Dstr_one, &src1, 0); /* round */ src1.val[0] = src1.val[0] & ~0xF; /* clear sign */ } /* end right shift */ else if (shift) { /* left shift? */ if (WordLshift (&src1, shift / 8)) /* do word shifts */ V = 1; if (NibbleLshift (&src1, shift % 8, 0)) V = 1; } /* end left shift */ cc = WriteDstr (op[4], op[5], &src1, V, acc); /* store result */ R[0] = 0; R[1] = op[2]; R[2] = 0; R[3] = op[5]; return cc ; /* CVTPL Operands: op[0:1] = source string descriptor op[2] = memory flag/register number op[3] = memory address Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of source string R2 = 0 R3 = 0 */ case CVTPL: if ((PSL & PSL_FPD) || (op[0] > 31)) RSVD_OPND_FAULT; ReadDstr (op[0], op[1], &src1, acc); /* get source */ V = result = 0; /* clear V, result */ for (i = (DSTRLNT * 8) - 1; i > 0; i--) { /* loop thru digits */ d = (src1.val[i / 8] >> ((i % 8) * 4)) & 0xF; if (d || result || V) { /* skip initial 0's */ if (result >= MAXDVAL) V = 1; result = ((result * 10) + d) & LMASK; if (result < d) V = 1; } /* end if */ } /* end for */ if (src1.sign) /* negative? */ result = (~result + 1) & LMASK; if (src1.sign ^ ((result & LSIGN) != 0)) /* test for overflow */ V = 1; if (op[2] < 0) /* if mem, store result */ Write (op[3], result, L_LONG, WA); /* before reg update */ R[0] = 0; /* update registers */ R[1] = op[1]; R[2] = 0; R[3] = 0; if (op[2] >= 0) /* if reg, store result */ R[op[2]] = result; /* after reg update */ if (V && (PSL & PSW_IV)) /* ovflo and IV? trap */ SET_TRAP (TRAP_INTOV); CC_IIZZ_L (result); return cc | (V? CC_V: 0); /* CVTLP Operands: op[0] = source long op[1:2] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = 0 R2 = 0 R3 = addr of dest string */ case CVTLP: if ((PSL & PSL_FPD) || (op[1] > 31)) RSVD_OPND_FAULT; dst = Dstr_zero; /* clear result */ result = op[0]; if ((result & LSIGN) != 0) { dst.sign = 1; result = (~result + 1) & LMASK; } for (i = 1; (i < (DSTRLNT * 8)) && result; i++) { d = result % 10; result = result / 10; dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4)); } cc = WriteDstr (op[1], op[2], &dst, 0, acc); /* write result */ R[0] = 0; R[1] = 0; R[2] = 0; R[3] = op[2]; return cc; /* CVTSP Operands: op[0:1] = source string descriptor op[2:3] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = address of sign byte of source string R2 = 0 R3 = addr of dest string */ case CVTSP: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31)) RSVD_OPND_FAULT; dst = Dstr_zero; /* clear result */ t = Read (op[1], L_BYTE, RA); /* read source sign */ if (t == C_MINUS) /* sign -, */ dst.sign = 1; else if ((t != C_PLUS) && (t != C_SPACE)) /* + or blank? */ RSVD_OPND_FAULT; for (i = 1; i <= op[0]; i++) { /* loop thru chars */ c = Read ((op[1] + op[0] + 1 - i) & LMASK, L_BYTE, RA); if ((c < C_ZERO) || (c > C_NINE)) /* [0:9]? */ RSVD_OPND_FAULT; d = c & 0xF; dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4)); } TestDstr (&dst); /* correct -0 */ cc = WriteDstr (op[2], op[3], &dst, 0, acc); /* write result */ R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[3]; return cc; /* CVTPS Operands: op[0:1] = source string descriptor op[2:3] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of source string R2 = 0 R3 = addr of dest string */ case CVTPS: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31)) RSVD_OPND_FAULT; lenl = ReadDstr (op[0], op[1], &dst, acc); /* get source, lw len */ lenp = LntDstr (&dst, lenl); /* get exact nz src len */ ProbeDstr (op[2], op[3], WA); /* test dst write */ Write (op[3], dst.sign? C_MINUS: C_PLUS, L_BYTE, WA); for (i = 1; i <= op[2]; i++) { /* loop thru chars */ d = (dst.val[i / 8] >> ((i % 8) * 4)) & 0xF;/* get digit */ c = d | C_ZERO; /* cvt to ASCII */ Write ((op[3] + op[2] + 1 - i) & LMASK, c, L_BYTE, WA); } cc = SetCCDstr (op[0], &dst, 0); /* set cc's */ if (lenp > op[2]) { /* src fit in dst? */ cc = cc | CC_V; /* set ovflo */ if (PSL & PSW_DV) SET_TRAP (TRAP_DECOVF); /* if enabled, trap */ } R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[3]; return cc; /* CVTTP Operands: op[0:1] = source string descriptor op[2] = table address op[3:4] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of source string R2 = 0 R3 = addr of dest string */ case CVTTP: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[3] > 31)) RSVD_OPND_FAULT; dst = Dstr_zero; /* clear result */ for (i = 1; i <= op[0]; i++) { /* loop thru char */ c = Read ((op[1] + op[0] - i) & LMASK, L_BYTE, RA); /* read char */ if (i != 1) { /* normal byte? */ if ((c < C_ZERO) || (c > C_NINE)) /* valid digit? */ RSVD_OPND_FAULT; d = c & 0xF; } else { /* highest byte */ t = Read ((op[2] + c) & LMASK, L_BYTE, RA); /* xlate */ d = (t >> 4) & 0xF; /* digit */ t = t & 0xF; /* sign */ if ((d > 0x9) || (t < 0xA)) RSVD_OPND_FAULT; if ((t == 0xB) || (t == 0xD)) dst.sign = 1; } dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4)); } TestDstr (&dst); /* correct -0 */ cc = WriteDstr (op[3], op[4], &dst, 0, acc); /* write result */ R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[4]; return cc; /* CVTPT Operands: op[0:1] = source string descriptor op[2] = table address op[3:4] = dest string descriptor Condition codes: NZV = set from result C = 0 Registers: R0 = 0 R1 = addr of source string R2 = 0 R3 = addr of dest string */ case CVTPT: if ((PSL & PSL_FPD) || (op[0] > 31) || (op[3] > 31)) RSVD_OPND_FAULT; lenl = ReadDstr (op[0], op[1], &dst, acc); /* get source, lw len */ lenp = LntDstr (&dst, lenl); /* get exact src len */ ProbeDstr (op[3], op[4], WA); /* test writeability */ for (i = 1; i <= op[3]; i++) { /* loop thru chars */ if (i != 1) { /* not last? */ d = (dst.val[i / 8] >> ((i % 8) * 4)) & 0xF; /* get digit */ c = d + C_ZERO; /* convert */ } else { /* translate last */ t = Read ((op[1] + (op[0] / 2)) & LMASK, L_BYTE, RA); c = Read ((op[2] + t) & LMASK, L_BYTE, RA); } Write ((op[4] + op[3] - i) & LMASK, c, L_BYTE, WA); } cc = SetCCDstr (op[0], &dst, 0); /* set cc's from src */ if (lenp > op[3]) { /* src fit in dst? */ cc = cc | CC_V; /* set ovflo */ if (PSL & PSW_DV) SET_TRAP (TRAP_DECOVF); /* if enabled, trap */ } R[0] = 0; R[1] = op[1]; R[2] = 0; R[3] = op[4]; return cc; /* EDITPC Operands: op[0:1] = source string descriptor op[2] = pattern string address op[3] = dest string address Condition codes: N = source is negative Z = source is zero V = significant digits lost C = significant digits seen Registers at packup: R0<31:16> = -count of source zeroes to supply R0<15:0> = remaining source length R1 = source address R2<31:24> = delta PC R2<19:16> = condition codes R2<15:8> = sign char R2<7:0> = fill char R3 = pattern string address R4 = original source length R5 = dest string addr Registers at end: R0 = source length R1 = source addr R2 = 0 R3 = addr of byte containing EO$END R4 = 0 R5 = addr of end of dst string + 1 Fault and abort conditions for EDITPC are complicated. In general: - It is safe to take a memory management fault on the read of any pattern byte. After correction of the fault, the pattern operator is fetched and executed again. - It is safe to take a memory management fault on a write-only operation, like fill. After correction of the fault, the pattern operator is fetched and executed again. - The move operators do not alter visible state (registers or saved cc) until all memory operations are complete. */ case EDITPC: if (PSL & PSL_FPD) { /* FPD set? */ SETPC (fault_PC + STR_GETDPC (R[2])); /* reset PC */ fill = ED_GETFILL (R[2]); /* get fill */ sign = ED_GETSIGN (R[2]); /* get sign */ cc = ED_GETCC (R[2]); /* get cc's */ R[0] = R[0] & ~0xFFE0; /* src len <= 31 */ } else { /* new instr */ if (op[0] > 31) /* lnt > 31? */ RSVD_OPND_FAULT; t = Read ((op[1] + (op[0] / 2)) & LMASK, L_BYTE, RA) & 0xF; if ((t == 0xB) || (t == 0xD)) { cc = CC_N | CC_Z; sign = C_MINUS; } else { cc = CC_Z; sign = C_SPACE; } fill = C_SPACE; R[0] = R[4] = op[0]; /* src len */ R[1] = op[1]; /* src addr */ R[2] = STR_PACK (cc, (sign << ED_V_SIGN) | (fill << ED_V_FILL)); /* delta PC, cc, sign, fill */ R[3] = op[2]; /* pattern */ R[5] = op[3]; /* dst addr */ PSL = PSL | PSL_FPD; /* set FPD */ } for ( ;; ) { /* loop thru pattern */ pop = Read (R[3], L_BYTE, RA); /* rd pattern op */ if (pop == EO_END) /* end? */ break; if (pop & EO_RPT_FLAG) { /* repeat class? */ rpt = pop & EO_RPT_MASK; /* isolate count */ if (rpt == 0) /* can't be zero */ RSVD_OPND_FAULT; pop = pop & ~EO_RPT_MASK; /* isolate op */ } switch (pop) { /* case on op */ case EO_END_FLOAT: /* end float */ if (!(cc & CC_C)) { /* not signif? */ Write (R[5], sign, L_BYTE, WA); /* write sign */ R[5] = (R[5] + 1) & LMASK; /* now fault safe */ cc = cc | CC_C; /* set signif */ } break; case EO_CLR_SIGNIF: /* clear signif */ cc = cc & ~CC_C; /* clr C */ break; case EO_SET_SIGNIF: /* set signif */ cc = cc | CC_C; /* set C */ break; case EO_STORE_SIGN: /* store sign */ Write (R[5], sign, L_BYTE, WA); /* write sign */ R[5] = (R[5] + 1) & LMASK; /* now fault safe */ break; case EO_LOAD_FILL: /* load fill */ fill = Read ((R[3] + 1) & LMASK, L_BYTE, RA); R[2] = ED_PUTFILL (R[2], fill); /* now fault safe */ R[3]++; break; case EO_LOAD_SIGN: /* load sign */ sign = edit_read_sign (acc); R[3]++; break; case EO_LOAD_PLUS: /* load sign if + */ if (!(cc & CC_N)) sign = edit_read_sign (acc); R[3]++; break; case EO_LOAD_MINUS: /* load sign if - */ if (cc & CC_N) sign = edit_read_sign (acc); R[3]++; break; case EO_INSERT: /* insert char */ c = Read ((R[3] + 1) & LMASK, L_BYTE, RA); Write (R[5], ((cc & CC_C)? c: fill), L_BYTE, WA); R[5] = (R[5] + 1) & LMASK; /* now fault safe */ R[3]++; break; case EO_BLANK_ZERO: /* blank zero */ t = Read ((R[3] + 1) & LMASK, L_BYTE, RA); if (t == 0) RSVD_OPND_FAULT; if (cc & CC_Z) { /* zero? */ do { /* repeat and blank */ Write ((R[5] - t) & LMASK, fill, L_BYTE, WA); } while (--t); } R[3]++; /* now fault safe */ break; case EO_REPL_SIGN: /* replace sign */ t = Read ((R[3] + 1) & LMASK, L_BYTE, RA); if (t == 0) RSVD_OPND_FAULT; if (cc & CC_Z) Write ((R[5] - t) & LMASK, fill, L_BYTE, WA); R[3]++; /* now fault safe */ break; case EO_ADJUST_LNT: /* adjust length */ t = Read ((R[3] + 1) & LMASK, L_BYTE, RA); if ((t == 0) || (t > 31)) RSVD_OPND_FAULT; R[0] = R[0] & WMASK; /* clr old ld zero */ if (R[0] > t) { /* decrease */ for (i = 0; i < (R[0] - t); i++) { /* loop thru src */ d = edit_read_src (i, acc); /* get nibble */ if (d) cc = (cc | CC_V | CC_C) & ~CC_Z; } /* end for */ edit_adv_src (R[0] - t); /* adv src ptr */ } /* end else */ else R[0] = R[0] | (((R[0] - t) & WMASK) << 16); R[3]++; break; case EO_FILL: /* fill */ for (i = 0; i < rpt; i++) /* fill string */ Write ((R[5] + i) & LMASK, fill, L_BYTE, WA); R[5] = (R[5] + rpt) & LMASK; /* now fault safe */ break; case EO_MOVE: for (i = 0; i < rpt; i++) { /* for repeat */ d = edit_read_src (i, acc); /* get nibble */ if (d) /* test for non-zero */ cc = (cc | CC_C) & ~CC_Z; c = (cc & CC_C)? (d | 0x30): fill; /* test for signif */ Write ((R[5] + i) & LMASK, c, L_BYTE, WA); } /* end for */ edit_adv_src (rpt); /* advance src */ R[5] = (R[5] + rpt) & LMASK; /* advance dst */ break; case EO_FLOAT: for (i = j = 0; i < rpt; i++, j++) { /* for repeat */ d = edit_read_src (i, acc); /* get nibble */ if (d && !(cc & CC_C)) { /* nz, signif clear? */ Write ((R[5] + j) & LMASK, sign, L_BYTE, WA); cc = (cc | CC_C) & ~CC_Z; /* set signif */ j++; /* extra dst char */ } /* end if */ c = (cc & CC_C)? (d | 0x30): fill; /* test for signif */ Write ((R[5] + j) & LMASK, c, L_BYTE, WA); } /* end for */ edit_adv_src (rpt); /* advance src */ R[5] = (R[5] + j) & LMASK; /* advance dst */ break; default: /* undefined */ RSVD_OPND_FAULT; } /* end case pattern */ R[3] = (R[3] + 1) & LMASK; /* next pattern byte */ R[2] = ED_PUTCC (R[2], cc); /* update cc's */ } /* end for pattern */ if (R[0]) /* pattern too short */ RSVD_OPND_FAULT; PSL = PSL & ~PSL_FPD; /* clear FPD */ if (cc & CC_Z) /* zero? clear n */ cc = cc & ~CC_N; if ((cc & CC_V) && (PSL & PSW_DV)) /* overflow & trap enabled? */ SET_TRAP (TRAP_DECOVF); R[0] = R[4]; /* restore src len */ R[1] = R[1] - (R[0] >> 1); /* restore src addr */ R[2] = R[4] = 0; return cc; default: RSVD_INST_FAULT; } /* end case op */ return cc; } /* Get packed decimal string Arguments: lnt = decimal string length adr = decimal string address src = decimal string structure acc = access mode The routine returns the length in int32's of the non-zero part of the string. To simplify the code elsewhere, digits are range checked, and bad digits cause a fault. */ int32 ReadDstr (int32 lnt, int32 adr, DSTR *src, int32 acc) { int32 c, i, end, t; *src = Dstr_zero; /* clear result */ end = lnt / 2; /* last byte */ for (i = 0; i <= end; i++) { /* loop thru string */ c = Read ((adr + end - i) & LMASK, L_BYTE, RA); /* get byte */ if (i == 0) { /* sign char? */ t = c & 0xF; /* save sign */ c = c & 0xF0; /* erase sign */ } if ((i == end) && ((lnt & 1) == 0)) c = c & 0xF; /* if (((c & 0xF0) > 0x90) || *//* check hi digit */ /* ((c & 0x0F) > 0x09)) *//* check lo digit */ /* RSVD_OPND_FAULT; */ src->val[i / 4] = src->val[i / 4] | (c << ((i % 4) * 8)); } /* end for */ if ((t == 0xB) || (t == 0xD)) /* if -, set sign */ src->sign = 1; return TestDstr (src); /* clean -0 */ } /* Store decimal string Arguments: lnt = decimal string length adr = decimal string address dst = decimal string structure V = initial overflow flag acc = access mode Returns condition codes. PSL.NZ are also set to their proper values PSL.V will be set on overflow; it must be initialized elsewhere (to allow for external overflow calculations) The rules for the stored sign and the PSW sign are: - Stored sign is negative if input is negative, and the result is non-zero or there was overflow - PSL sign is negative if input is negative, and the result is non-zero Thus, the stored sign and the PSL sign will differ in one case: a negative zero generated by overflow is stored with a negative sign, but PSL.N is clear */ int32 WriteDstr (int32 lnt, int32 adr, DSTR *dst, int32 pslv, int32 acc) { int32 c, i, cc, end; end = lnt / 2; /* end of string */ ProbeDstr (end, adr, WA); /* test writeability */ cc = SetCCDstr (lnt, dst, pslv); /* set cond codes */ dst->val[0] = dst->val[0] | 0xC | dst->sign; /* set sign */ for (i = 0; i <= end; i++) { /* store string */ c = (dst->val[i / 4] >> ((i % 4) * 8)) & 0xFF; Write ((adr + end - i) & LMASK, c, L_BYTE, WA); } /* end for */ return cc; } /* Set CC for decimal string Arguments: lnt = string length dst = decimal string structure pslv = initial V Output: cc = condition codes */ int32 SetCCDstr (int32 lnt, DSTR *dst, int32 pslv) { int32 psln, pslz, i, limit; uint32 mask; static uint32 masktab[8] = { 0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000, 0xFFF00000, 0xFF000000, 0xF0000000, 0x00000000 }; mask = 0; /* can't ovflo */ pslz = 1; /* assume all 0's */ limit = lnt / 8; /* limit for test */ for (i = 0; i < DSTRLNT; i++) { /* loop thru value */ if (i == limit) /* at limit, get mask */ mask = masktab[lnt % 8]; else if (i > limit) /* beyond, all ovflo */ mask = 0xFFFFFFFF; if (dst->val[i] & mask) /* test for ovflo */ pslv = 1; dst->val[i] = dst->val[i] & ~mask; /* clr digits past end */ if (dst->val[i]) /* test nz */ pslz = 0; } dst->sign = dst->sign & ~(pslz & ~pslv); psln = dst->sign & ~pslz; /* N = sign, if ~zero */ if (pslv && (PSL & PSW_DV)) SET_TRAP (TRAP_DECOVF); return (psln? CC_N: 0) | (pslz? CC_Z: 0) | (pslv? CC_V: 0); } /* Probe decimal string for accessibility */ void ProbeDstr (int32 lnt, int32 addr, int32 acc) { Read (addr, L_BYTE, acc); Read ((addr + lnt) & LMASK, L_BYTE, acc); return; } /* Add decimal string magnitudes Arguments: s1 = src1 decimal string s2 = src2 decimal string ds = dest decimal string cy = carry in Output = 1 if carry, 0 if no carry This algorithm courtesy Anton Chernoff, circa 1992 or even earlier. We trace the history of a pair of adjacent digits to see how the carry is fixed; each parenthesized item is a 4b digit. Assume we are adding: (a)(b) I + (x)(y) J First compute I^J: (a^x)(b^y) TMP Note that the low bit of each digit is the same as the low bit of the sum of the digits, ignoring the carry, since the low bit of the sum is the xor of the bits. Now compute I+J+66 to get decimal addition with carry forced left one digit: (a+x+6+carry mod 16)(b+y+6 mod 16) SUM Note that if there was a carry from b+y+6, then the low bit of the left digit is different from the expected low bit from the xor. If we xor this SUM into TMP, then the low bit of each digit is 1 if there was a carry, and 0 if not. We need to subtract 6 from each digit that did not have a carry, so take ~(SUM ^ TMP) & 0x11, shift it right 4 to the digits that are affected, and subtract 6*adjustment (actually, shift it right 3 and subtract 3*adjustment). */ int32 AddDstr (DSTR *s1, DSTR *s2, DSTR *ds, int32 cy) { int32 i; uint32 sm1, sm2, tm1, tm2, tm3, tm4; for (i = 0; i < DSTRLNT; i++) { /* loop low to high */ tm1 = s1->val[i] ^ (s2->val[i] + cy); /* xor operands */ sm1 = s1->val[i] + (s2->val[i] + cy); /* sum operands */ sm2 = sm1 + 0x66666666; /* force carry out */ cy = ((sm1 < s1->val[i]) || (sm2 < sm1)); /* check for overflow */ tm2 = tm1 ^ sm2; /* get carry flags */ tm3 = (tm2 >> 3) | (cy << 29); /* compute adjustment */ tm4 = 0x22222222 & ~tm3; /* clear where carry */ ds->val[i] = (sm2 - (3 * tm4)) & LMASK; /* final result */ } return cy; } /* Subtract decimal string magnitudes Arguments: s1 = src1 decimal string s2 = src2 decimal string ds = dest decimal string Outputs: s2 - s1 in ds Note: the routine assumes that s1 <= s2 */ void SubDstr (DSTR *s1, DSTR *s2, DSTR *ds) { int32 i; DSTR complX; for (i = 0; i < DSTRLNT; i++) /* 10's comp s2 */ complX.val[i] = 0x99999999 - s1->val[i]; AddDstr (&complX, s2, ds, 1); /* s1 + ~s2 + 1 */ return; } /* Compare decimal string magnitudes Arguments: s1 = src1 decimal string s2 = src2 decimal string Output = 1 if >, 0 if =, -1 if < */ int32 CmpDstr (DSTR *s1, DSTR *s2) { int32 i; for (i = DSTRMAX; i >=0; i--) { if (s1->val[i] > s2->val[i]) return 1; if (s1->val[i] < s2->val[i]) return -1; } return 0; } /* Test decimal string for zero Arguments: dsrc = decimal string structure Returns the non-zero length of the string, in int32 units If the string is zero, the sign is cleared */ int32 TestDstr (DSTR *dsrc) { int32 i; for (i = DSTRMAX; i >= 0; i--) { if (dsrc->val[i]) return (i + 1); } dsrc->sign = 0; return 0; } /* Get exact length of decimal string Arguments: dsrc = decimal string structure nz = result from TestDstr */ int32 LntDstr (DSTR *dsrc, int32 nz) { int32 i; if (nz == 0) return 0; for (i = 7; i >= 0; i--) { if ((dsrc->val[nz - 1] >> (i * 4)) & 0xF) break; } return ((nz - 1) * 8) + i; } /* Create table of multiples Arguments: dsrc = base decimal string structure mtable[10] = array of decimal string structures Note that dsrc has a high order zero nibble; this guarantees that the largest multiple won't overflow Also note that mtable[0] is not filled in */ void CreateTable (DSTR *dsrc, DSTR mtable[10]) { int32 (i); mtable[1] = *dsrc; for (i = 2; i < 10; i++) AddDstr (&mtable[1], &mtable[i-1], &mtable[i], 0); return; } /* Word shift right Arguments: dsrc = decimal string structure sc = shift count */ void WordRshift (DSTR *dsrc, int32 sc) { int32 i; if (sc) { for (i = 0; i < DSTRLNT; i++) { if ((i + sc) < DSTRLNT) dsrc->val[i] = dsrc->val[i + sc]; else dsrc->val[i] = 0; } } return; } /* Word shift left Arguments: dsrc = decimal string structure sc = shift count */ int32 WordLshift (DSTR *dsrc, int32 sc) { int32 i, c; c = 0; if (sc) { for (i = DSTRMAX; i >= 0; i--) { if (i >= sc) dsrc->val[i] = dsrc->val[i - sc]; else { c |= dsrc->val[i]; dsrc->val[i] = 0; } } } return c; } /* Nibble shift decimal string right Arguments: dsrc = decimal string structure sc = shift count cin = carry in */ uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin) { int32 i, s, nc; if ((s = sc * 4)) { for (i = DSTRMAX; i >= 0; i--) { nc = (dsrc->val[i] << (32 - s)) & LMASK; dsrc->val[i] = ((dsrc->val[i] >> s) | cin) & LMASK; cin = nc; } return cin; } return 0; } /* Nibble shift decimal string left Arguments: dsrc = decimal string structure sc = shift count cin = carry in */ uint32 NibbleLshift (DSTR *dsrc, int32 sc, uint32 cin) { int32 i, s, nc; if ((s = sc * 4)) { for (i = 0; i < DSTRLNT; i++) { nc = dsrc->val[i] >> (32 - s); dsrc->val[i] = ((dsrc->val[i] << s) | cin) & LMASK; cin = nc; } return cin; } return 0; } /* Do 4b of CRC calculation Arguments: crc = current CRC ^ char tbl = 16 lw table base Output: new CRC */ int32 do_crc_4b (int32 crc, int32 tbl, int32 acc) { int32 idx = (crc & 0xF) << 2; int32 t; crc = (crc >> 4) & 0x0FFFFFFF; t = Read ((tbl + idx) & LMASK, L_LONG, RA); return crc ^ t; } /* Edit routines */ int32 edit_read_src (int32 inc, int32 acc) { int32 c, r0, r1; if (R[0] & LSIGN) { /* ld zeroes? */ r0 = (R[0] + (inc << 16)) & LMASK; /* retire increment */ if (r0 & LSIGN) /* more? return 0 */ return 0; inc = (r0 >> 16) & 0x1F; /* effective inc */ } r1 = (R[1] + (inc / 2) + ((~R[0] & inc) & 1)) & LMASK; /* eff addr */ r0 = (R[0] - inc) & 0x1F; /* eff lnt left */ if (r0 == 0) { /* nothing left? */ R[0] = -1; /* out of input */ RSVD_OPND_FAULT; } c = Read (r1, L_BYTE, RA); return (((r0 & 1)? (c >> 4): c) & 0xF); } void edit_adv_src (int32 inc) { if (R[0] & LSIGN) { /* ld zeroes? */ R[0] = (R[0] + (inc << 16)) & LMASK; /* retire 0's */ if (R[0] & LSIGN) /* more to do? */ return; inc = (R[0] >> 16) & 0x1F; /* get excess */ if (inc == 0) /* more to do? */ return; } R[1] = (R[1] + (inc / 2) + ((~R[0] & inc) & 1)) & LMASK;/* retire src */ R[0] = (R[0] - inc) & 0x1F; return; } int32 edit_read_sign (int32 acc) { int32 sign; sign = Read ((R[3] + 1) & LMASK, L_BYTE, RA); /* read */ R[2] = ED_PUTSIGN (R[2], sign); /* now fault safe */ return sign; } #else /* CIS instructions - invoke emulator interface opnd[0:5] = six operands to be pushed (if PSL = 0) cc = condition codes opc = opcode If FPD is set, push old PC and PSL on stack, vector thru SCB. If FPD is clear, push opcode, old PC, operands, new PC, and PSL on stack, vector thru SCB. In both cases, the exception occurs in the current mode. */ int32 op_cis (int32 *opnd, int32 cc, int32 opc, int32 acc) { int32 vec; if (PSL & PSL_FPD) { /* FPD set? */ Read (SP - 1, L_BYTE, WA); /* wchk stack */ Write (SP - 8, fault_PC, L_LONG, WA); /* push old PC */ Write (SP - 4, PSL | cc, L_LONG, WA); /* push PSL */ SP = SP - 8; /* decr stk ptr */ vec = ReadLP ((SCBB + SCB_EMULFPD) & PAMASK); } else { if (opc == CVTPL) /* CVTPL? .wl */ opnd[2] = (opnd[2] >= 0)? ~opnd[2]: opnd[3]; Read (SP - 1, L_BYTE, WA); /* wchk stack */ Write (SP - 48, opc, L_LONG, WA); /* push opcode */ Write (SP - 44, fault_PC, L_LONG, WA); /* push old PC */ Write (SP - 40, opnd[0], L_LONG, WA); /* push operands */ Write (SP - 36, opnd[1], L_LONG, WA); Write (SP - 32, opnd[2], L_LONG, WA); Write (SP - 28, opnd[3], L_LONG, WA); Write (SP - 24, opnd[4], L_LONG, WA); Write (SP - 20, opnd[5], L_LONG, WA); Write (SP - 8, PC, L_LONG, WA); /* push cur PC */ Write (SP - 4, PSL | cc, L_LONG, WA); /* push PSL */ SP = SP - 48; /* decr stk ptr */ vec = ReadLP ((SCBB + SCB_EMULATE) & PAMASK); } PSL = PSL & ~(PSL_TP | PSL_FPD | PSW_DV | PSW_FU | PSW_IV | PSW_T); JUMP (vec & ~03); /* set new PC */ return 0; /* set new cc's */ } #endif