- Prefer Bob Supnik's pdp11_ke.c since it will be the official one.

had to move "d sr" to before "set ke enable" because of conflict with
  ke's "sr".  Notified Bob, hopefully this will go away.


git-svn-id: http://unix-jun72.googlecode.com/svn/trunk@255 44b2186c-a14b-0410-8c95-595313601b93
This commit is contained in:
tim.newsham
2008-05-17 20:20:01 +00:00
parent e10e625c0d
commit 786d0f3122
2 changed files with 264 additions and 212 deletions

View File

@ -1,6 +1,6 @@
/* pdp11_ke.c: /* pdp11_ke.c: PDP-11/20 extended arithmetic element
Copyright (c) 1993-2005, Robert M Supnik Copyright (c) 1993-2008, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
@ -16,281 +16,333 @@
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 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 IN AN ke_ACTION OF CONTRke_ACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 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 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 used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik. in this Software without prior written authorization from Robert M Supnik.
Some of this code is lifted directly from Tim Shoppa's KE11A code in apout, This code draws on prior work by Tim Shoppa and Brad Parker. My thanks for
Brad Parker <brad@heeltoe.com> 5/2008 to them for letting me use their work.
01-May-08 JBP cloned from pdp11_pt.c EAE PDP-11/20 extended arithmetic element
*/ */
#include "pdp11_defs.h" #include "pdp11_defs.h"
//#define PT_DIS 0
extern int32 int_req[IPL_HLVL];
static int32 AC; #define GET_SIGN_L(v) (((v) >> 31) & 1)
static int32 MQ; #define GET_SIGN_W(v) (((v) >> 15) & 1)
static int32 SC; #define GET_SIGN_B(v) (((v) >> 7) & 1)
static int32 SR;
/* KE11A I/O address offsets 0177300 - 0177316 */
#define KE_DIV 000 /* divide */
#define KE_AC 002 /* accumulator */
#define KE_MQ 004 /* MQ */
#define KE_MUL 006 /* multiply */
#define KE_SC 010 /* step counter */
#define KE_NOR 012 /* normalize */
#define KE_LSH 014 /* logical shift */
#define KE_ASH 016 /* arithmetic shift */
/* Status register */
#define KE_SR_C 0001 /* carry */
#define KE_SR_SXT 0002 /* AC<15:0> = MQ<15> */
#define KE_SR_Z 0004 /* AC = MQ = 0 */
#define KE_SR_MQZ 0010 /* MQ = 0 */
#define KE_SR_ACZ 0020 /* AC = 0 */
#define KE_SR_ACM1 0040 /* AC = 177777 */
#define KE_SR_N 0100 /* last op negative */
#define KE_SR_NXV 0200 /* last op ovf XOR N */
#define KE_SR_DYN (KE_SR_SXT|KE_SR_Z|KE_SR_MQZ|KE_SR_ACZ|KE_SR_ACM1)
/* Visible state */
uint32 ke_AC = 0;
uint32 ke_MQ = 0;
uint32 ke_SC = 0;
uint32 ke_SR = 0;
DEVICE ke_dev; DEVICE ke_dev;
t_stat ke_rd (int32 *data, int32 PA, int32 access); t_stat ke_rd (int32 *data, int32 PA, int32 access);
t_stat ke_wr (int32 data, int32 PA, int32 access); t_stat ke_wr (int32 data, int32 PA, int32 access);
t_stat ke_svc (UNIT *uptr);
t_stat ke_reset (DEVICE *dptr); t_stat ke_reset (DEVICE *dptr);
t_stat ke_attach (UNIT *uptr, char *ptr); uint32 ke_set_SR (void);
t_stat ke_detach (UNIT *uptr);
DIB ke_dib = { IOBA_KE, IOLN_KE, &ke_rd, &ke_wr, 0 }; DIB ke_dib = { IOBA_KE, IOLN_KE, &ke_rd, &ke_wr, 0 };
UNIT ke_unit = { UNIT ke_unit = {
UDATA (&ke_svc, UNIT_DISABLE, 0) UDATA (NULL, UNIT_DISABLE, 0)
}; };
REG ke_reg[] = { REG ke_reg[] = {
{ ORDATA (KE_AC, AC, 16) }, { ORDATA (AC, ke_AC, 16) },
{ ORDATA (KE_MQ, MQ, 16) }, { ORDATA (MQ, ke_MQ, 16) },
{ ORDATA (KE_SC, SC, 16) }, { ORDATA (SC, ke_SC, 6) },
{ ORDATA (KE_SR, SR, 16) }, { ORDATA (SR, ke_SR, 8) },
{ NULL } { NULL }
}; };
MTAB ke_mod[] = { MTAB ke_mod[] = {
{ MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL,
NULL, &show_addr, NULL }, NULL, &show_addr, NULL },
{ MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL,
NULL, &show_vec, NULL },
{ 0 } { 0 }
}; };
DEVICE ke_dev = { DEVICE ke_dev = {
"KE", &ke_unit, ke_reg, ke_mod, "KE", &ke_unit, ke_reg, ke_mod,
1, 10, 31, 1, DEV_RDX, 8, 1, 10, 31, 1, 8, 8,
NULL, NULL, &ke_reset, NULL, NULL, &ke_reset,
NULL, &ke_attach, &ke_detach, NULL, NULL, NULL,
&ke_dib, DEV_DISABLE | /*PT_DIS |*/ DEV_UBUS | DEV_QBUS &ke_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS
}; };
/* KE11A I/O address offsets 0177300 - 0177316 */ /* KE read - reads are always 16b, to even addresses */
#define KE_DIV 000/* Divide */
#define KE_AC 002/* Accumulator */
#define KE_MQ 004/* MQ */
#define KE_MUL 006/* Multiply */
#define KE_SC 010/* Step counter */
#define KE_SR 011/* Status register */
#define KE_NOR 012/* Normalize */
#define KE_LSH 014/* Logical shift */
#define KE_ASH 016/* Arithmetic shift */
/* Stolen from Tim Shoppa's KE11A in apout */
void set_SR(void)
{
SR = SR & 0301;/* clear the result bits we can set here */
if (((MQ & 0100000) == 0) && (AC == 0)) SR = SR | 002;
if (((MQ & 0100000) == 0100000) && (AC == 0177777)) SR = SR | 002;
if ((AC == 0) && (MQ == 0)) SR = SR | 0004;
if (MQ == 0) SR = SR | 0010;
if (AC == 0) SR = SR | 0020;
if (AC == 0177777) SR = SR | 0040;
}
t_stat ke_rd (int32 *data, int32 PA, int32 access) t_stat ke_rd (int32 *data, int32 PA, int32 access)
{ {
switch (PA & 077) { /* decode PA<5:0> */ switch (PA & 016) { /* decode PA<3:1> */
case KE_AC: case KE_AC: /* AC */
*data = AC; *data = ke_AC;
return SCPE_OK; break;
case KE_MQ:
*data = MQ;
return SCPE_OK;
case KE_NOR:
*data = SC;
return SCPE_OK;
case KE_SC:
set_SR();
*data = (SR << 8) | SC;
return SCPE_OK;
case KE_SR:
set_SR();
*data = (SR << 8);
return SCPE_OK;
case KE_DIV: case KE_MQ: /* MQ */
case KE_MUL: *data = ke_MQ;
case KE_LSH: break;
case KE_ASH:
case KE_NOR: /* norm (SC) */
*data = ke_SC;
break;
case KE_SC: /* SR/SC */
*data = (ke_set_SR () << 8) | ke_SC;
break;
default:
*data = 0; *data = 0;
return SCPE_OK; break;
} }
return SCPE_NXM; /* can't get here */ return SCPE_OK;
} }
/* KE write - writes trigger actual arithmetic */
t_stat ke_wr (int32 data, int32 PA, int32 access) t_stat ke_wr (int32 data, int32 PA, int32 access)
{ {
int32 divisor, quotient, remainder; int32 quo, t32, sout, sign;
int32 dividend, product; uint32 absd, absr;
int32 oldMQ;
switch (PA & 077) { /* decode PA<5:0> */ switch (PA & 017) { /* decode PA<3:0> */
case KE_DIV: case KE_DIV: /* divide */
SC = 0; if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */
dividend = (AC << 16) | MQ; data |= 0177400; /* sext data to 16b */
divisor = data; ke_SR = 0; /* N = V = C = 0 */
if (divisor >> 15) divisor = divisor | ~077777; t32 = (ke_AC << 16) | ke_MQ; /* 32b divd */
quotient = dividend / divisor; if (GET_SIGN_W (ke_AC)) /* sext (divd) */
MQ = quotient & 0177777; t32 = t32 | ~017777777777;
remainder = dividend % divisor; if (GET_SIGN_W (data)) /* sext (divr) */
AC = remainder & 0177777; data = data | ~077777;
SR = SR & 076; absd = abs (t32);
if ((quotient > 32767) || (quotient < -32768)) { /* did we overflow? */ absr = abs (data);
if (dividend < 0) SR = SR | 0100; if ((absd >> 16) >= absr) { /* divide fails? */
else SR = SR | 0200;
} else {
if (quotient < 0) SR = SR | 0300;
}
return SCPE_OK;
case KE_AC: /* Based on the documentation, here's what has happened:
AC = data;
if ((access == WRITEB) & (data >> 7))
AC = AC | 0177400;
return SCPE_OK;
case KE_AC + 1: SC = 16.
printf("write to AC+1; data=%o", data); SR<c> = (AC<15> == data<15>)
AC = (AC & 0377) | (data << 8); AC'MQ = (AC'MQ << 1) | SR<c>
return SCPE_OK; AC = SR<c>? AC - data: AC + data
SR<c> = (AC<15> == data<15>)
SC = SC - 1
stop
*/
case KE_MQ: sign = GET_SIGN_W (ke_AC ^ data) ^ 1; /* 1 if signs match */
MQ = data; ke_AC = (ke_AC << 1) | (ke_MQ >> 15);
if ((access == WRITEB) & (data >> 7)) MQ = MQ | 0177400; ke_AC = (sign? ke_AC - data: ke_AC + data) & DMASK;
if (MQ >> 15) AC = 0177777; ke_MQ = ((ke_MQ << 1) | sign) & DMASK;
else AC = 0; if (GET_SIGN_W (ke_AC ^ data) == 0) /* 0 if signs match */
return SCPE_OK; ke_SR |= KE_SR_C;
ke_SC = 15; /* SC clocked once */
ke_SR |= KE_SR_NXV; /* set overflow */
}
else {
ke_SC = 0;
quo = t32 / data;
ke_MQ = quo & DMASK; /* MQ has quo */
ke_AC = (t32 % data) & DMASK; /* AC has rem */
if ((quo > 32767) || (quo < -32768)) /* quo overflow? */
ke_SR |= KE_SR_NXV; /* set overflow */
}
if (GET_SIGN_W (ke_MQ)) /* result negative? */
ke_SR ^= (KE_SR_N | KE_SR_NXV); /* N = 1, compl NXV */
break;
case KE_MQ + 1: case KE_AC: /* AC */
printf("write to MQ+1; data=%o", data); if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */
MQ = (MQ & 0377) | (data << 8); data |= 0177400; /* sext data to 16b */
if (MQ >> 15) AC = 0177777; ke_AC = data;
else AC = 0; break;
return SCPE_OK;
case KE_MUL: case KE_AC + 1: /* AC odd byte */
SC = 0; ke_AC = (ke_AC & 0377) | (data << 8);
if (data >> 15) data = data | ~077777; break;
if (MQ >> 15) MQ = MQ | ~077777;
product = MQ * data;
MQ = product & 0177777;
AC = (product >> 16) & 0177777;
SR = SR & 076;
if (AC >> 15) SR = SR | 0300; /* set sign bit if necessary */
return SCPE_OK;
case KE_SC: case KE_MQ: /* MQ */
if (access == WRITEB) return SCPE_OK;/* byte writes are no-ops */ if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */
SR = (data >> 8) & 0177777; data |= 0177400; /* sext data to 16b */
SC = data & 0000077; ke_MQ = data;
return SCPE_OK; if (GET_SIGN_W (ke_MQ)) /* sext MQ to AC */
ke_AC = 0177777;
else ke_AC = 0;
break;
case KE_SR: case KE_MQ + 1: /* MQ odd byte */
return SCPE_OK; /* this is a No-op */ ke_MQ = (ke_MQ & 0377) | (data << 8);
if (GET_SIGN_W (ke_MQ)) /* sext MQ to AC */
ke_AC = 0177777;
else ke_AC = 0;
break;
case KE_NOR: /* Normalize */ case KE_MUL: /* multiply */
MQ = (AC << 16) | MQ; /* 32-bit number to normalize in MQ */ if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */
for (SC = 0; SC < 31; SC++) { data |= 0177400; /* sext data to 16b */
if (MQ == (0140000 << 16)) ke_SC = 0;
break; if (GET_SIGN_W (data)) /* sext operands */
if ((((MQ >> 30) & 3) == 1) || (((MQ >> 30) & 3) == 2)) data |= ~077777;
break; t32 = ke_MQ;
MQ = MQ << 1; if (GET_SIGN_W (t32))
} t32 |= ~077777;
printf("SC = %o\r\n", SC); t32 = t32 * data;
AC = (MQ >> 16) & 0177777; ke_AC = (t32 >> 16) & DMASK;
MQ = MQ & 0177777; ke_MQ = t32 & DMASK;
return SCPE_OK; if (GET_SIGN_W (ke_AC)) /* result negative? */
ke_SR = KE_SR_N | KE_SR_NXV; /* N = 1, V = C = 0 */
else ke_SR = 0; /* N = 0, V = C = 0 */
break;
case KE_LSH: /* Logical shift */ case KE_SC: /* SC */
MQ=(AC<<16)|MQ; /* Form a temporary 32-bit entity */ if (access == WRITEB) /* ignore byte writes */
oldMQ=MQ & 0x80000000; /* Save the sign bit for later */ return SCPE_OK;
SR=SR&0176; /* Clear overflow & carry bits */ ke_SR = (data >> 8) & (KE_SR_NXV|KE_SR_N|KE_SR_C);
data=data & 077; /* Convert data from 6-bit */ ke_SC = data & 077;
if (data>31) { break;
data=64-data; /* Shift in a -ve direction */
SR=SR|((MQ>>(data-1))&1); /* Get the bit that went off the end */ case KE_NOR: /* normalize */
MQ=MQ>>data; /* and do the right shift */ for (ke_SC = 0; ke_SC < 31; ke_SC++) { /* max 31 shifts */
} else { /* Else left shift */ if (((ke_AC == 0140000) && (ke_MQ == 0)) || /* special case? */
if ((MQ<<(data-1))&0x80000000) SR|=1; /* Get the bit off the end */ (GET_SIGN_W (ke_AC ^ (ke_AC << 1)))) /* AC<15> != AC<14>? */
MQ=MQ<<data; /* and do the left shift */ break;
} ke_AC = ((ke_AC << 1) | (ke_MQ >> 15)) & DMASK;
oldMQ= oldMQ ^ MQ; /* Any difference in sign bit? */ ke_MQ = (ke_MQ << 1) & DMASK;
if (oldMQ & 0x80000000) SR|=0200;/* Yes, set the overflow bit */ }
AC=(MQ>>16)&0177777; /* Save result in AC and MQ */ if (GET_SIGN_W (ke_AC)) /* result negative? */
MQ=MQ&0177777; ke_SR = KE_SR_N | KE_SR_NXV; /* N = 1, V = C = 0 */
set_SR(); else ke_SR = 0; /* N = 0, V = C = 0 */
break;
case KE_LSH: /* logical shift */
ke_SC = 0;
ke_SR = 0; /* N = V = C = 0 */
data = data & 077; /* 6b shift count */
if (data != 0) {
t32 = (ke_AC << 16) | ke_MQ; /* 32b operand */
if (sign = GET_SIGN_W (ke_AC)) /* sext operand */
t32 = t32 | ~017777777777;
if (data < 32) { /* [1,31] - left */
sout = (t32 >> (32 - data)) | (-sign << data);
t32 = ((uint32) t32) << data; /* do shift (zext) */
if (sout != (GET_SIGN_L (t32)? -1: 0)) /* bits lost = sext? */
ke_SR |= KE_SR_NXV; /* no, V = 1 */
if (sout & 1) /* last bit lost = 1? */
ke_SR |= KE_SR_C; /* yes, C = 1 */
}
else { /* [32,63] = -32,-1 */
if ((t32 >> (63 - data)) & 1) /* last bit lost = 1? */
ke_SR |= KE_SR_C; /* yes, C = 1*/
t32 = (data != 32)? ((uint32) t32) >> (64 - data): 0;
}
ke_AC = (t32 >> 16) & DMASK;
ke_MQ = t32 & DMASK;
}
if (GET_SIGN_W (ke_AC)) /* result negative? */
ke_SR ^= (KE_SR_N | KE_SR_NXV); /* N = 1, compl NXV */
break;
/* EAE ASH differs from EIS ASH and cannot use the same overflow test */
case KE_ASH: /* arithmetic shift */
ke_SC = 0;
ke_SR = 0; /* N = V = C = 0 */
data = data & 077; /* 6b shift count */
if (data != 0) {
t32 = (ke_AC << 16) | ke_MQ; /* 32b operand */
if (sign = GET_SIGN_W (ke_AC)) /* sext operand */
t32 = t32 | ~017777777777;
if (data < 32) { /* [1,31] - left */
sout = (t32 >> (31 - data)) | (-sign << data);
t32 = (t32 & 020000000000) | ((t32 << data) & 017777777777);
if (sout != (GET_SIGN_L (t32)? -1: 0)) /* bits lost = sext? */
ke_SR |= KE_SR_NXV; /* no, V = 1 */
if (sout & 1) /* last bit lost = 1? */
ke_SR |= KE_SR_C; /* yes, C = 1 */
}
else { /* [32,63] = -32,-1 */
if ((t32 >> (63 - data)) & 1) /* last bit lost = 1? */
ke_SR |= KE_SR_C; /* yes, C = 1 */
t32 = (data != 32)? /* special case 32 */
(((uint32) t32) >> (64 - data)) | (-sign << (data - 32)):
-sign;
}
ke_AC = (t32 >> 16) & DMASK;
ke_MQ = t32 & DMASK;
}
if (GET_SIGN_W (ke_AC)) /* result negative? */
ke_SR ^= (KE_SR_N | KE_SR_NXV); /* N = 1, compl NXV */
break;
default: /* all others ignored */
return SCPE_OK; return SCPE_OK;
} /* end switch PA */
case KE_ASH: /* Arithmetic shift */ ke_set_SR ();
MQ=(AC<<16)|MQ; /* Form a temporary 32-bit entity */
oldMQ=MQ & 0x80000000; /* Save the sign bit for later */
SR=SR&0176; /* Clear overflow & carry bits */
data=data & 077; /* Convert data from 6-bit */
if (data>31) {
data=64-data; /* Shift in a -ve direction */
divisor=1 << data; /* Work out the dividing factor */
SR=SR|((MQ>>(data-1))&1); /* Get the bit that went off the end */
MQ=MQ/divisor; /* and do the right shift */
} else { /* Else left shift */
product=1 << data; /* Work out the multiplying factor */
if ((MQ<<(data-1))&0x80000000) SR|=1; /* Get the bit off the end */
MQ=MQ*product; /* and do the left shift */
}
oldMQ= oldMQ ^ MQ; /* Any difference in sign bit? */
if (oldMQ & 0x80000000) SR|=0200;/* Yes, set the overflow bit */
AC=(MQ>>16)&0177777; /* Save result in AC and MQ */
MQ=MQ&0177777;
set_SR();
return SCPE_OK;
} /* end switch PA */
return SCPE_NXM; /* can't get here */
}
/* service */
t_stat ke_svc (UNIT *uptr)
{
return SCPE_OK; return SCPE_OK;
} }
/* support routines */ /* Update status register based on current AC, MQ */
uint32 ke_set_SR (void)
{
ke_SR &= ~KE_SR_DYN; /* clr dynamic bits */
if (ke_MQ == 0) /* MQ == 0? */
ke_SR |= KE_SR_MQZ;
if (ke_AC == 0) { /* AC == 0? */
ke_SR |= KE_SR_ACZ;
if (GET_SIGN_W (ke_MQ) == 0) /* MQ positive? */
ke_SR |= KE_SR_SXT;
if (ke_MQ == 0) /* MQ zero? */
ke_SR |= KE_SR_Z;
}
if (ke_AC == 0177777) { /* AC == 177777? */
ke_SR |= KE_SR_ACM1;
if (GET_SIGN_W (ke_MQ) == 1) /* MQ negative? */
ke_SR |= KE_SR_SXT;
}
return ke_SR;
}
/* Reset routine */
t_stat ke_reset (DEVICE *dptr) t_stat ke_reset (DEVICE *dptr)
{ {
ke_unit.buf = 0; ke_SR = 0;
sim_cancel (&ke_unit); /* deactivate unit */ ke_SC = 0;
ke_AC = 0;
ke_MQ = 0;
return SCPE_OK; return SCPE_OK;
} }
t_stat ke_attach (UNIT *uptr, char *cptr)
{
t_stat reason;
reason = attach_unit (uptr, cptr);
return reason;
}
t_stat ke_detach (UNIT *uptr)
{
return detach_unit (uptr);
}

View File

@ -16,8 +16,8 @@ set rl disabled
set tq disabled set tq disabled
set tc enabled set tc enabled
set rf enabled set rf enabled
d sr 173700
set ke enabled set ke enabled
set cpu history=1000 set cpu history=1000
load boot/m792low.load load boot/m792low.load
d sr 173700
go 73700 go 73700