diff --git a/src/apr.c b/src/apr.c index f7e1087..1d14b78 100644 --- a/src/apr.c +++ b/src/apr.c @@ -348,9 +348,9 @@ set_mc_rq(Apr *apr, bool value) { apr->mc_rq = value; // 7-9 if(value && (apr->mc_rd || apr->mc_wr)) - membus0 |= MEMBUS_RQ_CYC; + apr->membus.c12 |= MEMBUS_RQ_CYC; else - membus0 &= ~MEMBUS_RQ_CYC; + apr->membus.c12 &= ~MEMBUS_RQ_CYC; } void @@ -358,9 +358,9 @@ set_mc_wr(Apr *apr, bool value) { apr->mc_wr = value; // 7-9 if(value) - membus0 |= MEMBUS_WR_RQ; + apr->membus.c12 |= MEMBUS_WR_RQ; else - membus0 &= ~MEMBUS_WR_RQ; + apr->membus.c12 &= ~MEMBUS_WR_RQ; set_mc_rq(apr, apr->mc_rq); // 7-9 } @@ -369,9 +369,9 @@ set_mc_rd(Apr *apr, bool value) { apr->mc_rd = value; // 7-9 if(value) - membus0 |= MEMBUS_RD_RQ; + apr->membus.c12 |= MEMBUS_RD_RQ; else - membus0 &= ~MEMBUS_RD_RQ; + apr->membus.c12 &= ~MEMBUS_RD_RQ; set_mc_rq(apr, apr->mc_rq); // 7-9 } @@ -404,15 +404,15 @@ relocate(Apr *apr) apr->rla += apr->rlr; // 7-2, 7-10 - membus0 &= ~0007777777761LL; - membus0 |= ma_fmc_select ? MEMBUS_MA_FMC_SEL1 : MEMBUS_MA_FMC_SEL0; - membus0 |= (apr->ma&01777) << 4; - membus0 |= ((word)apr->rla&017) << 14; - membus0 |= apr->rla & 0020 ? MEMBUS_MA21_1|MEMBUS_MA21 : MEMBUS_MA21_0; - membus0 |= apr->rla & 0040 ? MEMBUS_MA20_1 : MEMBUS_MA20_0; - membus0 |= apr->rla & 0100 ? MEMBUS_MA19_1 : MEMBUS_MA19_0; - membus0 |= apr->rla & 0200 ? MEMBUS_MA18_1 : MEMBUS_MA18_0; - membus0 |= apr->ma & 01 ? MEMBUS_MA35_1 : MEMBUS_MA35_0; + apr->membus.c12 &= ~0007777777761LL; + apr->membus.c12 |= ma_fmc_select ? MEMBUS_MA_FMC_SEL1 : MEMBUS_MA_FMC_SEL0; + apr->membus.c12 |= (apr->ma&01777) << 4; + apr->membus.c12 |= ((word)apr->rla&017) << 14; + apr->membus.c12 |= apr->rla & 0020 ? MEMBUS_MA21_1|MEMBUS_MA21 : MEMBUS_MA21_0; + apr->membus.c12 |= apr->rla & 0040 ? MEMBUS_MA20_1 : MEMBUS_MA20_0; + apr->membus.c12 |= apr->rla & 0100 ? MEMBUS_MA19_1 : MEMBUS_MA19_0; + apr->membus.c12 |= apr->rla & 0200 ? MEMBUS_MA18_1 : MEMBUS_MA18_0; + apr->membus.c12 |= apr->ma & 01 ? MEMBUS_MA35_1 : MEMBUS_MA35_0; return ma_ok; } @@ -2748,13 +2748,11 @@ pulse(it0){ pulse(mai_addr_ack){ trace("MAI ADDR ACK\n"); - nextpulse(apr, mc_addr_ack); // 7-8 + curpulse(apr, mc_addr_ack); // 7-8 } pulse(mai_rd_rs){ trace("MAI RD RS\n"); - /* we do this here instead of whenever MC RD is set; 7-6, 7-9 */ - apr->mb = membus1; if(apr->ma == apr->mas) apr->mi = apr->mb; // 7-7 if(!apr->mc_stop) @@ -2795,8 +2793,8 @@ pulse(mc_wr_rs){ trace("MC WR RS\n"); if(apr->ma == apr->mas) apr->mi = apr->mb; // 7-7 - membus1 = apr->mb; // 7-8 - membus0 |= MEMBUS_WR_RS; // 7-8 + apr->membus.c34 |= apr->mb; // 7-8 + apr->membus.c12 |= MEMBUS_WR_RS; // 7-8 if(!apr->mc_stop) nextpulse(apr, mc_rs_t0); // 7-8 } @@ -3012,6 +3010,16 @@ pulse(key_manual){ nextpulse(apr, kt0); // 5-2 } +void +curpulse(Apr *apr, Pulse *p) +{ + if(apr->ncurpulses >= MAXPULSE){ + fprint(stderr, "error: too many current pulses\n"); + exit(1); + } + apr->clist[apr->ncurpulses++] = p; +} + void nextpulse(Apr *apr, Pulse *p) { @@ -3072,13 +3080,14 @@ aprmain(void *p) apr->iobus.c12_prev = apr->iobus.c12; apr->iobus.c34_prev = apr->iobus.c34; - membus0_last = membus0; + apr->membus.c12_prev = apr->membus.c12; + apr->membus.c34_prev = apr->membus.c34; for(i = 0; i < apr->ncurpulses; i++) apr->clist[i](apr); updatebus(&apr->iobus); - membus0_pulse = (membus0_last ^ membus0) & membus0; + updatebus(&apr->membus); /* This is simplified, we have no IOT RESET, * IOT INIT SET UP or IOT FINAL SETUP really. @@ -3126,23 +3135,30 @@ aprmain(void *p) } apr->iobus.c34 &= ~(IOBUS_PULSES | IOBUS_IOB_RESET); - /* Pulses to memory */ - if(membus0_pulse & (MEMBUS_WR_RS | MEMBUS_RQ_CYC)){ - wakemem(); - membus0 &= ~MEMBUS_WR_RS; + if(apr->membus.c12_pulse & (MEMBUS_WR_RS | MEMBUS_RQ_CYC)){ + wakemem(&apr->membus); + apr->membus.c12 &= ~MEMBUS_WR_RS; } /* Pulses from memory */ - if(membus0 & MEMBUS_MAI_ADDR_ACK){ - membus0 &= ~MEMBUS_MAI_ADDR_ACK; + if(apr->membus.c12 & MEMBUS_MAI_ADDR_ACK){ + apr->membus.c12 &= ~MEMBUS_MAI_ADDR_ACK; apr->extpulse &= ~EXT_NONEXIT_MEM; nextpulse(apr, mai_addr_ack); } - if(membus0 & MEMBUS_MAI_RD_RS){ - membus0 &= ~MEMBUS_MAI_RD_RS; + if(apr->membus.c12 & MEMBUS_MAI_RD_RS){ + apr->membus.c12 &= ~MEMBUS_MAI_RD_RS; nextpulse(apr, mai_rd_rs); } + if(apr->mc_rd && apr->membus.c34){ + /* 7-6, 7-9 */ + apr->mb |= apr->membus.c34; + apr->membus.c34 = 0; + } + + /* TODO: do this differently because the + * memory might have been busy. */ if(apr->extpulse & EXT_NONEXIT_MEM){ apr->extpulse &= ~EXT_NONEXIT_MEM; if(apr->mc_rq && !apr->mc_stop) diff --git a/src/main.c b/src/main.c index 25a0334..b3c1379 100644 --- a/src/main.c +++ b/src/main.c @@ -539,6 +539,7 @@ Apr apr; Tty tty; Ptr ptr; Ptp ptp; +Mem *coremems[4]; int main(int argc, char *argv[]) @@ -660,10 +661,20 @@ main(int argc, char *argv[]) extra_l = e; e += 1; initapr(&apr); - initmem(); +// initmem(); + coremems[0] = makecoremem("mem_0"); + coremems[1] = makecoremem("mem_1"); + coremems[2] = makecoremem("mem_2"); + coremems[3] = makecoremem("mem_3"); + attachmem(makefastmem(0), 0, &apr.membus, -1); + attachmem(coremems[0], 0, &apr.membus, 0); + attachmem(coremems[1], 0, &apr.membus, 1); + attachmem(coremems[2], 0, &apr.membus, 2); + attachmem(coremems[3], 0, &apr.membus, 3); inittty(&tty, &apr.iobus); initptr(&ptr, &apr.iobus); initptp(&ptp, &apr.iobus); + showmem(&apr.membus); for(;;){ start = SDL_GetTicks(); diff --git a/src/mem.c b/src/mem.c index 078665e..903e6a4 100644 --- a/src/mem.c +++ b/src/mem.c @@ -1,16 +1,9 @@ #include "pdp6.h" -#include -word memory[256*1024]; -//hword maxmem = 256*1024; -hword maxmem = 64*1024; -word fmem[16]; -word membus0, membus1; -word membus0_last, membus0_pulse; -word *hold; +Membus memterm; void -readmem(char *file, word *mem, word size) +readmem(const char *file, word *mem, word size) { FILE *f; char buf[100], *s; @@ -37,62 +30,260 @@ readmem(char *file, word *mem, word size) fclose(f); } -void -initmem(void) -{ - readmem("../mem", memory, maxmem); - readmem("../fmem", fmem, 16); -} +/* Both functions below are very confusing. I'm sorry. + * The schematics cannot be converted to C in a straightfoward way + * but I tried my best. */ -void -dumpmem(void) +/* This is based on the 161C memory */ +static int +wakecore(Mem *mem, Membus *bus) { - hword a; - FILE *f; + bool p2, p3; + CMem *core; + core = mem->module; - if(f = fopen("memdump", "w"), f == nil) - return; - for(a = 0; a < 16; a++) - fprint(f, "%02o: %012llo\n", a, fmem[a]); - for(a = 0; a < maxmem; a++){ - if(memory[a]){ - fprint(f, "%06o: ", a); - fprint(f, "%012llo\n", memory[a]); + /* If not connected to a bus, find out which to connect to. + * Lower numbers have higher priority but proc 2 and 3 have + * the same priority and requests are alternated between them. + * If no request is made, we should already be connected to a bus */ + pthread_mutex_lock(&core->mutex); + if(core->cmc_p_act < 0){ + if(mem->bus[0]->c12 & MEMBUS_RQ_CYC) + core->cmc_p_act = 0; + else if(mem->bus[1]->c12 & MEMBUS_RQ_CYC) + core->cmc_p_act = 1; + else{ + p2 = !!(mem->bus[2]->c12 & MEMBUS_RQ_CYC); + p3 = !!(mem->bus[3]->c12 & MEMBUS_RQ_CYC); + if(p2 && p3){ + if(core->cmc_last_proc == 2) + core->cmc_p_act = 3; + else if(core->cmc_last_proc == 3) + core->cmc_p_act = 2; + }else{ + if(p2) + core->cmc_p_act = 2; + else if(p3) + core->cmc_p_act = 3; + else{ + pthread_mutex_unlock(&core->mutex); + return 1; /* no request at all? */ + } + } + core->cmc_last_proc = core->cmc_p_act; } } - fclose(f); + pthread_mutex_unlock(&core->mutex); + + /* The executing thread can only service requests from its own processor + * due to synchronization with the processor's pulse cycle. */ + if(bus != mem->bus[core->cmc_p_act]) + return 1; + + if(core->cmc_aw_rq && bus->c12 & MEMBUS_RQ_CYC){ + /* accept cycle request */ + core->cmc_aw_rq = 0; + //trace(" accepting memory cycle from proc %d\n", core->cmc_p_act); + + core->cmb = 0; + core->cma = 0; + core->cma_rd_rq = 0; + core->cma_wr_rq = 0; + core->cmc_pse_sync = 0; + + /* strobe address and send acknowledge */ + core->cma |= bus->c12>>4 & 037777; + core->cma_rd_rq |= !!(bus->c12 & MEMBUS_RD_RQ); + core->cma_wr_rq |= !!(bus->c12 & MEMBUS_WR_RQ); + //trace(" sending ADDR ACK\n"); + bus->c12 |= MEMBUS_MAI_ADDR_ACK; + + /* read and send read restart */ + if(core->cma_rd_rq){ + core->cmb |= core->core[core->cma]; + bus->c34 |= core->cmb & FW; + bus->c12 |= MEMBUS_MAI_RD_RS; + //trace(" sending RD RS\n"); + if(core->cma_wr_rq) + core->cmb = 0; + else + core->cmc_p_act = -1; + } + core->cmc_pse_sync = 1; + if(!core->cma_wr_rq) + goto end; + } + /* write restart */ + if(core->cmc_pse_sync && bus->c12_pulse & MEMBUS_WR_RS){ + //trace(" accepting WR RS\n"); + core->cmc_p_act = -1; + core->cmb |= bus->c34 & FW; + bus->c34 = 0; +end: + //trace(" writing\n"); + core->core[core->cma] = core->cmb; + core->cmc_p_act = -1; /* this seems unnecessary */ + core->cmc_aw_rq = 1; + } + //if(core->cmc_p_act < 0) + // trace(" now disconnected from proc\n"); + return 0; } -/* When a cycle is requested we acknowledge the address - * by pulsing the processor through the bus. - * A read is completed immediately and signalled by a second pulse. - * A write is completed on a second call. */ -// TODO: implement this properly... according to the manual -void -wakemem(void) +/* This is based on the 162 memory */ +static int +wakeff(Mem *mem, Membus *bus) { - hword a; - if(membus0_pulse & MEMBUS_RQ_CYC){ - a = membus0>>4 & 037777; - if(membus0 & MEMBUS_MA21_1) a |= 0040000; - if(membus0 & MEMBUS_MA20_1) a |= 0100000; - if(membus0 & MEMBUS_MA19_1) a |= 0200000; - if(membus0 & MEMBUS_MA18_1) a |= 0400000; - if(a >= maxmem || - membus0 & MEMBUS_MA_FMC_SEL1 && a >= 16) - return; + FMem *ff; + hword fma; + bool fma_rd_rq, fma_wr_rq; + bool fmc_wr_sel; + ff = mem->module; - membus0 |= MEMBUS_MAI_ADDR_ACK; - hold = membus0 & MEMBUS_MA_FMC_SEL1 ? &fmem[a] : &memory[a]; - if(membus0 & MEMBUS_RD_RQ){ - membus1 = *hold & FW; - membus0 |= MEMBUS_MAI_RD_RS; - if(!(membus0 & MEMBUS_WR_RQ)) - hold = nil; + /* Only respond to one processor */ + if(bus != mem->bus[ff->fmc_p_sel]) + return 1; + + fma_rd_rq = !!(bus->c12 & MEMBUS_RD_RQ); + fma_wr_rq = !!(bus->c12 & MEMBUS_WR_RQ); + if(!ff->fmc_act && bus->c12 & MEMBUS_RQ_CYC){ + //trace(" accepting memory cycle from proc %d\n", ff->fmc_p_sel); + ff->fmc_act = 1; + + fma = bus->c12>>4 & 017; + //trace(" sending ADDR ACK\n"); + bus->c12 |= MEMBUS_MAI_ADDR_ACK; + + if(fma_rd_rq){ + bus->c34 |= ff->ff[fma] & FW; + bus->c12 |= MEMBUS_MAI_RD_RS; + //trace(" sending RD RS\n"); + if(!fma_wr_rq) + goto end; + } + if(fma_wr_rq){ + ff->ff[fma] = 0; + ff->fmc_wr = 1; } } - if(membus0 & MEMBUS_WR_RS && hold){ - *hold = membus1 & FW; - hold = nil; + fmc_wr_sel = ff->fmc_act && !fma_rd_rq; + if(fmc_wr_sel && ff->fmc_wr && bus->c34){ + //trace(" writing\n"); + ff->ff[fma] |= bus->c34 & FW; + bus->c34 = 0; } + if(bus->c12_pulse & MEMBUS_WR_RS){ + //trace(" accepting WR RS\n"); +end: + ff->fmc_act = 0; + ff->fmc_wr = 0; + } + //if(!ff->fmc_act) + // trace(" now available again\n"); + return 0; +} + +void +wakemem(Membus *bus) +{ + int sel; + int nxm; + + if(bus->c12 & MEMBUS_MA_FMC_SEL1){ + nxm = bus->fmem->wake(bus->fmem, bus); + if(nxm) + goto core; + }else{ + core: + sel = 0; + if(bus->c12 & MEMBUS_MA21_1) sel |= 001; + if(bus->c12 & MEMBUS_MA20_1) sel |= 002; + if(bus->c12 & MEMBUS_MA19_1) sel |= 004; + if(bus->c12 & MEMBUS_MA18_1) sel |= 010; + if(bus->cmem[sel]) + nxm = bus->cmem[sel]->wake(bus->cmem[sel], bus); + else + nxm = 1; + } + /* TODO: do something when memory didn't respond */ +} + +/* Allocate a 16k core memory module and + * a memory bus attachment. */ +Mem* +makecoremem(const char *file) +{ + CMem *core; + Mem *mem; + + core = malloc(sizeof(CMem)); + memset(core, 0, sizeof(CMem)); + core->filename = file; + pthread_mutex_init(&core->mutex, nil); + core->cmc_aw_rq = 1; + core->cmc_p_act = -1; + core->cmc_last_proc = 2; + readmem(core->filename, core->core, 040000); + + mem = malloc(sizeof(Mem)); + mem->module = core; + mem->bus[0] = &memterm; + mem->bus[1] = &memterm; + mem->bus[2] = &memterm; + mem->bus[3] = &memterm; + mem->wake = wakecore; + + return mem; +} + +Mem* +makefastmem(int p) +{ + FMem *ff; + Mem *mem; + + ff = malloc(sizeof(FMem)); + memset(ff, 0, sizeof(FMem)); + ff->fmc_p_sel = p; + + mem = malloc(sizeof(Mem)); + mem->module = ff; + mem->bus[0] = &memterm; + mem->bus[1] = &memterm; + mem->bus[2] = &memterm; + mem->bus[3] = &memterm; + mem->wake = wakeff; + + return mem; +} + +/* Attach mem to bus for processor p. + * If n < 0, it is attached as fast memory, + * else at addresses starting at n<<14. */ +void +attachmem(Mem *mem, int p, Membus *bus, int n) +{ + if(n < 0) + bus->fmem = mem; + else + bus->cmem[n] = mem; + mem->bus[p] = bus; +} + +void +showmem(Membus *bus) +{ + int i; + CMem *core; + FMem *ff; + + if(bus->fmem){ + ff = bus->fmem->module; + printf("fastmem: proc %d\n", ff->fmc_p_sel); + } + for(i = 0; i < 16; i++) + if(bus->cmem[i]){ + core = bus->cmem[i]->module; + printf("%06o %06o %s\n", i<<14, (i+1 << 14)-1, core->filename); + } } diff --git a/src/pdp6.h b/src/pdp6.h index a699189..cb67c36 100644 --- a/src/pdp6.h +++ b/src/pdp6.h @@ -2,6 +2,9 @@ #include #include #include +#include +#include +#include #define nelem(a) (sizeof(a)/sizeof(a[0])) #define nil NULL @@ -12,6 +15,7 @@ typedef uint64_t word; typedef uint32_t hword; typedef uint32_t u32; typedef uint16_t u16; +typedef int8_t i8; typedef uint8_t u8; typedef unsigned char uchar; typedef uchar bool; @@ -106,13 +110,18 @@ enum Opcode { }; +typedef struct Mem Mem; +typedef struct Membus Membus; +typedef struct FMem FMem; +typedef struct CMem CMem; +typedef struct Busdev Busdev; +typedef struct IOBus IOBus; +typedef struct Apr Apr; + /* * Memory */ -void initmem(void); -void dumpmem(void); -void wakemem(void); // 7-2, 7-10 enum { MEMBUS_MA21 = 0000000000001, @@ -135,12 +144,61 @@ enum { MEMBUS_MAI_RD_RS = 0200000000000, MEMBUS_MAI_ADDR_ACK = 0400000000000, }; -/* 0 is cable 1 & 2 (above bits); 1 is cable 3 & 4 (data) */ -extern word membus0, membus1; -/* record the state of membus0 of the last pulse step - * to recognize pulses or edges */ -extern word membus0_last, membus0_pulse; +/* A memory module connected to up to 4 Membuses */ +struct Mem +{ + void *module; + Membus *bus[4]; + int (*wake)(Mem *mem, Membus *bus); +}; + +struct Membus +{ + /* c12 are cables 1 and 2, c34 3 and 4. + * c??_prev records the previous state of the bus + * and c??_pulse is used to detect leading edges. */ + word c12, c12_prev, c12_pulse; + word c34, c34_prev, c34_pulse; + /* cycle counter to implement NXM timeout */ + int numcyc; + + Mem *fmem; /* fast memory */ + Mem *cmem[16]; /* 16 16k core modules */ +}; +extern Membus memterm; /* terminator */ +void wakemem(Membus *bus); + +/* Fast memory 162, 16 words */ +struct FMem +{ + word ff[16]; + i8 fmc_p_sel; + bool fmc_act; + bool fmc_wr; +}; +Mem *makefastmem(int p); + +/* Core memory 161C, 16k words */ +struct CMem +{ + const char *filename; + pthread_mutex_t mutex; + + word core[040000]; + word cmb; + hword cma; + bool cma_rd_rq, cma_wr_rq; + bool cmc_aw_rq; + i8 cmc_p_act; + u8 cmc_last_proc; + bool cmc_pse_sync; +}; +Mem *makecoremem(const char *file); + +void attachmem(Mem *mem, int p, Membus *bus, int n); +void readmem(const char *file, word *mem, word size); +void showmem(Membus *bus); /* * IO bus @@ -191,7 +249,6 @@ enum { #define IOB_DATAI (bus->c34 & IOBUS_IOB_DATAI) /* A peripheral device connected to an IO bus */ -typedef struct Busdev Busdev; struct Busdev { void *dev; @@ -199,7 +256,6 @@ struct Busdev u8 req; }; -typedef struct IOBus IOBus; struct IOBus { /* c12 are cables 1 and 2, c34 3 and 4. @@ -222,7 +278,6 @@ void recalc_req(IOBus *bus); /* Arithmetic Processor 166 */ #define CPA (0000>>2) #define PI (0004>>2) -typedef struct Apr Apr; typedef void Pulse(Apr *apr); #define pulse(p) static void p(Apr *apr) @@ -230,6 +285,7 @@ typedef void Pulse(Apr *apr); struct Apr { IOBus iobus; + Membus membus; hword ir; word mi; @@ -336,6 +392,7 @@ struct Apr int ncurpulses, nnextpulses; }; void initapr(Apr *apr); +void curpulse(Apr *apr, Pulse *p); void nextpulse(Apr *apr, Pulse *p); void *aprmain(void *p);