- add internal help entries - add descriptions to the register, modifier and debug tables. - changed device CD name to CDD to avoid name conflict with the CD command
1589 lines
48 KiB
C
1589 lines
48 KiB
C
/*
|
|
|
|
Copyright (c) 2015-2016, John Forecast
|
|
|
|
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
|
|
JOHN FORECAST 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 John Forecast shall not
|
|
be used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from John Forecast.
|
|
|
|
*/
|
|
|
|
/* cdc1700_dev1.c: equipment number 1 I/O device support
|
|
* Simh devices: tti, tto, ptr, ptp, cdr
|
|
*/
|
|
|
|
#include "cdc1700_defs.h"
|
|
|
|
extern char INTprefix[];
|
|
|
|
extern void fw_IOunderwayData(IO_DEVICE *, uint16);
|
|
extern void fw_IOcompleteData(t_bool, DEVICE *, IO_DEVICE *, uint16, const char *);
|
|
extern void fw_IOintr(t_bool, DEVICE *, IO_DEVICE *, uint16, uint16, uint16, const char *);
|
|
extern t_bool fw_reject(IO_DEVICE *, t_bool, uint8);
|
|
extern void fw_setForced(IO_DEVICE *, uint16);
|
|
extern void fw_clearForced(IO_DEVICE *, uint16);
|
|
|
|
extern void rebuildPending(void);
|
|
|
|
extern void RaiseExternalInterrupt(DEVICE *);
|
|
|
|
extern t_bool doDirectorFunc(DEVICE *, t_bool);
|
|
|
|
extern t_stat show_addr(FILE *, UNIT *, int32, CONST void *);
|
|
|
|
extern t_stat set_stoponrej(UNIT *, int32, CONST char *, void *);
|
|
extern t_stat clr_stoponrej(UNIT *, int32, CONST char *, void *);
|
|
|
|
extern t_stat set_protected(UNIT *, int32, CONST char *, void *);
|
|
extern t_stat clear_protected(UNIT *, int32, CONST char *, void *);
|
|
|
|
extern uint16 Areg, IOAreg;
|
|
|
|
extern t_bool IOFWinitialized;
|
|
|
|
t_stat tti_svc(UNIT *);
|
|
t_stat tto_svc(UNIT *);
|
|
t_stat tti_reset(DEVICE *);
|
|
t_stat tto_reset(DEVICE *);
|
|
|
|
void TTIstate(const char *, DEVICE *, IO_DEVICE *);
|
|
void TTOstate(const char *, DEVICE *, IO_DEVICE *);
|
|
void TTstate(const char *, DEVICE *, IO_DEVICE *);
|
|
uint16 TTrebuild(void);
|
|
t_bool TTreject(IO_DEVICE *, t_bool, uint8);
|
|
enum IOstatus TTin(IO_DEVICE *, uint8);
|
|
enum IOstatus TTout(IO_DEVICE *, uint8);
|
|
|
|
t_stat tt_help(FILE *, DEVICE *, UNIT *, int32, const char *);
|
|
|
|
/*
|
|
1711-A/B, 1712-A Teletypewriter
|
|
|
|
Addresses
|
|
Computer Instruction
|
|
Q Register Output From A Input to A
|
|
|
|
0090 Write Read
|
|
0091 Director Function Director Status
|
|
|
|
|
|
Operations:
|
|
|
|
Director Function
|
|
|
|
15 10 9 8 7 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| X | X | X | X | X | X | | | X | X | X | | | | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | | |
|
|
| | | | | | Clr Controller
|
|
| | | | | Clr Interrupts
|
|
| | | | Data Interrupt Req.
|
|
| | | Interrupt on EOP
|
|
| | Interrupt on Alarm
|
|
| Select Write Mode
|
|
Select Read Mode
|
|
|
|
Status Response:
|
|
|
|
Director Status
|
|
|
|
15 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| X | X | X | X | | | | X | X | | | | | | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | | | | | |
|
|
| | | | | | | | | Ready
|
|
| | | | | | | | Busy
|
|
| | | | | | | Interrupt
|
|
| | | | | | Data
|
|
| | | | | End of Operation
|
|
| | | | Alarm
|
|
| | | Lost Data
|
|
| | Read Mode
|
|
| Motor On
|
|
Manual Interrupt
|
|
|
|
The director status bits are distributed across the 3 IO_DEVICE data
|
|
structures (TTdev, TTIdev and TTOdev). Global status bits will be held
|
|
in TTdev and per-device status bits in the appropriate input or output
|
|
device:
|
|
|
|
IO_ST_READY TTdev
|
|
IO_ST_BUSY TTdev
|
|
IO_ST_INT TTIdev/TTOdev
|
|
IO_ST_DATA TTIdev/TTOdev
|
|
IO_ST_EOP TTIdev/TTOdev (equivalent to "Not Busy")
|
|
IO_ST_ALARM TTIdev/TTOdev
|
|
IO_ST_LOST TTdev
|
|
IO_1711_RMODE TTIdev (set)/TTOdev (clear)
|
|
IO_1711_MON TTdev
|
|
IO_1711_MANUAL TTdev
|
|
*/
|
|
|
|
IO_DEVICE TTIdev = IODEV("TTI", "1711-A", 1711, 1, 1, 0,
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
TTIstate, NULL, NULL, NULL,
|
|
0xF, 2,
|
|
MASK_REGISTER0 | MASK_REGISTER1,
|
|
MASK_REGISTER1, 0, 0, 0, 0, NULL);
|
|
|
|
IO_DEVICE TTOdev = IODEV("TTO", "1711-A", 1711, 1, 1, 0,
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
TTOstate, NULL, NULL, NULL,
|
|
0xF, 2,
|
|
MASK_REGISTER0 | MASK_REGISTER1,
|
|
MASK_REGISTER1, 0, 0, 0, 0, NULL);
|
|
|
|
IO_DEVICE TTdev = IODEV("TT", "1711-A", 1711, 1, 1, 0,
|
|
TTreject, TTin, TTout, NULL, NULL,
|
|
TTstate, NULL, NULL, NULL,
|
|
0xF, 2,
|
|
MASK_REGISTER0 | MASK_REGISTER1,
|
|
0, 0, 0, 0, 0, NULL);
|
|
|
|
/*
|
|
* Define usage for "private IO_DEVICE data areas
|
|
*/
|
|
#define iod_holdFull iod_private4
|
|
#define iod_indelay iod_private9
|
|
#define iod_rmode iod_private4
|
|
|
|
#define IODP_TTI_XFER 0x01 /* Transfer to data hold reg */
|
|
#define IODP_TTI_MOTION 0x02 /* Paper motion delay */
|
|
|
|
#define IO_1711_CONTR (IO_1711_MANUAL | IO_1711_MON | IO_ST_LOST | \
|
|
IO_ST_BUSY | IO_ST_READY);
|
|
#define IO_1711_IDEVICE (IO_ST_ALARM | IO_ST_EOP | IO_ST_INT | IO_ST_DATA)
|
|
#define IO_1711_ODEVICE (IO_ST_ALARM | IO_ST_EOP | IO_ST_INT | IO_ST_DATA)
|
|
|
|
/* TTI data structures
|
|
|
|
tti_dev TTI device descriptor
|
|
tti_unit TTI unit descriptor
|
|
tti_reg TTI register list
|
|
tti_mod TTI modifiers list
|
|
*/
|
|
|
|
uint8 tti_manualIntr = 0x7;
|
|
|
|
#define TTUF_V_HDX (TTUF_V_UF + 0)
|
|
#define TTUF_HDX (1 << TTUF_V_HDX)
|
|
|
|
UNIT tti_unit = { UDATA(&tti_svc, UNIT_IDLE+TT_MODE_KSR+TTUF_HDX, 0), KBD_POLL_WAIT };
|
|
|
|
REG tti_reg[] = {
|
|
{ HRDATAD(MODE, TTdev.iod_rmode, 1, "Read/Write mode (Read == TRUE)") },
|
|
{ HRDATAD(FUNCTION, TTdev.FUNCTION, 16, "Last director function issued") },
|
|
{ HRDATAD(STATUS, TTdev.STATUS, 16, "Director status register") },
|
|
{ HRDATAD(IENABLE, TTIdev.IENABLE, 16, "Interrupts enabled") },
|
|
{ HRDATAD(INTRKEY, tti_manualIntr, 8, "Manual interrupt keycode") },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB tti_mod[] = {
|
|
{ MTAB_XTD | MTAB_VDV, 0, "1711-A Console Terminal (Input)" },
|
|
{ TTUF_HDX, 0, "full duplex", "FDX",
|
|
NULL, NULL, NULL, "Set TT device to full duplex" },
|
|
{ TTUF_HDX, TTUF_HDX, "half duplex", "HDX",
|
|
NULL, NULL, NULL, "Set TT device to half duplex" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "EQUIPMENT", NULL,
|
|
NULL, &show_addr, NULL, "Display equipment address" },
|
|
{ 0 }
|
|
};
|
|
|
|
DEBTAB tti_deb[] = {
|
|
{ "TRACE", DBG_DTRACE, "Trace device I/O requests" },
|
|
{ "STATE", DBG_DSTATE, "Display device state changes" },
|
|
{ "INTR", DBG_DINTR, "Display device interrupt requests" },
|
|
{ "LOCATION", DBG_DLOC, "Display address of I/O instructions" },
|
|
{ "FIRSTREJ", DBG_DFIRSTREJ, "Suppress display of 2nd ... I/O rejects" },
|
|
{ "ALL", DBG_DTRACE | DBG_DSTATE | DBG_DLOC },
|
|
{ NULL }
|
|
};
|
|
|
|
DEVICE tti_dev = {
|
|
"TTI", &tti_unit, tti_reg, tti_mod,
|
|
1, 10, 31, 1, 8, 8,
|
|
NULL, NULL, &tti_reset,
|
|
NULL, NULL, NULL,
|
|
&TTdev,
|
|
DEV_DEBUG | DEV_NOEQUIP | DEV_INDEV, 0, tti_deb,
|
|
NULL, NULL, &tt_help, NULL, NULL, NULL
|
|
};
|
|
|
|
/* TTO data structures
|
|
|
|
tto_dev TTO device descriptor
|
|
tto_unit TTO unit descriptor
|
|
tto_reg TTO register list
|
|
tto_mod TTO modifiers list
|
|
*/
|
|
|
|
UNIT tto_unit = { UDATA(&tto_svc, TT_MODE_KSR+TTUF_HDX, 0), TT_OUT_WAIT };
|
|
|
|
REG tto_reg[] = {
|
|
{ HRDATAD(MODE, TTdev.iod_rmode, 1, "Read/Write mode (Read == TRUE)") },
|
|
{ HRDATAD(FUNCTION, TTdev.FUNCTION, 16, "Last director function issued") },
|
|
{ HRDATAD(STATUS, TTdev.STATUS, 16, "Director status register") },
|
|
{ HRDATAD(IENABLE, TTOdev.IENABLE, 16, "Interrupts enabled") },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB tto_mod[] = {
|
|
{ MTAB_XTD | MTAB_VDV, 0, "1711-A Console Terminal (Output)" },
|
|
{ TTUF_HDX, 0, "full duplex", "FDX",
|
|
NULL, NULL, NULL, "Set TT device to full duplex" },
|
|
{ TTUF_HDX, TTUF_HDX, "half duplex", "HDX",
|
|
NULL, NULL, NULL, "Set TT device to half duplex" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "EQUIPMENT", NULL,
|
|
NULL, &show_addr, NULL, "Display equipment address" },
|
|
{ 0 }
|
|
};
|
|
|
|
|
|
DEBTAB tto_deb[] = {
|
|
{ "TRACE", DBG_DTRACE, "Trace device I/O requests" },
|
|
{ "STATE", DBG_DSTATE, "Display devide state changes" },
|
|
{ "INTR", DBG_DINTR, "Display device interrupt requests" },
|
|
{ "LOCATION", DBG_DLOC, "Display address for I/O instructions" },
|
|
{ "FIRSTREJ", DBG_DFIRSTREJ, "Suppress display of 2nd ... I/O rejects" },
|
|
{ "ALL", DBG_DTRACE | DBG_DSTATE | DBG_DLOC },
|
|
{ NULL }
|
|
};
|
|
|
|
DEVICE tto_dev = {
|
|
"TTO", &tto_unit, tto_reg, tto_mod,
|
|
1, 10, 31, 1, 8, 8,
|
|
NULL, NULL, &tto_reset,
|
|
NULL, NULL, NULL,
|
|
&TTdev,
|
|
DEV_DEBUG | DEV_NOEQUIP | DEV_OUTDEV, 0, tto_deb,
|
|
NULL, NULL, &tt_help, NULL, NULL, NULL
|
|
};
|
|
|
|
/*
|
|
* Support routines for terminal physical input device
|
|
*/
|
|
|
|
/*
|
|
* Dump the current state of the TTI device
|
|
*/
|
|
void TTIstate(const char *where, DEVICE *dev, IO_DEVICE *iod)
|
|
{
|
|
char temp[32];
|
|
|
|
temp[0] = '\0';
|
|
|
|
if (TTIdev.iod_holdFull)
|
|
sprintf(temp, ", Hold full (%02X)", tti_unit.buf & 0xFF);
|
|
|
|
fprintf(DBGOUT,
|
|
"%s[TTI %s: Func: %04X, Sta: %04X, Ena: %04X, Dly: %c%s]\r\n",
|
|
INTprefix, where,
|
|
TTIdev.FUNCTION, TTIdev.STATUS, TTIdev.IENABLE,
|
|
TTIdev.iod_indelay + '0', temp);
|
|
}
|
|
|
|
/* Unit service */
|
|
|
|
t_stat tti_svc(UNIT *uptr)
|
|
{
|
|
int32 c, out;
|
|
|
|
if (TTIdev.iod_indelay != 0) {
|
|
/*
|
|
* Waiting for functions related to character input:
|
|
*
|
|
* 1. Transfering the character from the TTY to the hold buffer.
|
|
* 2. Wait for carriage control motion (CR, LF etc)
|
|
*/
|
|
if ((TTIdev.iod_indelay & IODP_TTI_XFER) != 0) {
|
|
TTIdev.iod_indelay &= ~IODP_TTI_XFER;
|
|
TTIdev.iod_holdFull = TRUE;
|
|
if ((tti_dev.dctrl & DBG_DTRACE) != 0)
|
|
fprintf(DBGOUT, "%s[TTI: tti_svc() transfer complete]\r\n", INTprefix);
|
|
|
|
if ((TTIdev.iod_indelay & IODP_TTI_MOTION) != 0) {
|
|
sim_activate(uptr, TT_IN_MOTION);
|
|
if ((tti_dev.dctrl & DBG_DTRACE) != 0)
|
|
fprintf(DBGOUT, "%s[TTI: tti_svc() motion delay]\r\n", INTprefix);
|
|
|
|
return SCPE_OK;
|
|
}
|
|
}
|
|
|
|
if ((TTIdev.iod_indelay & IODP_TTI_MOTION) != 0) {
|
|
TTIdev.iod_indelay &= ~IODP_TTI_MOTION;
|
|
if ((tti_dev.dctrl & DBG_DTRACE) != 0)
|
|
fprintf(DBGOUT, "%s[TTI: tti_svc() motion delay complete\r\n", INTprefix);
|
|
}
|
|
|
|
TTdev.STATUS &= IO_ST_BUSY;
|
|
TTIdev.STATUS |= IO_ST_EOP;
|
|
|
|
fw_IOintr(FALSE, &tti_dev, &TTIdev, 0, 0, 0xFFFF, "Motion delay");
|
|
TTrebuild();
|
|
|
|
/*
|
|
* Resume normal polling
|
|
*/
|
|
sim_activate(uptr, uptr->wait);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* Restart the poller.
|
|
*/
|
|
sim_activate(uptr, uptr->wait);
|
|
|
|
if ((c = sim_poll_kbd()) < SCPE_KFLAG)
|
|
return c; /* No character or error */
|
|
|
|
out = c & 0xfF;
|
|
|
|
if (out == tti_manualIntr) {
|
|
if ((tti_dev.dctrl & DBG_DTRACE) != 0)
|
|
fprintf(DBGOUT, "%s[TTI: tti_svc() manual interrupt]\r\n", INTprefix);
|
|
|
|
TTdev.STATUS |= IO_1711_MANUAL;
|
|
TTIdev.STATUS |= IO_ST_INT;
|
|
RaiseExternalInterrupt(&tti_dev);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
if ((c & SCPE_BREAK) != 0)
|
|
c = 0;
|
|
else c = sim_tt_inpcvt(c, TT_GET_MODE(uptr->flags) | TTUF_KSR);
|
|
|
|
if (TTIdev.iod_holdFull) {
|
|
if ((tti_dev.dctrl & DBG_DTRACE) != 0)
|
|
fprintf(DBGOUT, "%s[TTI: tti_svc() hold register full]\r\n", INTprefix);
|
|
|
|
TTIdev.STATUS |= IO_ST_ALARM | IO_ST_LOST;
|
|
fw_IOintr(FALSE, &tti_dev, &TTIdev, 0, 0, 0xFFFF, "Lost char");
|
|
TTrebuild();
|
|
return SCPE_OK;
|
|
}
|
|
|
|
if (((uptr->flags & TTUF_HDX) != 0) && out &&
|
|
((out = sim_tt_outcvt(out, TT_GET_MODE(uptr->flags) | TTUF_KSR)) >= 0)) {
|
|
sim_putchar(out);
|
|
tto_unit.pos++;
|
|
}
|
|
uptr->buf = c;
|
|
uptr->pos++;
|
|
|
|
/*
|
|
* Start a delay while the input character is transferred from the TTY to
|
|
* the hold buffer.
|
|
*/
|
|
TTIdev.iod_indelay = IODP_TTI_XFER;
|
|
if ((out == '\r') || (out == '\n') || (out == '\f'))
|
|
TTIdev.iod_indelay |= IODP_TTI_MOTION;
|
|
|
|
sim_cancel(uptr);
|
|
sim_activate(uptr, TT_IN_XFER);
|
|
|
|
TTdev.STATUS |= IO_ST_BUSY;
|
|
TTIdev.STATUS |= IO_ST_DATA;
|
|
|
|
if ((tti_dev.dctrl & DBG_DTRACE) != 0) {
|
|
fprintf(DBGOUT, "%s[TTI: tti_svc() transfer started]\r\n", INTprefix);
|
|
if ((tti_dev.dctrl & DBG_DSTATE) != 0)
|
|
TTIstate("tti_svc", &tti_dev, &TTIdev);
|
|
}
|
|
|
|
fw_IOintr(FALSE, &tti_dev, &TTIdev, 0, 0, 0xFFFF, "Input char");
|
|
TTrebuild();
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Reset routine */
|
|
|
|
t_stat tti_reset(DEVICE *dptr)
|
|
{
|
|
/*** Reset TT? ***/
|
|
TTIdev.STATUS = IO_1711_RMODE;
|
|
TTIdev.iod_holdFull = FALSE;
|
|
TTIdev.iod_indelay = 0;
|
|
|
|
tti_unit.buf = 0;
|
|
|
|
if (!sim_is_running)
|
|
sim_activate(&tti_unit, tti_unit.wait);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Perform I/O */
|
|
|
|
enum IOstatus TTIin(IO_DEVICE *iod, uint8 reg)
|
|
{
|
|
/*
|
|
* The logical TT device driver only passes INP operations for the data
|
|
* register (0x90).
|
|
*/
|
|
TTIdev.iod_holdFull = FALSE;
|
|
Areg = tti_unit.buf;
|
|
|
|
TTdev.STATUS &= ~IO_ST_BUSY;
|
|
TTIdev.STATUS |= IO_ST_EOP;
|
|
TTIdev.STATUS &= ~(IO_ST_INT | IO_ST_DATA);
|
|
TTrebuild();
|
|
rebuildPending();
|
|
|
|
return IO_REPLY;
|
|
}
|
|
|
|
void TTIcint(void)
|
|
{
|
|
/*
|
|
* Clear all interrupt enables
|
|
*/
|
|
TTIdev.iod_ienable = 0;
|
|
TTIdev.iod_oldienable = 0;
|
|
|
|
/*
|
|
* Clear all pending interrupts
|
|
*/
|
|
TTIdev.STATUS &= ~IO_1711_IDEVICE;
|
|
if (TTIdev.iod_holdFull)
|
|
TTIdev.STATUS |= IO_ST_DATA;
|
|
}
|
|
|
|
void TTIccont(void)
|
|
{
|
|
tti_reset(&tti_dev);
|
|
TTIdev.iod_holdFull = FALSE;
|
|
|
|
TTIcint();
|
|
}
|
|
|
|
/*
|
|
* Support routines for terminal physical output device
|
|
*/
|
|
|
|
/*
|
|
* Dump the current state of the TTO device
|
|
*/
|
|
void TTOstate(const char *where, DEVICE *dev, IO_DEVICE *iod)
|
|
{
|
|
char temp[32];
|
|
|
|
temp[0] = '\0';
|
|
|
|
if (TTOdev.iod_holdFull)
|
|
sprintf(temp, ", Hold full (%02X)", tto_unit.buf & 0xFF);
|
|
|
|
fprintf(DBGOUT,
|
|
"%s[TTO %s: Func: %04X, Sta: %04X, Ena: %04X%s]\r\n",
|
|
INTprefix, where,
|
|
TTOdev.FUNCTION, TTOdev.STATUS, TTOdev.IENABLE, temp);
|
|
}
|
|
|
|
/* Unit service */
|
|
|
|
t_stat tto_svc(UNIT *uptr)
|
|
{
|
|
int32 c;
|
|
t_stat r;
|
|
|
|
c = sim_tt_outcvt(uptr->buf, TT_GET_MODE(uptr->flags) | TTUF_KSR);
|
|
if (c >= 0) {
|
|
if ((r = sim_putchar_s(c)) != SCPE_OK) {
|
|
sim_activate(uptr, uptr->wait); /* Try again later */
|
|
return (r == SCPE_STALL) ? SCPE_OK : r;
|
|
}
|
|
}
|
|
|
|
TTOdev.iod_holdFull = FALSE;
|
|
TTOdev.STATUS |= IO_ST_EOP | IO_ST_DATA;
|
|
TTdev.STATUS &= ~IO_ST_BUSY;
|
|
|
|
if ((tto_dev.dctrl & DBG_DTRACE) != 0) {
|
|
fprintf(DBGOUT, "%s[TTO: tto_svc()]\r\n", INTprefix);
|
|
if ((tto_dev.dctrl & DBG_DSTATE) != 0)
|
|
TTOstate("tto_svc", &tto_dev, &TTOdev);
|
|
}
|
|
|
|
uptr->pos++;
|
|
|
|
fw_IOintr(FALSE, &tto_dev, &TTOdev, 0, 0, 0xFFFF, "Output done");
|
|
TTrebuild();
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Reset routine */
|
|
|
|
t_stat tto_reset(DEVICE *dptr)
|
|
{
|
|
/*** Reset TT? ***/
|
|
TTOdev.STATUS = IO_ST_DATA;
|
|
TTOdev.iod_holdFull = FALSE;
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Perform I/O */
|
|
|
|
enum IOstatus TTOout(IO_DEVICE *iod, uint8 reg)
|
|
{
|
|
/*
|
|
* The logical TT device driver only passes OUT operations for the data
|
|
* register (0x90).
|
|
*/
|
|
sim_activate(&tto_unit, tto_unit.wait);
|
|
|
|
tto_unit.buf = Areg;
|
|
TTOdev.iod_holdFull = TRUE;
|
|
|
|
TTOdev.STATUS &= ~(IO_ST_EOP | IO_ST_INT | IO_ST_DATA);
|
|
TTdev.STATUS |= IO_ST_BUSY;
|
|
TTrebuild();
|
|
rebuildPending();
|
|
|
|
return IO_REPLY;
|
|
}
|
|
|
|
void TTOcint(void)
|
|
{
|
|
/*
|
|
* Clear all interrupt enables
|
|
*/
|
|
TTOdev.iod_ienable = 0;
|
|
TTOdev.iod_oldienable = 0;
|
|
|
|
/*
|
|
* Clear all pending interrupts
|
|
*/
|
|
TTOdev.STATUS &= ~IO_1711_ODEVICE;
|
|
|
|
if (!TTOdev.iod_holdFull)
|
|
TTOdev.STATUS |= IO_ST_DATA;
|
|
}
|
|
|
|
void TTOccont(void)
|
|
{
|
|
tto_reset(&tto_dev);
|
|
sim_cancel(&tto_unit);
|
|
|
|
TTOcint();
|
|
}
|
|
|
|
/*
|
|
* Support routines for the terminal logical device
|
|
*/
|
|
|
|
/*
|
|
* Dump the current internal state of the TT device. Note that "dev" is
|
|
* not used by this routine since it is a logical device which has no
|
|
* associated physical device.
|
|
*/
|
|
void TTstate(const char *where, DEVICE *dev, IO_DEVICE *iod)
|
|
{
|
|
fprintf(DBGOUT,
|
|
"%s[TT %s: Func: %04X, Sta: %04X, Mode: %c]\r\n",
|
|
INTprefix, where, iod->FUNCTION, iod->STATUS,
|
|
iod->iod_rmode ? 'R' : 'W');
|
|
|
|
if ((tti_dev.dctrl & DBG_DSTATE) != 0)
|
|
TTIstate(where, &tti_dev, &TTIdev);
|
|
|
|
if ((tto_dev.dctrl & DBG_DSTATE) != 0)
|
|
TTOstate(where, &tto_dev, &TTOdev);
|
|
}
|
|
|
|
/*
|
|
* Reset routine
|
|
*/
|
|
void TTreset(void)
|
|
{
|
|
TTdev.STATUS = IO_1711_MON | IO_ST_READY;
|
|
TTdev.iod_rmode = TRUE;
|
|
}
|
|
|
|
/* Rebuild the director status register */
|
|
|
|
uint16 TTrebuild(void)
|
|
{
|
|
TTdev.STATUS &= IO_1711_CONTR;
|
|
if (TTdev.iod_rmode) {
|
|
TTdev.STATUS |= (TTIdev.STATUS & IO_1711_IDEVICE) | IO_1711_RMODE;
|
|
} else TTdev.STATUS |= TTOdev.STATUS & IO_1711_ODEVICE;
|
|
TTdev.STATUS |= IO_1711_MON | IO_ST_READY;
|
|
|
|
return TTdev.STATUS;
|
|
}
|
|
|
|
/* Check if I/O should be rejected */
|
|
|
|
t_bool TTreject(IO_DEVICE *iod, t_bool output, uint8 reg)
|
|
{
|
|
if (reg == 0) {
|
|
if (output)
|
|
return (TTdev.STATUS & IO_ST_BUSY) != 0;
|
|
return !TTIdev.iod_holdFull;
|
|
}
|
|
|
|
if (output) {
|
|
uint16 func = Areg & IO_1711_DIRMSK;
|
|
|
|
if (func != 0) {
|
|
if ((func & (IO_DIR_ALARM | IO_DIR_EOP | IO_DIR_DATA | \
|
|
IO_DIR_CINT | IO_DIR_CCONT)) != 0)
|
|
return FALSE;
|
|
|
|
/*
|
|
* Select read/write mode must be set - reject if both are set.
|
|
*/
|
|
if ((func & (IO_1711_SREAD | IO_1711_SWRITE)) ==
|
|
(IO_1711_SREAD | IO_1711_SWRITE))
|
|
return TRUE;
|
|
|
|
return (TTdev.STATUS & IO_ST_BUSY) != 0;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Perform I/O */
|
|
|
|
enum IOstatus TTin(IO_DEVICE *iod, uint8 reg)
|
|
{
|
|
/*
|
|
* Certain invalid operations will already have been rejected in TTreject().
|
|
*/
|
|
if (reg == 0)
|
|
return TTIin(&TTIdev, reg);
|
|
|
|
Areg = TTrebuild();
|
|
return IO_REPLY;
|
|
}
|
|
|
|
enum IOstatus TTout(IO_DEVICE *iod, uint8 reg)
|
|
{
|
|
t_bool rmode, changed = FALSE;
|
|
DEVICE *dptr;
|
|
|
|
/*
|
|
* Certain invalid operation will already have been rejected in TTreject().
|
|
*/
|
|
if (reg == 0)
|
|
return TTOout(&TTOdev, reg);
|
|
|
|
if ((IOAreg & IO_DIR_CCONT) != 0) {
|
|
/*
|
|
* Clear both sides of the controller and switch to read-mode.
|
|
*/
|
|
if (((tti_dev.dctrl & DBG_DSTATE) != 0) ||
|
|
((tto_dev.dctrl & DBG_DSTATE)!= 0))
|
|
fprintf(DBGOUT, "%s[TT: Controller Reset\r\n", INTprefix);
|
|
|
|
TTIccont();
|
|
TTOccont();
|
|
TTdev.STATUS &= ~IO_ST_BUSY;
|
|
|
|
TTdev.iod_rmode = TRUE;
|
|
}
|
|
|
|
if ((IOAreg & IO_DIR_CINT) != 0) {
|
|
/*
|
|
* Clear interrupts for the currently active mode
|
|
*/
|
|
if (TTdev.iod_rmode)
|
|
TTIcint();
|
|
else TTOcint();
|
|
TTdev.STATUS &= ~IO_1711_MANUAL;
|
|
}
|
|
|
|
/*
|
|
* If Clear Controller or Clear Interrupts was set, don't process
|
|
* read/write select bits.
|
|
*/
|
|
if ((IOAreg & (IO_DIR_CINT | IO_DIR_CCONT)) == 0) {
|
|
/*
|
|
* Check for switching modes. We've already rejected a request to select
|
|
* both modes.
|
|
*/
|
|
if ((IOAreg & IO_1711_SREAD) != 0)
|
|
TTdev.iod_rmode = TRUE;
|
|
if ((IOAreg & IO_1711_SWRITE) != 0) {
|
|
TTdev.iod_rmode = FALSE;
|
|
TTIdev.STATUS &= ~IO_ST_LOST;
|
|
}
|
|
}
|
|
|
|
rebuildPending();
|
|
|
|
rmode = TTdev.iod_rmode;
|
|
|
|
if ((IOAreg & (IO_DIR_ALARM | IO_DIR_EOP | IO_DIR_DATA)) != 0) {
|
|
iod = rmode ? &TTIdev : &TTOdev;
|
|
dptr = rmode ? &tti_dev : &tto_dev;
|
|
|
|
iod->iod_oldienable = iod->iod_ienable;
|
|
iod->iod_ienable |= IOAreg & (IO_DIR_ALARM | IO_DIR_EOP | IO_DIR_DATA);
|
|
changed = iod->iod_ienable != iod->iod_oldienable;
|
|
}
|
|
|
|
if (changed)
|
|
if (!rmode)
|
|
fw_IOintr(FALSE, dptr, iod, 0, 0, 0xFFFF, "Can output");
|
|
|
|
TTrebuild();
|
|
|
|
return IO_REPLY;
|
|
}
|
|
|
|
t_stat tt_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
|
{
|
|
const char helpString[] =
|
|
/****************************************************************************/
|
|
" The TTI/TTO device is a 1711-A teletype console. The device is\n"
|
|
" implemented as 2 seperate devices within the simulator; TTI for input\n"
|
|
" and TTO for output.\n"
|
|
"1 Hardware Description\n"
|
|
" The 1711-A consists of a teletype console terminal with an extra button\n"
|
|
" which is used to generate a 'manual interrupt'. By default, the\n"
|
|
" simulator uses the 'Control+G' combination to generate the interrupt.\n"
|
|
" This key combination may be changed by:\n\n"
|
|
"+sim> DEPOSIT TTI INTRKEY keycodeValue\n\n"
|
|
"2 Equipment Address\n"
|
|
" The console device is part of the low-speed package and, as such, is at\n"
|
|
" fixed equipment address 1, station 1.\n"
|
|
"2 $Registers\n"
|
|
"\n"
|
|
" These registers contain the emulated state of the device. These values\n"
|
|
" don't necessarily relate to any detail of the original device being\n"
|
|
" emulated but are merely internal details of the emulation.\n"
|
|
"1 Configuration\n"
|
|
" A %D device is configured with various simh SET commands\n"
|
|
"2 $Set commands\n";
|
|
|
|
return scp_help(st, dptr, uptr, flag, helpString, cptr);
|
|
}
|
|
|
|
t_stat ptr_svc(UNIT *);
|
|
t_stat ptr_reset(DEVICE *);
|
|
t_stat ptr_attach(UNIT *, CONST char *);
|
|
t_stat ptr_detach(UNIT *);
|
|
|
|
enum IOstatus PTRin(IO_DEVICE *, uint8);
|
|
enum IOstatus PTRout(IO_DEVICE *, uint8);
|
|
|
|
t_stat ptr_help(FILE *, DEVICE *, UNIT *, int32, const char *);
|
|
|
|
/*
|
|
1721-A/B/C/D, 1722-A/B Paper Tape Reader
|
|
|
|
Addresses
|
|
Computer Instruction
|
|
Q Register Output From A Input to A
|
|
|
|
00A0 Read
|
|
00A1 Director Function Director Status
|
|
|
|
|
|
Operations:
|
|
|
|
Director Function
|
|
|
|
15 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| X | X | X | X | X | X | X | X | X | | | | X | | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | |
|
|
| | | | | Clr Controller
|
|
| | | | Clr Interrupts
|
|
| | | Data Interrupt Req.
|
|
| | Interrupt on Alarm
|
|
| Start Motion
|
|
Stop Motion
|
|
Status Response:
|
|
|
|
Director Status
|
|
|
|
15 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| X | X | X | X | X | | | | | | | X | | | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | | | | | |
|
|
| | | | | | | | | Ready
|
|
| | | | | | | | Busy
|
|
| | | | | | | Interrupt
|
|
| | | | | | Data
|
|
| | | | | Alarm
|
|
| | | | Lost Data
|
|
| | | Protected
|
|
| | Existence Code
|
|
| Paper Motion Failure
|
|
Power On
|
|
*/
|
|
|
|
IO_DEVICE PTRdev = IODEV(NULL, "1721-A", 1721, 1, 2, 0,
|
|
fw_reject, PTRin, PTRout, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
0xF, 2,
|
|
MASK_REGISTER0 | MASK_REGISTER1,
|
|
MASK_REGISTER1, 0, 0, 0, 0, NULL);
|
|
|
|
/*
|
|
* Define usage for "private" IO_DEVICE data areas.
|
|
*/
|
|
#define iod_PTRmotion iod_private
|
|
/*
|
|
* Current state of the device (STOPPED/STARTED).
|
|
*/
|
|
#define IODP_PTRSTOPPED 0x0000
|
|
#define IODP_PTRSTARTED 0x0001
|
|
#define IODP_PTR_MASK 0x0001
|
|
|
|
/* PTR data structures
|
|
|
|
ptr_dev PTR device descriptor
|
|
ptr_unit PTR unit descriptor
|
|
ptr_reg PTR register list
|
|
ptr_mod PTR modifiers list
|
|
*/
|
|
|
|
UNIT ptr_unit = {
|
|
UDATA(&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), PTR_IN_WAIT
|
|
};
|
|
|
|
REG ptr_reg[] = {
|
|
{ HRDATAD(FUNCTION, PTRdev.FUNCTION, 16, "Last director function issued") },
|
|
{ HRDATAD(STATUS, PTRdev.STATUS, 16, "Director status register") },
|
|
{ HRDATAD(IENABLE, PTRdev.IENABLE, 16, "Interrupts enabled") },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB ptr_mod[] = {
|
|
{ MTAB_XTD | MTAB_VDV, 0, "1721-A Paper Tape Reader" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "EQUIPMENT", NULL,
|
|
NULL, &show_addr, NULL, "Display equipment address" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "STOPONREJECT",
|
|
&set_stoponrej, NULL, NULL, "Stop simulation if I/O is rejected" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOSTOPONREJECT",
|
|
&clr_stoponrej, NULL, NULL, "Don't stop simulation if I/O is rejected" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "PROTECT",
|
|
&set_protected, NULL, NULL, "Device is protected (unimplemented)" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOPROTECT",
|
|
&clear_protected, NULL, NULL, "Device is unprotected (unimplemented)" },
|
|
{ 0 }
|
|
};
|
|
|
|
DEBTAB ptr_deb[] = {
|
|
{ "TRACE", DBG_DTRACE, "Traced device I/O requests" },
|
|
{ "STATE", DBG_DSTATE, "Display device state changes" },
|
|
{ "LOCATION", DBG_DLOC, "Display address of I/O instructions" },
|
|
{ "FIRSTREJ", DBG_DFIRSTREJ, "Suppress display of 2nd ... I/O rejected" },
|
|
{ "ALL", DBG_DTRACE | DBG_DSTATE | DBG_DLOC },
|
|
{ NULL }
|
|
};
|
|
|
|
DEVICE ptr_dev = {
|
|
"PTR", &ptr_unit, ptr_reg, ptr_mod,
|
|
1, 10, 31, 1, 8, 8,
|
|
NULL, NULL, &ptr_reset,
|
|
NULL, &ptr_attach, &ptr_detach,
|
|
&PTRdev,
|
|
DEV_DEBUG | DEV_NOEQUIP | DEV_INDEV | DEV_PROTECT, 0, ptr_deb,
|
|
NULL, NULL, &ptr_help, NULL, NULL, NULL
|
|
};
|
|
|
|
/*
|
|
* Dump the current internal state of the PTR device.
|
|
*/
|
|
const char *PTRprivateState[2] = {
|
|
"", "In Motion"
|
|
};
|
|
|
|
void PTRstate(const char *where, DEVICE *dev, IO_DEVICE *iod)
|
|
{
|
|
fprintf(DBGOUT,
|
|
"%s[%s %s: Func: %04X, Sta: %04X, Ena: %04X, Private: %s\r\n",
|
|
INTprefix, dev->name, where,
|
|
iod->FUNCTION, iod->STATUS, iod->IENABLE,
|
|
PTRprivateState[iod->iod_PTRmotion & IODP_PTR_MASK]);
|
|
}
|
|
|
|
/* Unit service */
|
|
|
|
t_stat ptr_svc(UNIT *uptr)
|
|
{
|
|
int32 temp;
|
|
|
|
if ((ptr_dev.dctrl & DBG_DTRACE) != 0) {
|
|
fprintf(DBGOUT, "%s[PTR: ptr_svc() entry]\r\n", INTprefix);
|
|
if ((ptr_dev.dctrl & DBG_DSTATE) != 0)
|
|
PTRstate("svc entry", &ptr_dev, &PTRdev);
|
|
}
|
|
|
|
if ((uptr->flags & UNIT_ATT) == 0) {
|
|
if ((ptr_dev.dctrl & DBG_DTRACE) != 0)
|
|
fprintf(DBGOUT,
|
|
"%s[PTR: ptr_svc() exit - no attached file]\r\n", INTprefix);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
if ((temp = getc(uptr->fileref)) == EOF) {
|
|
if (feof(uptr->fileref)) {
|
|
if ((ptr_dev.dctrl & DBG_DTRACE) != 0)
|
|
fprintf(DBGOUT, "%s[PTR: ptr_svc() exit - EOF]\r\n", INTprefix);
|
|
|
|
/*
|
|
* We've run off the end of the tape. Indicate motion failure and alarm
|
|
* status and generate an interrupt if requested.
|
|
*/
|
|
fw_IOintr(FALSE, &ptr_dev, &PTRdev, IO_ST_ALARM | IO_1721_MOTIONF, IO_ST_READY, 0xFFFF, "End of tape");
|
|
return SCPE_OK;
|
|
} else perror("PTR I/O error");
|
|
clearerr(uptr->fileref);
|
|
if ((ptr_dev.dctrl & DBG_DTRACE) != 0)
|
|
fprintf(DBGOUT, "%s[PTR: ptr_svc() exit - Read Error]\r\n", INTprefix);
|
|
return SCPE_IOERR;
|
|
}
|
|
uptr->buf = temp & 0xFF;
|
|
uptr->pos++;
|
|
|
|
fw_IOcompleteData(FALSE, &ptr_dev, &PTRdev, 0xFFFF, "Read Complete");
|
|
|
|
if ((ptr_dev.dctrl & DBG_DTRACE) != 0) {
|
|
fprintf(DBGOUT,
|
|
"%s[PTR: ptr_svc() exit => %2X]\r\n", INTprefix, uptr->buf);
|
|
if ((ptr_dev.dctrl & DBG_DSTATE) != 0)
|
|
PTRstate("svc exit", &ptr_dev, &PTRdev);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Reset routine */
|
|
|
|
t_stat ptr_reset(DEVICE *dptr)
|
|
{
|
|
DEVRESET(&PTRdev);
|
|
|
|
PTRdev.iod_PTRmotion = IODP_PTRSTOPPED;
|
|
|
|
if ((ptr_unit.flags & UNIT_ATT) != 0)
|
|
fw_setForced(&PTRdev, IO_1721_POWERON | IO_ST_READY);
|
|
|
|
ptr_unit.buf = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Attach routine */
|
|
|
|
t_stat ptr_attach(UNIT *uptr, CONST char *cptr)
|
|
{
|
|
t_stat r;
|
|
|
|
if ((r = attach_unit(uptr, cptr)) != SCPE_OK)
|
|
return r;
|
|
|
|
fw_setForced(&PTRdev, IO_1721_POWERON | IO_ST_READY);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Detach routine */
|
|
|
|
t_stat ptr_detach(UNIT *uptr)
|
|
{
|
|
if ((uptr->flags & UNIT_ATT) == 0)
|
|
return SCPE_OK;
|
|
|
|
fw_clearForced(&PTRdev, IO_1721_POWERON | IO_ST_READY);
|
|
return detach_unit(uptr);
|
|
}
|
|
|
|
/* Perform I/O */
|
|
|
|
enum IOstatus PTRin(IO_DEVICE *iod, uint8 reg)
|
|
{
|
|
if ((ptr_dev.dctrl & DBG_DSTATE) != 0)
|
|
PTRstate("before", &ptr_dev, &PTRdev);
|
|
|
|
/*
|
|
* The framework only passes IN operations for the data register (0x90)
|
|
*/
|
|
Areg = (Areg & 0xFF00) | ptr_unit.buf;
|
|
fw_IOunderwayData(&PTRdev, 0);
|
|
if (PTRdev.iod_PTRmotion == IODP_PTRSTARTED)
|
|
sim_activate(&ptr_unit, ptr_unit.wait);
|
|
|
|
if ((ptr_dev.dctrl & DBG_DSTATE) != 0)
|
|
PTRstate("after", &ptr_dev, &PTRdev);
|
|
return IO_REPLY;
|
|
}
|
|
|
|
enum IOstatus PTRout(IO_DEVICE *iod, uint8 reg)
|
|
{
|
|
if ((ptr_dev.dctrl & DBG_DSTATE) != 0)
|
|
PTRstate("before", &ptr_dev, &PTRdev);
|
|
|
|
switch (reg) {
|
|
case 0x00:
|
|
if ((ptr_dev.dctrl & DBG_DSTATE) != 0)
|
|
PTRstate("after", &ptr_dev, &PTRdev);
|
|
return IO_REJECT;
|
|
|
|
case 0x01:
|
|
doDirectorFunc(&ptr_dev, FALSE);
|
|
|
|
if ((IOAreg & IO_DIR_START) != 0) {
|
|
fw_setForced(&PTRdev, IO_ST_BUSY);
|
|
PTRdev.iod_PTRmotion = IODP_PTRSTARTED;
|
|
}
|
|
if ((IOAreg & IO_DIR_STOP) != 0) {
|
|
fw_clearForced(&PTRdev, IO_ST_BUSY);
|
|
PTRdev.iod_PTRmotion = IODP_PTRSTOPPED;
|
|
}
|
|
|
|
if (PTRdev.iod_PTRmotion == IODP_PTRSTARTED)
|
|
sim_activate(&ptr_unit, ptr_unit.wait);
|
|
}
|
|
if ((ptr_dev.dctrl & DBG_DSTATE) != 0)
|
|
PTRstate("after", &ptr_dev, &PTRdev);
|
|
return IO_REPLY;
|
|
}
|
|
|
|
t_stat ptr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
|
{
|
|
const char helpString[] =
|
|
/****************************************************************************/
|
|
" The %D device is a 1721-A paper tape reader.\n"
|
|
"1 Hardware Description\n"
|
|
" The 1721-A consists of a controller and a physical paper tape reader.\n"
|
|
"2 Equipment Address\n"
|
|
" The paper tape reader is part of the low-speed package and, as such, is\n"
|
|
" at fixed equipment address 1, station 2.\n"
|
|
"2 $Registers\n"
|
|
"\n"
|
|
" These registers contain the emulated state of the device. These values\n"
|
|
" don't necessarily relate to any detail of the original device being\n"
|
|
" emulated but are merely internal details of the emulation. STATUS always\n"
|
|
" contains the current status of the device as it would be read by an\n"
|
|
" application program.\n"
|
|
"1 Configuration\n"
|
|
" A %D device is configured with various simh SET and ATTACH commands\n"
|
|
"2 $Set commands\n";
|
|
|
|
return scp_help(st, dptr, uptr, flag, helpString, cptr);
|
|
}
|
|
|
|
t_stat ptp_svc(UNIT *);
|
|
t_stat ptp_reset(DEVICE *);
|
|
|
|
enum IOstatus PTPin(IO_DEVICE *, uint8);
|
|
enum IOstatus PTPout(IO_DEVICE *, uint8);
|
|
|
|
t_stat ptp_help(FILE *, DEVICE *, UNIT *, int32, const char *);
|
|
|
|
/*
|
|
1723-A/B, 1724-A/B Paper Tape Punch
|
|
|
|
Addresses
|
|
Computer Instruction
|
|
Q Register Output From A Input to A
|
|
|
|
00C0 Write
|
|
00C1 Director Function Director Status
|
|
|
|
Operations:
|
|
|
|
Director Function
|
|
|
|
15 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| X | X | X | X | X | X | X | X | X | | | | X | | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | |
|
|
| | | | | Clr Controller
|
|
| | | | Clr Interrupts
|
|
| | | Data Interrupt Req.
|
|
| | Interrupt on Alarm
|
|
| Start Motion
|
|
Stop Motion
|
|
|
|
Status Response:
|
|
|
|
Director Status
|
|
|
|
15 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| X | X | X | X | | | | | | X | | X | | | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | | | | | |
|
|
| | | | | | | | | Ready
|
|
| | | | | | | | Busy
|
|
| | | | | | | Interrupt
|
|
| | | | | | Data
|
|
| | | | | Alarm
|
|
| | | | Protected
|
|
| | | Existence Code
|
|
| | Tape Break
|
|
| Power On
|
|
Tape Supply Low
|
|
*/
|
|
|
|
IO_DEVICE PTPdev = IODEV(NULL, "1723-A", 1723, 1, 4, 0,
|
|
fw_reject, PTPin, PTPout, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
0xF, 2,
|
|
MASK_REGISTER0 | MASK_REGISTER1,
|
|
MASK_REGISTER1, 0, 0, 0, 0, NULL);
|
|
|
|
/*
|
|
* Define usage for "private" IO_DEVICE data areas.
|
|
*/
|
|
#define iod_PTPdelay iod_private
|
|
|
|
/*
|
|
* Current delay state of the device.
|
|
*/
|
|
#define IODP_PTPINTRWAIT 0x0001
|
|
#define IODP_PTPDATAWAIT 0x0002
|
|
#define IODP_PTP_MASK (IODP_PTPINTRWAIT | IODP_PTPDATAWAIT)
|
|
|
|
/* PTP data structures
|
|
|
|
ptp_dev PTP device descriptor
|
|
ptp_unit PTP unit descriptor
|
|
ptp_reg PTP register list
|
|
ptp_mod PTP modifiers list
|
|
*/
|
|
|
|
UNIT ptp_unit = {
|
|
UDATA(&ptp_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), PTP_OUT_WAIT
|
|
};
|
|
|
|
REG ptp_reg[] = {
|
|
{ HRDATAD(FUNCTION, PTPdev.FUNCTION, 16, "Last director function issued") },
|
|
{ HRDATAD(STATUS, PTPdev.STATUS, 16, "Director status register") },
|
|
{ HRDATAD(IENABLE, PTPdev.IENABLE, 16, "Interrupts enabled") },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB ptp_mod[] = {
|
|
{ MTAB_XTD | MTAB_VDV, 0, "1723-A Paper Tape Punch" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "EQUIPMENT", NULL,
|
|
NULL, &show_addr, NULL, "Display equipment address" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "STOPONREJECT",
|
|
&set_stoponrej, NULL, NULL, "Stop simulation if I/O is rejected" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOSTOPONREJECT",
|
|
&clr_stoponrej, NULL, NULL, "Don't stop simulation if I/O is rejected" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "PROTECT",
|
|
&set_protected, NULL, NULL, "Device is protected (unimplemented)" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOPROTECT",
|
|
&clear_protected, NULL, NULL, "Device is unprotected (unimplemented)" },
|
|
{ 0 }
|
|
};
|
|
|
|
DEBTAB ptp_deb[] = {
|
|
{ "TRACE", DBG_DTRACE, "Trace device I/O requests" },
|
|
{ "STATE", DBG_DSTATE, "Display device state changes" },
|
|
{ "LOCATION", DBG_DLOC, "Display address of I/O instructions" },
|
|
{ "FIRSTREJ", DBG_DFIRSTREJ, "Suppress display of 2nd ... I/O rejects" },
|
|
{ "ALL", DBG_DTRACE | DBG_DSTATE | DBG_DLOC },
|
|
{ NULL }
|
|
};
|
|
|
|
DEVICE ptp_dev = {
|
|
"PTP", &ptp_unit, ptp_reg, ptp_mod,
|
|
1, 10, 31, 1, 8, 8,
|
|
NULL, NULL, &ptp_reset,
|
|
NULL, NULL, NULL,
|
|
&PTPdev,
|
|
DEV_DEBUG | DEV_NOEQUIP | DEV_OUTDEV | DEV_PROTECT, 0, ptp_deb,
|
|
NULL, NULL, &ptp_help, NULL, NULL, NULL
|
|
};
|
|
|
|
/*
|
|
* Dump the current internal state of the PTP device.
|
|
*/
|
|
const char *PTPprivateState[4] = {
|
|
"", "INTRWAIT", "DATAWAIT", "DATAWAIT,INTRWAIT"
|
|
};
|
|
|
|
void PTPstate(const char *where, DEVICE *dev, IO_DEVICE *iod)
|
|
{
|
|
fprintf(DBGOUT,
|
|
"%s[%s %s: Func: %04X, Sta: %04X, Ena: %04X, Private: %s\r\n",
|
|
INTprefix, dev->name, where,
|
|
iod->FUNCTION, iod->STATUS, iod->IENABLE,
|
|
PTPprivateState[iod->iod_PTPdelay & IODP_PTP_MASK]);
|
|
}
|
|
|
|
/* Unit service */
|
|
|
|
t_stat ptp_svc(UNIT *uptr)
|
|
{
|
|
if ((ptp_dev.dctrl & DBG_DTRACE) != 0) {
|
|
fprintf(DBGOUT, "%s[PTP: ptp_svc() entry]\r\n", INTprefix);
|
|
if ((ptp_dev.dctrl & DBG_DSTATE) != 0)
|
|
PTPstate("svc entry", &ptp_dev, &PTPdev);
|
|
}
|
|
|
|
if ((PTPdev.iod_PTPdelay & IODP_PTPINTRWAIT) != 0) {
|
|
/*
|
|
* Generate an interrupt indicating that the motor is up to speed.
|
|
*/
|
|
PTPdev.iod_PTPdelay &= ~IODP_PTPINTRWAIT;
|
|
fw_IOintr(FALSE, &ptp_dev, &PTPdev, 0, 0, 0xFFFF, "Up to speed");
|
|
|
|
if ((PTPdev.iod_PTPdelay & IODP_PTP_MASK) != 0)
|
|
sim_activate(&ptp_unit, ptp_unit.wait);
|
|
goto done;
|
|
}
|
|
|
|
if ((PTPdev.iod_PTPdelay & IODP_PTPDATAWAIT) != 0) {
|
|
/*
|
|
* Now process the actual output of data to be punched.
|
|
*/
|
|
PTPdev.iod_PTPdelay &= ~IODP_PTPDATAWAIT;
|
|
|
|
if ((uptr->flags & UNIT_ATT) != 0) {
|
|
if (putc(uptr->buf, uptr->fileref) == EOF) {
|
|
perror("PTP I/O error");
|
|
clearerr(uptr->fileref);
|
|
} else uptr->pos++;
|
|
}
|
|
|
|
fw_IOcompleteData(FALSE, &ptp_dev, &PTPdev, 0xFFFF, "Output complete");
|
|
}
|
|
|
|
done:
|
|
if ((ptp_dev.dctrl & DBG_DTRACE) != 0) {
|
|
fprintf(DBGOUT, "%s[PTP: ptp_svc() exit]\r\n", INTprefix);
|
|
if ((ptp_dev.dctrl & DBG_DSTATE) != 0)
|
|
PTPstate("svc exit", &ptp_dev, &PTPdev);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Reset routine */
|
|
|
|
t_stat ptp_reset(DEVICE *dptr)
|
|
{
|
|
DEVRESET(&PTPdev);
|
|
|
|
PTPdev.iod_PTPdelay = 0;
|
|
fw_setForced(&PTPdev, IO_1723_POWERON | IO_ST_READY);
|
|
|
|
ptp_unit.buf = 0;
|
|
if (!sim_is_running)
|
|
sim_activate(&ptp_unit, ptp_unit.wait);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Perform I/O */
|
|
|
|
enum IOstatus PTPin(IO_DEVICE *iod, uint8 reg)
|
|
{
|
|
/*
|
|
* The framework only passes IN operations for the data register (0x90)
|
|
*/
|
|
return IO_REJECT;
|
|
}
|
|
|
|
enum IOstatus PTPout(IO_DEVICE *iod, uint8 reg)
|
|
{
|
|
if ((ptp_dev.dctrl & DBG_DSTATE) != 0)
|
|
PTPstate("before", &ptp_dev, &PTPdev);
|
|
|
|
switch (reg) {
|
|
case 0x00:
|
|
PTPdev.iod_PTPdelay |= IODP_PTPDATAWAIT;
|
|
ptp_unit.buf = Areg;
|
|
|
|
fw_IOunderwayData(&PTPdev, IO_ST_INT);
|
|
|
|
rebuildPending();
|
|
|
|
sim_activate(&ptp_unit, ptp_unit.wait);
|
|
|
|
if ((ptp_dev.dctrl & DBG_DSTATE) != 0)
|
|
PTPstate("after", &ptp_dev, &PTPdev);
|
|
break;
|
|
|
|
case 0x01:
|
|
/*
|
|
* Check for illegal combination of commands
|
|
*/
|
|
if (STARTSTOP(Areg))
|
|
return IO_REJECT;
|
|
|
|
if (doDirectorFunc(&ptp_dev, FALSE)) {
|
|
/*
|
|
* The device interrupt mask has been explicitly changed. If the
|
|
* interrupt on data was just set and the device is ready, generate
|
|
* a delayed interrupt.
|
|
*/
|
|
if ((ICHANGED(&PTPdev) & IO_DIR_DATA) != 0) {
|
|
if ((PTPdev.STATUS & IO_ST_READY) != 0) {
|
|
if ((PTPdev.iod_PTPdelay & IODP_PTP_MASK) == 0) {
|
|
if ((ptp_dev.dctrl & DBG_DTRACE) != 0)
|
|
fprintf(DBGOUT,
|
|
"%sPTP: Mask change interrupt\n", INTprefix);
|
|
sim_activate(&ptp_unit, ptp_unit.wait);
|
|
PTPdev.iod_PTPdelay |= IODP_PTPINTRWAIT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IOAreg != 0) {
|
|
if ((IOAreg & (IO_DIR_START | IO_DIR_STOP)) != 0) {
|
|
sim_activate(&ptp_unit, 5 * ptp_unit.wait);
|
|
PTPdev.iod_PTPdelay |= IODP_PTPINTRWAIT;
|
|
if ((IOAreg & IO_DIR_START) != 0) {
|
|
fw_setForced(&PTPdev, IO_ST_BUSY);
|
|
PTPdev.STATUS |= IO_ST_DATA;
|
|
}
|
|
if ((IOAreg & IO_DIR_STOP) != 0) {
|
|
fw_clearForced(&PTPdev, IO_ST_BUSY);
|
|
PTPdev.STATUS &= ~IO_ST_DATA;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((ptp_dev.dctrl & DBG_DSTATE) != 0)
|
|
PTPstate("after", &ptp_dev, &PTPdev);
|
|
break;
|
|
}
|
|
return IO_REPLY;
|
|
}
|
|
|
|
t_stat ptp_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
|
{
|
|
const char helpString[] =
|
|
/****************************************************************************/
|
|
" The %D device is a 1723-A paper tape punch.\n"
|
|
"1 Hardware Description\n"
|
|
" The 1723-A consists of a controller and a physical paper tape punch.\n"
|
|
"2 Equipment Address\n"
|
|
" The paper tape reader is part of the low-speed package and, as such, is\n"
|
|
" at fixed equipment address 1, station 4.\n"
|
|
"2 $Registers\n"
|
|
"\n"
|
|
" These registers contain the emulated state of the device. These values\n"
|
|
" don't necessarily relate to any detail of the original device being\n"
|
|
" emulated but are merely internal details of the emulation. STATUS always\n"
|
|
" contains the current status of the device as it would be read by an\n"
|
|
" application program.\n"
|
|
"1 Configuration\n"
|
|
" A %D device is configured with various simh SET and ATTACH commands\n"
|
|
"2 $Set commands\n";
|
|
|
|
return scp_help(st, dptr, uptr, flag, helpString, cptr);
|
|
}
|
|
|
|
t_stat cdr_svc(UNIT *);
|
|
t_stat cdr_reset(DEVICE *);
|
|
|
|
enum IOstatus CDRin(IO_DEVICE *, uint8);
|
|
enum IOstatus CDRout(IO_DEVICE *, uint8);
|
|
|
|
/*
|
|
1729-A/B Card Reader
|
|
|
|
Addresses
|
|
Computer Instruction
|
|
Q Register Output From A Input to A
|
|
|
|
00E0 Read
|
|
00E1 Director Function Director Status
|
|
|
|
|
|
Operations:
|
|
|
|
Director Function
|
|
|
|
15 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| X | X | X | X | X | X | X | X | X | | | | | | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | | |
|
|
| | | | | | Clr Controller
|
|
| | | | | Clr Interrupts
|
|
| | | | Data Interrupt Req.
|
|
| | | Interrupt on End of Record
|
|
| | Interrupt on Alarm
|
|
| Start Motion
|
|
Stop Motion
|
|
|
|
Status Response:
|
|
|
|
Director Status
|
|
|
|
15 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| X | X | X | X | X | X | | | | | | | | | | |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| | | | | | | | | |
|
|
| | | | | | | | | Ready
|
|
| | | | | | | | Busy
|
|
| | | | | | | Interrupt
|
|
| | | | | | Data
|
|
| | | | | End Of Record
|
|
| | | | Alarm
|
|
| | | Lost Data
|
|
| | Protected
|
|
| Existence Code
|
|
Read Station Empty
|
|
|
|
*/
|
|
|
|
IO_DEVICE CDRdev = IODEV(NULL, "1729", 1729, 1, 6, 0,
|
|
fw_reject, CDRin, CDRout, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
0xF, 2,
|
|
MASK_REGISTER0 | MASK_REGISTER1,
|
|
MASK_REGISTER1, 0, 0, 0, 0, NULL);
|
|
|
|
/* CDR data structures
|
|
|
|
cdr_dev CDR device descriptor
|
|
cdr_unit CDR unit descriptor
|
|
cdr_reg CDR register list
|
|
cdr_mod CDR modifiers list
|
|
*/
|
|
|
|
UNIT cdr_unit = {
|
|
UDATA(&cdr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), SERIAL_IN_WAIT
|
|
};
|
|
|
|
REG cdr_reg[] = {
|
|
{ HRDATAD(FUNCTION, CDRdev.FUNCTION, 16, "Last director function issued") },
|
|
{ HRDATAD(STATUS, CDRdev.STATUS, 16, "Director status register") },
|
|
{ HRDATAD(IENABLE, CDRdev.IENABLE, 16, "Interrupts enabled") },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB cdr_mod[] = {
|
|
{ MTAB_XTD | MTAB_VDV, 0, "1729 Card Reader" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, "EQUIPMENT", NULL,
|
|
NULL, &show_addr, NULL, "Display equipment address" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "PROTECT",
|
|
&set_protected, NULL, NULL, "Device is protected (unimplemented)" },
|
|
{ MTAB_XTD|MTAB_VDV, 0, NULL, "NOPROTECT",
|
|
&clear_protected, NULL, NULL, "Device is unprotected (unimplemented)" },
|
|
{ 0 }
|
|
};
|
|
|
|
DEBTAB cdr_deb[] = {
|
|
{ "TRACE", DBG_DTRACE, "Trace device I/O requests" },
|
|
{ "STATE", DBG_DSTATE, "Display device state changes" },
|
|
{ "LOCATION", DBG_DLOC, "Display address for I/O instructions" },
|
|
{ "FIRSTREJ", DBG_DFIRSTREJ, "Suppress display of 2nd ... I/O rejects" },
|
|
{ "ALL", DBG_DTRACE | DBG_DSTATE | DBG_DLOC },
|
|
{ NULL }
|
|
};
|
|
|
|
DEVICE cdr_dev = {
|
|
"CDR", &cdr_unit, cdr_reg, cdr_mod,
|
|
1, 10, 31, 1, 8, 8,
|
|
NULL, NULL, &cdr_reset,
|
|
NULL, NULL, NULL,
|
|
&CDRdev,
|
|
DEV_DEBUG | DEV_NOEQUIP | DEV_INDEV | DEV_PROTECT, 0, cdr_deb,
|
|
NULL, NULL, NULL, NULL, NULL, NULL
|
|
};
|
|
|
|
/* Unit service */
|
|
|
|
t_stat cdr_svc(UNIT *uptr)
|
|
{
|
|
/*** TODO: Implement Card Reader support ***/
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Reset routine */
|
|
|
|
t_stat cdr_reset(DEVICE *dptr)
|
|
{
|
|
DEVRESET(&CDRdev);
|
|
|
|
ptr_unit.buf = 0;
|
|
if (!sim_is_running)
|
|
sim_activate(&cdr_unit, cdr_unit.wait);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Perform I/O */
|
|
|
|
enum IOstatus CDRin(IO_DEVICE *iod, uint8 reg)
|
|
{
|
|
/*
|
|
* The framework only passes IN operations for the data register (0x90)
|
|
*/
|
|
Areg = cdr_unit.buf;
|
|
CDRdev.STATUS &= IO_ST_BUSY | IO_ST_DATA;
|
|
return IO_REPLY;
|
|
}
|
|
|
|
enum IOstatus CDRout(IO_DEVICE *iod, uint8 reg)
|
|
{
|
|
switch (reg) {
|
|
case 0x00:
|
|
return IO_REJECT;
|
|
|
|
case 0x01:
|
|
doDirectorFunc(&cdr_dev, FALSE);
|
|
/*** TODO: Process local director functions ***/
|
|
break;
|
|
}
|
|
return IO_REPLY;
|
|
}
|
|
|
|
/*
|
|
* Return device 1 interrupt status. If any of the sub-devices have their
|
|
* interrupt status active, return the device 1 interrupt mask bit.
|
|
*/
|
|
uint16 dev1INTR(DEVICE *dptr)
|
|
{
|
|
uint16 status;
|
|
|
|
status = TTIdev.STATUS | TTOdev.STATUS | PTRdev.STATUS | PTPdev.STATUS | CDRdev.STATUS;
|
|
|
|
return (status & IO_ST_INT) != 0 ? 1 << 1 : 0;
|
|
}
|
|
|
|
/*
|
|
* Update a buffer indicating which device 1 stations are asserting interrupt
|
|
* status.
|
|
*/
|
|
void dev1Interrupts(char *buf)
|
|
{
|
|
buf[0] = '\0';
|
|
|
|
if ((TTIdev.STATUS & IO_ST_INT) != 0)
|
|
strcat(buf, " TTI");
|
|
if ((TTOdev.STATUS & IO_ST_INT) != 0)
|
|
strcat(buf, "TTO");
|
|
if ((PTRdev.STATUS & IO_ST_INT) != 0)
|
|
strcat(buf, " PTR");
|
|
if ((PTPdev.STATUS & IO_ST_INT) != 0)
|
|
strcat(buf, " PTP");
|
|
if ((CDRdev.STATUS & IO_ST_INT) != 0)
|
|
strcat(buf, " CDR");
|
|
}
|