1657 lines
64 KiB
C
1657 lines
64 KiB
C
/* vax_cis.c: VAX CIS instructions
|
|
|
|
Copyright (c) 2004-2016, 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.
|
|
|
|
30-May-16 RMS Fixed WordLshift FOR REAL
|
|
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"
|
|
|
|
/* 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];
|
|
|
|
if (((DR_GETIGRP(IG_PACKD) == DR_GETIGRP(drom[opc][0])) &&
|
|
(!(cpu_instruction_set & VAX_PACKED))) ||
|
|
((DR_GETIGRP(IG_EMONL) == DR_GETIGRP(drom[opc][0])) &&
|
|
(!(cpu_instruction_set & VAX_EMONL)))) { /* Emulated? */
|
|
/* CIS and emulate only instructions - invoke emulator interface
|
|
opnd[0:5] = six operands to be pushed (if PSL<fpd> = 0)
|
|
cc = condition codes
|
|
opc = opcode
|
|
*/
|
|
return cpu_emulate_exception (op, cc, opc, acc);
|
|
}
|
|
|
|
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(MOVP);
|
|
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];
|
|
/* fall through */
|
|
case ADDP6: case SUBP6:
|
|
if ((PSL & PSL_FPD) || (op[0] > 31) ||
|
|
(op[2] > 31) || (op[4] > 31))
|
|
RSVD_OPND_FAULT(ADDP-SUBP);
|
|
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(MULP);
|
|
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(DIVP);
|
|
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];
|
|
/* fall through */
|
|
case CMPP4:
|
|
if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31))
|
|
RSVD_OPND_FAULT(CMPP);
|
|
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(ASHP);
|
|
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(CVTPL);
|
|
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(CVTLP);
|
|
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(CVTSP);
|
|
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(CVTSP);
|
|
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(CVTSP);
|
|
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(CVTPS);
|
|
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(CVTTP);
|
|
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(CVTTP);
|
|
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(CVTTP);
|
|
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(CVTPT);
|
|
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(EDITPC);
|
|
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(EDITPC);
|
|
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(EDITPC);
|
|
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(EDITPC);
|
|
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(EDITPC);
|
|
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(EDITPC);
|
|
} /* 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(EDITPC);
|
|
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(opc);
|
|
}
|
|
/* 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 != 0) {
|
|
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, zc;
|
|
|
|
c = 0;
|
|
if (sc != 0) {
|
|
for (i = DSTRMAX; i >= 0; i--) { /* work hi to low */
|
|
if ((i + sc) <= DSTRMAX) /* move in range? */
|
|
dsrc->val[i + sc] = dsrc->val[i];
|
|
else c |= dsrc->val[i]; /* no, count as ovflo */
|
|
}
|
|
zc = (sc >= DSTRLNT)? DSTRLNT: sc; /* cap fill */
|
|
for (i = 0; i < zc; i++) /* fill with 0s */
|
|
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) != 0) {
|
|
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) != 0) {
|
|
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(edit_read_src);
|
|
}
|
|
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;
|
|
}
|