/* b5500_urec.c: Burrioughs 5500 Unit record devices. | |
Copyright (c) 2016, Richard Cornwell | |
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 | |
RICHARD CORNWELL 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. | |
This is the standard card reader. 10,14 | |
This is the standard card punch. 10 | |
This is the standard line printer. 22,26 | |
This is the standard operators console. 30 | |
*/ | |
#include "b5500_defs.h" | |
#include "sim_card.h" | |
#include "sim_defs.h" | |
#include "sim_console.h" | |
#define UNIT_CDR UNIT_ATTABLE | UNIT_RO | UNIT_DISABLE | MODE_029 | |
#define UNIT_CDP UNIT_ATTABLE | UNIT_DISABLE | MODE_029 | |
#define UNIT_LPR UNIT_ATTABLE | UNIT_DISABLE | |
/* For Card reader, when set returns end of file at end of deck. */ | |
/* Reset after sent to system */ | |
#define MODE_EOF (0x40 << UNIT_V_CARD_MODE) | |
#define TMR_RTC 0 | |
/* std devices. data structures | |
cdr_dev Card Reader device descriptor | |
cdr_unit Card Reader unit descriptor | |
cdr_reg Card Reader register list | |
cdr_mod Card Reader modifiers list | |
*/ | |
/* Device status information stored in u5 */ | |
#define URCSTA_CHMASK 0003 /* Mask of I/O channel to send data on */ | |
#define URCSTA_CARD 0004 /* Unit has card in buffer */ | |
#define URCSTA_FULL 0004 /* Unit has full buffer */ | |
#define URCSTA_BUSY 0010 /* Device is busy */ | |
#define URCSTA_BIN 0020 /* Card reader in binary mode */ | |
#define URCSTA_ACTIVE 0040 /* Unit is active */ | |
#define URCSTA_EOF 0100 /* Flag the end of file */ | |
#define URCSTA_INPUT 0200 /* Console fill buffer from keyboard */ | |
#define URCSTA_FILL 010000 /* Fill unit buffer */ | |
#define URCSTA_CMD_V 16 | |
#define URCSTA_SKIP 000017 /* Skip mask */ | |
#define URCSTA_DOUBLE 000020 /* Double space skip */ | |
#define URCSTA_SINGLE 000040 /* Single space skip. */ | |
#define URCSTA_READ 000400 /* Read flag */ | |
#define URCSTA_WC 001000 /* Use word count */ | |
#define URCSTA_DIRECT 002000 /* Direction, Long line */ | |
#define URCSTA_BINARY 004000 /* Binary transfer */ | |
#define URCSTA_INHIBIT 040000 /* Inhibit transfer to memory */ | |
/* Simulator debug controls */ | |
DEBTAB cdr_debug[] = { | |
{"CMD", DEBUG_CMD, "Show command execution to devices"}, | |
{"DATA", DEBUG_DATA, "Show data transfers"}, | |
{"DETAIL", DEBUG_DETAIL, "Show details about device"}, | |
{"EXP", DEBUG_EXP, "Show console data"}, | |
{"CARD", DEBUG_CARD, "Show Card read/punches"}, | |
{0, 0} | |
}; | |
#if NUM_DEVS_CDR > 0 | |
t_stat cdr_boot(int32, DEVICE *); | |
t_stat cdr_srv(UNIT *); | |
t_stat cdr_attach(UNIT *, CONST char *); | |
t_stat cdr_detach(UNIT *); | |
t_stat cdr_help(FILE *, DEVICE *, UNIT *, int32, const char *); | |
const char *cdr_description(DEVICE *dptr); | |
#endif | |
#if NUM_DEVS_CDP > 0 | |
t_stat cdp_srv(UNIT *); | |
t_stat cdp_attach(UNIT *, CONST char *); | |
t_stat cdp_detach(UNIT *); | |
t_stat cdp_help(FILE *, DEVICE *, UNIT *, int32, const char *); | |
const char *cdp_description(DEVICE *dptr); | |
#endif | |
#if NUM_DEVS_LPR > 0 | |
struct _lpr_data | |
{ | |
uint8 lbuff[145]; /* Output line buffer */ | |
} | |
lpr_data[NUM_DEVS_LPR]; | |
t_stat lpr_srv(UNIT *); | |
t_stat lpr_attach(UNIT *, CONST char *); | |
t_stat lpr_detach(UNIT *); | |
t_stat lpr_setlpp(UNIT *, int32, CONST char *, void *); | |
t_stat lpr_getlpp(FILE *, UNIT *, int32, CONST void *); | |
t_stat lpr_help(FILE *, DEVICE *, UNIT *, int32, const char *); | |
const char *lpr_description(DEVICE *dptr); | |
#endif | |
#if NUM_DEVS_CON > 0 | |
struct _con_data | |
{ | |
uint8 ibuff[145]; /* Input line buffer */ | |
uint8 inptr; | |
uint8 outptr; | |
} | |
con_data[NUM_DEVS_CON]; | |
t_stat con_ini(DEVICE *); | |
t_stat con_srv(UNIT *); | |
t_stat con_attach(UNIT *, CONST char *); | |
t_stat con_detach(UNIT *); | |
t_stat con_help(FILE *, DEVICE *, UNIT *, int32, const char *); | |
const char *con_description(DEVICE *dptr); | |
#endif | |
#if NUM_DEVS_CDR > 0 | |
UNIT cdr_unit[] = { | |
{UDATA(cdr_srv, UNIT_CDR, 0)}, /* A */ | |
#if NUM_DEVS_CDR > 1 | |
{UDATA(cdr_srv, UNIT_CDR|UNIT_DIS, 0)}, /* B */ | |
#endif | |
}; | |
MTAB cdr_mod[] = { | |
{MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", | |
&sim_card_set_fmt, &sim_card_show_fmt, NULL, | |
"Sets card format"}, | |
{MODE_EOF, MODE_EOF, "EOF", "EOF", NULL, NULL, NULL, | |
"Causes EOF to be set when reader empty"}, | |
{0} | |
}; | |
DEVICE cdr_dev = { | |
"CR", cdr_unit, NULL, cdr_mod, | |
NUM_DEVS_CDR, 8, 15, 1, 8, 8, | |
NULL, NULL, NULL, &cdr_boot, &cdr_attach, &cdr_detach, | |
NULL, DEV_DISABLE | DEV_DEBUG, 0, cdr_debug, | |
NULL, NULL, &cdr_help, NULL, NULL, | |
&cdr_description | |
}; | |
#endif | |
#if NUM_DEVS_CDP > 0 | |
UNIT cdp_unit[] = { | |
{UDATA(cdp_srv, UNIT_CDP, 0)}, /* A */ | |
}; | |
MTAB cdp_mod[] = { | |
{MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", | |
&sim_card_set_fmt, &sim_card_show_fmt, NULL, | |
"Sets card format"}, | |
{0} | |
}; | |
DEVICE cdp_dev = { | |
"CP", cdp_unit, NULL, cdp_mod, | |
NUM_DEVS_CDP, 8, 15, 1, 8, 8, | |
NULL, NULL, NULL, NULL, &cdp_attach, &cdp_detach, | |
NULL, DEV_DISABLE | DEV_DEBUG, 0, cdr_debug, | |
NULL, NULL, &cdp_help, NULL, NULL, | |
&cdp_description | |
}; | |
#endif | |
#if NUM_DEVS_LPR > 0 | |
UNIT lpr_unit[] = { | |
{UDATA(lpr_srv, UNIT_LPR, 59)}, /* A */ | |
#if NUM_DEVS_LPR > 1 | |
{UDATA(lpr_srv, UNIT_LPR|UNIT_DIS, 59)}, /* B */ | |
#endif | |
}; | |
MTAB lpr_mod[] = { | |
{MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LINESPERPAGE", "LINESPERPAGE", | |
&lpr_setlpp, &lpr_getlpp, NULL, | |
"Sets number of lines on a printed page"}, | |
{0} | |
}; | |
DEVICE lpr_dev = { | |
"LP", lpr_unit, NULL, lpr_mod, | |
NUM_DEVS_LPR, 8, 15, 1, 8, 8, | |
NULL, NULL, NULL, NULL, &lpr_attach, &lpr_detach, | |
NULL, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, | |
NULL, NULL, &lpr_help, NULL, NULL, | |
&lpr_description | |
}; | |
#endif | |
#if NUM_DEVS_CON > 0 | |
UNIT con_unit[] = { | |
{UDATA(con_srv, UNIT_IDLE, 0), 0}, /* A */ | |
}; | |
DEVICE con_dev = { | |
"CON", con_unit, NULL, NULL, | |
NUM_DEVS_CON, 8, 15, 1, 8, 8, | |
NULL, NULL, con_ini, NULL, NULL, NULL, | |
NULL, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, | |
NULL, NULL, &con_help, NULL, NULL, | |
&con_description | |
}; | |
#endif | |
#if ((NUM_DEVS_CDR > 0) | (NUM_DEVS_CDP > 0)) | |
/* | |
* Device entry points for card reader. | |
* And Card punch. | |
*/ | |
t_stat card_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc) | |
{ | |
UNIT *uptr; | |
int u; | |
if (dev == CARD1_DEV) | |
u = 0; | |
else if (dev == CARD2_DEV) | |
u = 1; | |
else | |
return SCPE_NXDEV; | |
/* Check if card reader or card punch */ | |
if (cmd & URCSTA_READ) { | |
uptr = &cdr_unit[u]; | |
if ((uptr->flags & UNIT_ATT) == 0) | |
return SCPE_UNATT; | |
/* Are we currently tranfering? */ | |
if (uptr->u5 & URCSTA_ACTIVE) | |
return SCPE_BUSY; | |
/* Check if we ran out of cards */ | |
if (uptr->u5 & URCSTA_EOF) { | |
/* If end of file, return to system */ | |
if (uptr->flags & MODE_EOF) { | |
sim_debug(DEBUG_DETAIL, &cdr_dev, "cdr %d %d report eof\n", u, | |
chan); | |
chan_set_eof(chan); | |
uptr->flags &= ~MODE_EOF; | |
} | |
/* Clear unit ready */ | |
iostatus &= ~(CARD1_FLAG << u); | |
return SCPE_UNATT; | |
} | |
if (cmd & URCSTA_BINARY) { | |
uptr->u5 |= URCSTA_BIN; | |
*wc = 20; | |
} else { | |
uptr->u5 &= ~URCSTA_BIN; | |
*wc = 10; | |
} | |
uptr->u5 &= ~URCSTA_CHMASK; | |
uptr->u5 |= URCSTA_ACTIVE|chan; | |
uptr->u4 = 0; | |
sim_activate(uptr, 500000); | |
return SCPE_OK; | |
} else { | |
/* Talking to punch */ | |
if (u != 0) | |
return SCPE_NXDEV; | |
sim_debug(DEBUG_DETAIL, &cdr_dev, "cdp %d %d start\n", u, chan); | |
uptr = &cdp_unit[0]; | |
if ((uptr->flags & UNIT_ATT) == 0) | |
return SCPE_UNATT; | |
if (uptr->u5 & URCSTA_ACTIVE) | |
return SCPE_BUSY; | |
uptr->u5 &= ~URCSTA_CHMASK; | |
uptr->u5 |= URCSTA_ACTIVE|chan; | |
uptr->u4 = 0; | |
*wc = 10; | |
sim_activate(uptr, 500000); | |
sim_debug(DEBUG_DETAIL, &cdr_dev, "cdp %d %d go\n", u, chan); | |
return SCPE_OK; | |
} | |
return SCPE_IOERR; | |
} | |
/* Handle transfer of data for card reader */ | |
t_stat | |
cdr_srv(UNIT *uptr) { | |
int chan = URCSTA_CHMASK & uptr->u5; | |
int u = (uptr - cdr_unit); | |
if (uptr->u5 & URCSTA_EOF) { | |
sim_debug(DEBUG_DETAIL, &cdr_dev, "cdr %d %d unready\n", u, chan); | |
iostatus &= ~(CARD1_FLAG << u); | |
uptr->u5 &= ~ URCSTA_EOF; | |
return SCPE_OK; | |
} | |
/* Check if new card requested. */ | |
if (uptr->u4 == 0 && uptr->u5 & URCSTA_ACTIVE && | |
(uptr->u5 & URCSTA_CARD) == 0) { | |
switch(sim_read_card(uptr)) { | |
case SCPE_UNATT: | |
iostatus &= ~(CARD1_FLAG << u); | |
uptr->u5 &= ~(URCSTA_ACTIVE); | |
iostatus &= ~(CARD1_FLAG << u); | |
chan_set_notrdy(chan); | |
break; | |
case SCPE_EOF: | |
/* If end of file, return to system */ | |
if (uptr->flags & MODE_EOF) { | |
sim_debug(DEBUG_DETAIL, &cdr_dev, "cdr %d %d set eof\n", u, chan); | |
chan_set_eof(chan); | |
uptr->flags &= ~MODE_EOF; | |
} | |
uptr->u5 &= ~(URCSTA_ACTIVE); | |
uptr->u5 |= URCSTA_EOF; | |
chan_set_notrdy(chan); | |
sim_activate(uptr, 500); | |
break; | |
case SCPE_IOERR: | |
chan_set_error(chan); | |
uptr->u5 &= ~(URCSTA_ACTIVE); | |
uptr->u5 |= URCSTA_EOF; | |
chan_set_end(chan); | |
break; | |
case SCPE_OK: | |
uptr->u5 |= URCSTA_CARD; | |
sim_activate(uptr, 500); | |
break; | |
} | |
return SCPE_OK; | |
} | |
/* Copy next column over */ | |
if (uptr->u5 & URCSTA_CARD && | |
uptr->u4 < ((uptr->u5 & URCSTA_BIN) ? 160 : 80)) { | |
struct _card_data *data; | |
uint8 ch = 0; | |
int u = (uptr - cdr_unit); | |
data = (struct _card_data *)uptr->up7; | |
if (uptr->u5 & URCSTA_BIN) { | |
ch = (data->image[uptr->u4 >> 1] >> | |
((uptr->u4 & 1)? 0 : 6)) & 077; | |
} else { | |
ch = sim_hol_to_bcd(data->image[uptr->u4]); | |
/* Remap some characters from 029 to BCL */ | |
switch(ch) { | |
case 0: ch = 020; break; /* Translate blanks */ | |
case 10: /* Check if 0 punch of 82 punch */ | |
if (data->image[uptr->u4] != 0x200) { | |
ch = 0; | |
if (uptr->u4 == 0) | |
chan_set_parity(chan); | |
} | |
break; | |
case 0111: | |
ch = 0; | |
/* Handle invalid punch */ | |
chan_set_parity(chan); | |
break; /* Translate ? to error*/ | |
} | |
} | |
sim_debug(DEBUG_DATA, &cdr_dev, "cdr %d: Char > %03o '%c' %d\n", u, ch, | |
sim_six_to_ascii[ch & 077], uptr->u4); | |
if(chan_write_char(chan, &ch, 0)) { | |
uptr->u5 &= ~(URCSTA_ACTIVE|URCSTA_CARD); | |
chan_set_end(chan); | |
/* Drop ready a bit after the last card is read */ | |
if (sim_card_eof(uptr)) { | |
uptr->u5 |= URCSTA_EOF; | |
sim_activate(uptr, 100); | |
} | |
} else { | |
uptr->u4++; | |
sim_activate(uptr, 100); | |
} | |
} | |
/* Check if last column */ | |
if (uptr->u5 & URCSTA_CARD && | |
uptr->u4 == ((uptr->u5 & URCSTA_BIN) ? 160 : 80)) { | |
uptr->u5 &= ~(URCSTA_ACTIVE|URCSTA_CARD); | |
chan_set_end(chan); | |
/* Drop ready a bit after the last card is read */ | |
if (sim_card_eof(uptr)) { | |
uptr->u5 |= URCSTA_EOF; | |
} | |
} | |
return SCPE_OK; | |
} | |
/* Boot from given device */ | |
t_stat | |
cdr_boot(int32 unit_num, DEVICE * dptr) | |
{ | |
UNIT *uptr = &dptr->units[unit_num]; | |
uint8 dev; | |
t_uint64 desc; | |
if ((uptr->flags & UNIT_ATT) == 0) | |
return SCPE_UNATT; /* attached? */ | |
dev = (uptr == &cdr_unit[0]) ? CARD1_DEV : CARD2_DEV; | |
uptr->u5 &= ~URCSTA_ACTIVE; | |
desc = ((t_uint64)dev) << DEV_V | DEV_IORD| DEV_BIN | 020LL; | |
/* Read in one record */ | |
return chan_boot(desc); | |
} | |
t_stat | |
cdr_attach(UNIT * uptr, CONST char *file) | |
{ | |
t_stat r; | |
int u = uptr-cdr_unit; | |
if ((r = sim_card_attach(uptr, file)) != SCPE_OK) | |
return r; | |
uptr->u5 &= URCSTA_BUSY; | |
uptr->u4 = 0; | |
uptr->u6 = 0; | |
iostatus |= (CARD1_FLAG << u); | |
return SCPE_OK; | |
} | |
t_stat | |
cdr_detach(UNIT * uptr) | |
{ | |
int u = uptr-cdr_unit; | |
iostatus &= ~(CARD1_FLAG << u); | |
return sim_card_detach(uptr); | |
} | |
t_stat | |
cdr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
{ | |
fprintf (st, "B124 Card Reader\n\n"); | |
fprintf (st, "The system supports up to two card readers, the second one is disabled\n"); | |
fprintf (st, "by default. To have the card reader return the EOF flag when the deck\n"); | |
fprintf (st, "has finished reading do:\n"); | |
fprintf (st, " sim> SET CRn EOF\n"); | |
fprintf (st, "This flag is cleared each time a deck has been read, so it must be set\n"); | |
fprintf (st, "again after each deck. MCP does not require this to be set as long as\n"); | |
fprintf (st, "the deck includes a ?END card\n"); | |
fprint_set_help(st, dptr); | |
fprint_show_help(st, dptr); | |
return SCPE_OK; | |
} | |
const char * | |
cdr_description(DEVICE *dptr) | |
{ | |
return "B124 Card Reader"; | |
} | |
#endif | |
#if NUM_DEVS_CDR > 0 | NUM_DEVS_CDP > 0 | |
/* Handle transfer of data for card punch */ | |
t_stat | |
cdp_srv(UNIT *uptr) { | |
int chan = URCSTA_CHMASK & uptr->u5; | |
int u = (uptr - cdp_unit); | |
if (uptr->u5 & URCSTA_BUSY) { | |
/* Done waiting, punch card */ | |
if (uptr->u5 & URCSTA_FULL) { | |
sim_debug(DEBUG_DETAIL, &cdp_dev, "cdp %d %d punch\n", u, chan); | |
switch(sim_punch_card(uptr, NULL)) { | |
case SCPE_EOF: | |
case SCPE_UNATT: | |
sim_debug(DEBUG_DETAIL, &cdp_dev, "cdp %d %d set eof\n", u, | |
chan); | |
chan_set_eof(chan); | |
break; | |
/* If we get here, something is wrong */ | |
case SCPE_IOERR: | |
chan_set_error(chan); | |
break; | |
case SCPE_OK: | |
break; | |
} | |
uptr->u5 &= ~URCSTA_FULL; | |
chan_set_end(chan); | |
} | |
uptr->u5 &= ~URCSTA_BUSY; | |
} | |
/* Copy next column over */ | |
if (uptr->u5 & URCSTA_ACTIVE && uptr->u4 < 80) { | |
struct _card_data *data; | |
uint8 ch = 0; | |
data = (struct _card_data *)uptr->up7; | |
if(chan_read_char(chan, &ch, 0)) { | |
uptr->u5 |= URCSTA_BUSY|URCSTA_FULL; | |
uptr->u5 &= ~URCSTA_ACTIVE; | |
} else { | |
sim_debug(DEBUG_DATA, &cdp_dev, "cdp %d: Char %d < %02o\n", u, | |
uptr->u4, ch); | |
data->image[uptr->u4++] = sim_bcd_to_hol(ch & 077); | |
} | |
sim_activate(uptr, 10); | |
} | |
/* Check if last column */ | |
if (uptr->u5 & URCSTA_ACTIVE && uptr->u4 == 80) { | |
uptr->u5 |= URCSTA_BUSY|URCSTA_FULL; | |
uptr->u5 &= ~URCSTA_ACTIVE; | |
} | |
return SCPE_OK; | |
} | |
t_stat | |
cdp_attach(UNIT * uptr, CONST char *file) | |
{ | |
t_stat r; | |
if ((r = sim_card_attach(uptr, file)) != SCPE_OK) | |
return r; | |
uptr->u5 = 0; | |
iostatus |= PUNCH_FLAG; | |
return SCPE_OK; | |
} | |
t_stat | |
cdp_detach(UNIT * uptr) | |
{ | |
if (uptr->u5 & URCSTA_FULL) | |
sim_punch_card(uptr, NULL); | |
iostatus &= ~PUNCH_FLAG; | |
return sim_card_detach(uptr); | |
} | |
t_stat | |
cdp_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
{ | |
fprintf (st, "B303 Card Punch\n\n"); | |
fprintf (st, "The B303 Card Punch is only capable of punching text decks, binary decks\n"); | |
fprintf (st, "where not supported.\n"); | |
fprint_set_help(st, dptr); | |
fprint_show_help(st, dptr); | |
return SCPE_OK; | |
} | |
const char * | |
cdp_description(DEVICE *dptr) | |
{ | |
return "B303 Card Punch"; | |
} | |
#endif | |
/* Line printer routines | |
*/ | |
#if NUM_DEVS_LPR > 0 | |
t_stat | |
lpr_setlpp(UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
int i; | |
if (cptr == NULL) | |
return SCPE_ARG; | |
if (uptr == NULL) | |
return SCPE_IERR; | |
i = 0; | |
while(*cptr != '\0') { | |
if (*cptr < '0' || *cptr > '9') | |
return SCPE_ARG; | |
i = (i * 10) + (*cptr++) - '0'; | |
} | |
if (i < 20 || i > 100) | |
return SCPE_ARG; | |
uptr->capac = i; | |
uptr->u4 = 0; | |
return SCPE_OK; | |
} | |
t_stat | |
lpr_getlpp(FILE *st, UNIT *uptr, int32 v, CONST void *desc) | |
{ | |
if (uptr == NULL) | |
return SCPE_IERR; | |
fprintf(st, "linesperpage=%d", uptr->capac); | |
return SCPE_OK; | |
} | |
void | |
print_line(UNIT * uptr, int unit) | |
{ | |
/* Convert word record into column image */ | |
/* Check output type, if auto or text, try and convert record to bcd first */ | |
/* If failed and text report error and dump what we have */ | |
/* Else if binary or not convertable, dump as image */ | |
char out[150]; /* Temp conversion buffer */ | |
int i; | |
int chan = uptr->u5 & URCSTA_CHMASK; | |
if ((uptr->flags & (UNIT_ATT)) == 0) | |
return; /* attached? */ | |
if (uptr->u3 > 0) { | |
/* Try to convert to text */ | |
memset(out, 0, sizeof(out)); | |
/* Scan each column */ | |
for (i = 0; i < uptr->u3; i++) { | |
int bcd = lpr_data[unit].lbuff[i] & 077; | |
out[i] = con_to_ascii[bcd]; | |
} | |
/* Trim trailing spaces */ | |
for (--i; i > 0 && out[i] == ' '; i--) ; | |
out[i+1] = '\0'; | |
sim_debug(DEBUG_DETAIL, &lpr_dev, "lpr print %s\n", out); | |
if (uptr->u5 & (URCSTA_DOUBLE << URCSTA_CMD_V)) { | |
out[++i] = '\r'; | |
out[++i] = '\n'; | |
uptr->u4 ++; | |
} | |
out[++i] = '\r'; | |
out[++i] = '\n'; | |
uptr->u4++; | |
out[++i] = '\0'; | |
/* Print out buffer */ | |
sim_fwrite(&out, 1, i, uptr->fileref); | |
uptr->u5 &= ~URCSTA_EOF; | |
} | |
switch ((uptr->u5 >> URCSTA_CMD_V) & URCSTA_SKIP) { | |
case 0: /* No special skip */ | |
break; | |
case 1: | |
case 2: /* Skip to top of form */ | |
case 12: | |
uptr->u4 = uptr->capac+1; | |
break; | |
case 3: /* Even lines */ | |
if ((uptr->u4 & 1) == 1) { | |
sim_fwrite("\r", 1, 1, uptr->fileref); | |
sim_fwrite("\n", 1, 1, uptr->fileref); | |
uptr->u4++; | |
uptr->u5 &= ~URCSTA_EOF; | |
} | |
break; | |
case 4: /* Odd lines */ | |
if ((uptr->u4 & 1) == 0) { | |
sim_fwrite("\r", 1, 1, uptr->fileref); | |
sim_fwrite("\n", 1, 1, uptr->fileref); | |
uptr->u4++; | |
uptr->u5 &= ~URCSTA_EOF; | |
} | |
break; | |
case 5: /* Half page */ | |
while((uptr->u4 != (uptr->capac/2)) || | |
(uptr->u4 != (uptr->capac))) { | |
sim_fwrite("\r", 1, 1, uptr->fileref); | |
sim_fwrite("\n", 1, 1, uptr->fileref); | |
uptr->u4++; | |
if (((uint32)uptr->u4) > uptr->capac) { | |
uptr->u4 = 1; | |
break; | |
} | |
uptr->u5 &= ~URCSTA_EOF; | |
} | |
break; | |
case 6: /* 1/4 Page */ | |
while((uptr->u4 != (uptr->capac/4)) || | |
(uptr->u4 != (uptr->capac/2)) || | |
(uptr->u4 != (uptr->capac/2+uptr->capac/4)) || | |
(uptr->u4 != (uptr->capac))) { | |
sim_fwrite("\r", 1, 1, uptr->fileref); | |
sim_fwrite("\n", 1, 1, uptr->fileref); | |
uptr->u4++; | |
if (((uint32)uptr->u4) > uptr->capac) { | |
uptr->u4 = 1; | |
break; | |
} | |
uptr->u5 &= ~URCSTA_EOF; | |
} | |
break; | |
case 7: /* User defined, now 1 line */ | |
case 8: | |
case 9: | |
case 10: | |
case 11: | |
sim_fwrite("\r", 1, 1, uptr->fileref); | |
sim_fwrite("\n", 1, 1, uptr->fileref); | |
uptr->u4++; | |
break; | |
} | |
if (((uint32)uptr->u4) > uptr->capac) { | |
uptr->u4 = 1; | |
uptr->u5 |= URCSTA_EOF; | |
sim_fwrite("\f", 1, 1, uptr->fileref); | |
sim_fseek(uptr->fileref, 0, SEEK_CUR); | |
sim_debug(DEBUG_DETAIL, &lpr_dev, "lpr %d page\n", unit); | |
} | |
} | |
t_stat lpr_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc) | |
{ | |
UNIT *uptr; | |
int u; | |
if (dev == PRT1_DEV) | |
u = 0; | |
else if (dev == PRT2_DEV) | |
u = 1; | |
else | |
return SCPE_NXDEV; | |
uptr = &lpr_unit[u]; | |
/* Are we currently tranfering? */ | |
if (uptr->u5 & URCSTA_BUSY) | |
return SCPE_BUSY; | |
if ((uptr->flags & UNIT_ATT) == 0) | |
return SCPE_UNATT; | |
if (*wc == 0 && (cmd & URCSTA_INHIBIT) == 0) | |
*wc = (cmd & URCSTA_DIRECT) ? 17 : 15; | |
/* Remember not to drop the FULL */ | |
uptr->u5 &= ~((077 << URCSTA_CMD_V) | URCSTA_CHMASK); | |
uptr->u5 |= URCSTA_BUSY|chan; | |
uptr->u5 |= (cmd & (URCSTA_SKIP|URCSTA_SINGLE|URCSTA_DOUBLE)) | |
<< URCSTA_CMD_V; | |
uptr->u3 = 0; | |
sim_debug(DEBUG_CMD, &lpr_dev, "%d: Cmd WRS %d %02o %o\n", u, chan, | |
cmd & (URCSTA_SKIP|URCSTA_SINGLE|URCSTA_DOUBLE),uptr->u5); | |
sim_activate(uptr, 100); | |
return SCPE_OK; | |
} | |
/* Handle transfer of data for printer */ | |
t_stat | |
lpr_srv(UNIT *uptr) { | |
int chan = URCSTA_CHMASK & uptr->u5; | |
int u = (uptr - lpr_unit); | |
if (uptr->u5 & URCSTA_FULL) { | |
sim_debug(DEBUG_CMD, &lpr_dev, "lpr %d: done\n", u); | |
uptr->u5 &= ~URCSTA_FULL; | |
IAR |= (IRQ_3 << u); | |
} | |
/* Copy next column over */ | |
if ((uptr->u5 & URCSTA_BUSY) != 0) { | |
if(chan_read_char(chan, &lpr_data[u].lbuff[uptr->u3], 0)) { | |
/* Done waiting, print line */ | |
print_line(uptr, u); | |
memset(&lpr_data[u].lbuff[0], 0, 144); | |
uptr->u5 |= URCSTA_FULL; | |
uptr->u5 &= ~URCSTA_BUSY; | |
chan_set_wc(chan, (uptr->u3/8)); | |
chan_set_end(chan); | |
sim_activate(uptr, 20000); | |
return SCPE_OK; | |
} else { | |
sim_debug(DEBUG_DATA, &lpr_dev, "lpr %d: Char < %02o\n", u, | |
lpr_data[u].lbuff[uptr->u3]); | |
uptr->u3++; | |
} | |
sim_activate(uptr, 50); | |
} | |
return SCPE_OK; | |
} | |
t_stat | |
lpr_attach(UNIT * uptr, CONST char *file) | |
{ | |
t_stat r; | |
int u = (uptr - lpr_unit); | |
if ((r = attach_unit(uptr, file)) != SCPE_OK) | |
return r; | |
uptr->u5 = 0; | |
uptr->u4 = 0; | |
uptr->u3 = 0; | |
iostatus |= PRT1_FLAG << u; | |
return SCPE_OK; | |
} | |
t_stat | |
lpr_detach(UNIT * uptr) | |
{ | |
int u = (uptr - lpr_unit); | |
if (uptr->u5 & URCSTA_FULL) | |
print_line(uptr, u); | |
iostatus &= ~(PRT1_FLAG << u); | |
return detach_unit(uptr); | |
} | |
t_stat | |
lpr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
{ | |
fprintf (st, "B320 Line Printer\n\n"); | |
fprintf (st, "The system supports up to two line printers, the second one is disabled\n"); | |
fprintf (st, "by default. The B320 Line printer can be configured to any number of\n"); | |
fprintf (st, "lines per page with the:\n"); | |
fprintf (st, " sim> SET LPn LINESPERPAGE=n\n\n"); | |
fprintf (st, "The default is 59 lines per page. The Line Printer has the following\n"); | |
fprintf (st, "control tape attached.\n"); | |
fprintf (st, " Channel 1: Skip to top of page\n"); | |
fprintf (st, " Channel 2: Skip to top of page\n"); | |
fprintf (st, " Channel 3: Skip to next even line\n"); | |
fprintf (st, " Channel 4: Skip to next odd line\n"); | |
fprintf (st, " Channel 5: Skip to middle or top of page\n"); | |
fprintf (st, " Channel 6: Skip 1/4 of page\n"); | |
fprintf (st, " Channel 7: Skip one line\n"); | |
fprintf (st, " Channel 8: Skip one line\n"); | |
fprintf (st, " Channel 9: Skip one line\n"); | |
fprintf (st, " Channel 10: Skip one line\n"); | |
fprintf (st, " Channel 11: Skip one line\n"); | |
fprintf (st, " Channel 12: Skip to top of page\n"); | |
fprint_set_help(st, dptr); | |
fprint_show_help(st, dptr); | |
return SCPE_OK; | |
} | |
const char * | |
lpr_description(DEVICE *dptr) | |
{ | |
return "B320 Line Printer"; | |
} | |
#endif | |
#if NUM_DEVS_CON > 0 | |
/* | |
* Console printer routines. | |
*/ | |
t_stat | |
con_ini(DEVICE *dptr) { | |
UNIT *uptr = &con_unit[0]; | |
uptr->u5 = 0; | |
iostatus |= SPO_FLAG; | |
if (!sim_is_active(uptr)) | |
sim_activate(uptr, 1000); | |
return SCPE_OK; | |
} | |
t_stat | |
con_cmd(uint16 cmd, uint16 dev, uint8 chan, uint16 *wc) | |
{ | |
UNIT *uptr = &con_unit[0]; | |
/* Are we currently tranfering? */ | |
if (uptr->u5 & (URCSTA_READ|URCSTA_FILL|URCSTA_BUSY|URCSTA_INPUT)) | |
return SCPE_BUSY; | |
if (cmd & URCSTA_READ) { | |
if (uptr->u5 & (URCSTA_INPUT|URCSTA_FILL)) | |
return SCPE_BUSY; | |
/* Activate input so we can get response */ | |
uptr->u5 = 0; | |
uptr->u5 |= URCSTA_INPUT|chan; | |
sim_putchar('I'); | |
sim_putchar(' '); | |
sim_debug(DEBUG_CMD, &con_dev, ": Cmd RDS\n"); | |
uptr->u3 = 0; | |
} else { | |
if (uptr->u5 & (URCSTA_INPUT|URCSTA_FILL)) | |
return SCPE_BUSY; | |
sim_putchar('R'); | |
sim_putchar(' '); | |
sim_debug(DEBUG_CMD, &con_dev, ": Cmd WRS\n"); | |
uptr->u5 = 0; | |
uptr->u5 |= URCSTA_FILL|chan; | |
uptr->u3 = 0; | |
} | |
return SCPE_OK; | |
} | |
/* Handle transfer of data for printer */ | |
t_stat | |
con_srv(UNIT *uptr) { | |
t_stat r; | |
uint8 ch; | |
int chan = uptr->u5 & URCSTA_CHMASK; | |
uptr->u5 &= ~URCSTA_BUSY; /* Clear busy */ | |
/* Copy next column over */ | |
if (uptr->u5 & URCSTA_FILL) { | |
if(chan_read_char(chan, &ch, 0)) { | |
sim_putchar('\r'); | |
sim_putchar('\n'); | |
sim_debug(DEBUG_EXP, &con_dev, "\n\r"); | |
uptr->u5 &= ~URCSTA_FILL; | |
chan_set_end(chan); | |
} else { | |
ch &= 077; | |
sim_debug(DEBUG_EXP, &con_dev, "%c", con_to_ascii[ch]); | |
sim_putchar((int32)con_to_ascii[ch]); | |
} | |
} | |
if (uptr->u5 & URCSTA_READ) { | |
ch = con_data[0].ibuff[con_data[0].outptr++]; | |
if(chan_write_char(chan, &ch, | |
(con_data[0].inptr == con_data[0].outptr))) { | |
sim_putchar('\r'); | |
sim_putchar('\n'); | |
sim_debug(DEBUG_EXP, &con_dev, "\n\r"); | |
uptr->u5 &= ~URCSTA_READ; | |
chan_set_end(chan); | |
} | |
} | |
r = sim_poll_kbd(); | |
if (r & SCPE_KFLAG) { | |
ch = r & 0377; | |
if (uptr->u5 & URCSTA_INPUT) { | |
/* Handle end of buffer */ | |
switch (ch) { | |
case 033: | |
con_data[0].inptr = 0; | |
/* Fall through */ | |
case '\r': | |
case '\n': | |
uptr->u5 &= ~URCSTA_INPUT; | |
uptr->u5 |= URCSTA_READ; | |
break; | |
case '\b': | |
case 0x7f: | |
if (con_data[0].inptr != 0) { | |
con_data[0].inptr--; | |
sim_putchar('\b'); | |
sim_putchar(' '); | |
sim_putchar('\b'); | |
} | |
break; | |
default: | |
if (con_data[0].inptr < sizeof(con_data[0].ibuff)) { | |
ch = ascii_to_con[0177&ch]; | |
if (ch == 0xff) { | |
sim_putchar('\007'); | |
break; | |
} | |
sim_putchar((int32)con_to_ascii[ch]); | |
con_data[0].ibuff[con_data[0].inptr++] = ch; | |
} | |
break; | |
} | |
} else { | |
if (ch == 033) { | |
IAR |= IRQ_2; | |
con_data[0].inptr = 0; | |
con_data[0].outptr = 0; | |
} | |
} | |
} | |
if (uptr->u5 & (URCSTA_FILL|URCSTA_READ)) | |
sim_activate(uptr, 1000); | |
else | |
sim_clock_coschedule_tmr (con_unit, TMR_RTC, 1); | |
return SCPE_OK; | |
} | |
t_stat | |
con_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
{ | |
fprintf (st, "Supervisory Printer\n\n"); | |
fprintf (st, "This is the interface from the operator to the system. The printer\n"); | |
fprintf (st, "operated in a half duplex mode. To request the system to accept input\n"); | |
fprintf (st, "press the <esc> key and wait until the system responds with a line with\n"); | |
fprintf (st, "I as the first character. When you have finished typing your line, press\n"); | |
fprintf (st, "return or enter key. Backspace will delete the last character.\n"); | |
fprintf (st, "All responses from the system are prefixed with a R and blank as the\n"); | |
fprintf (st, "first character\n"); | |
return SCPE_OK; | |
} | |
const char * | |
con_description(DEVICE *dptr) | |
{ | |
return "Supervisory Printer"; | |
} | |
#endif | |