/* id_pt.c: Interdata paper tape reader | |
Copyright (c) 2000-2008, Robert M. Supnik | |
Permission is hereby granted, free of charge, to any person obtaining a | |
copy of this software and associated documentation files (the "Software"), | |
to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom the | |
Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
Except as contained in this notice, the name of Robert M Supnik shall not be | |
used in advertising or otherwise to promote the sale, use or other dealings | |
in this Software without prior written authorization from Robert M Supnik. | |
pt paper tape reader and punch | |
25-Apr-03 RMS Revised for extended file support | |
10-Apr-03 RMS Fixed type problem in ptr service (Mark Pizzolato) | |
*/ | |
#include "id_defs.h" | |
#include <ctype.h> | |
/* Device definitions */ | |
#define PTR 0 /* unit subscripts */ | |
#define PTP 1 | |
#define STA_OVR 0x80 /* overrun */ | |
#define STA_NMTN 0x10 /* no motion */ | |
#define STA_MASK (STA_BSY | STA_OVR | STA_DU) /* static bits */ | |
#define SET_EX (STA_OVR | STA_NMTN) /* set EX */ | |
#define CMD_V_RUN 4 /* run/stop */ | |
#define CMD_V_SLEW 2 /* slew/step */ | |
#define CMD_V_RD 0 /* read/write */ | |
extern uint32 int_req[INTSZ], int_enb[INTSZ]; | |
uint32 pt_run = 0, pt_slew = 0; /* ptr modes */ | |
uint32 pt_rd = 1, pt_chp = 0; /* pt state */ | |
uint32 pt_arm = 0; /* int arm */ | |
uint32 pt_sta = STA_BSY; /* status */ | |
uint32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ | |
uint32 pt (uint32 dev, uint32 op, uint32 dat); | |
t_stat ptr_svc (UNIT *uptr); | |
t_stat ptp_svc (UNIT *uptr); | |
t_stat pt_boot (int32 unitno, DEVICE *dptr); | |
t_stat pt_reset (DEVICE *dptr); | |
/* PT data structures | |
pt_dev PT device descriptor | |
pt_unit PT unit descriptors | |
pt_reg PT register list | |
*/ | |
DIB pt_dib = { d_PT, -1, v_PT, NULL, &pt, NULL }; | |
UNIT pt_unit[] = { | |
{ UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), | |
SERIAL_IN_WAIT }, | |
{ UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT } | |
}; | |
REG pt_reg[] = { | |
{ HRDATA (STA, pt_sta, 8) }, | |
{ HRDATA (RBUF, pt_unit[PTR].buf, 8) }, | |
{ DRDATA (RPOS, pt_unit[PTR].pos, T_ADDR_W), PV_LEFT }, | |
{ DRDATA (RTIME, pt_unit[PTR].wait, 24), PV_LEFT }, | |
{ FLDATA (RSTOP_IOE, ptr_stopioe, 0) }, | |
{ HRDATA (PBUF, pt_unit[PTP].buf, 8) }, | |
{ DRDATA (PPOS, pt_unit[PTP].pos, T_ADDR_W), PV_LEFT }, | |
{ DRDATA (PTIME, pt_unit[PTP].wait, 24), PV_LEFT }, | |
{ FLDATA (PSTOP_IOE, ptp_stopioe, 0) }, | |
{ FLDATA (IREQ, int_req[l_PT], i_PT) }, | |
{ FLDATA (IENB, int_enb[l_PT], i_PT) }, | |
{ FLDATA (IARM, pt_arm, 0) }, | |
{ FLDATA (RD, pt_rd, 0) }, | |
{ FLDATA (RUN, pt_run, 0) }, | |
{ FLDATA (SLEW, pt_slew, 0) }, | |
{ FLDATA (CHP, pt_chp, 0) }, | |
{ HRDATA (DEVNO, pt_dib.dno, 8), REG_HRO }, | |
{ NULL } | |
}; | |
MTAB pt_mod[] = { | |
{ MTAB_XTD|MTAB_VDV, 0, "devno", "DEVNO", | |
&set_dev, &show_dev, NULL }, | |
{ 0 } | |
}; | |
DEVICE pt_dev = { | |
"PT", pt_unit, pt_reg, pt_mod, | |
2, 10, 31, 1, 16, 8, | |
NULL, NULL, &pt_reset, | |
&pt_boot, NULL, NULL, | |
&pt_dib, DEV_DISABLE | |
}; | |
/* Paper tape: IO routine */ | |
uint32 pt (uint32 dev, uint32 op, uint32 dat) | |
{ | |
uint32 t, old_rd, old_run; | |
switch (op) { /* case IO op */ | |
case IO_ADR: /* select */ | |
return BY; /* byte only */ | |
case IO_OC: /* command */ | |
old_rd = pt_rd; /* save curr rw */ | |
old_run = pt_run; /* save curr run */ | |
pt_arm = int_chg (v_PT, dat, pt_arm); /* upd int ctrl */ | |
pt_rd = io_2b (dat, CMD_V_RD, pt_rd); /* upd read/wr */ | |
if (old_rd != pt_rd) { /* rw change? */ | |
pt_sta = pt_sta & ~STA_OVR; /* clr overrun */ | |
if (sim_is_active (&pt_unit[pt_rd? PTR: PTP])) { | |
pt_sta = pt_sta | STA_BSY; /* busy = 1 */ | |
CLR_INT (v_PT); /* clear int */ | |
} | |
else { /* not active */ | |
pt_sta = pt_sta & ~STA_BSY; /* busy = 0 */ | |
if (pt_arm) /* no, set int */ | |
SET_INT (v_PT); | |
} | |
} | |
if (pt_rd) { /* reader? */ | |
pt_run = io_2b (dat, CMD_V_RUN, pt_run); /* upd run/stop */ | |
pt_slew = io_2b (dat, CMD_V_SLEW, pt_slew); /* upd slew/inc */ | |
if (pt_run) { /* run set? */ | |
if (old_run == 0) { /* run 0 -> 1? */ | |
sim_activate (&pt_unit[PTR], pt_unit[PTR].wait); | |
pt_sta = pt_sta & ~STA_DU; /* clear eof */ | |
} | |
} | |
else sim_cancel (&pt_unit[PTR]); /* clr, stop rdr */ | |
} | |
else pt_sta = pt_sta & ~STA_DU; /* punch, clr eof */ | |
break; | |
case IO_RD: /* read */ | |
if (pt_run && !pt_slew) { /* incremental? */ | |
sim_activate (&pt_unit[PTR], pt_unit[PTR].wait); | |
pt_sta = pt_sta & ~STA_DU; /* clr eof */ | |
} | |
pt_chp = 0; /* clr char pend */ | |
if (pt_rd) /* set busy */ | |
pt_sta = pt_sta | STA_BSY; | |
return (pt_unit[PTR].buf & 0xFF); /* return char */ | |
case IO_WD: /* write */ | |
pt_unit[PTP].buf = dat & DMASK8; /* save char */ | |
if (!pt_rd) /* set busy */ | |
pt_sta = pt_sta | STA_BSY; | |
sim_activate (&pt_unit[PTP], pt_unit[PTP].wait); | |
break; | |
case IO_SS: /* status */ | |
t = pt_sta & STA_MASK; /* get status */ | |
if (pt_rd && !pt_run && !sim_is_active (&pt_unit[PTR])) | |
t = t | STA_NMTN; /* stopped? */ | |
if ((pt_unit[pt_rd? PTR: PTP].flags & UNIT_ATT) == 0) | |
t = t | STA_DU; /* offline? */ | |
if (t & SET_EX) /* test for EX */ | |
t = t | STA_EX; | |
return t; | |
} | |
return 0; | |
} | |
/* Unit service */ | |
t_stat ptr_svc (UNIT *uptr) | |
{ | |
int32 temp; | |
if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ | |
return IORETURN (ptr_stopioe, SCPE_UNATT); | |
if (pt_rd) { /* read mode? */ | |
pt_sta = pt_sta & ~STA_BSY; /* clear busy */ | |
if (pt_arm) /* if armed, intr */ | |
SET_INT (v_PT); | |
if (pt_chp) /* overrun? */ | |
pt_sta = pt_sta | STA_OVR; | |
} | |
pt_chp = 1; /* char pending */ | |
if ((temp = getc (uptr->fileref)) == EOF) { /* error? */ | |
if (feof (uptr->fileref)) { /* eof? */ | |
pt_sta = pt_sta | STA_DU; /* set DU */ | |
if (ptr_stopioe) | |
sim_printf ("PTR end of file\n"); | |
else return SCPE_OK; | |
} | |
else sim_perror ("PTR I/O error"); | |
clearerr (uptr->fileref); | |
return SCPE_IOERR; | |
} | |
uptr->buf = temp & DMASK8; /* store char */ | |
uptr->pos = uptr->pos + 1; /* incr pos */ | |
if (pt_slew) /* slew? continue */ | |
sim_activate (uptr, uptr->wait); | |
return SCPE_OK; | |
} | |
t_stat ptp_svc (UNIT *uptr) | |
{ | |
if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ | |
return IORETURN (ptp_stopioe, SCPE_UNATT); | |
if (!pt_rd) { /* write mode? */ | |
pt_sta = pt_sta & ~STA_BSY; /* clear busy */ | |
if (pt_arm) /* if armed, intr */ | |
SET_INT (v_PT); | |
} | |
if (putc (uptr->buf, uptr -> fileref) == EOF) { /* write char */ | |
sim_perror ("PTP I/O error"); | |
clearerr (uptr -> fileref); | |
return SCPE_IOERR; | |
} | |
uptr -> pos = uptr -> pos + 1; /* incr pos */ | |
return SCPE_OK; | |
} | |
/* Reset routine */ | |
t_stat pt_reset (DEVICE *dptr) | |
{ | |
sim_cancel (&pt_unit[PTR]); /* deactivate units */ | |
sim_cancel (&pt_unit[PTP]); | |
pt_rd = 1; /* read */ | |
pt_chp = pt_run = pt_slew = 0; /* stop, inc, disarm */ | |
pt_sta = STA_BSY; /* buf empty */ | |
CLR_INT (v_PT); /* clear int */ | |
CLR_ENB (v_PT); /* disable int */ | |
pt_arm = 0; /* disarm int */ | |
return SCPE_OK; | |
} | |
/* Bootstrap routine */ | |
#define BOOT_START 0x50 | |
#define BOOT_LEN (sizeof (boot_rom) / sizeof (uint8)) | |
#define BOOT3_START 0x3E | |
#define BOOT3_LEN (sizeof (boot_rom) / sizeof (uint8)) | |
static uint8 boot_rom[] = { | |
0xD5, 0x00, 0x00, 0xCF, /* ST AL CF */ | |
0x43, 0x00, 0x00, 0x80 /* BR 80 */ | |
}; | |
static uint8 boot3_rom[] = { | |
0xC8, 0x20, 0x00, 0x80, /* ST LHI 2,80 */ | |
0xC8, 0x30, 0x00, 0x01, /* LHI 3,1 */ | |
0xC8, 0x40, 0x00, 0xCF, /* LHI 4,CF */ | |
0xD3, 0xA0, 0x00, 0x78, /* LB A,78 */ | |
0xDE, 0xA0, 0x00, 0x79, /* OC A,79 */ | |
0x9D, 0xAE, /* LP SSR A,E */ | |
0x42, 0xF0, 0x00, 0x52, /* BTC F,LP */ | |
0x9B, 0xAE, /* RDR A,E */ | |
0x08, 0xEE, /* LHR E,E */ | |
0x43, 0x30, 0x00, 0x52, /* BZ LP */ | |
0x43, 0x00, 0x00, 0x6C, /* BR STO */ | |
0x9D, 0xAE, /* LP1 SSR A,E */ | |
0x42, 0xF0, 0x00, 0x64, /* BTC F,LP1 */ | |
0x9B, 0xAE, /* RDR A,E */ | |
0xD2, 0xE2, 0x00, 0x00, /* STO STB E,0(2) */ | |
0xC1, 0x20, 0x00, 0x64, /* BXLE 2,LP1 */ | |
0x43, 0x00, 0x00, 0x80 /* BR 80 */ | |
}; | |
t_stat pt_boot (int32 unitno, DEVICE *dptr) | |
{ | |
if (decrom[0xD5] & dec_flgs) /* AL defined? */ | |
IOWriteBlk (BOOT3_START, BOOT3_LEN, boot3_rom); /* no, 50 seq */ | |
else IOWriteBlk (BOOT_START, BOOT_LEN, boot_rom); /* copy AL boot */ | |
IOWriteB (AL_DEV, pt_dib.dno); /* set dev no */ | |
IOWriteB (AL_IOC, 0x99); /* set dev cmd */ | |
IOWriteB (AL_SCH, 0); /* clr sch dev no */ | |
PC = BOOT_START; | |
return SCPE_OK; | |
} | |
/* Dump routine */ | |
#define LOAD_START 0x80 | |
#define LOAD_LO 0x8A | |
#define LOAD_HI 0x8E | |
#define LOAD_CS 0x93 | |
#define LOAD_LEN (sizeof (load_rom) / sizeof (uint8)) | |
#define LOAD_LDR 50 | |
static uint8 load_rom[] = { | |
0x24, 0x21, /* BOOT LIS R2,1 */ | |
0x23, 0x03, /* BS BOOT */ | |
0x00, 0x00, /* 32b psw pointer */ | |
0x00, 0x00, /* 32b reg pointer */ | |
0xC8, 0x10, /* ST LHI R1,lo */ | |
0x00, 0x00, | |
0xC8, 0x30, /* LHI R3,hi */ | |
0x00, 0x00, | |
0xC8, 0x60, /* LHI R3,cs */ | |
0x00, 0x00, | |
0xD3, 0x40, /* LB R4,X'78' */ | |
0x00, 0x78, | |
0xDE, 0x40, /* OC R4,X'79' */ | |
0x00, 0x79, | |
0x9D, 0x45, /* LDR SSR R4,R5 */ | |
0x20, 0x91, /* BTBS 9,.-2 */ | |
0x9B, 0x45, /* RDR R4,R5 */ | |
0x08, 0x55, /* L(H)R R5,R5 */ | |
0x22, 0x34, /* BZS LDR */ | |
0xD2, 0x51, /* LOOP STB R5,0(R1) */ | |
0x00, 0x00, | |
0x07, 0x65, /* X(H)R R6,R5 */ | |
0x9A, 0x26, /* WDR R2,R6 */ | |
0x9D, 0x45, /* SSR R4,R5 */ | |
0x20, 0x91, /* BTBS 9,.-2 */ | |
0x9B, 0x45, /* RDR R4,R5 */ | |
0xC1, 0x10, /* BXLE R1,LOOP */ | |
0x00, 0xA6, | |
0x24, 0x78, /* LIS R7,8 */ | |
0x91, 0x7C, /* SLLS R7,12 */ | |
0x95, 0x57, /* EPSR R5,R7 */ | |
0x22, 0x03 /* BS .-6 */ | |
}; | |
t_stat pt_dump (FILE *of, CONST char *cptr, CONST char *fnam) | |
{ | |
uint32 i, lo, hi, cs; | |
const char *tptr; | |
extern DEVICE cpu_dev; | |
if ((cptr == NULL) || (*cptr == 0)) | |
return SCPE_2FARG; | |
tptr = get_range (NULL, cptr, &lo, &hi, cpu_dev.aradix, 0xFFFF, 0); | |
if ((tptr == NULL) || (lo < INTSVT)) | |
return SCPE_ARG; | |
if (*tptr != 0) | |
return SCPE_2MARG; | |
for (i = lo, cs = 0; i <= hi; i++) | |
cs = cs ^ IOReadB (i); | |
IOWriteBlk (LOAD_START, LOAD_LEN, load_rom); | |
IOWriteB (LOAD_LO, (lo >> 8) & 0xFF); | |
IOWriteB (LOAD_LO + 1, lo & 0xFF); | |
IOWriteB (LOAD_HI, (hi >> 8) & 0xFF); | |
IOWriteB (LOAD_HI + 1, hi & 0xFF); | |
IOWriteB (LOAD_CS, cs & 0xFF); | |
for (i = 0; i < LOAD_LDR; i++) | |
fputc (0, of); | |
for (i = LOAD_START; i < (LOAD_START + LOAD_LEN); i++) | |
fputc (IOReadB (i), of); | |
for (i = 0; i < LOAD_LDR; i++) | |
fputc (0, of); | |
for (i = lo; i <= hi; i++) | |
fputc (IOReadB (i), of); | |
for (i = 0; i < LOAD_LDR; i++) | |
fputc (0, of); | |
return SCPE_OK; | |
} |