1
0
mirror of https://github.com/aap/pdp6.git synced 2026-02-12 11:07:34 +00:00
Files
aap.pdp6/emu/dt.c
2018-02-07 01:37:09 +01:00

578 lines
12 KiB
C

#include "pdp6.h"
#include <fcntl.h>
#include <unistd.h>
char *dt_ident = DT_IDENT;
char *dx_ident = DX_IDENT;
// TODO: find out how this should be controlled
#define DEVNO 1
enum
{
DTSIZE = 922512
};
/* Transport */
static uchar
dxread(Dx555 *dx)
{
if(dx->dt->ut_rev)
return *dx->cur ^ 010;
else
return *dx->cur;
}
static void
dxwrite(Dx555 *dx, uchar d)
{
if(dx->dt->ut_rev)
*dx->cur = d ^ 010;
else
*dx->cur = d;
}
static void
dxmove(Dx555 *dx)
{
// At end of tape read the last word forever
if(dx->dt->ut_rev){
dx->cur--;
if(dx->cur < dx->start)
dx->cur = dx->start+5;
}else{
dx->cur++;
if(dx->cur >= dx->end)
dx->cur = dx->end-6;
}
}
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);
dx->fd = open(path, O_RDWR | O_CREAT, 0666);
if(dx->fd < 0)
fprintf(stderr, "couldn't open file %s\n", path);
else
read(dx->fd, dx->start, DTSIZE);
}
/* Connect a transport to a control */
void
dxconn(Dt551 *dt, Dx555 *dx, int n)
{
dt->dx[n%8] = dx;
dx->dt = dt;
dx->unit = n;
}
Device*
makedx(int argc, char *argv[])
{
Dx555 *dx;
dx = malloc(sizeof(Dx555));
memset(dx, 0, sizeof(Dx555));
dx->dev.type = dx_ident;
dx->dev.name = "";
dx->dev.attach = dxattach;
dx->dev.ioconnect = nil;
dx->start = malloc(DTSIZE);
dx->cur = dx->start;
dx->end = dx->start + DTSIZE;
memset(dx->start, 0, DTSIZE);
return &dx->dev;
}
/* Control */
enum
{
TapeEndF = 022,
TapeEndR = 055,
BlockSpace = 025,
BlockEndF = 026,
BlockEndR = 045,
DataSync = 032, /* rev guard */
BlockSync = 051, /* guard */
DataEndF = 073, /* pre-final, final, ck, rev lock */
DataEndR = 010, /* lock, rev ck, rev final, rev pre-final */
Data = 070,
RW_NULL = 0,
RW_RQ = 1,
RW_ACTIVE = 2,
};
#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_END (dt->tmk == (0500|BlockEndF))
#define DATA_SYNC (dt->tmk == (0600|DataSync))
#define BM_SYNC (dt->tmk == (0700|BlockSync))
#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)
#define BLOCK (dt->tdata == 040 || dt->tdata == 020 || dt->tdata == 010)
#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)
#define UT_BM ((dt->ut_fcn & 03) == 02)
#define UT_DATA ((dt->ut_fcn & 03) == 03)
#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_WREN_DATA (UT_WRITE && dt->ut_wren && !UT_WRITE_PREVENT)
#define RW_ODD (!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 TBM0 (dt->tbm == 10)
#define TBM3 (dt->tbm == 1)
// all but block space
#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))
#define PRINT1 \
printf("%c%02o(%o,%02o,%d)", dt->rw_state == RW_ACTIVE ? '+' : dt->rw_state == RW_RQ ? '-' : ' ', \
dt->tmk&077, l&07, dt->rwb, dt->tct); \
putchar(' '); \
if(STOP) \
printf(" [STOP] "); \
else if(SYNC) \
printf(" [SYNC] "); \
else if(REV_CKS) \
printf(" [REV_CKS] "); \
else if(BLOCK) \
printf(" [BLOCK] "); \
else if(PRE_FINAL) \
printf(" [PRE FINAL] "); \
else if(FWD_CKS) \
printf(" [FWD CKS %02o] ", dt->lb);
#define PRINT2 \
if(END_ZONE) \
printf("TapeEndF "); \
else if(BM_SPACE) \
printf("BlockSpace %o ", dt->tbm); \
else if(BM_END) \
printf("BlockEndF "); \
else if(DATA_SYNC) \
printf("DataSync "); \
else if(BM_SYNC) \
printf("BlockSync "); \
else if(FWD_DATA_END) \
printf("DataEndF "); \
else if(REV_DATA_END) \
printf("DataEndR "); \
else if(DATA) \
printf("Data ");
static void
recalc_dt_req(Dt551 *dt)
{
int req;
if(dt->ut_jb_done_flag && dt->ut_jb_done_enable ||
dt->ut_tape_end_flag && dt->ut_tape_end_enable ||
dt->time_flag && dt->time_enable ||
dt->ut_info_error ||
dt->ut_illegal_op)
req = dt->ut_pia;
else
req = 0;
setreq(dt->bus, UTC, req);
}
static void
dtcycle(void *p)
{
Dt551 *dt;
dt = (Dt551*)p;
uchar l, wb;
int data1edge, data7edge;
int bm0edge, bm3edge;
// TODO: maybe do something else here?
if(dt->seldx == nil)
return;
if(!dt->ut_go)
return;
if(dt->rw_state != RW_ACTIVE)
dt->tct = 0;
l = dxread(dt->seldx);
/* TP0 */
if(dt->rw_state == RW_ACTIVE)
dt->ut_wren = 1;
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);
/* TP1 */
//**/ 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");
dxmove(dt->seldx);
recalc_dt_req(dt);
}
static void
wake_dt(void *dev)
{
Dt551 *dt;
IOBus *bus;
dt = (Dt551*)dev;
bus = dt->bus;
if(IOB_RESET){
dt->ut_incomp_block = 0;
dt->ut_pia = 0;
dt->ut_units = 0;
dt->ut_units_select = 0;
dt->ut_fcn = 0;
dt->ut_time = 0;
dt->ut_wren = 0;
dt->ut_go = 0;
dt->ut_rev = 0;
dt->ut_tape_end_flag = 0;
dt->ut_tape_end_enable = 0;
dt->ut_jb_done_flag = 0;
dt->ut_jb_done_enable = 0;
dt->time_enable = 0;
dt->time_flag = 0;
dt->ut_illegal_op = 0;
dt->ut_info_error = 0;
dt->tmk = 0;
dt->rwb = 0;
dt->tbm = 0;
dt->tdata = 0;
dt->utek = 0;
dt->uteck = 0;
dt->rw_state = RW_NULL;
}
if(bus->devcode == UTS){
if(IOB_STATUS){
//printf("UTS STATUS\n");
// we have no delays (yet?) 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;
if(dt->ut_incomp_block) bus->c12 |= F29;
if(dt->ut_wren) bus->c12 |= F30;
if(dt->time_flag) bus->c12 |= F31;
if(dt->ut_info_error) bus->c12 |= F32;
if(dt->ut_illegal_op) bus->c12 |= F33;
if(dt->ut_tape_end_flag) bus->c12 |= F34;
if(dt->ut_jb_done_flag) bus->c12 |= F35;
}
}
if(bus->devcode == UTC){
if(IOB_STATUS){
//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;
if(dt->ut_go) bus->c12 |= F22;
if(dt->ut_rev) bus->c12 |= F23;
if(dt->time_enable) bus->c12 |= F24;
bus->c12 |= (dt->ut_time & 03) << 9;
bus->c12 |= (dt->ut_fcn & 07) << 6;
bus->c12 |= (dt->ut_units & 07) << 3;
bus->c12 |= dt->ut_pia & 07;
}
if(IOB_CONO_CLEAR){
//printf("UTC CONO CLEAR\n");
dt->ut_incomp_block = 0;
dt->ut_wren = 0;
dt->time_flag = 0;
dt->ut_info_error = 0;
dt->ut_illegal_op = 0;
dt->ut_tape_end_flag = 0;
dt->ut_jb_done_flag = 0;
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;
dt->ut_time = bus->c12>>9 & 03;
dt->time_enable = bus->c12>>11 & 1;
dt->ut_rev = bus->c12>>12 & 1;
dt->ut_go = bus->c12>>13 & 1;
dt->ut_jb_done_enable = bus->c12>>14 & 1;
dt->ut_tape_end_enable = bus->c12>>15 & 1;
dt->ut_units_select = bus->c12>>16 & 1;
// UT CONO SET LONG
// 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++;
}
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;
}
if(dt->ut_btm_switch != UT_WRTM)
dt->ut_illegal_op = 1;
}
if(!UT_DN){
if(UT_WRTM)
dt->rw_state = RW_ACTIVE;
else
dt->rw_state = RW_RQ;
}
}
}
ret:
recalc_dt_req(dt);
}
static void
dtioconnect(Device *dev, IOBus *bus)
{
Dt551 *dt;
dt = (Dt551*)dev;
dt->bus = bus;
bus->dev[UTC] = (Busdev){ dt, wake_dt, 0 };
bus->dev[UTS] = (Busdev){ dt, wake_dt, 0 };
}
/* Connect a dt control to a data control */
void
dtconn(Dc136 *dc, Dt551 *dt)
{
dt->dc = dc;
dc->devp[DEVNO] = dt;
}
Device*
makedt(int argc, char *argv[])
{
Dt551 *dt;
Thread th;
dt = malloc(sizeof(Dt551));
memset(dt, 0, sizeof(Dt551));
dt->dev.type = dt_ident;
dt->dev.name = "";
dt->dev.attach = nil;
dt->dev.ioconnect = dtioconnect;
// should have 30000 cycles per second
th = (Thread){ nil, dtcycle, dt, 1000, 0 };
addthread(th);
return &dt->dev;
}