1
0
mirror of https://github.com/aap/pdp6.git synced 2026-01-13 07:20:13 +00:00

617 lines
12 KiB
C

#include "common.h"
#include "pdp6.h"
#include <fcntl.h>
#include <unistd.h>
#define MOVEDLY 33333
// very approximate, unfortunately no hard data for this
#define STARTDLY 200000000
#define STOPDLY 100000000 // not sure how to use this one here
#define TURNDLY 250000000
// ~350 lines per inch, ~4.56in per block
// again very approximate
#define STARTDIST 7*350
#define STOPDIST 8*350
// after turnaround we will have passed the location that we turned at
// TENDMP assumes one block of turnaround space
// MACDMP assumes two
#define TURNDIST 2*350
struct Ux555
{
int fd;
u8 *buf;
int size, pos;
bool written;
bool flapping;
u64 timer;
bool tp;
bool wrlock;
int num; // 0-7
// from controller:
int go;
int rev;
};
struct Ut551
{
Dc136 *dc;
int dcdev;
Ux555 *transports[8];
Ux555 *sel;
bool btm_sw;
bool units_select;
bool tape_end_en;
bool jb_done_en;
bool go;
bool rev;
bool time_en;
int time;
int fcn;
int units;
int pia;
bool incomp_block;
bool wren;
bool time_flag;
bool info_error;
bool illegal_op;
bool tape_end_flag;
bool jb_done_flag;
int utek;
bool uteck;
int lb;
int rwb;
int wb;
int tbm;
int tdata;
int tmk;
int tct;
int ramp; // read amplifiers
int state;
bool start_dly;
u64 dlyend;
};
Ux555*
attach_ux(Ut551 *ut, int num)
{
int i;
Ux555 *ux = malloc(sizeof(Ux555));
ux->fd = -1;
ux->buf = nil;
ux->size = 0;
ux->pos = 0;
ux->tp = 0;
ux->wrlock = 0;
ux->num = num & 7;
ux->go = 0;
ux->rev = 0;
for(i = 0; i < 8; i++)
if(ut->transports[i] == nil) {
ut->transports[i] = ux;
break;
}
addcleanup((void (*)(void*))uxunmount, ux);
return ux;
}
void
uxunmount(Ux555 *ux)
{
if(ux->fd < 0)
return;
if(ux->written) {
lseek(ux->fd, 0, SEEK_SET);
write(ux->fd, ux->buf, ux->size);
}
close(ux->fd);
ux->fd = -1;
free(ux->buf);
ux->buf = nil;
}
void
uxmount(Ux555 *ux, const char *path)
{
uxunmount(ux);
ux->fd = open(path, O_RDWR);
if(ux->fd < 0) {
ux->fd = open(path, O_RDONLY);
if(ux->fd < 0) {
fprintf(stderr, "can't open file <%s>\n", path);
return;
}
ux->wrlock = 1;
}
ux->size = lseek(ux->fd, 0, SEEK_END);
ux->buf = malloc(ux->size);
lseek(ux->fd, 0, SEEK_SET);
read(ux->fd, ux->buf, ux->size);
ux->written = 0;
ux->pos = 100;
ux->flapping = ux->pos >= ux->size; // hopefully always true
ux->tp = 0;
ux->go = 0;
ux->rev = 0;
printf("μt unit %d file <%s>, len %d lock? %d\n", ux->num, path, ux->size, ux->wrlock);
}
static void
uxmove(Ux555 *ux)
{
if(!ux->go || ux->timer >= simtime)
return;
ux->timer += MOVEDLY;
if(ux->rev) {
if(--ux->pos < 0)
//printf("flap back\n"),
ux->flapping = 1;
} else {
if(++ux->pos >= ux->size)
//printf("flap fwd\n"),
ux->flapping = 1;
}
if(!ux->flapping)
ux->tp = 1;
}
static void
uxsetmotion(Ux555 *ux, int go, int rev)
{
if(ux->go != go) {
if(!ux->go) {
//printf("start transport\n");
// start transport
ux->timer = simtime + STARTDLY;
ux->pos += rev ? -STARTDIST : STARTDIST;
} else {
// stop transport
//printf("stop transport\n");
ux->pos += ux->rev ? -STOPDIST : STOPDIST;
}
ux->go = go;
} else if(ux->go && ux->rev != rev) {
//printf("turn transport\n");
// turn around transport
ux->timer = simtime + TURNDLY;
ux->pos += rev ? -TURNDIST : TURNDIST;
}
ux->rev = rev;
}
#define IOB pdp->iob
enum {
RW_NULL,
RW_RQ,
RW_ACTIVE,
};
#define UT_DN (ut->fcn == 0)
#define UT_RDA (ut->fcn == 1)
#define UT_RDBM (ut->fcn == 2)
#define UT_RDD (ut->fcn == 3)
#define UT_WRTM (ut->fcn == 4)
#define UT_WRA (ut->fcn == 5)
#define UT_WRBM (ut->fcn == 6)
#define UT_WRD (ut->fcn == 7)
#define UT_WRITE (ut->fcn & 4)
#define UT_READ !UT_WRITE
#define UT_ALL (UT_RDA || UT_WRA)
#define UT_BM (UT_RDBM || UT_WRBM)
#define UT_DATA (UT_RDD || UT_WRD)
#define MK_BM_SPACE ((ut->tmk & 0477) == 0425)
#define MK_BM_SYNC (ut->tmk == 0751)
#define MK_DATA_SYNC (ut->tmk == 0632)
#define MK_BM_END (ut->tmk == 0526)
#define MK_FWD_DATA_END (ut->tmk == 0773 || ut->tmk == 0473)
#define MK_REV_DATA_END (ut->tmk == 0610 || ut->tmk == 0410)
#define MK_END (ut->tmk == 0622)
#define MK_DATA (ut->tmk == 0470)
#define MK_DATA_END (MK_FWD_DATA_END || MK_REV_DATA_END)
#define UTE_DC_DISCONNECT (ut->dc->darq || ut->dc->device != ut->dcdev)
#define UTE_MK (MK_BM_END || MK_BM_SYNC || MK_DATA_END || MK_DATA_SYNC || MK_DATA)
static void calc_ut_req(PDP6 *pdp, Ut551 *ut);
static int
utsel(Ut551 *ut)
{
int i, num, nsel;
num = ut->units;
// weirdness: ~UNITS SELECT only disables 0 and 1
if(!ut->units_select && num < 2)
num = -1;
nsel = 0;
ut->sel = nil;
for(i = 0; i < 8; i++)
if(ut->transports[i] && ut->transports[i]->num == num) {
ut->sel = ut->transports[i];
nsel++;
}
if(nsel != 1)
ut->sel = nil;
return nsel;
}
static void
tp0(Ut551 *ut)
{
Ux555 *ux = ut->sel;
if(ut->state == RW_ACTIVE) {
ut->wren = 1;
if(UT_WRITE) {
// RWB(J)->WB
ut->wb = (ut->rwb >> (ut->tct?0:3))&7;
if(ut->rev) ut->wb ^= 7;
if(ux->wrlock)
ut->illegal_op = 1;
}
}
}
static void
tp1(Ut551 *ut)
{
// write complement - rather useless for us here
if(UT_WRITE)
ut->wb ^= 7;
// advance TMK
if(!ut->start_dly && !UT_WRTM)
ut->tmk = ((ut->tmk<<1) | (ut->ramp>>3)&1)&0777 | ut->tmk&0400;
// strobe data into RWB
if(ut->state == RW_ACTIVE && UT_READ) {
int c = ut->rev && !UT_RDBM ? 7 : 0;
if(ut->tct)
ut->rwb |= ut->ramp&7 ^ c;
else
ut->rwb |= (ut->ramp&7 ^ c) << 3;
}
}
static void
tp2(Ut551 *ut)
{
bool wasdone = ut->jb_done_flag;
if(ut->state == RW_ACTIVE) {
if(ut->tct) {
// RW EVEN
ut->lb ^= ~ut->rwb & 077;
if(UT_READ && (ut->tdata&0102)==0) {
// RWB<->DC
ut->rwb |= dctkgv(ut->dc, ut->dcdev, ut->rwb, ut->rev && !UT_RDBM);
if(UTE_DC_DISCONNECT)
ut->incomp_block = 1;
}
}
ut->tct ^= 1;
if(UT_BM && MK_BM_END)
ut->jb_done_flag = 1;
if(!UT_ALL && (MK_BM_SYNC || MK_DATA_SYNC)) {
ut->info_error = 1;
ut->state = RW_NULL;
}
} else
ut->tct = 0;
if(ut->uteck && UTE_MK != (ut->utek==040))
ut->info_error = 1;
if(!MK_BM_SPACE)
ut->utek = (ut->utek>>1) | (ut->utek&1)<<5;
// only interested in rising edges here
int tbmedge = ~ut->tbm;
if(MK_BM_SYNC) ut->tbm = 010 | ut->tbm>>1;
else if(MK_BM_SPACE) ut->tbm >>= 1;
tbmedge &= ut->tbm;
// need both rising and falling edges
int tdedge = ut->tdata;
if(MK_DATA_SYNC) ut->tdata = 0200 | ut->tdata>>1;
else if(MK_DATA_END) ut->tdata >>= 1;
tdedge ^= ut->tdata;
if(tdedge & ut->tdata & 0100)
ut->lb = 0;
if(ut->state == RW_RQ) {
if(UT_BM && tbmedge & 1 ||
UT_ALL && tbmedge & 010 ||
UT_DATA && tdedge & ut->tdata & 0100)
ut->state = RW_ACTIVE;
} else if(ut->state == RW_ACTIVE && UT_DATA && (tdedge & ~ut->tdata & 2)) {
if(UTE_DC_DISCONNECT)
ut->jb_done_flag = 1;
else
ut->state = RW_RQ;
}
if(ut->jb_done_flag && !wasdone)
ut->state = RW_NULL;
}
static void
tp3(Ut551 *ut)
{
if(ut->state == RW_ACTIVE && !ut->tct)
ut->rwb = 0;
if(UT_READ && (UT_ALL | UT_DATA) && ut->tdata & 1 &&
ut->lb != 077)
ut->info_error = 1;
if(ut->tbm & 1) {
if(MK_BM_SPACE)
ut->utek = 040;
else
ut->uteck = 1;
}
if(MK_BM_SYNC)
ut->uteck = 0;
if(MK_END) {
ut->tape_end_flag = 1;
ut->state = RW_NULL;
}
}
static void
tp4(Ut551 *ut)
{
if(ut->state != RW_ACTIVE)
ut->wren = 0;
else if(!ut->tct) {
if(UT_WRITE && UT_DATA && ut->tdata & 2)
ut->rwb |= ut->lb;
if(UT_WRITE && (ut->tdata&0102)==0 || UT_WRTM) {
// RWB<->DC
// writing - so no need to check RDBM
ut->rwb |= dctkgv(ut->dc, ut->dcdev, ut->rwb, ut->rev);
if(UTE_DC_DISCONNECT)
ut->incomp_block = 1;
}
}
}
static void
cycle_ut(PDP6 *pdp, IOdev *dev, int pwr)
{
int i;
Ut551 *ut = (Ut551*)dev->dev;
Ux555 *ux;
if(!pwr) {
ut->dlyend = NEVER;
return;
}
int doreq = 0;
if(ut->start_dly && ut->dlyend < simtime) {
//printf("delay done\n");
ut->dlyend = NEVER;
ut->start_dly = 0;
ut->time_flag = 1;
if(utsel(ut) != 1) {
ut->go = 0;
ut->illegal_op = 1;
}
if(UT_WRTM != ut->btm_sw)
ut->illegal_op = 1;
if(UT_WRTM)
ut->state = RW_ACTIVE;
else if(!UT_DN)
ut->state = RW_RQ;
doreq = 1;
}
if(ut->tape_end_flag)
ut->go = 0;
// move transports
ux = ut->sel;
if(ux)
uxsetmotion(ux, ut->go, ut->rev);
for(i = 0; i < 8; i++)
if(ut->transports[i])
uxmove(ut->transports[i]);
if(ux && ux->tp) {
ux->tp = 0;
doreq = 1;
// rising edge of time track
int mask = ux->rev ? 017 : 0;
ut->ramp = ux->buf[ux->pos] ^ mask;
// which fires TP0 - write load
tp0(ut);
// write back at some point
if(UT_WRITE && ut->wren && !ux->wrlock) {
if(UT_WRTM && ut->btm_sw)
ux->buf[ux->pos] = ((ut->wb<<1)&010 | ut->wb) ^ mask;
else
ux->buf[ux->pos] = (ut->ramp&010 | ut->wb) ^ mask;
ux->written = 1;
}
// ca 16.6μs later
// falling edge of time track
// write complement - read strobe
tp1(ut);
// NB we don't also write the complement
tp2(ut);
tp3(ut);
tp4(ut);
}
if(doreq) calc_ut_req(pdp, ut);
}
static void
handle_ut(PDP6 *pdp, IOdev *dev, int cmd)
{
Ut551 *ut = (Ut551*)dev->dev;
switch(cmd) {
case IOB_RESET:
case IOB_CONO_CLR:
ut->incomp_block = 0;
ut->wren = 0;
ut->time_flag = 0;
ut->info_error = 0;
ut->illegal_op = 0;
ut->tape_end_flag = 0;
ut->jb_done_flag = 0;
ut->units_select = 0;
ut->tape_end_en = 0;
ut->jb_done_en = 0;
ut->go = 0;
ut->rev = 0;
ut->time_en = 0;
ut->time = 0;
ut->fcn = 0;
ut->units = 0;
ut->pia = 0;
ut->state = RW_NULL;
break;
case IOB_CONO_SET:
//printf("CONO UT %o (PC %06o)\n", (int)IOB&0777777, pdp->pc);
if(IOB & F1) ut->units_select = 1;
if(IOB & F2) ut->tape_end_en = 1;
if(IOB & F3) ut->jb_done_en = 1;
if(IOB & F4) ut->go = 1;
if(IOB & F5) ut->rev = 1;
if(IOB & F6) ut->time_en = 1;
ut->time |= (IOB>>27) & 3;
ut->fcn |= (IOB>>24) & 7;
ut->units |= (IOB>>21) & 7;
ut->pia |= (IOB>>18) & 7;
// command is effective before selection changes
if(ut->sel) uxsetmotion(ut->sel, ut->go, ut->rev);
utsel(ut);
if(ut->time == 0) {
// bit strange for DN and WRTM
ut->state = RW_RQ;
} else {
static int dlytab[4] = { 0, 35000000, 225000000, 300000000 };
ut->dlyend = simtime + dlytab[ut->time];
ut->start_dly = 1;
// T CLEAR
ut->tbm = 0;
ut->tdata = 0;
ut->tmk = 0;
ut->uteck = 0;
}
break;
case IOB_STATUS:
if(ut->units_select) IOB |= F19;
if(ut->tape_end_en) IOB |= F20;
if(ut->jb_done_en) IOB |= F21;
if(ut->go) IOB |= F22;
if(ut->rev) IOB |= F23;
if(ut->time_en) IOB |= F24;
IOB |= ut->time << 9;
IOB |= ut->fcn << 6;
IOB |= ut->units << 3;
IOB |= ut->pia;
break;
}
calc_ut_req(pdp, ut);
}
static void
handle_uts(PDP6 *pdp, IOdev *dev, int cmd)
{
Ut551 *ut = (Ut551*)dev->dev;
switch(cmd) {
case IOB_STATUS:
if(ut->start_dly) IOB |= F25;
if(ut->state == RW_RQ) IOB |= F26;
if(ut->state == RW_ACTIVE) IOB |= F27;
if(ut->state == RW_NULL) IOB |= F28;
if(ut->incomp_block) IOB |= F29;
if(ut->wren) IOB |= F30;
if(ut->time_flag) IOB |= F31;
if(ut->info_error) IOB |= F32;
if(ut->illegal_op) IOB |= F33;
if(ut->tape_end_flag) IOB |= F34;
if(ut->jb_done_flag) IOB |= F35;
break;
}
}
static Ut551 ut;
static IOdev ut_dev = { 0, 0210, &ut, handle_ut, cycle_ut };
static IOdev uts_dev = { 0, 0214, &ut, handle_uts, nil };
static void
calc_ut_req(PDP6 *pdp, Ut551 *ut)
{
int req = 0;
if(ut->pia &&
(ut->time_flag && ut->time_en ||
ut->jb_done_flag && ut->jb_done_en ||
ut->tape_end_flag && ut->tape_end_en ||
ut->illegal_op || ut->info_error))
req = 0200>>ut->pia;
setreq(pdp, &ut_dev, req);
}
Ut551*
attach_ut(PDP6 *pdp, Dc136 *dc)
{
ut.dc = dc;
ut.dcdev = 1;
installdev(pdp, &ut_dev);
installdev(pdp, &uts_dev);
return &ut;
}