blob: e75a53b8d84ada6e5fe34220fa4c54f52b98062c [file] [log] [blame] [raw]
/* 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 };
extern int32 R[16];
extern int32 PSL;
extern int32 trpirq;
extern int32 p1;
extern int32 fault_PC;
extern int32 ibcnt, ppc;
extern int32 sim_interval;
extern jmp_buf save_env;
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<fpd> = 0:
op[0:1] = source string descriptor
op[2] = fill character
op[3] = table address
op[4:5] = destination string descriptor
Registers if PSL<fpd> = 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<fpd> = 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<fpd> = 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<fpd> = 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 compl;
for (i = 0; i < DSTRLNT; i++) /* 10's comp s2 */
compl.val[i] = 0x99999999 - s1->val[i];
AddDstr (&compl, 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
extern int32 R[16];
extern int32 PSL;
extern int32 SCBB;
extern int32 fault_PC;
extern int32 ibcnt, ppc;
extern int32 pcq[PCQ_SIZE];
extern int32 pcq_p;
extern jmp_buf save_env;
/* CIS instructions - invoke emulator interface
opnd[0:5] = six operands to be pushed (if PSL<fpd> = 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