1
0
mirror of https://github.com/livingcomputermuseum/UniBone.git synced 2026-02-19 22:06:12 +00:00
Files
livingcomputermuseum.UniBone/10.02_devices/2_src/cpu20/ka11.c
Joerg Hoppe 10f0540c4a CPU20 WAIT
2019-10-08 15:05:37 +02:00

698 lines
14 KiB
C

#include "11.h"
#include "ka11.h"
#include "gpios.hpp" // ARM_DEBUG_PIN*
void unibone_grant_interrupts(void) ;
int unibone_dato(unsigned addr, unsigned data);
int unibone_datob(unsigned addr, unsigned data);
int unibone_dati(unsigned addr, unsigned *data);
void unibone_prioritylevelchange(uint8_t level);
void unibone_bus_init(unsigned pulsewidth_ms) ;
int
dati_bus(Bus *bus)
{
unsigned int data;
if(!unibone_dati(bus->addr, &data))
return 1;
bus->data = data;
return 0;
}
int
dato_bus(Bus *bus)
{
return !unibone_dato(bus->addr, bus->data);
}
int
datob_bus(Bus *bus)
{
return !unibone_datob(bus->addr, bus->data);
}
void
levelchange(word psw)
{
unibone_prioritylevelchange(psw>>5);
}
enum {
PSW_PR = 0340,
PSW_T = 020,
PSW_N = 010,
PSW_Z = 004,
PSW_V = 002,
PSW_C = 001,
};
enum {
TRAP_STACK = 1,
TRAP_PWR = 2,
TRAP_BR7 = 4,
TRAP_BR6 = 010,
TRAP_BR5 = 040,
TRAP_BR4 = 0100,
TRAP_CSTOP = 01000 // can't happen?
};
#define ISSET(f) ((cpu->psw&(f)) != 0)
enum {
STATE_HALTED = 0,
STATE_RUNNING = 1,
STATE_WAITING = 2
};
word
sgn(word w)
{
return (w>>15)&1;
}
word
sxt(byte b)
{
return (word)(int8_t)b;
}
static uint32
ubxt(word a)
{
return (a&0160000)==0160000 ? a|0600000 : a;
}
void
tracestate(KA11 *cpu)
{
(void)cpu;
trace(" R0 %06o R1 %06o R2 %06o R3 %06o R4 %06o R5 %06o R6 %06o R7 %06o\n"
" 10 %06o 11 %06o 12 %06o 13 %06o 14 %06o 15 %06o 16 %06o 17 %06o\n"
" BA %06o IR %06o PSW %03o\n"
,
cpu->r[0], cpu->r[1], cpu->r[2], cpu->r[3],
cpu->r[4], cpu->r[5], cpu->r[6], cpu->r[7],
cpu->r[8], cpu->r[9], cpu->r[10], cpu->r[11],
cpu->r[12], cpu->r[13], cpu->r[14], cpu->r[15],
cpu->ba, cpu->ir, cpu->psw);
}
void
printstate(KA11 *cpu)
{
(void)cpu;
printf(" R0 %06o R1 %06o R2 %06o R3 %06o R4 %06o R5 %06o R6 %06o R7 %06o\n"
" 10 %06o 11 %06o 12 %06o 13 %06o 14 %06o 15 %06o 16 %06o 17 %06o\n"
" BA %06o IR %06o PSW %03o\n"
,
cpu->r[0], cpu->r[1], cpu->r[2], cpu->r[3],
cpu->r[4], cpu->r[5], cpu->r[6], cpu->r[7],
cpu->r[8], cpu->r[9], cpu->r[10], cpu->r[11],
cpu->r[12], cpu->r[13], cpu->r[14], cpu->r[15],
cpu->ba, cpu->ir, cpu->psw);
}
// only to be called from ka11_condstep() thread
void
ka11_reset(KA11 *cpu)
{
Busdev *bd;
cpu->traps = 0;
cpu->external_intr = 0;
cpu->mutex = PTHREAD_MUTEX_INITIALIZER ;
for(bd = cpu->bus->devs; bd; bd = bd->next)
bd->reset(bd->dev);
}
int
dati(KA11 *cpu, int b)
{
trace("dati %06o:\n", cpu->ba);
if(!b && cpu->ba&1)
goto be;
/* internal registers */
if((cpu->ba&0177400) == 0177400){
switch(cpu->ba&0377){
case 0170: case 0171:
cpu->bus->data = cpu->sw;
goto ok;
case 0376: case 0377:
cpu->bus->data = cpu->psw;
goto ok;
/* respond but don't return real data */
case 0147:
cpu->bus->data = 0;
goto ok;
}
}
cpu->bus->addr = ubxt(cpu->ba)&~1;
if(dati_bus(cpu->bus))
goto be;
ok:
trace("%06o\n", cpu->bus->data);
cpu->be = 0;
return 0;
be:
trace("BE\n");
cpu->be++;
return 1;
}
int
dato(KA11 *cpu, int b)
{
trace("dato %06o %06o %d\n", cpu->ba, cpu->bus->data, b);
if(!b && cpu->ba&1)
goto be;
/* internal registers */
if((cpu->ba&0177400) == 0177400){
switch(cpu->ba&0377){
case 0170: case 0171:
/* can't write switches */
goto ok;
case 0376: case 0377:
/* writes 0 for the odd byte.
I think this is correct. */
cpu->psw = cpu->bus->data;
levelchange(cpu->psw);
goto ok;
}
}
if(b){
cpu->bus->addr = ubxt(cpu->ba);
if(datob_bus(cpu->bus))
goto be;
}else{
cpu->bus->addr = ubxt(cpu->ba)&~1;
if(dato_bus(cpu->bus))
goto be;
}
ok:
cpu->be = 0;
return 0;
be:
cpu->be++;
return 1;
}
static void
svc(KA11 *cpu, Bus *bus)
{
int l;
Busdev *bd;
static int brtraps[4] = { TRAP_BR4, TRAP_BR5, TRAP_BR6, TRAP_BR7 };
for(l = 0; l < 4; l++){
cpu->br[l].bg = nil;
cpu->br[l].dev = nil;
}
cpu->traps &= ~(TRAP_BR4|TRAP_BR5|TRAP_BR6|TRAP_BR7);
for(bd = bus->devs; bd; bd = bd->next){
l = bd->svc(bus, bd->dev);
if(l >= 4 && l <= 7 && cpu->br[l-4].bg == nil){
cpu->br[l-4].bg = bd->bg;
cpu->br[l-4].dev = bd->dev;
cpu->traps |= brtraps[l-4];
}
}
}
static int
addrop(KA11 *cpu, int m, int b)
{
int r;
int ai;
r = m&7;
m >>= 3;
ai = 1 + (!b || (r&6)==6 || m&1);
if(m == 0){
assert(0);
return 0;
}
switch(m&6){
case 0: // REG
cpu->b = cpu->ba = cpu->r[r];
return 0; // this already is mode 1
case 2: // INC
cpu->ba = cpu->r[r];
cpu->b = cpu->r[r] = cpu->r[r] + ai;
break;
case 4: // DEC
cpu->b = cpu->ba = cpu->r[r]-ai;
if(r == 6 && (cpu->ba&~0377) == 0) cpu->traps |= TRAP_STACK;
cpu->r[r] = cpu->ba;
break;
case 6: // INDEX
cpu->ba = cpu->r[7];
cpu->r[7] += 2;
if(dati(cpu, 0)) return 1;
cpu->b = cpu->ba = cpu->bus->data + cpu->r[r];
break;
}
if(m&1){
if(dati(cpu, 0)) return 1;
cpu->b = cpu->ba = cpu->bus->data;
}
return 0;
}
static int
fetchop(KA11 *cpu, int t, int m, int b)
{
int r;
r = m&7;
if((m&070) == 0)
cpu->r[t] = cpu->r[r];
else{
if(dati(cpu, b)) return 1;
cpu->r[t] = cpu->bus->data;
if(b && cpu->ba&1) cpu->r[t] = cpu->r[t]>>8;
}
if(b) cpu->r[t] = sxt(cpu->r[t]);
return 0;
}
static int
readop(KA11 *cpu, int t, int m, int b)
{
return !(addrop(cpu, m, b) == 0 && fetchop(cpu, t, m, b) == 0);
}
static int
writedest(KA11 *cpu, word v, int b)
{
int d;
if((cpu->ir & 070) == 0){
d = cpu->ir & 7;
if(b) SETMASK(cpu->r[d], v, 0377);
else cpu->r[d] = v;
}else{
if(cpu->ba&1) v <<= 8;
cpu->bus->data = v;
if(dato(cpu, b)) return 1;
}
return 0;
}
static void
setnz(KA11 *cpu, word w)
{
cpu->psw &= ~(PSW_N|PSW_Z);
if(w & 0100000) cpu->psw |= PSW_N;
if(w == 0) cpu->psw |= PSW_Z;
}
void
step(KA11 *cpu)
{
uint by;
uint br;
uint b;
uint c;
uint src, dst, sf, df, sm, dm;
word mask, sign;
int inhov;
byte oldpsw;
// printf("fetch from %06o\n", cpu->r[7]);
// printstate(cpu);
#define SP cpu->r[6]
#define PC cpu->r[7]
#define SR cpu->r[010]
#define DR cpu->r[011]
#define TV cpu->r[012]
#define BA cpu->ba
#define PSW cpu->psw
#define RD_B if(sm != 0) if(readop(cpu, 010, src, by)) goto be;\
if(dm != 0) if(readop(cpu, 011, dst, by)) goto be;\
if(sm == 0) fetchop(cpu, 010, src, by);\
if(dm == 0) fetchop(cpu, 011, dst, by)
#define RD_U if(dm != 0) if(readop(cpu, 011, dst, by)) goto be;\
if(dm == 0) fetchop(cpu, 011, dst, by);\
SR = DR
#define WR if(writedest(cpu, b, by)) goto be
#define NZ setnz(cpu, b)
#define SVC goto service
#define TRAP(v) TV = v; goto trap
#define CLC cpu->psw &= ~PSW_C
#define CLV cpu->psw &= ~PSW_V
#define CLCV cpu->psw &= ~(PSW_V|PSW_C)
#define SEV cpu->psw |= PSW_V
#define SEC cpu->psw |= PSW_C
#define C if(b & 0200000) SEC
#define NC if((b & 0200000) == 0) SEC
#define BXT if(by) b = sxt(b)
#define BR PC += br
#define CBR(c) if(((c)>>(cpu->psw&017)) & 1) BR
#define PUSH SP -= 2; if(!inhov && (SP&~0377) == 0) cpu->traps |= TRAP_STACK
#define POP SP += 2
#define OUT(a,d) cpu->ba = (a); cpu->bus->data = (d); if(dato(cpu, 0)) goto be
#define IN(d) if(dati(cpu, 0)) goto be; d = cpu->bus->data
#define INA(a,d) cpu->ba = a; if(dati(cpu, 0)) goto be; d = cpu->bus->data
#define TR(m) trace("%06o "#m"\n", PC-2)
#define TRB(m) trace("%06o "#m"%s\n", PC-2, by ? "B" : "")
oldpsw = PSW;
INA(PC, cpu->ir);
PC += 2; /* don't increment on bus error! */
by = !!(cpu->ir&B15);
br = sxt(cpu->ir)<<1;
src = cpu->ir>>6 & 077;
sf = src & 7;
sm = src>>3 & 7;
dst = cpu->ir & 077;
df = dst & 7;
dm = dst>>3 & 7;
if(by) mask = M8, sign = B7;
else mask = M16, sign = B15;
inhov = 0;
/* Binary */
switch(cpu->ir & 0170000){
case 0110000: case 0010000: TRB(MOV);
RD_B; CLV;
b = SR; NZ;
if(dm==0) cpu->r[df] = SR;
else writedest(cpu, SR, by);
SVC;
case 0120000: case 0020000: TRB(CMP);
RD_B; CLCV;
b = SR + W(~DR) + 1; NC; BXT;
if(sgn((SR ^ DR) & ~(DR ^ b))) SEV;
NZ; SVC;
case 0130000: case 0030000: TRB(BIT);
RD_B; CLV;
b = DR & SR;
NZ; SVC;
case 0140000: case 0040000: TRB(BIC);
RD_B; CLV;
b = DR & ~SR;
NZ; WR; SVC;
case 0150000: case 0050000: TRB(BIS);
RD_B; CLV;
b = DR | SR;
NZ; WR; SVC;
case 0060000: TR(ADD);
by = 0; RD_B; CLCV;
b = SR + DR; C;
if(sgn(~(SR ^ DR) & (DR ^ b))) SEV;
NZ; WR; SVC;
case 0160000: TR(SUB);
by = 0; RD_B; CLCV;
b = DR + W(~SR) + 1; NC;
if(sgn((SR ^ DR) & (DR ^ b))) SEV;
NZ; WR; SVC;
/* Reserved instructions */
case 0170000: case 0070000: goto ri;
}
/* Unary */
switch(cpu->ir & 0007700){
case 0005000: TRB(CLR);
RD_U; CLCV;
b = 0;
NZ; WR; SVC;
case 0005100: TRB(COM);
RD_U; CLV; SEC;
b = W(~SR);
NZ; WR; SVC;
case 0005200: TRB(INC);
RD_U; CLV;
b = W(SR+1); BXT;
if(sgn(~SR&b)) SEV;
NZ; WR; SVC;
case 0005300: TRB(DEC);
RD_U; CLV;
b = W(SR+~0); BXT;
if(sgn(SR&~b)) SEV;
NZ; WR; SVC;
case 0005400: TRB(NEG);
RD_U; CLCV;
b = W(~SR+1); BXT; if(b) SEC;
if(sgn(b&SR)) SEV;
NZ; WR; SVC;
case 0005500: TRB(ADC);
RD_U; c = ISSET(PSW_C); CLCV;
b = SR + c; C; BXT;
if(sgn(~SR&b)) SEV;
NZ; WR; SVC;
case 0005600: TRB(SBC);
RD_U; c = !ISSET(PSW_C)-1; CLCV;
b = W(SR+c); if(c && SR == 0) SEC; BXT;
if(sgn(SR&~b)) SEV;
NZ; WR; SVC;
case 0005700: TRB(TST);
RD_U; CLCV;
b = SR;
NZ; SVC;
case 0006000: TRB(ROR);
RD_U; c = ISSET(PSW_C); CLCV;
b = (SR&mask) >> 1; if(c) b |= sign; if(SR & 1) SEC; BXT;
if((PSW>>3^PSW)&1) SEV;
NZ; WR; SVC;
case 0006100: TRB(ROL);
RD_U; c = ISSET(PSW_C); CLCV;
b = (SR<<1) & mask; if(c) b |= 1; if(SR & B15) SEC; BXT;
if((PSW>>3^PSW)&1) SEV;
NZ; WR; SVC;
case 0006200: TRB(ASR);
RD_U; c = ISSET(PSW_C); CLCV;
b = W(SR>>1) | SR&B15; if(SR & 1) SEC; BXT;
if((PSW>>3^PSW)&1) SEV;
NZ; WR; SVC;
case 0006300: TRB(ASL);
RD_U; CLCV;
b = W(SR<<1); if(SR & B15) SEC; BXT;
if((PSW>>3^PSW)&1) SEV;
NZ; WR; SVC;
case 0006400:
case 0006500:
case 0006600:
case 0006700:
goto ri;
}
switch(cpu->ir & 0107400){
case 0004000:
case 0004400: TR(JSR);
if(dm == 0) goto ill;
if(addrop(cpu, dst, 0)) goto be;
DR = cpu->b;
PUSH; OUT(SP, cpu->r[sf]);
cpu->r[sf] = PC; PC = DR;
SVC;
case 0104000: TR(EMT); TRAP(030);
case 0104400: TR(TRAP); TRAP(034);
}
/* Branches */
switch(cpu->ir & 0103400){
case 0000400: TR(BR); BR; SVC;
case 0001000: TR(BNE); CBR(0x0F0F); SVC;
case 0001400: TR(BEQ); CBR(0xF0F0); SVC;
case 0002000: TR(BGE); CBR(0xCC33); SVC;
case 0002400: TR(BLT); CBR(0x33CC); SVC;
case 0003000: TR(BGT); CBR(0x0C03); SVC;
case 0003400: TR(BLE); CBR(0xF3FC); SVC;
case 0100000: TR(BPL); CBR(0x00FF); SVC;
case 0100400: TR(BMI); CBR(0xFF00); SVC;
case 0101000: TR(BHI); CBR(0x0505); SVC;
case 0101400: TR(BLOS); CBR(0xFAFA); SVC;
case 0102000: TR(BVC); CBR(0x3333); SVC;
case 0102400: TR(BVS); CBR(0xCCCC); SVC;
case 0103000: TR(BCC); CBR(0x5555); SVC;
case 0103400: TR(BCS); CBR(0xAAAA); SVC;
}
// Hope we caught all instructions we meant to
assert((cpu->ir & 0177400) == 0);
/* Misc */
switch(cpu->ir & 0300){
case 0100: TR(JMP);
if(dm == 0) goto ill;
if(addrop(cpu, dst, 0)) goto be;
PC = cpu->b;
SVC;
case 0200:
switch(cpu->ir&070){
case 000: TR(RTS);
BA = SP; POP;
PC = cpu->r[df];
IN(cpu->r[df]);
SVC;
case 010: case 020: case 030:
goto ri;
case 040: case 050: TR(CCC); PSW &= ~(cpu->ir&017); SVC;
case 060: case 070: TR(SEC); PSW |= cpu->ir&017; SVC;
}
case 0300: TR(SWAB);
RD_U; CLC;
b = WD(DR & 0377, (DR>>8) & 0377);
NZ; WR; SVC;
}
/* Operate */
switch(cpu->ir & 7){
case 0: TR(HALT); cpu->state = STATE_HALTED; return;
case 1: TR(WAIT); cpu->state = STATE_WAITING; return;
case 2: TR(RTI);
BA = SP; POP; IN(PC);
BA = SP; POP; IN(PSW);
levelchange(cpu->psw) ;
SVC;
case 3: TR(BPT); TRAP(014);
case 4: TR(IOT); TRAP(020);
case 5: TR(RESET); ka11_reset(cpu); unibone_bus_init(10) ; SVC;
}
// All other instructions should be reserved now
ri: TRAP(010);
ill: TRAP(4);
be: if(cpu->be > 1){
printf("double bus error, HALT\n");
cpu->state = STATE_HALTED;
return;
}
trace("bus error at %06o\n", cpu->ba);
TRAP(4);
trap:
trace("TRAP %o\n", TV);
PUSH; OUT(SP, PSW);
PUSH; OUT(SP, PC);
INA(TV, PC);
INA(TV+2, PSW);
levelchange(PSW);
/* no trace trap after a trap */
oldpsw = PSW;
tracestate(cpu);
return; // TODO: is this correct?
// SVC;
service:
{
// external interrupt from parallel threads?
pthread_mutex_lock(&cpu->mutex) ;
bool external_intr = cpu->external_intr ;
word external_intrvec = cpu->external_intrvec ;
cpu->external_intr = 0 ;
pthread_mutex_unlock(&cpu->mutex) ;
if (external_intr){
TRAP(external_intrvec);
}
}
c = PSW >> 5;
if(oldpsw & PSW_T){
oldpsw &= ~PSW_T;
TRAP(014);
}else if(cpu->traps & TRAP_STACK){
cpu->traps &= ~TRAP_STACK;
inhov = 1;
TRAP(4);
}else if(cpu->traps & TRAP_PWR){
cpu->traps &= ~TRAP_PWR;
TRAP(024);
}else if(c < 7 && cpu->traps & TRAP_BR7){
cpu->traps &= ~TRAP_BR7;
TRAP(cpu->br[3].bg(cpu->br[3].dev));
}else if(c < 6 && cpu->traps & TRAP_BR6){
cpu->traps &= ~TRAP_BR6;
TRAP(cpu->br[2].bg(cpu->br[2].dev));
}else if(c < 5 && cpu->traps & TRAP_BR5){
cpu->traps &= ~TRAP_BR5;
TRAP(cpu->br[1].bg(cpu->br[1].dev));
}else if(c < 4 && cpu->traps & TRAP_BR4){
cpu->traps &= ~TRAP_BR4;
TRAP(cpu->br[0].bg(cpu->br[0].dev));
}else
// TODO? console stop
/* fetch next instruction */
return;
}
// to be called from parallel threads to signal asnyc intr
// (unibusadapter worker thread)
void
ka11_setintr(KA11 *cpu, unsigned vec)
{
pthread_mutex_lock(&cpu->mutex) ;
cpu->external_intr = true;
cpu->external_intrvec = vec;
trace("INTR vec=%03o\n", vec) ;
if (cpu->state == STATE_WAITING) // atomically
cpu->state = STATE_RUNNING ;
pthread_mutex_unlock(&cpu->mutex) ;
}
// only to be called from ka11_condstep() thread
void
ka11_pwrdown(KA11 *cpu)
{
cpu->traps |= TRAP_PWR;
}
// only to be called from ka11_condstep() thread
// if locked, will lock DATI and unibus adapter()!
void
ka11_pwrup(KA11 *cpu)
{
INA(024, PC);
INA(024+2, PSW);
return ;
be:
trace("BE\n");
cpu->be++ ;
}
void
ka11_condstep(KA11 *cpu)
{
if(cpu->state == STATE_RUNNING || cpu->state == STATE_WAITING)
// GRANT Interrupts before opcode fetch, or when CPU is on WAIT
unibone_grant_interrupts() ;
if((cpu->state == STATE_RUNNING) ||
(cpu->state == STATE_WAITING && cpu->traps)){
cpu->state = STATE_RUNNING;
// external_intr WAIT handled atomically in ka11_setintr() !
svc(cpu, cpu->bus);
step(cpu);
}
}
void
run(KA11 *cpu)
{
cpu->state = STATE_RUNNING;
while(cpu->state != STATE_HALTED){
ka11_condstep(cpu);
}
printstate(cpu);
}