/* | |
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 | |
*/ | |
#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, 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, 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, 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, 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, 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); | |
} | |
/* | |
* 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; | |
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"); | |
} |