1
0
mirror of https://github.com/livingcomputermuseum/UniBone.git synced 2026-01-31 22:03:16 +00:00
Files
livingcomputermuseum.UniBone/10.02_devices/2_src/cpu20/eae.c
Joerg Hoppe 2530d9cbb5 Initial
2019-04-05 11:30:26 +02:00

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);
}