mirror of
https://github.com/livingcomputermuseum/UniBone.git
synced 2026-01-31 22:03:16 +00:00
477 lines
7.4 KiB
C
477 lines
7.4 KiB
C
#include "11.h"
|
|
|
|
enum {
|
|
SR_C = 1,
|
|
SR_AC_EQ_MQ15 = 2,
|
|
SR_AC_MQ_0 = 4,
|
|
SR_MQ_0 = 010,
|
|
SR_AC_0 = 020,
|
|
SR_AC_1 = 040,
|
|
SR_NEG = 0100,
|
|
SR_OV = 0200
|
|
};
|
|
|
|
enum {
|
|
KE_DIV = 0777300,
|
|
KE_AC = 0777302,
|
|
KE_MQ = 0777304,
|
|
KE_MULT = 0777306,
|
|
KE_SCSR = 0777310,
|
|
KE_NORM = 0777312,
|
|
KE_LSH = 0777314,
|
|
KE_ASH = 0777316,
|
|
};
|
|
|
|
#define B31 (1<<31)
|
|
#define B30 (1<<30)
|
|
|
|
static void
|
|
setflags(KE11 *ke)
|
|
{
|
|
ke->sr &= ~(SR_AC_EQ_MQ15|SR_AC_MQ_0|SR_MQ_0|SR_AC_0|SR_AC_1);
|
|
if(ke->ac == 0 && sgn(ke->mq)==0 ||
|
|
ke->ac == 0177777 && sgn(ke->mq))
|
|
ke->sr |= SR_AC_EQ_MQ15;
|
|
if(ke->mq == 0 && ke->ac == 0)
|
|
ke->sr |= SR_AC_MQ_0;
|
|
if(ke->mq == 0)
|
|
ke->sr |= SR_MQ_0;
|
|
if(ke->ac == 0)
|
|
ke->sr |= SR_AC_0;
|
|
if(ke->ac == 0177777)
|
|
ke->sr |= SR_AC_1;
|
|
}
|
|
|
|
static void
|
|
setflag(KE11 *ke, int flg, uint32 c)
|
|
{
|
|
if(c)
|
|
ke->sr |= flg;
|
|
else
|
|
ke->sr &= ~flg;
|
|
}
|
|
|
|
/* Functions are a transcription of the flow charts in the manual.
|
|
May be a bit inefficient but it's nice to be accurate. */
|
|
|
|
static void
|
|
mult(KE11 *ke)
|
|
{
|
|
uint32 sum, md;
|
|
int c;
|
|
|
|
sum = 0;
|
|
md = ke->ac<<16 | ke->mq;
|
|
c = 0;
|
|
ke->sc = 16;
|
|
do{
|
|
if((ke->x & 1) != c){
|
|
if(ke->x&1)
|
|
sum += ~md + 1;
|
|
else
|
|
sum += md;
|
|
}
|
|
md <<= 1;
|
|
c = ke->x&1;
|
|
ke->x >>= 1;
|
|
ke->sc--;
|
|
}while(ke->sc);
|
|
ke->ac = sum>>16;
|
|
ke->mq = sum;
|
|
ke->sr &= ~SR_C;
|
|
setflags(ke);
|
|
}
|
|
|
|
static uint32
|
|
abs32(uint32 x)
|
|
{
|
|
return x&B31 ? ~x + 1 : x;
|
|
}
|
|
|
|
static void
|
|
divide(KE11 *ke)
|
|
{
|
|
uint32 dd, dr;
|
|
uint16 q;
|
|
int c;
|
|
int s;
|
|
int ov;
|
|
|
|
dd = ke->ac<<16 | ke->mq;
|
|
dr = ke->x<<16;
|
|
q = 0;
|
|
ov = 0;
|
|
ke->sr &= ~SR_C;
|
|
s = !!(dd&B31);
|
|
ke->sc = 16;
|
|
do{
|
|
c = (dr&B31) == (dd&B31);
|
|
dr >>= 1;
|
|
if(dr&B30)
|
|
dr |= B31;
|
|
q <<= 1;
|
|
q |= c;
|
|
if(c)
|
|
dd += ~dr + 1;
|
|
else
|
|
dd += dr;
|
|
if(ke->sc == 16 && dd != 0 && !!(dd>>31 & 1) == s)
|
|
goto ov;
|
|
ke->sc--;
|
|
}while(ke->sc);
|
|
|
|
if(dd == 0 ||
|
|
/* is this correct? */
|
|
dd + abs32(dr) != 0 && !!(dd>>31 & 1) == s){
|
|
c = 1;
|
|
q <<= 1;
|
|
q |= c;
|
|
}else{
|
|
c = (dd&B31) == (dr&B31);
|
|
if(c)
|
|
dd += ~dr + 1;
|
|
else
|
|
dd += dr;
|
|
q += c;
|
|
c = 0;
|
|
q <<= 1;
|
|
}
|
|
|
|
/* DOCU manual says DD in one place and DR in another...
|
|
* which is correct? have to check */
|
|
if((dr>>31 & 1) != (q>>15 & 1) != s)
|
|
ov:
|
|
ov = 1;
|
|
|
|
ke->ac = dd;
|
|
ke->mq = q;
|
|
setflag(ke, SR_OV, ov != (ke->sr>>6 & 1));
|
|
setflags(ke);
|
|
if(ov)
|
|
setflag(ke, SR_NEG, s);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
norm(KE11 *ke)
|
|
{
|
|
uint32 sh;
|
|
|
|
sh = ke->ac<<16 | ke->mq;
|
|
ke->sc = 0;
|
|
ke->sr &= ~(SR_OV|SR_C);
|
|
setflag(ke, SR_NEG, sh & B31);
|
|
if(ke->sr & SR_NEG)
|
|
ke->sr |= SR_OV;
|
|
loop:
|
|
if((sh>>31 & 1) != (sh>>30 & 1))
|
|
goto done;
|
|
if(sh == (3U<<30))
|
|
goto done;
|
|
if(ke->sc == 31)
|
|
goto done;
|
|
sh <<= 1;
|
|
ke->sc++;
|
|
goto loop;
|
|
done:
|
|
ke->ac = sh>>16;
|
|
ke->mq = sh;
|
|
setflags(ke);
|
|
}
|
|
|
|
static void
|
|
lsh(KE11 *ke)
|
|
{
|
|
uint32 sh;
|
|
int ov;
|
|
|
|
sh = ke->ac<<16 | ke->mq;
|
|
setflag(ke, SR_NEG, sh&B31);
|
|
ov = 0;
|
|
loop:
|
|
ke->sc &= 077;
|
|
if(ke->sc == 0)
|
|
goto done;
|
|
if(ke->sc&040){
|
|
/* negative, shift right */
|
|
setflag(ke, SR_C, sh&1);
|
|
sh >>= 1;
|
|
ke->sr &= ~SR_NEG;
|
|
ke->sc++;
|
|
}else{
|
|
/* positive, shift left */
|
|
setflag(ke, SR_C, sh&B31);
|
|
sh <<= 1;
|
|
if((ke->sr>>6 & 1) != (sh>>31 & 1))
|
|
ov = 1; /* sign changed */
|
|
setflag(ke, SR_NEG, sh&B31);
|
|
ke->sc--;
|
|
}
|
|
goto loop;
|
|
done:
|
|
setflag(ke, SR_OV, ov != (ke->sr>>6 & 1));
|
|
ke->ac = sh>>16;
|
|
ke->mq = sh;
|
|
setflags(ke);
|
|
}
|
|
|
|
static void
|
|
ash(KE11 *ke)
|
|
{
|
|
uint32 sh;
|
|
int ov;
|
|
|
|
sh = ke->ac<<16 | ke->mq;
|
|
setflag(ke, SR_NEG, sh&B31);
|
|
ov = 0;
|
|
loop:
|
|
ke->sc &= 077;
|
|
if(ke->sc == 0)
|
|
goto done;
|
|
if(ke->sc&040){
|
|
/* negative, shift right */
|
|
setflag(ke, SR_C, sh&1);
|
|
sh >>= 1;
|
|
if(ke->sr & SR_NEG)
|
|
sh |= B31;
|
|
ke->sc++;
|
|
}else{
|
|
/* positive, shift left */
|
|
sh <<= 1;
|
|
setflag(ke, SR_C, sh&B31);
|
|
if(ke->sr & SR_NEG)
|
|
sh |= B31;
|
|
else
|
|
sh &= ~B31;
|
|
if((ke->sr & SR_C) != (ke->sr>>6 & 1))
|
|
ov = 1; /* sign changed */
|
|
ke->sc--;
|
|
}
|
|
goto loop;
|
|
done:
|
|
setflag(ke, SR_OV, ov != (ke->sr>>6 & 1));
|
|
ke->ac = sh>>16;
|
|
ke->mq = sh;
|
|
setflags(ke);
|
|
}
|
|
|
|
int
|
|
dato_ke11(Bus *bus, void *dev)
|
|
{
|
|
KE11 *ke = dev;
|
|
if(bus->addr >= 0777300 && bus->addr < 0777320){
|
|
// printf("EAE DATO %o %o\n", bus->addr, bus->data);
|
|
switch(bus->addr){
|
|
case KE_DIV:
|
|
ke->x = bus->data;
|
|
divide(ke);
|
|
break;
|
|
case KE_AC:
|
|
ke->ac = bus->data;
|
|
break;
|
|
case KE_MQ:
|
|
ke->mq = bus->data;
|
|
/* sign extend into AC */
|
|
if(ke->mq&0100000)
|
|
ke->ac = 0177777;
|
|
else
|
|
ke->ac = 0;
|
|
break;
|
|
case KE_MULT:
|
|
ke->x = bus->data;
|
|
mult(ke);
|
|
break;
|
|
case KE_SCSR:
|
|
ke->sc = bus->data & 077;
|
|
SETMASK(ke->sr, bus->data>>8, 0301);
|
|
break;
|
|
case KE_NORM:
|
|
norm(ke);
|
|
break;
|
|
case KE_LSH:
|
|
ke->sc = bus->data & 077;
|
|
lsh(ke);
|
|
break;
|
|
case KE_ASH:
|
|
ke->sc = bus->data & 077;
|
|
ash(ke);
|
|
break;
|
|
default: assert(0); /* can't happen */
|
|
}
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
datob_ke11(Bus *bus, void *dev)
|
|
{
|
|
KE11 *ke = dev;
|
|
if(bus->addr >= 0777300 && bus->addr < 0777320){
|
|
// printf("EAE DATOB %o %o\n", bus->addr, bus->data);
|
|
switch(bus->addr){
|
|
case KE_DIV:
|
|
ke->x = sxt(bus->data);
|
|
divide(ke);
|
|
break;
|
|
case KE_AC:
|
|
ke->ac = sxt(bus->data);
|
|
break;
|
|
case KE_AC+1:
|
|
SETMASK(ke->ac, bus->data, 0177400);
|
|
break;
|
|
case KE_MQ:
|
|
ke->mq = sxt(bus->data);
|
|
/* sign extend into AC */
|
|
if(ke->mq&0100000)
|
|
ke->ac = 0177777;
|
|
else
|
|
ke->ac = 0;
|
|
break;
|
|
case KE_MQ+1:
|
|
SETMASK(ke->mq, bus->data, 0177400);
|
|
/* sign extend into AC */
|
|
if(ke->mq&0100000)
|
|
ke->ac = 0177777;
|
|
else
|
|
ke->ac = 0;
|
|
break;
|
|
case KE_MULT:
|
|
ke->x = sxt(bus->data);
|
|
mult(ke);
|
|
break;
|
|
case KE_SCSR:
|
|
break;
|
|
case KE_NORM:
|
|
norm(ke);
|
|
break;
|
|
case KE_LSH:
|
|
ke->sc = bus->data & 077;
|
|
lsh(ke);
|
|
break;
|
|
case KE_ASH:
|
|
ke->sc = bus->data & 077;
|
|
ash(ke);
|
|
break;
|
|
case KE_DIV+1:
|
|
case KE_MULT+1:
|
|
case KE_SCSR+1:
|
|
case KE_NORM+1:
|
|
case KE_LSH+1:
|
|
case KE_ASH+1:
|
|
break;
|
|
|
|
default: assert(0); /* can't happen */
|
|
}
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
dati_ke11(Bus *bus, void *dev)
|
|
{
|
|
KE11 *ke = dev;
|
|
if(bus->addr >= 0777300 && bus->addr < 0777320){
|
|
// printf("EAE DATI %o\n", bus->addr);
|
|
switch(bus->addr){
|
|
case KE_DIV:
|
|
case KE_MULT:
|
|
case KE_LSH:
|
|
case KE_ASH:
|
|
bus->data = 0;
|
|
break;
|
|
case KE_AC:
|
|
bus->data = ke->ac;
|
|
break;
|
|
case KE_MQ:
|
|
bus->data = ke->mq;
|
|
break;
|
|
case KE_SCSR:
|
|
bus->data = WD(ke->sr, ke->sc);
|
|
break;
|
|
case KE_NORM:
|
|
bus->data = ke->sc & 077;
|
|
break;
|
|
|
|
default: assert(0); /* can't happen */
|
|
}
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
reset_ke11(void *dev)
|
|
{
|
|
KE11 *ke = dev;
|
|
ke->ac = 0;
|
|
ke->mq = 0;
|
|
ke->x = 0;
|
|
ke->sc = 0;
|
|
ke->sr = 0;
|
|
setflags(ke);
|
|
}
|
|
|
|
void
|
|
eaetest(KE11 *ke)
|
|
{
|
|
/*
|
|
// normalize
|
|
// ke->ac = ~0; ke->mq = ~0;
|
|
// ke->ac = 0177770; ke->mq = 0;
|
|
// ke->ac = 0100000; ke->mq = 0;
|
|
ke->ac = 0; ke->mq = 0;
|
|
norm(ke);
|
|
*/
|
|
/*
|
|
ke->sc = 0100-3;
|
|
ke->ac = 0123456; ke->mq = 0111222;
|
|
ash(ke);
|
|
*/
|
|
/*
|
|
ke->ac = 0; ke->mq = 00123;
|
|
ke->x = 0321;
|
|
mult(ke);
|
|
*/
|
|
/*
|
|
ke->ac = 00021; ke->mq = 0;
|
|
ke->x = 0321;
|
|
divide(ke);
|
|
*/
|
|
|
|
/* int a, b, c, d;
|
|
//some multiplication test
|
|
for(a = 0; a < 0077777; a++){
|
|
for(b = 0; b < 0077777; b++){
|
|
c = a * b;
|
|
ke->ac = 0; ke->mq = a;
|
|
ke->x = b;
|
|
mult(ke);
|
|
d = ke->ac<<16 | ke->mq;
|
|
assert(c == d);
|
|
if(a == 0)
|
|
continue;
|
|
ke->x = a;
|
|
divide(ke);
|
|
assert(ke->ac == 0);
|
|
assert(ke->mq == b);
|
|
}
|
|
printf("%o\n", a);
|
|
}
|
|
// division
|
|
for(a = 0; a < 0077777; a++){
|
|
printf("%o\n", a);
|
|
for(b = 1; b < 0077777; b++){
|
|
ke->ac = 0; ke->mq = a;
|
|
ke->x = b;
|
|
divide(ke);
|
|
assert(ke->mq == a / b);
|
|
assert(ke->ac == a % b);
|
|
}
|
|
}
|
|
*/
|
|
|
|
// printf("%06o %06o %d %o\n", ke->ac, ke->mq, ke->sc, ke->sr);
|
|
}
|