blob: 28969c7196d2dec13bd38af54ba4940c4e2a454c [file] [log] [blame] [raw]
/* i7090_lpr.c: IBM 7090 Standard line printer.
Copyright (c) 2005-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 line printer that all 70xx systems have.
For WRS read next 24 words and fill print buffer.
Row 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, 11, 12
For RDS read rows 9, 8, 7, 6, 5, 4, 3, 2, 1,
Echo 8|4
read row 10
Echo 8|3
read row 11
Echo 9
read row 12
Echo 8, 7, 6, 5, 4, 3, 2, 1
*/
#include "i7090_defs.h"
#include "sim_console.h"
#include "sim_card.h"
#ifdef NUM_DEVS_LPR
#define UNIT_LPR UNIT_ATTABLE | UNIT_DISABLE
#define ECHO (1 << UNIT_V_LOCAL)
/* std devices. data structures
chan_dev Channel device descriptor
chan_unit Channel unit descriptor
chan_reg Channel register list
chan_mod Channel modifiers list
*/
/* Output selection is stored in u3 */
/* Line count is stored in u4 */
/* Device status information stored in u5 */
/* Position is stored in u6 */
#define LPRSTA_RCMD 002000 /* Read command */
#define LPRSTA_WCMD 004000 /* Write command */
#define LPRSTA_EOR 010000 /* Hit end of record */
#define LPRSTA_BINMODE 020000 /* Line printer started in bin mode */
#define LPRSTA_CHANGE 040000 /* Turn DEV_WRITE on */
#define LPRSTA_COL72 0100000 /* Mask to last column printed */
#define LPRSTA_IMAGE 0200000 /* Image to print */
struct _lpr_data
{
t_uint64 wbuff[24]; /* Line buffer */
char lbuff[74]; /* Output line buffer */
}
lpr_data[NUM_DEVS_LPR];
uint32 lpr_cmd(UNIT *, uint16, uint16);
t_stat lpr_srv(UNIT *);
void lpr_ini(UNIT *, t_bool);
t_stat lpr_reset(DEVICE *);
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 *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *lpr_description (DEVICE *dptr);
extern char six_to_ascii[64];
UNIT lpr_unit[] = {
#if NUM_DEVS_LPR > 1
{UDATA(&lpr_srv, UNIT_S_CHAN(CHAN_A) | UNIT_LPR | ECHO, 55)}, /* A */
#endif
#if NUM_DEVS_LPR > 2
{UDATA(&lpr_srv, UNIT_S_CHAN(CHAN_C) | UNIT_LPR, 55)}, /* B */
#endif
#if NUM_DEVS_LPR > 3
{UDATA(&lpr_srv, UNIT_S_CHAN(CHAN_E) | UNIT_LPR | UNIT_DIS, 55)}, /* C */
#endif
{UDATA(&lpr_srv, UNIT_S_CHAN(CHAN_CHPIO) | UNIT_LPR, 55)}, /* 704 */
};
MTAB lpr_mod[] = {
{ECHO, 0, NULL, "NOECHO", NULL, NULL, NULL, "Done echo to console"},
{ECHO, ECHO, "ECHO", "ECHO", NULL, NULL, NULL, "Echo output to console"},
{MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LINESPERPAGE", "LINESPERPAGE",
&lpr_setlpp, &lpr_getlpp, NULL, "Number of lines per page"},
#if NUM_CHAN != 1
{MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN", &set_chan,
&get_chan, NULL},
#endif
{0}
};
DEVICE lpr_dev = {
"LP", lpr_unit, NULL, lpr_mod,
NUM_DEVS_LPR, 8, 15, 1, 8, 36,
NULL, NULL, &lpr_reset, NULL, &lpr_attach, &lpr_detach,
&lpr_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &lpr_help, NULL, NULL, &lpr_description
};
/* Line printer routines
*/
/*
* Line printer routines
*/
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;
}
t_stat
print_line(UNIT * uptr, int chan, 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 */
uint16 buff[80]; /* Temp conversion buffer */
int i, j;
int outsel = uptr->u3;
int prt_flg = 1;
if ((uptr->flags & (UNIT_ATT | ECHO)) == 0)
return SCPE_UNATT; /* attached? */
if (outsel & PRINT_3) {
if (uptr->flags & UNIT_ATT)
sim_fwrite("\r\n", 1, 2, uptr->fileref);
if (uptr->flags & ECHO) {
sim_putchar('\r');
sim_putchar('\n');
}
uptr->u5 &= ~LPRSTA_COL72;
uptr->u4++;
}
if (outsel & PRINT_4) {
if (uptr->flags & UNIT_ATT)
sim_fwrite("\r\n\r\n", 1, 4, uptr->fileref);
if (uptr->flags & ECHO) {
sim_putchar('\r');
sim_putchar('\n');
sim_putchar('\r');
sim_putchar('\n');
}
uptr->u5 &= ~LPRSTA_COL72;
uptr->u4++;
uptr->u4++;
}
/* Try to convert to text */
memset(buff, 0, sizeof(buff));
/* Bit flip into temp buffer */
for (i = 0; i < 24; i++) {
int bit = 1 << (i / 2);
t_uint64 mask = 1;
t_uint64 wd = 0;
int b = 36 * (i & 1);
int col;
wd = lpr_data[unit].wbuff[i];
for (col = 35; col >= 0; mask <<= 1, col--) {
if (wd & mask)
buff[col + b] |= bit;
}
lpr_data[unit].wbuff[i] = 0;
}
/* Space out printer based on last output */
if ((outsel & PRINT_9)) {
/* Trim trailing spaces */
for (j = 72; j > 0 && lpr_data[unit].lbuff[j] == ' '; j--) ;
j++;
if ((uptr->u5 & LPRSTA_COL72) == 0)
j = 0;
for (i = j; i < 72; i++) {
if (uptr->flags & UNIT_ATT)
sim_fwrite(" ", 1, 1, uptr->fileref);
if (uptr->flags & ECHO)
sim_putchar(' ');
}
} else {
if (uptr->flags & UNIT_ATT)
sim_fwrite("\n\r", 1, 2, uptr->fileref);
if (uptr->flags & ECHO) {
sim_putchar('\n');
sim_putchar('\r');
}
uptr->u4++;
uptr->u5 &= ~LPRSTA_COL72;
}
/* Scan each column */
for (i = 0; i < 72; i++) {
int bcd = sim_hol_to_bcd(buff[i]);
if (bcd == 0x7f)
lpr_data[unit].lbuff[i] = '{';
else {
if (bcd == 020)
bcd = 10;
if (uptr->u5 & LPRSTA_BINMODE) {
char ch = (buff[i] != 0) ? '1' : ' ';
lpr_data[unit].lbuff[i] = ch;
} else
lpr_data[unit].lbuff[i] = sim_six_to_ascii[bcd];
}
}
sim_debug(DEBUG_DETAIL, &lpr_dev, "WRS unit=%d %3o [%72s]\n", unit,
outsel >> 3, &lpr_data[unit].lbuff[0]);
/* Trim trailing spaces */
for (j = 71; j > 0 && lpr_data[unit].lbuff[j] == ' '; j--) ;
/* Print out buffer */
if (uptr->flags & UNIT_ATT)
sim_fwrite(lpr_data[unit].lbuff, 1, j+1, uptr->fileref);
if (uptr->flags & ECHO) {
for(i = 0; i <= j; i++)
sim_putchar(lpr_data[unit].lbuff[i]);
}
uptr->u5 |= LPRSTA_COL72;
/* Put output to column where we left off */
if (outsel != 0) {
uptr->u5 &= ~LPRSTA_COL72;
}
/* Space printer */
if (outsel & PRINT_2) {
if (uptr->flags & UNIT_ATT)
sim_fwrite("\r\n", 1, 2, uptr->fileref);
if (uptr->flags & ECHO) {
sim_putchar('\r');
sim_putchar('\n');
}
uptr->u4++;
}
if (outsel & PRINT_1) {
while (uptr->u4 < (int32)uptr->capac) {
if (uptr->flags & UNIT_ATT)
sim_fwrite("\r\n", 1, 2, uptr->fileref);
if (uptr->flags & ECHO) {
sim_putchar('\r');
sim_putchar('\n');
}
uptr->u4++;
}
}
if (uptr->u4 >= (int32)uptr->capac) {
uptr->u4 -= (int32)uptr->capac;
dev_pulse[chan] |= PRINT_I;
}
return SCPE_OK;
}
uint32 lpr_cmd(UNIT * uptr, uint16 cmd, uint16 dev)
{
int chan = UNIT_G_CHAN(uptr->flags);
int u = (uptr - lpr_unit);
int i;
/* Check if valid */
if ((dev & 03) == 0 || (dev & 03) == 3)
return SCPE_NODEV;
/* Check if attached */
if ((uptr->flags & (UNIT_ATT | ECHO)) == 0) {
chan_set_error(chan);
sim_debug(DEBUG_EXP, &lpr_dev, "unit=%d not ready\n", u);
return SCPE_IOERR;
}
/* Check if still active */
if (uptr->u5 & URCSTA_CMD) {
sim_debug(DEBUG_EXP, &lpr_dev, "unit=%d busy\n", u);
return SCPE_BUSY;
}
/* Ok, issue command if correct */
if (cmd == IO_WRS || cmd == IO_RDS) {
/* Start device */
if (((uptr->u5 & (URCSTA_ON | URCSTA_IDLE)) ==
(URCSTA_ON | URCSTA_IDLE)) && uptr->wait <= 30) {
uptr->wait += 85; /* Wait for next latch point */
} else
uptr->wait = 330; /* Startup delay */
for (i = 0; i < 24; lpr_data[u].wbuff[i++] = 0) ;
uptr->u6 = 0;
uptr->u5 &= ~(LPRSTA_WCMD | LPRSTA_RCMD | URCSTA_WRITE | URCSTA_READ);
uptr->u3 = 0;
dev_pulse[chan] = 0;
if (cmd == IO_WRS) {
sim_debug(DEBUG_CMD, &lpr_dev, "WRS %o unit=%d %d\n", dev, u, uptr->wait);
uptr->u5 |= LPRSTA_WCMD | URCSTA_CMD | URCSTA_WRITE;
} else {
sim_debug(DEBUG_CMD, &lpr_dev, "RDS %o unit=%d %d\n", dev, u, uptr->wait);
uptr->u5 |= LPRSTA_RCMD | URCSTA_CMD | URCSTA_READ;
}
if ((dev & 03) == 2)
uptr->u5 |= LPRSTA_BINMODE;
else
uptr->u5 &= ~LPRSTA_BINMODE;
chan_set_sel(chan, 1);
chan_clear_status(chan);
sim_activate(uptr, us_to_ticks(1000)); /* activate */
return SCPE_OK;
} else {
chan_set_attn(chan);
}
return SCPE_IOERR;
}
t_stat lpr_srv(UNIT * uptr)
{
int chan = UNIT_G_CHAN(uptr->flags);
int u = (uptr - lpr_unit);
int pos;
int r;
int eor = 0;
/* Channel has disconnected, abort current line. */
if (uptr->u5 & URCSTA_CMD && chan_stat(chan, DEV_DISCO)) {
print_line(uptr, chan, u);
uptr->u5 &= ~(URCSTA_WRITE | URCSTA_READ | URCSTA_CMD | LPRSTA_EOR | LPRSTA_CHANGE);
uptr->u6 = 0;
chan_clear(chan, DEV_WEOR | DEV_SEL);
sim_debug(DEBUG_CHAN, &lpr_dev, "unit=%d disconnect\n", u);
return SCPE_OK;
}
/* If change requested, do that first */
if (uptr->u5 & LPRSTA_CHANGE) {
/* Wait until word read by CPU or timeout */
if (chan_test(chan, DEV_FULL)) {
uptr->wait -= 50;
if (uptr->wait == 50)
uptr->u5 &= ~LPRSTA_CHANGE;
sim_activate(uptr, us_to_ticks(100));
return SCPE_OK;
} else {
chan_set(chan, DEV_WRITE);
sim_activate(uptr, uptr->wait);
uptr->u5 &= ~LPRSTA_CHANGE;
uptr->wait = 0;
return SCPE_OK;
}
}
/* Check to see if we have timed out */
if (uptr->wait != 0) {
uptr->wait--;
/* If at end of record and channel is still active, do another print */
if (((uptr->u5 & (URCSTA_IDLE|URCSTA_CMD|URCSTA_WRITE|URCSTA_READ|
URCSTA_ON)) == (URCSTA_IDLE|URCSTA_CMD|URCSTA_ON))
&& uptr->wait == 1 && chan_test(chan, STA_ACTIVE)) {
/* Restart same command */
uptr->u5 |= (URCSTA_WRITE | URCSTA_READ) & (uptr->u5 >> 5);
uptr->u6 = 0;
chan_set(chan, DEV_WRITE);
sim_debug(DEBUG_CHAN, &lpr_dev, "unit=%d restarting\n", u);
}
sim_activate(uptr, us_to_ticks(1000)); /* activate */
return SCPE_OK;
}
/* If no request, go to idle mode */
if ((uptr->u5 & (URCSTA_READ | URCSTA_WRITE)) == 0) {
if ((uptr->u5 & (URCSTA_IDLE | URCSTA_ON)) == (URCSTA_IDLE | URCSTA_ON)) {
uptr->wait = 85; /* Delay 85ms */
uptr->u5 &= ~URCSTA_IDLE; /* Not running */
sim_activate(uptr, us_to_ticks(1000));
} else {
uptr->wait = 330; /* Delay 330ms */
uptr->u5 &= ~URCSTA_ON; /* Turn motor off */
}
return SCPE_OK;
}
/* Motor is on and up to speed */
uptr->u5 |= URCSTA_ON;
uptr->u5 &= ~URCSTA_IDLE;
pos = uptr->u6;
uptr->u3 |= dev_pulse[chan] & PRINT_M;
/* Check if he write out last data */
if (uptr->u5 & URCSTA_READ) {
int wrow = 0;
t_uint64 wd = 0;
int action = 0;
/* Case 0: Read word from MF memory, DEV_WRITE=1 */
/* Case 1: Read word from MF memory, write echo back */
/* Case 2: Write echoback, after gone switch to read */
/* Case 3: Write echoback */
/* Case 4: No update, DEV_WRITE=1 */
eor = (uptr->u5 & LPRSTA_BINMODE) ? 1 : 0;
switch (pos) {
case 46:
print_line(uptr, chan, u);
pos = 0;
/* Fall through */
case 0:
case 1: /* Row 9 */
case 2:
case 3: /* Row 8 */
case 4:
case 5: /* Row 7 */
case 6:
case 7: /* Row 6 */
case 8:
case 9: /* Row 5 */
case 10:
case 11: /* Row 4 */
case 12:
case 13: /* Row 3 */
case 14:
case 15: /* Row 2 */
case 16: /* Row 1R */
wrow = pos;
break;
case 17: /* Row 1L and start Echo */
wrow = pos;
action = 1;
break;
case 18: /* Echo 8-4 R */
wd = lpr_data[u].wbuff[2];
wd &= lpr_data[u].wbuff[10];
action = 2;
wrow = pos;
break;
case 19: /* Echo 8-4 L */
wd = lpr_data[u].wbuff[3];
wd &= lpr_data[u].wbuff[11];
action = 3;
wrow = pos;
break;
case 20: /* Row 10 R */
wrow = 18;
break;
case 21: /* Row 10 L */
wrow = 19;
action = 1;
break;
case 22: /* Echo 8-3 */
/* Fill for echo back */
wd = lpr_data[u].wbuff[12];
wd &= lpr_data[u].wbuff[2];
action = 2;
wrow = pos;
break;
case 23:
wd = lpr_data[u].wbuff[13];
wd &= lpr_data[u].wbuff[3];
action = 3;
wrow = pos;
break;
case 24: /* Row 11 R */
wrow = 20;
break;
case 25: /* Row 11 L */
wrow = 21;
action = 1;
break;
case 26: /* Echo 9 */
wd = lpr_data[u].wbuff[0];
action = 2;
wrow = pos;
break;
case 27:
wd = lpr_data[u].wbuff[1];
action = 3;
wrow = pos;
break;
case 28:
wrow = 22;
break;
case 29: /* Row 12 */
wrow = 23;
action = 1;
break;
case 45: /* Echo 1 */
eor = 1;
/* Fall through */
case 30:
case 31: /* Echo 8 */
case 32:
case 33: /* Echo 7 */
case 34:
case 35: /* Echo 6 */
case 36:
case 37: /* Echo 5 */
case 38:
case 39: /* Echo 4 */
case 40:
case 41: /* Echo 3 */
case 42:
case 43: /* Echo 2 */
case 44: /* Echo 1 */
wrow = pos - 28;
wd = lpr_data[u].wbuff[wrow];
action = 2;
break;
}
if (action == 0 || action == 1) {
/* If reading grab next word */
r = chan_read(chan, &lpr_data[u].wbuff[wrow], 0);
sim_debug(DEBUG_DATA, &lpr_dev, "print read row < %d %d %012llo eor=%d\n",
pos, wrow, lpr_data[u].wbuff[wrow], 0);
if (action == 1)
chan_clear(chan, DEV_WRITE);
} else { /* action == 2 || action == 3 */
/* Place echo data in buffer */
sim_debug(DEBUG_DATA, &lpr_dev, "print read row > %d %d %012llo eor=%d\n",
pos, wrow, wd, eor);
r = chan_write(chan, &wd, 0);
/* Change back to reading */
if (action == 3) {
uptr->wait = 650;
uptr->u6 = ++pos;
uptr->u5 &= ~(LPRSTA_EOR);
uptr->u5 |= LPRSTA_CHANGE;
sim_activate(uptr, us_to_ticks(100));
return SCPE_OK;
}
}
} else {
eor = (pos == 23 || (uptr->u5 & LPRSTA_BINMODE && pos == 1)) ? 1 : 0;
if (pos == 24 || (uptr->u5 & LPRSTA_BINMODE && pos == 2)) {
print_line(uptr, chan, u);
pos = 0;
}
r = chan_read(chan, &lpr_data[u].wbuff[pos], 0);
sim_debug(DEBUG_DATA, &lpr_dev, "print row %d %012llo %d\n", pos,
lpr_data[u].wbuff[pos], eor);
}
uptr->u6 = pos + 1;
switch (r) {
case END_RECORD:
uptr->wait = 100; /* Print wheel gap */
uptr->u5 |= LPRSTA_EOR | URCSTA_IDLE;
uptr->u5 &= ~(URCSTA_WRITE | URCSTA_READ);
chan_set(chan, DEV_REOR);
break;
case DATA_OK:
if (eor) {
uptr->wait = 100; /* Print wheel gap */
uptr->u5 |= LPRSTA_EOR | URCSTA_IDLE;
uptr->u5 &= ~(URCSTA_WRITE | URCSTA_READ);
chan_set(chan, DEV_REOR);
} else {
uptr->wait = 0;
uptr->u5 &= ~(LPRSTA_EOR);
sim_activate(uptr, (pos & 1) ? us_to_ticks(500) : us_to_ticks(16000));
return SCPE_OK;
}
break;
case TIME_ERROR:
chan_set_attn(chan);
chan_set(chan, DEV_REOR);
uptr->wait = 13 * (12 - (pos / 2)) + 85;
uptr->u5 &= ~(URCSTA_READ | URCSTA_WRITE);
uptr->u5 |= URCSTA_IDLE;
break;
}
sim_activate(uptr, us_to_ticks(1000));
return SCPE_OK;
}
void
lpr_ini(UNIT * uptr, t_bool f)
{
int u = (uptr - lpr_unit);
uptr->u3 = 0;
uptr->u4 = 0;
uptr->u5 = 0;
memset(&lpr_data[u].lbuff, ' ', sizeof(lpr_data[u].lbuff));
}
t_stat
lpr_reset(DEVICE * dptr)
{
return SCPE_OK;
}
t_stat
lpr_attach(UNIT * uptr, CONST char *file)
{
t_stat r;
if ((r = attach_unit(uptr, file)) != SCPE_OK)
return r;
uptr->u5 = 0;
return SCPE_OK;
}
t_stat
lpr_detach(UNIT * uptr)
{
int u = (uptr - lpr_unit);
return detach_unit(uptr);
}
t_stat
lpr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
const char *cpu = cpu_description(&cpu_dev);
extern void fprint_attach_help_ex (FILE *st, DEVICE *dptr, t_bool silent);
fprintf (st, "%s\n\n", lpr_description(dptr));
#if NUM_DEVS_LPR > 3
fprintf (st, "The %s supports up to four line printers", cpu);
#elif NUM_DEVS_LPR > 2
fprintf (st, "The %s supports up to three line printers", cpu);
#elif NUM_DEVS_LPR > 1
fprintf (st, "The %s supports up to two line printers", cpu);
#elif NUM_DEVS_LPR > 0
fprintf (st, "The %s supports one line printer", cpu);
#endif
fprintf (st, "by default. The Line printer can\n");
fprintf (st, "The printer acted as the console printer:\n\n");
fprintf (st, " sim> SET %s ECHO\n\n", dptr->name);
fprintf (st, "Causes all output sent to printer to also go to console.\n");
help_set_chan_type(st, dptr, "Line printers");
fprint_set_help(st, dptr);
fprint_show_help(st, dptr);
return SCPE_OK;
}
const char *
lpr_description(DEVICE *dptr)
{
return "716 Line Printer";
}
#endif