diff --git a/emu/Makefile b/emu/Makefile index eac4f5c..4d32bdf 100644 --- a/emu/Makefile +++ b/emu/Makefile @@ -14,3 +14,6 @@ pdp6: $(SRC) $(H) test_dc: test_dc.c dc.c $(H) $(CC) -o $@ $(CFLAGS) test_dc.c dc.c + +test_dt: test_dt.c dc.c dt.c $(H) + $(CC) -o $@ $(CFLAGS) test_dt.c dc.c dt.c diff --git a/emu/apr.c b/emu/apr.c index 9701938..b94eade 100644 --- a/emu/apr.c +++ b/emu/apr.c @@ -230,11 +230,8 @@ makeapr(int argc, char *argv[]) apr->dev.type = apr_ident; apr->dev.name = ""; - apr->dev.attach = nil; - apr->dev.ioconnect = nil; apr->dev.examine = ex_apr; apr->dev.deposit = dep_apr; - apr->dev.next = nil; apr->iobus.dev[CPA] = (Busdev){ apr, wake_cpa, 0 }; apr->iobus.dev[PI] = (Busdev){ apr, wake_pi, 0 }; diff --git a/emu/cmd.c b/emu/cmd.c index 1be104a..71992e3 100644 --- a/emu/cmd.c +++ b/emu/cmd.c @@ -260,6 +260,27 @@ c_attach(int argc, char *argv[]) dev->attach(dev, argv[2]); } +static void +c_detach(int argc, char *argv[]) +{ + Device *dev; + + if(argc < 2){ + printf("Not enough arguments\n"); + return; + } + dev = getdevice(argv[1]); + if(dev == nil){ + printf("No device: %s\n", argv[1]); + return; + } + if(dev->detach == nil){ + printf("No detach for device type %s\n", dev->type); + return; + } + dev->detach(dev); +} + /* ioconnect dev busdev */ static void c_ioconnect(int argc, char *argv[]) @@ -615,6 +636,8 @@ struct { "make a device: mkdev name type [args]" }, { "attach", c_attach, "attach a file to a devie: attach name filename" }, + { "detach", c_detach, + "detach a file from a devie: attach name" }, { "connectio", c_ioconnect, "connect device to IO bus: connectio devname procname" }, { "connectmem", c_memconnect, diff --git a/emu/dc.c b/emu/dc.c index 4350fd7..84ddfdd 100644 --- a/emu/dc.c +++ b/emu/dc.c @@ -244,7 +244,6 @@ makedc(int argc, char *argv[]) dc->dev.type = dc_ident; dc->dev.name = ""; - dc->dev.attach = nil; dc->dev.ioconnect = dcioconnect; return &dc->dev; diff --git a/emu/dt.c b/emu/dt.c index 9df883c..c4bb4ca 100644 --- a/emu/dt.c +++ b/emu/dt.c @@ -10,11 +10,14 @@ char *dx_ident = DX_IDENT; enum { - DTSIZE = 922512 +// DTSIZE = 922512 + // 01102 blocks, 2700 word for start/end each + safe zone + DTSIZE = 987288 + 1000 }; /* Transport */ +#if 0 static uchar dxread(Dx555 *dx) { @@ -32,6 +35,25 @@ dxwrite(Dx555 *dx, uchar d) else *dx->cur = d; } +#endif + +static uchar +dxread_(Dx555 *dx) +{ + if(dx->dt->ut_rev) + return *dx->cur ^ 017; + else + return *dx->cur; +} + +static void +dxwrite_(Dx555 *dx, uchar d) +{ + if(dx->dt->ut_rev) + *dx->cur = d ^ 017; + else + *dx->cur = d; +} static void dxmove(Dx555 *dx) @@ -48,19 +70,31 @@ dxmove(Dx555 *dx) } } +static void +dxdetach(Device *dev) +{ + Dx555 *dx; + + dx = (Dx555*)dev; + if(dx->fd < 0) + return; + + lseek(dx->fd, 0, SEEK_SET); + write(dx->fd, dx->start, dx->size); + close(dx->fd); + dx->fd = -1; + memset(dx->start, 0, dx->size); +} + static void dxattach(Device *dev, const char *path) { Dx555 *dx; - int fd; dx = (Dx555*)dev; - fd = dx->fd; - if(fd >= 0){ - dx->fd = -1; - close(dx->fd); - } - memset(dx->start, 0, DTSIZE); + dxdetach(dev); + memset(dx->start, 0, dx->size); + // TODO: resize dx->fd = open(path, O_RDWR | O_CREAT, 0666); if(dx->fd < 0) fprintf(stderr, "couldn't open file %s\n", path); @@ -88,12 +122,14 @@ makedx(int argc, char *argv[]) dx->dev.type = dx_ident; dx->dev.name = ""; dx->dev.attach = dxattach; - dx->dev.ioconnect = nil; + dx->dev.detach = dxdetach; - dx->start = malloc(DTSIZE); + dx->fd = -1; + dx->size = DTSIZE; + dx->start = malloc(dx->size); dx->cur = dx->start; - dx->end = dx->start + DTSIZE; - memset(dx->start, 0, DTSIZE); + dx->end = dx->start + dx->size; + memset(dx->start, 0, dx->size); return &dx->dev; } @@ -105,8 +141,10 @@ enum TapeEndF = 022, TapeEndR = 055, + /* block mark high half */ BlockSpace = 025, + /* block mark low half */ BlockEndF = 026, BlockEndR = 045, @@ -126,19 +164,17 @@ enum #define DATA_SYNC (dt->tmk == (0600|DataSync)) #define END_ZONE (dt->tmk == (0600|TapeEndF)) -#define BM_SPACE (dt->tmk == (0400|BlockSpace) ||\ - dt->tmk == (0500|BlockSpace) ||\ - dt->tmk == (0600|BlockSpace) ||\ - dt->tmk == (0700|BlockSpace)) +#define BM_SPACE ((dt->tmk&0477) == (0400|BlockSpace)) #define BM_END (dt->tmk == (0500|BlockEndF)) -#define DATA_SYNC (dt->tmk == (0600|DataSync)) #define BM_SYNC (dt->tmk == (0700|BlockSync)) +#define REV_BM (dt->tmk == (0500|BlockEndR)) #define FWD_DATA_END (dt->tmk == (0400|DataEndF) ||\ dt->tmk == (0700|DataEndF)) #define REV_DATA_END (dt->tmk == (0400|DataEndR) ||\ dt->tmk == (0600|DataEndR)) #define DATA (dt->tmk == (0400|Data)) + #define STOP (dt->tdata == 0 || dt->tdata == 1) #define SYNC (dt->tdata == 0200) #define REV_CKS (dt->tdata == 0100) @@ -146,6 +182,7 @@ enum #define PRE_FINAL (dt->tdata == 004) #define FWD_CKS (dt->tdata == 002) + #define UT_WRITE ((dt->ut_fcn & 04) == 04) #define UT_READ ((dt->ut_fcn & 04) == 0) #define UT_ALL ((dt->ut_fcn & 03) == 01) @@ -154,26 +191,33 @@ enum #define UT_DN (dt->ut_fcn == 0) #define UT_WRTM (dt->ut_fcn == 04) + #define DC_SELECT1 (dt->dc->devp[dt->dc->device] == dt) -#define UTE_DC_DISCONNECT (!(DC_SELECT1 && !dt->dc->da_rq)) -#define UT_WRITE_PREVENT (dt->seldx->wrlock) +#define UT_WRITE_PREVENT (UT_WRITE && dt->seldx->wrlock) #define UT_WREN_DATA (UT_WRITE && dt->ut_wren && !UT_WRITE_PREVENT) -#define RW_ODD (!dt->tct && dt->rw_state == RW_ACTIVE) +#define UT_WREN_BTM (UT_WREN_DATA && dt->ut_btm_switch && UT_WRTM) // 3-14 +#define RW_ODD (!dt->tct && dt->rw_state == RW_ACTIVE) // 3-6 +#define RW_EVEN (dt->tct && dt->rw_state == RW_ACTIVE) #define RW_BM_DONE (UT_BM && BM_END) -#define RW_DATA_CONT (DC_SELECT1 && !dt->dc->da_rq && UT_DATA && dt->rw_state == RW_ACTIVE) -#define RW_DATA_STOP (UTE_DC_DISCONNECT && UT_DATA && dt->rw_state == RW_ACTIVE) +#define RW_DATA_CONT (DC_SELECT1 && !dt->dc->da_rq && UT_DATA && prev.rw_state == RW_ACTIVE) +// TODO: what is RW_DATA_WR_CONT? +// TODO: this is really RW_DATA_WR_STOP +#define RW_DATA_STOP (UTE_DC_DISCONNECT && UT_DATA && prev.rw_state == RW_ACTIVE) #define TBM0 (dt->tbm == 10) #define TBM3 (dt->tbm == 1) -// all but block space +#define UTE_UNIT_OK (nunits == 1) +#define UTE_DC_DISCONNECT (!(DC_SELECT1 && !dt->dc->da_rq)) +// all but block space and rev block mark #define UTE_MK (DATA_SYNC || END_ZONE || BM_END ||\ - DATA_SYNC || BM_SYNC || FWD_DATA_END || REV_DATA_END || DATA) -#define UTE_ERROR (dt->uteck && (dt->utek == 040) != UTE_MK) -// not sure if BM SYNC || DATA SYNC is correct -#define UTE_ACTIVE_ERROR (!UT_ALL && dt->rw_state == RW_ACTIVE && (BM_SYNC || DATA_SYNC)) - + BM_SYNC || FWD_DATA_END || REV_DATA_END || DATA) +#define UTE_ERROR (dt->uteck && (prev.utek == 040) != UTE_MK) +// TODO: not sure if BM SYNC || DATA SYNC is correct +#define UTE_ACTIVE_ERROR (!UT_ALL && prev.rw_state == RW_ACTIVE && (BM_SYNC || DATA_SYNC)) +//#define debug(...) printf(__VA_ARGS__) +#define debug(...) 0 #define PRINT1 \ printf("%c%02o(%o,%02o,%d)", dt->rw_state == RW_ACTIVE ? '+' : dt->rw_state == RW_RQ ? '-' : ' ', \ @@ -194,21 +238,23 @@ enum #define PRINT2 \ if(END_ZONE) \ - printf("TapeEndF "); \ + debug("%c%d %02o TapeEndF ", state, dt->tct, dt->lb); \ else if(BM_SPACE) \ - printf("BlockSpace %o ", dt->tbm); \ + debug("%c%d %02o BlockSpace %o ", state, dt->tct, dt->lb, dt->tbm); \ else if(BM_END) \ - printf("BlockEndF "); \ + debug("%c%d %02o BlockEndF %o ", state, dt->tct, dt->lb, dt->tbm); \ + else if(REV_BM) \ + debug("%c%d %02o BlockEndR %o ", state, dt->tct, dt->lb, dt->tbm); \ else if(DATA_SYNC) \ - printf("DataSync "); \ + debug("%c%d %02o DataSync %o ", state, dt->tct, dt->lb, dt->tdata); \ else if(BM_SYNC) \ - printf("BlockSync "); \ + debug("%c%d %02o BlockSync %o ", state, dt->tct, dt->lb, dt->tbm); \ else if(FWD_DATA_END) \ - printf("DataEndF "); \ + debug("%c%d %02o DataEndF %o ", state, dt->tct, dt->lb, dt->tdata); \ else if(REV_DATA_END) \ - printf("DataEndR "); \ + debug("%c%d %02o DataEndR %o ", state, dt->tct, dt->lb, dt->tdata); \ else if(DATA) \ - printf("Data "); + debug("%c%d %02o Data %o ", state, dt->tct, dt->lb, dt->tdata); static void recalc_dt_req(Dt551 *dt) @@ -225,14 +271,278 @@ recalc_dt_req(Dt551 *dt) setreq(dt->bus, UTC, req); } +#define LDB(p, s, w) ((w)>>(p) & (1<<(s))-1) +#define XLDB(ppss, w) LDB((ppss)>>6 & 077, (ppss)&077, w) +#define MASK(p, s) ((1<<(s))-1 << (p)) +#define DPB(b, p, s, w) ((w)&~MASK(p,s) | (b)<<(p) & MASK(p,s)) +#define XDPB(b, ppss, w) DPB(b, (ppss)>>6 & 077, (ppss)&077, w) + +word dtbuf; +char state = 'n'; + +#define REV (dt->ut_rev && !UT_BM) + +static void +dt_tp0(Dt551 *dt) +{ + dt->sense = dxread_(dt->seldx); + + // 3-7 + if(UT_WRITE && dt->rw_state == RW_ACTIVE) + dt->ut_wren = 1; + + // 3-7 + if(UT_WRITE){ // TODO + int c; + c = REV ? 7 : 0; + if(dt->tct) + dt->wb = dt->rwb&7 ^ c; + else + dt->wb = dt->rwb>>3&7 ^ c; + /* copy to mark track; 3-13/14 */ + if(UT_WREN_BTM) + dt->wb |= dt->wb<<1 & 010; + else + dt->wb = dt->wb&7 | dt->sense&010; + if(UT_WRITE_PREVENT) + dt->ut_illegal_op = 1; // 3-16 + if(UT_WREN_DATA){ + dxwrite_(dt->seldx, dt->wb); +debug("writing %02o\n", dt->wb); + } + } +} + +static void +dt_tp1(Dt551 *dt) +{ + /* complement WB here, totally useless */ + //dt->wb = ~dt->wb & 07; + + /* advance mark track window */ + if(!UT_WRTM) + dt->tmk = ((dt->tmk << 1) | LDB(3, 1, dt->sense)) & 0777 | + dt->tmk&0400; + + /* Strobe sensors into RWB */ + // 3-12 + if(dt->rw_state == RW_ACTIVE && UT_READ){ + int c = REV ? 7 : 0; + if(dt->tct) + dt->rwb |= (dt->sense^c)&07; + else + dt->rwb |= ((dt->sense^c)&07) << 3; + } +} + +static void +dt_tp2(Dt551 *dt) +{ + Dt551 prev; + int tbedge, tdedge; + + prev = *dt; + + if(END_ZONE){ + dt->ut_go = 0; + dt->ut_tape_end_flag = 1; + /* TODO: is this right? */ + dt->rw_state = RW_NULL; + } + + /* Block mark timing. + * Shift down starting at block sync, + * shifts to 10 before rev block number, + * shifts to 01 before fwd block number. */ + // 3-5 + if(BM_SYNC) + dt->tbm |= 020; + if(BM_SYNC || BM_SPACE) + dt->tbm >>= 1; + tbedge = ~prev.tbm & dt->tbm; + + /* Data timing. + * Shift down starting at data sync. + * shifts to 100 before reverse check sum, + * shifts to 001 after forward check sum */ + // 3-8, 3-9 + if(DATA_SYNC) + dt->tdata = 0400; + if(FWD_DATA_END || REV_DATA_END || DATA_SYNC) + dt->tdata >>= 1; + tdedge = ~prev.tdata & dt->tdata; + + + /* Send 6 bit byte to DC every two steps when reading */ + if(UT_READ && RW_EVEN) + /* except checksums */ + if((prev.tdata & 0102) == 0){ + int rwb = dt->rwb; + if(REV) + rwb = rwb>>3 & 07 | rwb<<3 & 070; + dcgv(dt->dc, DEVNO, rwb, REV); + if(UTE_DC_DISCONNECT) + dt->ut_incomp_block = 1; // 3-17 + } + + /* Clear LB before reading the reverse checksum */ + if(tdedge & 0100) + dt->lb = 0; // 3-9 + + /* Keep track of checksum */ + if(dt->tct) + // Manual says XOR, but also talks of parity of zeros +// dt->lb ^= dt->rwb; + dt->lb ^= ~dt->rwb & 077; + + if(prev.rw_state != RW_ACTIVE) + dt->tct = 0; + else + dt->tct ^= 1; + + /* Ring count UTEK */ + if(!BM_SPACE){ + if(dt->utek == 1) + dt->utek = 0100; + dt->utek >>= 1; + } + + /* Go into active state */ + if(prev.rw_state == RW_RQ) + if(UT_BM && tbedge & 1 || // 3-6 + UT_DATA && tdedge & 0100 || // 3-9 + UT_ALL && tbedge & 010) // 3-11 + dt->rw_state = RW_ACTIVE; + + /* What to do after block */ + if(tdedge & 1){ + if(RW_DATA_CONT) // 3-10 + dt->rw_state = RW_RQ; + if(RW_DATA_STOP) // 3-11 + dt->ut_jb_done_flag = 1; + /* test checksum. NB: manual says should be 0 + * TODO: not sure about ACTIVE */ + if(prev.rw_state == RW_ACTIVE && dt->lb != 077) +debug("CHECKSUM ERR\n"), + dt->ut_info_error = 1; // 3-13 + } + + /* Job done after block mark */ + if(prev.rw_state == RW_ACTIVE && RW_BM_DONE) // 3-8 + dt->ut_jb_done_flag = 1; + + /* Catch wrongly set active state; 3-16 */ + if(UTE_ACTIVE_ERROR){ + dt->ut_info_error = 1; + dt->rw_state = RW_NULL; + } + /* Catch invalid mark code */ + if(UTE_ERROR) +debug("TRACK ERROR\n"), + dt->ut_info_error = 1; // 3-15 + + /* Stop activity once job gets done */ + if(!prev.ut_jb_done_flag && dt->ut_jb_done_flag) // 3-8 + dt->rw_state = RW_NULL; +} + +static void +dt_tp3(Dt551 *dt) +{ + // 3-6 + if(RW_ODD) + dt->rwb = 0; + + // 3-15 + /* before fwd block mark */ + if(dt->tbm & 1){ + if(BM_SPACE) + dt->utek = 040; // UTE PRESET 100000 + else + dt->uteck = 1; + } + if(BM_SYNC) + dt->uteck = 0; +} + +static void +dt_tp4(Dt551 *dt) +{ + /* stop writing WB here */ + if(dt->rw_state != RW_ACTIVE) + dt->ut_wren = 0; + + /* get 6 bit byte from DC every two steps when writing */ + if(UT_WRITE && RW_ODD){ + /* except checksums */ + if((dt->tdata & 0102) == 0){ + dt->rwb |= dctk(dt->dc, DEVNO, REV); + if(UTE_DC_DISCONNECT) + dt->ut_incomp_block = 1; // 3-17 + }else +debug("Getting LB\n"), + dt->rwb |= dt->lb; + // 3-6 + if(REV) + dt->rwb = dt->rwb>>3 & 07 | dt->rwb<<3 & 070; + } +} + +/* count dialled transports and select last */ +static int +selectdx(Dt551 *dt) +{ + int i, n, nunits; + n = dt->ut_units == 0 ? 8 : dt->ut_units; + nunits = 0; + dt->seldx = nil; + if(dt->ut_units_select) + for(i = 0; i < 8; i++) + if(dt->dx[i] && dt->dx[i]->unit == n){ + dt->seldx = dt->dx[i]; + nunits++; + } + return nunits; +} + +/* Read one line of bits and move the tape. + * This should occur once every 33.33μs */ static void dtcycle(void *p) { Dt551 *dt; dt = (Dt551*)p; - uchar l, wb; - int data1edge, data7edge; - int bm0edge, bm3edge; + + if(dt->delay){ + if(--dt->delay == 0){ + // UT_START goes to 0 here + + int nunits; + + dt->time_flag = 1; + + /* check for legal selection */ + nunits = selectdx(dt); + + // UTE_SW_ERROR; 3-16 + if(!UTE_UNIT_OK || + dt->ut_btm_switch != UT_WRTM) +debug("ILL op %d %d %d\n", nunits, dt->ut_btm_switch, UT_WRTM), + dt->ut_illegal_op = 1; + if(!UTE_UNIT_OK){ + dt->ut_go = 0; // 3-3 + return; + } + + if(UT_WRTM) // 3-14 + dt->rw_state = RW_ACTIVE; + else if(!UT_DN) // TODO: is this right? + dt->rw_state = RW_RQ; + }else + /* Don't move during delay. + * TODO: is this right? */ + return; + } // TODO: maybe do something else here? if(dt->seldx == nil) @@ -241,166 +551,33 @@ dtcycle(void *p) if(!dt->ut_go) return; + // sense; start writing + dt_tp0(dt); - if(dt->rw_state != RW_ACTIVE) - dt->tct = 0; - l = dxread(dt->seldx); +dtbuf = dtbuf<<3 & FW; +dtbuf |= dt->sense & 07; -/* TP0 */ + // read strobe + dt_tp1(dt); - if(dt->rw_state == RW_ACTIVE) - dt->ut_wren = 1; + // interpret mark code + dt_tp2(dt); - if(UT_WRITE){ - if(UT_WRITE_PREVENT) - dt->ut_illegal_op = 1; - if(dt->tct) - wb = l&010 | dt->rwb&07; - else - wb = l&010 | dt->rwb>>3 & 07; - } - if(UT_WREN_DATA) - dxwrite(dt->seldx, wb); + // clear rwb; mark track error sync + dt_tp3(dt); -/* TP1 */ + // get next rwb from DC to write + dt_tp4(dt); -//**/ PRINT1 -//**/ PRINT2 - - dt->tmk = ((dt->tmk << 1) | !!(l&010)) & 0377 | - dt->tmk&0400 | (dt->tmk<<1)&0400; - if(UT_READ){ - if(dt->tct != dt->ut_rev) - dt->rwb |= l&07; - else - dt->rwb |= (l&07) << 3; - } - -/* TP2 */ -///**/ printf("%2o%c%c%c ", dt->utek, UTE_MK ? 'M' : ' ', UTE_ERROR ? 'E' : ' ', dt->uteck ? 'C' : ' '); - - if(UTE_ERROR || UTE_ACTIVE_ERROR) -printf("UT INFO ERROR\n"), - dt->ut_info_error = 1; - - if(!BM_SPACE){ - if(dt->utek == 1) - dt->utek = 040; - else - dt->utek >>= 1; - } - - if(dt->tct) - dt->lb ^= ~dt->rwb & 077; - -//**/ if(dt->rw_state == RW_ACTIVE && dt->tct) -//**/ printf("(%02o, %02o) ", dt->rwb, dt->lb); - - if(UT_READ && dt->rw_state == RW_ACTIVE && dt->tct){ - // before tct complement - if(!(UT_DATA && dt->tdata & 0102)){ - dcgv(dt->dc, DEVNO, dt->rwb, dt->ut_rev); - if(UTE_DC_DISCONNECT) - dt->ut_incomp_block = 1; - } - } - - - if(END_ZONE){ - dt->ut_tape_end_flag = 1; - dt->ut_go = 0; - } - - bm0edge = 0; - bm3edge = 0; - if(BM_SYNC) - dt->tbm |= 020; // shifted to 010 (TBM0) now - if(BM_SPACE || BM_SYNC){ - dt->tbm >>= 1; - if(TBM0) - bm0edge = 1; - else if(TBM3) - bm3edge = 1; - } - - data1edge = 0; // goes up before rev checksum is expected - data7edge = 0; // goes up when checksum is seen - if(DATA_SYNC) - dt->tdata |= 0400; // shifted to 0200 now - if(FWD_DATA_END || REV_DATA_END || DATA_SYNC){ - dt->tdata >>= 1; - if(dt->tdata == 0100) - data1edge = 1; - else if(dt->tdata == 1) - data7edge = 1; - } - - if(data1edge) - dt->lb = 0; - - // TODO: not sure about rw_active - if(data7edge && dt->rw_state == RW_ACTIVE) - if(dt->lb != 077) -printf("UT INFO ERROR\n"), - dt->ut_info_error = 1; - - // before active is set below - if(dt->rw_state == RW_ACTIVE) - dt->tct = !dt->tct; - - // manual mentions tdata6(0) going to 0??? - if(dt->rw_state == RW_ACTIVE){ - if(data7edge && RW_DATA_STOP || - RW_BM_DONE){ -printf("FINISHING JOB\n"); - dt->ut_jb_done_flag = 1; - dt->rw_state = RW_NULL; - } - if(UTE_ACTIVE_ERROR) - dt->rw_state = RW_NULL; - if(data7edge && RW_DATA_CONT) - dt->rw_state = RW_RQ; - }else if(dt->rw_state == RW_RQ){ - if(UT_BM && bm3edge || - UT_DATA && data1edge || - UT_ALL && bm0edge) - dt->rw_state = RW_ACTIVE; - } - -/* TP3 */ - - if(RW_ODD) - dt->rwb = 0; - - if(TBM3 && BM_SPACE) - dt->utek = 040; - if(BM_SYNC) - dt->uteck = 0; - if(TBM3 && !BM_SPACE) - dt->uteck = 1; - -/* TP4 */ - - if(UT_WRITE && RW_ODD){ - /* Take checksum data from LB */ - if(UT_DATA && dt->tdata & 0102) - dt->rwb = dt->lb; - else{ - dt->rwb |= dctk(dt->dc, DEVNO, dt->ut_rev); - if(UTE_DC_DISCONNECT) - dt->ut_incomp_block = 1; - } - if(dt->ut_rev) - dt->rwb = dt->rwb>>3 & 07 | dt->rwb<<3 & 070; - } - if(dt->rw_state == RW_ACTIVE) - dt->ut_wren = 0; - -//**/ printf("\n"); + state = dt->rw_state == RW_NULL ? 'n' : + dt->rw_state == RW_RQ ? 'r' : + dt->rw_state == RW_ACTIVE ? 'a' : 'x'; + PRINT2 + else + debug("%c%d -- ", state, dt->tct); + debug(" %012lo %02o %d.%02o\n", dtbuf, dt->rwb, dt->uteck, dt->utek); dxmove(dt->seldx); - - recalc_dt_req(dt); } static void @@ -444,7 +621,7 @@ wake_dt(void *dev) if(bus->devcode == UTS){ if(IOB_STATUS){ //printf("UTS STATUS\n"); - // we have no delays (yet?) F25 + if(dt->delay) bus->c12 |= F25; if(dt->rw_state == RW_RQ) bus->c12 |= F26; if(dt->rw_state == RW_ACTIVE) bus->c12 |= F27; if(dt->rw_state == RW_NULL) bus->c12 |= F28; @@ -459,7 +636,7 @@ wake_dt(void *dev) } if(bus->devcode == UTC){ if(IOB_STATUS){ -//printf("UTC STATUS\n"); +printf("UTC STATUS\n"); if(dt->ut_units_select) bus->c12 |= F19; if(dt->ut_tape_end_enable) bus->c12 |= F20; if(dt->ut_jb_done_enable) bus->c12 |= F21; @@ -481,12 +658,11 @@ wake_dt(void *dev) dt->ut_tape_end_flag = 0; dt->ut_jb_done_flag = 0; + // 3-5 dt->rw_state = RW_NULL; } if(IOB_CONO_SET){ //printf("UTC CONO SET\n"); - int i, n, nunits; - dt->ut_pia = bus->c12 & 07; dt->ut_units = bus->c12>>3 & 07; dt->ut_fcn = bus->c12>>6 & 07; @@ -498,42 +674,37 @@ wake_dt(void *dev) dt->ut_tape_end_enable = bus->c12>>15 & 1; dt->ut_units_select = bus->c12>>16 & 1; - // UT CONO SET LONG + // UT CONO SET LONG, 1ms after UT CONO SET // triggers UT START level until end of delay - // find selected transport unit - n = dt->ut_units == 0 ? 8 : dt->ut_units; - dt->seldx = nil; - nunits = 0; - if(dt->ut_units_select) - for(i = 0; i < 8; i++) - if(dt->dx[i] && dt->dx[i]->unit == n){ - dt->seldx = dt->dx[i]; - nunits++; - } + /* update selection, also done after delay */ + selectdx(dt); + + dt->delay = 0; if(dt->ut_time){ - // wait delay - dt->time_flag = 1; - dt->uteck = 0; - - if(nunits != 1){ - dt->ut_go = 0; - dt->ut_illegal_op = 1; - goto ret; + switch(dt->ut_time){ + case 1: // 20ms + dt->delay = 600; + break; + case 2: // 160ms + dt->delay = 4800; + break; + case 3: // 300ms + dt->delay = 9000; + break; } - if(dt->ut_btm_switch != UT_WRTM) - dt->ut_illegal_op = 1; - } + // T CLEAR; 3-4 + dt->tmk = 0; + dt->uteck = 0; // 3-15 - if(!UT_DN){ - if(UT_WRTM) - dt->rw_state = RW_ACTIVE; - else + dt->time_flag = 0; + // now wait + }else + if(!UT_DN && // TODO: is this right? + !UT_WRTM) // 3-14 dt->rw_state = RW_RQ; - } } } -ret: recalc_dt_req(dt); } @@ -555,6 +726,27 @@ dtconn(Dc136 *dc, Dt551 *dt) dc->devp[DEVNO] = dt; } +int +dtdep(Device *dev, const char *reg, word data) +{ + Dt551 *dt; + + dt = (Dt551*)dev; + if(strcmp(reg, "btm_wr") == 0) dt->ut_btm_switch = data&1; + else return 1; + return 0; +} + +word +dtex(Device *dev, const char *reg) +{ + Dt551 *dt; + + dt = (Dt551*)dev; + if(strcmp(reg, "btm_wr") == 0) return dt->ut_btm_switch; + return ~0; +} + Device* makedt(int argc, char *argv[]) { @@ -566,11 +758,13 @@ makedt(int argc, char *argv[]) dt->dev.type = dt_ident; dt->dev.name = ""; - dt->dev.attach = nil; dt->dev.ioconnect = dtioconnect; + dt->dev.deposit = dtdep; + dt->dev.examine = dtex; - // should have 30000 cycles per second - th = (Thread){ nil, dtcycle, dt, 1000, 0 }; + // should have 30000 cycles per second, so one every 33μs + // APR at 1 has an approximate cycle time of 200-300ns + th = (Thread){ nil, dtcycle, dt, 150, 0 }; addthread(th); return &dt->dev; diff --git a/emu/init.ini b/emu/init.ini index 243443d..609fdb7 100644 --- a/emu/init.ini +++ b/emu/init.ini @@ -12,10 +12,11 @@ mkdev cmem1 cmem161C mem_1 mkdev cmem2 cmem161C mem_2 mkdev cmem3 cmem161C mem_3 #mkdev netmem netmem its.pdp10.se 10006 -mkdev netmem netmem localhost 10006 +#mkdev netmem netmem localhost 10006 +mkdev netmem netmem maya 10006 connectdev dc dt0 -connectdev dt0 dx0 8 +connectdev dt0 dx0 1 connectio tty apr connectio ptr apr connectio ptp apr @@ -34,4 +35,3 @@ attach ptr ../code/test.rim attach ptp ../code/ptp.out #attach dx0 ../test/out.dt6 attach dx0 ../test/test.dt6 - diff --git a/emu/main.c b/emu/main.c index 5c9b817..0a1eaab 100644 --- a/emu/main.c +++ b/emu/main.c @@ -621,7 +621,26 @@ dofile(const char *path) void quit(int code) { - //SDL_Quit(); + int i; + Device *dev; + Membus *mbus; + + /* Detach all files */ + for(dev = devlist; dev; dev = dev->next) + if(dev->detach) + dev->detach(dev); + + /* Sync memory to disk */ + for(dev = devlist; dev; dev = dev->next){ + if(strcmp(dev->type, apr_ident) != 0) + continue; + + mbus = &((Apr*)dev)->membus; + for(i = 0; i < 16; i++) + if(mbus->cmem[i] && mbus->cmem[i]->sync) + mbus->cmem[i]->sync(mbus->cmem[i]); + } + putchar('\n'); exit(code); } diff --git a/emu/mem.c b/emu/mem.c index c108d22..e799a99 100644 --- a/emu/mem.c +++ b/emu/mem.c @@ -33,6 +33,36 @@ readmem(const char *file, word *mem, word size) fclose(f); } +void +writemem(const char *file, word *mem, word size) +{ + FILE *f; + hword i, a; + + if(f = fopen(file, "w"), f == nil) + return; + + a = 0; + for(i = 0; i < size; i++) + if(mem[i] != 0){ + if(a != i){ + a = i; + fprintf(f, "%06o:\n", a); + } + fprintf(f, "%012lo\n", mem[a++]); + } + + fclose(f); +} + +static void +synccore(Mem *mem) +{ + CMem *core; + core = mem->module; + writemem(core->filename, core->core, 040000); +} + /* 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. */ @@ -245,11 +275,9 @@ makecoremem(const char *file) memset(core, 0, sizeof(CMem)); core->filename = strdup(file); mem = malloc(sizeof(Mem)); + memset(mem, 0, sizeof(Mem)); mem->dev.type = cmem_ident; mem->dev.name = ""; - mem->dev.attach = nil; - mem->dev.ioconnect = nil; - mem->dev.next = nil; mem->module = core; mem->bus[0] = &memterm; @@ -258,6 +286,7 @@ makecoremem(const char *file) mem->bus[3] = &memterm; mem->wake = wakecore; mem->poweron = powercore; + mem->sync = synccore; return mem; } @@ -274,11 +303,9 @@ makefastmem(int p) ff->fmc_p_sel = p; mem = malloc(sizeof(Mem)); + memset(mem, 0, sizeof(Mem)); mem->dev.type = fmem_ident; mem->dev.name = ""; - mem->dev.attach = nil; - mem->dev.ioconnect = nil; - mem->dev.next = nil; mem->module = ff; mem->bus[0] = &memterm; diff --git a/emu/mem_0 b/emu/mem_0 index 3083bc9..1910e3b 100644 --- a/emu/mem_0 +++ b/emu/mem_0 @@ -9,16 +9,14 @@ 720200000000 254200000300 777600000277 -0 -0 -0 -0 -0 +000020: 710600000060 710740000010 254000000021 710440000026 710740000010 254000000024 -0 +000027: 254000000021 +000200: +000000000567 diff --git a/emu/netmem.c b/emu/netmem.c index c251fd2..9e8f2c9 100644 --- a/emu/netmem.c +++ b/emu/netmem.c @@ -32,9 +32,10 @@ netmemcycle(void *dev) } len = buf[0]<<8 | buf[1]; if(len > 9){ - fprintf(stderr, "netmem botch, closing\n"); + fprintf(stderr, "netmem botch(%d), closing\n", len); close(nm->fd); nm->fd = -1; + return; } memset(buf, 0, sizeof(buf)); readn(nm->fd, buf, len); @@ -96,9 +97,6 @@ makenetmem(int argc, char *argv[]) memset(nm, 0, sizeof(Netmem)); nm->dev.type = netmem_ident; nm->dev.name = ""; - nm->dev.attach = nil; - nm->dev.ioconnect = nil; - nm->dev.next = nil; // TODO: don't hardcode; apr = getdevice("apr"); diff --git a/emu/pdp6.h b/emu/pdp6.h index fb35632..a8ec46a 100644 --- a/emu/pdp6.h +++ b/emu/pdp6.h @@ -103,8 +103,10 @@ struct Device char *name; /* attach file to device */ void (*attach)(Device *dev, const char *path); + void (*detach)(Device *dev); /* connect device to iobus */ void (*ioconnect)(Device *dev, IOBus *bus); + /* manipulate device registers */ word (*examine)(Device *dev, const char *reg); int (*deposit)(Device *dev, const char *reg, word data); }; @@ -146,10 +148,11 @@ enum { struct Mem { Device dev; - void *module; + void *module; /* CMem, FMem */ Membus *bus[4]; int (*wake)(Mem *mem, Membus *bus); void (*poweron)(Mem *mem); + void (*sync)(Mem *mem); /* sync file to disk */ }; struct Membus @@ -554,6 +557,7 @@ struct Dx555 uchar *start; uchar *cur; uchar *end; + uint size; }; #define DX_IDENT "dx555" extern char *dx_ident; @@ -598,17 +602,21 @@ struct Dt551 int ut_info_error; int ut_incomp_block; - int tmk; /* 9 bits */ - int rwb; /* 6 bits */ - int lb; /* 6 bits */ - int tbm; /* 4 bits */ - int tdata; /* 8 bits */ - int tct; /* 1 bit */ + int tmk; /* mark track window, 9b */ + int rwb; /* read write buffer, 6b */ + int lb; /* longitudinal buffer, computes check sum, 6b */ + int tbm; /* block mark timing, 4b */ + int tdata; /* data timing, 8b */ + int tct; /* selects half of 6 bit rwb, 1b */ + /* error check */ int utek; /* 6 bits */ int uteck; int rw_state; /* null, rq, active */ + int delay; + int sense; + int wb; // int req; }; @@ -616,11 +624,6 @@ struct Dt551 extern char *dt_ident; Device *makedt(int argc, char *argv[]); void dtconn(Dc136 *dc, Dt551 *dt); -/* -void dtcono(Dt551 *dt, word iob); -void dtcycle(Dt551 *dt); -*/ - typedef struct Netmem Netmem; struct Netmem diff --git a/emu/test_dt.c b/emu/test_dt.c new file mode 100644 index 0000000..90c399d --- /dev/null +++ b/emu/test_dt.c @@ -0,0 +1,345 @@ +#include "pdp6.h" + +Dc136 *dc; +Dt551 *dt; +Dx555 *dx; +IOBus iobus; +IOBus *bus = &iobus; +int pireq; +Thread dtthread; + +void +setreq(IOBus *bus, int dev, u8 pia) +{ + if(dev == DC) + pireq = pia; +} + +void +addthread(Thread th) +{ + dtthread = th; +} + + +void +wakedev(IOBus *bus, int d) +{ + Busdev *dev; + bus->devcode = d; + dev = &bus->dev[d]; + if(dev->wake) + dev->wake(dev->dev); +} + +void +bus_reset(IOBus *bus) +{ + int d; + bus->c34 |= IOBUS_IOB_RESET; + for(d = 0; d < 128; d++) + wakedev(bus, d); + bus->c34 &= ~IOBUS_IOB_RESET; +} + +void +cono(IOBus *bus, int d, hword w) +{ + bus->c34 |= IOBUS_CONO_CLEAR; + wakedev(bus, d); + bus->c34 &= ~IOBUS_CONO_CLEAR; + + bus->c34 |= IOBUS_CONO_SET; + bus->c12 = w; + wakedev(bus, d); + bus->c34 &= ~IOBUS_CONO_SET; + bus->c12 = 0; +} + +void +datao(IOBus *bus, int d, word w) +{ + bus->c34 |= IOBUS_DATAO_CLEAR; + wakedev(bus, d); + bus->c34 &= ~IOBUS_DATAO_CLEAR; + + bus->c34 |= IOBUS_DATAO_SET; + bus->c12 = w; + wakedev(bus, d); + bus->c34 &= ~IOBUS_DATAO_SET; + bus->c12 = 0; +} + +word +coni(IOBus *bus, int d) +{ + word w; + bus->c34 |= IOBUS_IOB_STATUS; + wakedev(bus, d); + bus->c34 &= ~IOBUS_IOB_STATUS; + w = bus->c12; + bus->c12 = 0; + return w; +} + +word +datai(IOBus *bus, int d) +{ + word w; + bus->c34 |= IOBUS_IOB_DATAI; + wakedev(bus, d); + bus->c34 &= ~IOBUS_IOB_DATAI; + w = bus->c12; + bus->c12 = 0; + return w; +} + +void +printdc(Dc136 *dc) +{ + printf("DB/%012lo DA/%012lo CCT/%o SCT/%o CHMOD/%o\n", + dc->db, dc->da, dc->cct, dc->sct, dc->ch_mode); + printf(" CLBD/%o DBDAMOVE/%o DARQ/%o DBRQ/%o INOUT/%o DEV/%o PIA/%o\n", + dc->data_clbd, dc->dbda_move, dc->da_rq, dc->db_rq, + dc->inout, dc->device, dc->pia); +} + +// DC controls +enum { + CLBD = 010000, + + DBDAMOVE = 04000, + DARQ = 02000, + DBRQ = 01000, + + IN = 0000, + OUT = 0400, + + CHMOD_6 = 0000, + CHMOD_36 = 0100, + CHMOD_12 = 0200, + CHMOD_18 = 0300, + + DEV1 = 010, + DEV2 = 020, + DEV3 = 030, + DEV4 = 040, + DEV5 = 050, + DEV6 = 060, +}; + +enum { + SEL = 0200000, + GO = 0020000, + FWD = 0000000, + REV = 0010000, + DLY0 = 0000000, + DLY1 = 0001000, + DLY2 = 0002000, + DLY3 = 0003000, + FN_DN = 0000000, + FN_RA = 0000100, + FN_RBN = 0000200, + FN_RD = 0000300, + FN_WTM = 0000400, + FN_WA = 0000500, + FN_WBN = 0000600, + FN_WD = 0000700 +}; + +word writebuf[133*01102 + 6000]; +word *bufp; +int bufsz; + +void +fillbuf(int n) +{ + bufp = writebuf; + bufsz = n; + while(n--) + bufp[n] = n | 0112200000000; +} + +void +fillbuf_fmt(void) +{ + int i, j; + bufp = writebuf; + + for(i = 0; i < 6; i++) + *bufp++ = 0707707707707; // 55 55 + + for(i = 0; i < 01102; i++){ + *bufp++ = 0070707070770; // 25 26 + *bufp++ = 0077070007000; // 32 10 + *bufp++ = 0007000007000; // 10 10 + *bufp++ = 0007000777000; // 10 70 + + for(j = 0; j < 125; j++) + *bufp++ = 0777000777000; // 70 70 + + *bufp++ = 0777000777077; // 70 73 + *bufp++ = 0777077777077; // 73 73 + *bufp++ = 0777077707007; // 73 51 + *bufp++ = 0700707070707; // 45 25 + } + + for(i = 0; i < 6; i++) + *bufp++ = 0070070070070; // 22 22 + + bufsz = bufp - writebuf; + bufp = writebuf; +} + +void +fwdtest_w(void) +{ + word w; + printf("forward test:\n\n"); + dx->cur = dx->start; + + fillbuf(128); + + cono(bus, DC, DARQ|DBRQ|OUT|CHMOD_6|DEV1|7); + cono(bus, UTC, SEL|GO|FWD|DLY3|FN_WBN|DEV1|0); + while(dt->ut_go){ + dtthread.f(dt); + if(pireq == 7){ + if(bufsz > 0){ + w = bufp[--bufsz]; + datao(bus, DC, w); + printf("DC send: %012lo\n", w); + } +// break; + } + } +// printdc(dc); +} + +void +revtest_w(void) +{ + word w; + printf("reverse test:\n\n"); + dx->cur = dx->end-1; + + fillbuf(128); + + cono(bus, DC, DARQ|DBRQ|OUT|CHMOD_6|DEV1|7); + cono(bus, UTC, SEL|GO|REV|DLY3|FN_WD|DEV1|0); + while(dt->ut_go){ + dtthread.f(dt); + if(pireq == 7){ + if(bufsz > 0){ + w = bufp[--bufsz]; + datao(bus, DC, w); + printf("DC send: %012lo\n", w); + } +// break; + } + } +// printdc(dc); +} + +void +fwdtest_r(void) +{ + word w; + printf("forward test:\n\n"); + dx->cur = dx->start; + + cono(bus, DC, DBDAMOVE|IN|CHMOD_6|DEV1|7); + cono(bus, UTC, SEL|GO|FWD|DLY3|FN_RD|DEV1|0); + while(dt->ut_go){ + dtthread.f(dt); + if(pireq == 7){ + w = datai(bus, DC); + printf("DC got: %012lo\n", w); +// break; + } + } +// printdc(dc); +} + +void +revtest_r(void) +{ + word w; + printf("reverse test:\n\n"); + dx->cur = dx->end-1; + + cono(bus, DC, DBDAMOVE|IN|CHMOD_6|DEV1|7); + cono(bus, UTC, SEL|GO|REV|DLY3|FN_RD|DEV1|0); + while(dt->ut_go){ + dtthread.f(dt); + if(pireq == 7){ + w = datai(bus, DC); + printf("DC got: %012lo\n", w); +// break; + } + } +// printdc(dc); +} + +void +fmttest(void) +{ + word w; + printf("format test:\n\n"); + dx->cur = dx->start; + + fillbuf_fmt(); + + cono(bus, DC, DARQ|DBRQ|OUT|CHMOD_6|DEV1|7); + cono(bus, UTC, SEL|GO|FWD|DLY3|FN_WTM|DEV1|0); + while(dt->ut_go){ + dtthread.f(dt); + if(pireq == 7){ + if(bufsz > 0){ + --bufsz; + w = *bufp++; + datao(bus, DC, w); +// printf("DC send: %012lo\n", w); + }else + cono(bus, UTC, SEL|DLY2|DEV1|0); +// break; + } + } +// printdc(dc); +} + +void +dxstore(Dx555 *dx, const char *file) +{ + FILE *f; + f = fopen(file, "wb"); + if(f == nil) + return; + fwrite(dx->start, 1, dx->size, f); + fclose(f); +} + +int +main() +{ + dc = (Dc136*)makedc(0, nil); + dc->dev.ioconnect((Device*)dc, bus); + dt = (Dt551*)makedt(0, nil); + dt->dev.ioconnect((Device*)dt, bus); + dtconn(dc, dt); + dx = (Dx555*)makedx(0, nil); + dxconn(dt, dx, 1); + dx->dev.attach((Device*)dx, "../test/out.dt6"); + + bus_reset(bus); + assert(coni(bus, DC) == 0); + + printdc(dc); + +// fmttest(); +// fwdtest_r(); + + dxstore(dx, "foo.dt6"); + + return 0; +} diff --git a/emu/util.c b/emu/util.c index 0bda9cb..b6ef53f 100644 --- a/emu/util.c +++ b/emu/util.c @@ -44,7 +44,7 @@ readn(int fd, void *data, int n) while(n > 0){ m = read(fd, data, n); - if(m == -1) + if(m <= 0) return -1; data += m; n -= m;