SEL32: Update Ping and ICMP support code to use correct packet size. SEL32: Update SetupNet script to support latest Fedora release. SEL32: Improve disk write speed. SEL32: Add .tap file reassignent support in sel32_mt.c.
672 lines
29 KiB
C
672 lines
29 KiB
C
/* sel32_lpr.c: SEL32 922x & 924x High Speed Line Printer
|
|
|
|
Copyright (c) 2018-2023, James C. Bevier
|
|
Portions provided by Richard Cornwell and other SIMH contributers
|
|
|
|
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
|
|
JAMES C. BEVIER 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.
|
|
|
|
These units each buffer one record in local memory and signal
|
|
ready when the buffer is full or empty. The channel must be
|
|
ready to recieve/transmit data when they are activated since
|
|
they will transfer their block during chan_cmd. All data is
|
|
transmitted as BCD characters.
|
|
*/
|
|
|
|
#include "sel32_defs.h"
|
|
#include <ctype.h>
|
|
|
|
/**** COMMANDS TO PRINT BUFFER THEN DO FORMS CONTROL */
|
|
/*
|
|
LP.CMD1 DATAW X'01000000' PRINT ONLY - NO FORMS CONTROL
|
|
LP.CMD2 DATAW X'05000000' PRINT BUFFER, <CR>
|
|
LP.CMD3 DATAW X'15000000' PRINT BUFFER, <LF>
|
|
LP.CMD4 DATAW X'25000000' PRINT BUFFER, <LF> <LF>
|
|
LP.CMD5 DATAW X'35000000' PRINT BUFFER, <LF> <LF> <LF>
|
|
LP.CMD6 DATAW X'45000000' PRINT BUFFER, <FF>
|
|
LP.CMD7 DATAW X'85000000' PRINT BUFFER, <CR>, THEN CLEAR BUFFER
|
|
*
|
|
**** COMMANDS TO DO FORMS CONTROL AND THEN PRINT BUFFER.
|
|
**** NOTE: THESE COMMANDS ARE ARRANGED SO THAT BY USING THE INDEX
|
|
**** OF THE FORMS CONTROL TABLE AND A OFFSET INTO THIS TABLE
|
|
**** YOU CAN GET THE APPROPRIATE COMMAND FOR THE FC CHAR.
|
|
*
|
|
LP.CMD8 DATAW X'0D000000' <CR>, PRINT BUFFER, <CR>
|
|
LP.CMD9 DATAW X'4D000000' <FF>, PRINT BUFFER, <CR>
|
|
DATAW X'4D000000' <FF>, PRINT BUFFER, <CR>
|
|
LP.CMD10 DATAW X'2D000000' <LF> <LF>, PRINT BUFFER <CR>
|
|
LP.CMD11 DATAW X'1D000000' <LF>, PRINT BUFFER, <CR>
|
|
LP.CMD12 DATAW X'3D000000' <LF> <LF> <LF>, PRINT, <CR> (SPARE)
|
|
*
|
|
**** COMMANDS THAT DO ONLY FORMS CONTROL (NO PRINTING)
|
|
*
|
|
LP.CMD13 DATAW X'03000000' <CR>
|
|
LP.CMD14 DATAW X'47000000' <FF>
|
|
DATAW X'47000000' <FF>
|
|
LP.CMD15 DATAW X'27000000' <LF> <LF>
|
|
LP.CMD16 DATAW X'17000000' <LF>
|
|
LP.CMD17 DATAW X'37000000' <LF> <LF> <LF> (SPARE)
|
|
*
|
|
** LINE PRINTER FORMS CONTROL TABLE
|
|
*
|
|
LPFCTBL EQU $
|
|
2B DATAB C'+' 0x2b FORMS CONTROL FOR CR THEN PRINT
|
|
31 DATAB C'1' 0x31 FORMS CONTROL FOR FF THEN PRINT
|
|
2D DATAB C'-' 0x2d FORMS CONTROL FOR FF THEN PRINT
|
|
30 DATAB C'0' 0x30 FORMS CONTROL FOR 2 LF'S THEN PRINT
|
|
20 DATAB C' ' 0x20 FORMS CONTROL FOR LF THEN PRINT
|
|
*/
|
|
|
|
#if NUM_DEVS_LPR > 0
|
|
|
|
#define UNIT_LPR UNIT_ATTABLE|UNIT_DISABLE|UNIT_SEQ
|
|
|
|
#define CMD u3
|
|
/* u3 holds command and status information */
|
|
|
|
#define LPR_INCH 0x00 /* INCH command */
|
|
/* print buffer then CC commands */
|
|
#define LPR_PBNCC 0x01 /* print only, no forms control */
|
|
#define LPR_PBC 0x05 /* print buffer, then <CR> */
|
|
#define LPR_PBL 0x15 /* print buffer, then <LF> */
|
|
#define LPR_PBLL 0x25 /* print buffer, then <LF> <LF> */
|
|
#define LPR_PBLLL 0x35 /* print buffer, then <LF> <LF> <LF> */
|
|
#define LPR_PBF 0x45 /* print buffer, then <FF> */
|
|
#define LPR_PBCCB 0x85 /* print buffer, then <CR> <CLEAR BUFFER> */
|
|
/* Do CC then print commands then CC */
|
|
#define LPR_CPBC 0x0d /* <CR> print buffer <CR> */
|
|
#define LPR_LPBC 0x1d /* <LF> print buffer <CR> */
|
|
#define LPR_LLPBC 0x2d /* <LF> <LF> print buffer <CR> */
|
|
#define LPR_LLLPBC 0x3d /* <LF> <LF> <LF> print buffer <CR> */
|
|
#define LPR_FPBC 0x4d /* <FF> print buffer <CR> */
|
|
/* Do CC only, no print */
|
|
#define LPR_NPC 0x03 /* <CR> */
|
|
#define LPR_NPL 0x17 /* <LF> */
|
|
#define LPR_NPLL 0x27 /* <LF> <LF> */
|
|
#define LPR_NPLLL 0x37 /* <LF> <LF> <LF> */
|
|
#define LPR_NPF 0x47 /* <FF> */
|
|
|
|
#define LPR_SNS 0x04 /* Sense command */
|
|
#define LPR_CMDMSK 0xff /* Mask command part. */
|
|
#define LPR_FULL 0x100 /* Buffer full (BOF) */
|
|
#define LPR_PRE 0x200 /* Apply pre CC */
|
|
#define LPR_POST 0x400 /* Apply post CC */
|
|
|
|
#define CNT u4
|
|
/* u4 holds current line count */
|
|
|
|
/* channel status bits 13-15 */
|
|
/* 0x0c - normal completion - OK & carriage is not at bottom of form */
|
|
/* 0x0e - Unit check - Sense error present with SNS_PRINTF status */
|
|
/* 0x0d - Unit exception - OK & carriage is at bottom of form */
|
|
|
|
#define SNS u5
|
|
/* in u5 packs sense byte 0,1 and 3 */
|
|
/* Sense byte 0 */
|
|
#define SNS_CMDREJ 0x80000000 /* Command reject + Unit check */
|
|
#define SNS_OPRINTR 0x40000000 /* Operator intervention required */
|
|
/* reason code is in status byte 1 */
|
|
#define SNS_BUSCHK 0x20000000 /* Parity error on bus */
|
|
/* bits 3-7 are unused */
|
|
#define SNS_NU3 0x10000000 /* Not used */
|
|
#define SNS_NU4 0x08000000 /* Not used */
|
|
#define SNS_NU5 0x04000000 /* Not used */
|
|
#define SNS_NU6 0x02000000 /* Not used */
|
|
#define SNS_NU7 0x01000000 /* Not used */
|
|
/* Sense byte 1 */
|
|
#define SNS_DEVVFY 0x00800000 /* Device Verify Interface Cable Disconnected */
|
|
/* plus SNS_OPRINTR */
|
|
#define SNS_DEVPWR 0x00400000 /* Device Powered Off + SNS_OPRINTR */
|
|
#define SNS_DEVCHK 0x00200000 /* Device Check - Not Ready + SNS_OPRINTR */
|
|
#define SNS_OFFLINE 0x00100000 /* Off Line + SNS_OPRINTR */
|
|
#define SNS_NU2 0x00080000 /* Not used */
|
|
#define SNS_NU1 0x00040000 /* Not used */
|
|
#define SNS_BEGOF 0x00020000 /* Beginning of form */
|
|
#define SNS_BOF 0x00010000 /* Bottom of form on printer */
|
|
/* Sense byte 2-3 have remaining channel cnt of zero */
|
|
|
|
#define CBP u6
|
|
/* u6 hold buffer position */
|
|
|
|
/* std devices. data structures
|
|
lpr_dev Line Printer device descriptor
|
|
lpr_unit Line Printer unit descriptor
|
|
lpr_reg Line Printer register list
|
|
lpr_mod Line Printer modifiers list
|
|
*/
|
|
|
|
struct _lpr_data
|
|
{
|
|
uint8 lbuff[160]; /* Output line buffer */
|
|
};
|
|
|
|
struct _lpr_data lpr_data[NUM_DEVS_LPR];
|
|
|
|
/* forward definitions */
|
|
t_stat lpr_preio(UNIT *uptr, uint16 chan);
|
|
t_stat lpr_startcmd(UNIT *, uint16, uint8);
|
|
void lpr_ini(UNIT *, t_bool);
|
|
t_stat lpr_haltio(UNIT *uptr);
|
|
t_stat lpr_rschnlio(UNIT *uptr);
|
|
t_stat lpr_srv(UNIT *);
|
|
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 *, DEVICE *, UNIT *, int32, const char *);
|
|
const char *lpr_description (DEVICE *dptr);
|
|
|
|
/* channel program information */
|
|
CHANP lpr_chp[NUM_DEVS_LPR] = {0};
|
|
|
|
MTAB lpr_mod[] = {
|
|
{MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LINESPERPAGE", "LINESPERPAGE",
|
|
&lpr_setlpp, &lpr_getlpp, NULL, "Number of lines per page"},
|
|
{MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr,
|
|
&show_dev_addr, NULL},
|
|
{0}
|
|
};
|
|
|
|
UNIT lpr_unit[] = {
|
|
{UDATA(&lpr_srv, UNIT_LPR, 66), 300, UNIT_ADDR(0x7EF8)}, /* A */
|
|
#if NUM_DEVS_LPR > 1
|
|
{UDATA(&lpr_srv, UNIT_LPR, 66), 300, UNIT_ADDR(0x7EF9)}, /* B */
|
|
#endif
|
|
};
|
|
|
|
/* Device Information Block */
|
|
DIB lpr_dib = {
|
|
lpr_preio, /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/ /* Pre Start I/O */
|
|
lpr_startcmd, /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */
|
|
lpr_haltio, /* t_stat (*halt_io)(UNIT *uptr) */ /* Halt I/O */
|
|
NULL, /* t_stat (*stop_io)(UNIT *uptr) */ /* Stop I/O */
|
|
NULL, /* t_stat (*test_io)(UNIT *uptr) */ /* Test I/O */
|
|
NULL, /* t_stat (*rsctl_io)(UNIT *uptr) */ /* Reset Controller */
|
|
lpr_rschnlio, /* t_stat (*rschnl_io)(UNIT *uptr) */ /* Reset Channel */
|
|
NULL, /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */ /* Process IOCL */
|
|
lpr_ini, /* void (*dev_ini)(UNIT *, t_bool) */ /* init function */
|
|
lpr_unit, /* UNIT* units */ /* Pointer to units structure */
|
|
lpr_chp, /* CHANP* chan_prg */ /* Pointer to chan_prg structure */
|
|
NULL, /* IOCLQ *ioclq_ptr */ /* IOCL entries, 1 per UNIT */
|
|
NUM_DEVS_LPR, /* uint8 numunits */ /* number of units defined */
|
|
0x01, /* uint8 mask */ /* 2 devices - device mask */
|
|
0x7e00, /* uint16 chan_addr */ /* parent channel address */
|
|
0, /* uint32 chan_fifo_in */ /* fifo input index */
|
|
0, /* uint32 chan_fifo_out */ /* fifo output index */
|
|
{0} /* uint32 chan_fifo[FIFO_SIZE] */ /* interrupt status fifo for channel */
|
|
};
|
|
|
|
DEVICE lpr_dev = {
|
|
"LPR", lpr_unit, NULL, lpr_mod,
|
|
NUM_DEVS_LPR, 8, 15, 1, 8, 8,
|
|
NULL, NULL, NULL, NULL, &lpr_attach, &lpr_detach,
|
|
/* ctxt is the DIB pointer */
|
|
&lpr_dib, DEV_DISABLE|DEV_DEBUG, 0, dev_debug,
|
|
NULL, NULL, &lpr_help, NULL, NULL, &lpr_description,
|
|
};
|
|
|
|
/* initialize the line printer */
|
|
void lpr_ini(UNIT *uptr, t_bool f) {
|
|
uptr->CMD &= ~(LPR_CMDMSK); /* zero cmd */
|
|
sim_cancel(uptr); /* stop any timers */
|
|
uptr->SNS = 0; /* no status */
|
|
uptr->CBP = 0; /* start of buffer */
|
|
uptr->CNT = 0; /* restart line count */
|
|
}
|
|
|
|
/* handle rschnlio cmds for lpr */
|
|
t_stat lpr_rschnlio(UNIT *uptr) {
|
|
DEVICE *dptr = get_dev(uptr); /* get device pointer */
|
|
uint16 chsa = GET_UADDR(uptr->CMD);
|
|
int cmd = uptr->CMD & LPR_CMDMSK;
|
|
|
|
sim_debug(DEBUG_EXP, dptr,
|
|
"lpr_rschnl chsa %04x cmd = %02x\n", chsa, cmd);
|
|
lpr_ini(uptr, 0); /* reset the unit */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* start a line printer operation */
|
|
t_stat lpr_preio(UNIT *uptr, uint16 chan) {
|
|
DEVICE *dptr = get_dev(uptr);
|
|
int unit = (uptr - dptr->units);
|
|
uint16 chsa = GET_UADDR(uptr->CMD);
|
|
|
|
sim_debug(DEBUG_CMD, dptr, "lpr_preio CMD %08x unit %02x chsa %04x\n",
|
|
uptr->CMD, unit, chsa);
|
|
if ((uptr->CMD & LPR_CMDMSK) != 0) { /* just return if busy */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"lpr_preio unit %02x chsa %04x BUSY\n", unit, chsa);
|
|
return SNS_BSY;
|
|
}
|
|
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"lpr_preio unit %02x chsa %04x OK\n", unit, chsa);
|
|
return SCPE_OK; /* good to go */
|
|
}
|
|
|
|
/* start an I/O operation */
|
|
t_stat lpr_startcmd(UNIT *uptr, uint16 chan, uint8 cmd)
|
|
{
|
|
DEVICE *dptr = get_dev(uptr); /* get device pointer */
|
|
|
|
if ((uptr->CMD & LPR_CMDMSK) != 0) { /* unit busy */
|
|
return SNS_BSY; /* yes, busy (already tested) */
|
|
}
|
|
|
|
uptr->CMD &= ~(LPR_POST|LPR_PRE); /* set no CC */
|
|
if (((cmd & 0x03) == 0x03) || (cmd & 0x0f) == 0x0d) {
|
|
uptr->CMD |= LPR_PRE; /* apply pre CC */
|
|
}
|
|
if (((cmd & 0x0f) == 0x05) || (cmd & 0x0f) == 0x0d) {
|
|
uptr->CMD |= LPR_POST; /* apply post CC */
|
|
}
|
|
sim_debug(DEBUG_CMD, dptr, "lpr_startcmd Cmd %02x\n", cmd);
|
|
|
|
/* process the command */
|
|
switch (cmd & LPR_CMDMSK) {
|
|
case 0x00: /* INCH command */
|
|
/* the IOP should already have the inch buffer set, so ignore */
|
|
sim_debug(DEBUG_CMD, dptr, "lpr_startcmd %04x: Cmd INCH\n", chan);
|
|
return SNS_CHNEND|SNS_DEVEND; /* all is well */
|
|
break;
|
|
|
|
/* No CC */
|
|
case 0x01: /* print only, no forms control */
|
|
/* print buffer then CC commands */
|
|
case 0x05: /* print buffer, then <CR> */
|
|
case 0x15: /* print buffer, then <LF> */
|
|
case 0x25: /* print buffer, then <LF> <LF> */
|
|
case 0x35: /* print buffer, then <LF> <LF> <LF> */
|
|
case 0x45: /* print buffer, then <FF> */
|
|
case 0x85: /* print buffer, then <CR> <CLEAR BUFFER> */
|
|
/* Do CC then print commands then CC */
|
|
case 0x0d: /* <CR> print buffer <CR> */
|
|
case 0x1d: /* <LF> print buffer <CR> */
|
|
case 0x2d: /* <LF> <LF> print buffer <CR> */
|
|
case 0x3d: /* <LF> <LF> <LF> print buffer <CR> */
|
|
case 0x4d: /* <FF> print buffer <CR> */
|
|
/* Do CC only, no print */
|
|
case 0x03: /* <CR> */
|
|
case 0x17: /* <LF> */
|
|
case 0x27: /* <LF> <LF> */
|
|
case 0x37: /* <LF> <LF> <LF> */
|
|
case 0x47: /* <FF> */
|
|
|
|
/* process the command */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"lpr_startcmd %04x: Cmd %02x print\n", chan, cmd&LPR_CMDMSK);
|
|
uptr->CMD &= ~(LPR_CMDMSK); /* zero cmd */
|
|
uptr->CMD |= (cmd & LPR_CMDMSK); /* save new command in CMD */
|
|
sim_activate(uptr, 100); /* Start unit off */
|
|
return 0; /* we are good to go */
|
|
|
|
case 0x4: /* Sense Status */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"lpr_startcmd %04x: Cmd %02x sense\n", chan, cmd&LPR_CMDMSK);
|
|
uptr->CMD &= ~(LPR_CMDMSK); /* zero cmd */
|
|
uptr->CMD |= (cmd & LPR_CMDMSK); /* save new command in CMD */
|
|
sim_activate(uptr, 100); /* Start unit off */
|
|
return 0; /* we are good to go */
|
|
|
|
default: /* invalid command */
|
|
sim_debug(DEBUG_EXP, dptr,
|
|
"lpr_startcmd %04x: Cmd %02x INVALID\n", chan, cmd&LPR_CMDMSK);
|
|
uptr->SNS |= SNS_CMDREJ;
|
|
break;
|
|
}
|
|
if (uptr->SNS & 0xff)
|
|
return SNS_CHNEND|SNS_DEVEND|STATUS_PCHK;
|
|
return SNS_CHNEND|SNS_DEVEND;
|
|
}
|
|
|
|
/* Handle transfer of data for printer */
|
|
t_stat lpr_srv(UNIT *uptr) {
|
|
int chsa = GET_UADDR(uptr->CMD);
|
|
int u = (uptr - lpr_unit);
|
|
int cmd = (uptr->CMD & 0xff);
|
|
CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */
|
|
DEVICE *dptr = get_dev(uptr); /* get device pointer */
|
|
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"lpr_srv called chsa %04x cmd %02x CMD %08x addr %06x cnt %04x\n",
|
|
chsa, cmd, uptr->CMD, chp->ccw_addr, chp->ccw_count);
|
|
|
|
/* using IOP lp status bit assignments */
|
|
if (cmd == 0x04) { /* sense? */
|
|
uint8 ch; /* get current status */
|
|
ch = (uptr->SNS >> 24) & 0xff; /* Get status */
|
|
ch &= ~SNS_BOF; /* remove BOF flag */
|
|
if (chan_write_byte(chsa, &ch)) { /* write byte 0 status to memory */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"lpr_srv write1 error CMD %08x read %02x SNS %02x ccw_count %02x\n",
|
|
uptr->CMD, ch, uptr->SNS, chp->ccw_count);
|
|
uptr->CMD &= ~(LPR_CMDMSK); /* clear command */
|
|
uptr->SNS = 0; /* no status */
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); /* 4 byte req'd */
|
|
return SCPE_OK;
|
|
}
|
|
ch = (uptr->SNS >> 16) & 0xff; /* Get status */
|
|
if (chan_write_byte(chsa, &ch)) { /* write the status to memory */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"lpr_srv write2 error CMD %08x read %02x SNS %02x ccw_count %02x\n",
|
|
uptr->CMD, ch, uptr->SNS, chp->ccw_count);
|
|
uptr->CMD &= ~(LPR_CMDMSK); /* clear command */
|
|
uptr->SNS = 0; /* no status */
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); /* 4 byte req'd */
|
|
return SCPE_OK;
|
|
}
|
|
#ifdef MPX_WANTS_ONLY_2_BYTES
|
|
ch = 0; /* byte 2 is always zero */
|
|
if (chan_write_byte(chsa, &ch)) { /* write the status to memory */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"lpr_srv write3 error CMD %08x read %02x SNS %02x ccw_count %02x\n",
|
|
uptr->CMD, ch, uptr->SNS, chp->ccw_count);
|
|
uptr->CMD &= ~(LPR_CMDMSK); /* clear command */
|
|
uptr->SNS = 0; /* no status */
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); /* 4 byte req'd */
|
|
return SCPE_OK;
|
|
}
|
|
ch = 0; /* byte 3 is always zero */
|
|
if (chan_write_byte(chsa, &ch)) { /* write the status to memory */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"lpr_srv write4 error CMD %08x read %02x SNS %02x ccw_count %02x\n",
|
|
uptr->CMD, ch, uptr->SNS, chp->ccw_count);
|
|
uptr->CMD &= ~(LPR_CMDMSK); /* clear command */
|
|
uptr->SNS = 0; /* no status */
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_PCHK); /* 4 byte req'd */
|
|
return SCPE_OK;
|
|
}
|
|
#endif
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"lpr_srv sense write CMD %08x read %02x SNS %02x ccw_count %02x\n",
|
|
uptr->CMD, ch, uptr->SNS, chp->ccw_count);
|
|
uptr->CMD &= LMASK; /* make non-busy */
|
|
uptr->SNS = 0; /* no status */
|
|
chan_end(chsa, SNS_DEVEND|SNS_CHNEND); /* we are done */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* make sure we have a file attached, else give error */
|
|
if ((uptr->flags & UNIT_ATT) == 0) {
|
|
uptr->CMD &= LMASK; /* make non-busy */
|
|
uptr->SNS |= SNS_DEVCHK; /* show device check */
|
|
uptr->SNS |= SNS_OPRINTR; /* operator intervention required */
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"lpr_startcmd Cmd %02x LPR not attached SNS %08x\n", cmd, uptr->SNS);
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND|STATUS_CHECK);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* process any CC before printing buffer */
|
|
if ((uptr->CMD & LPR_PRE) && (((cmd & 0x03) == 0x03) ||
|
|
(cmd & 0x0f) == 0x0d)) {
|
|
uptr->CMD &= ~LPR_PRE; /* remove pre flag */
|
|
/* we have CC to do */
|
|
switch ((cmd & 0xf0) >> 4) {
|
|
case 0: /* <CR> (0x0d) */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0d;
|
|
break;
|
|
case 3: /* <LF> <LF> <LF> */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0a;
|
|
uptr->CNT++; /* increment the line count */
|
|
/* drop thru */
|
|
case 2: /* <LF> <LF> */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0a;
|
|
uptr->CNT++; /* increment the line count */
|
|
/* drop thru */
|
|
case 1: /* <LF> (0x0a) */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0a;
|
|
uptr->CNT++; /* increment the line count */
|
|
break;
|
|
case 4: /* <FF> (0x0c) */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0d; /* add C/R */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0a; /* add L/F */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0a; /* add FF */
|
|
uptr->CNT = 0; /* restart line count */
|
|
/* set beginning of form */
|
|
uptr->SNS |= SNS_BEGOF;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Copy next byte from users buffer */
|
|
while ((uptr->CMD & LPR_FULL) == 0) { /* copy in a char if not full */
|
|
if (chan_read_byte(chsa, &lpr_data[u].lbuff[uptr->CBP])) {
|
|
uptr->CMD |= LPR_FULL; /* end of buffer or error */
|
|
break; /* done reading */
|
|
} else {
|
|
/* remove nulls */
|
|
if (lpr_data[u].lbuff[uptr->CBP] == '\0') {
|
|
lpr_data[u].lbuff[uptr->CBP] = ' ';
|
|
}
|
|
/* remove backspace */
|
|
if (lpr_data[u].lbuff[uptr->CBP] == 0x8) {
|
|
lpr_data[u].lbuff[uptr->CBP] = ' ';
|
|
}
|
|
uptr->CBP++; /* next buffer loc */
|
|
}
|
|
}
|
|
|
|
/* remove trailing blanks before we apply trailing carriage control */
|
|
while (uptr->CBP > 0) {
|
|
if ((lpr_data[u].lbuff[uptr->CBP-1] == ' ') ||
|
|
(lpr_data[u].lbuff[uptr->CBP-1] == '\0')) {
|
|
uptr->CBP--;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* process any CC after printing buffer */
|
|
if ((uptr->CMD & LPR_FULL) && (uptr->CMD & LPR_POST) &&
|
|
((cmd & 0x0f) == 0x0d)) {
|
|
/* we have CC to do */
|
|
uptr->CMD &= ~LPR_POST; /* remove post flag */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0d; /* just a <CR> */
|
|
}
|
|
|
|
/* process any CC after printing buffer */
|
|
if ((uptr->CMD & LPR_FULL) && (uptr->CMD & LPR_POST) &&
|
|
((cmd & 0x0f) == 0x05)) {
|
|
/* we have CC to do */
|
|
uptr->CMD &= ~LPR_POST; /* remove post flag */
|
|
switch ((cmd & 0xf0) >> 4) {
|
|
case 0: /* <CR> (0x0d) */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0d;
|
|
break;
|
|
case 3: /* <LF> <LF> <LF> */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0a;
|
|
uptr->CNT++; /* increment the line count */
|
|
/* drop thru */
|
|
case 2: /* <LF> <LF> */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0a;
|
|
uptr->CNT++; /* increment the line count */
|
|
/* drop thru */
|
|
case 1: /* <LF> (0x0a) */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0a;
|
|
uptr->CNT++; /* increment the line count */
|
|
break;
|
|
case 4: /* <FF> (0x0c) */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0d; /* add C/R */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0a; /* add L/F */
|
|
lpr_data[u].lbuff[uptr->CBP++] = 0x0a; /* add FF */
|
|
uptr->CNT = 0; /* restart line count */
|
|
/* set beginning of form */
|
|
uptr->SNS |= SNS_BEGOF;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* print the line if buffer is full */
|
|
if (uptr->CMD & LPR_FULL || uptr->CBP >= 156) {
|
|
lpr_data[u].lbuff[uptr->CBP] = 0x00; /* NULL terminate */
|
|
sim_fwrite(&lpr_data[u].lbuff, 1, uptr->CBP, uptr->fileref); /* Print our buffer */
|
|
sim_debug(DEBUG_DETAIL, dptr, "LPR %d %s\n", uptr->CNT, (char*)&lpr_data[u].lbuff);
|
|
uptr->CMD &= ~(LPR_FULL|LPR_CMDMSK); /* clear old status */
|
|
uptr->CBP = 0; /* start at beginning of buffer */
|
|
if ((uint32)uptr->CNT >= uptr->capac) { /* see if at max lines/page */
|
|
uptr->CNT = 0; /* yes, restart count */
|
|
uptr->SNS |= SNS_BOF; /* set BOF for SENSE */
|
|
sim_debug(DEBUG_CMD, dptr, "lpr_srv Got BOF\n");
|
|
/* IOP spec says to give unit exception if at BOF */
|
|
chan_end(chsa, SNS_DEVEND|SNS_CHNEND|SNS_UNITEXP); /* we are done */
|
|
} else {
|
|
uptr->SNS &= ~SNS_BOF; /* reset BOF for SENSE */
|
|
if (uptr->CNT == 0) {
|
|
/* set beginning of form */
|
|
uptr->SNS |= SNS_BEGOF;
|
|
}
|
|
chan_end(chsa, SNS_DEVEND|SNS_CHNEND); /* we are done */
|
|
}
|
|
/* done, so no time out */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* should not get here */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Handle haltio transfers for printer */
|
|
t_stat lpr_haltio(UNIT *uptr) {
|
|
uint16 chsa = GET_UADDR(uptr->CMD);
|
|
int cmd = uptr->CMD & LPR_CMDMSK;
|
|
CHANP *chp = find_chanp_ptr(chsa); /* find the chanp pointer */
|
|
|
|
sim_debug(DEBUG_EXP, &lpr_dev,
|
|
"lpr_haltio enter chsa %04x cmd = %02x\n", chsa, cmd);
|
|
|
|
/* terminate any input command */
|
|
/* UTX wants SLI bit, but no unit exception */
|
|
/* status must not have an error bit set */
|
|
/* otherwise, UTX will panic with "bad status" */
|
|
if ((uptr->CMD & LPR_CMDMSK) != 0) { /* is unit busy */
|
|
sim_debug(DEBUG_CMD, &con_dev,
|
|
"lpr_haltio HIO chsa %04x cmd = %02x ccw_count %02x\n",
|
|
chsa, cmd, chp->ccw_count);
|
|
sim_cancel(uptr); /* stop timer */
|
|
} else {
|
|
sim_debug(DEBUG_CMD, &con_dev,
|
|
"lpr_haltio HIO not busy chsa %04x cmd = %02x ccw_count %02x\n",
|
|
chsa, cmd, chp->ccw_count);
|
|
}
|
|
/* stop any I/O and post status and return error status */
|
|
chp->ccw_count = 0; /* zero the count */
|
|
chp->ccw_flags &= ~(FLAG_DC|FLAG_CC); /* reset chaining bits */
|
|
uptr->CMD &= LMASK; /* make non-busy */
|
|
uptr->SNS = 0; /* no status */
|
|
uptr->CBP = 0; /* start of buffer */
|
|
sim_debug(DEBUG_CMD, &con_dev,
|
|
"lpr_haltio HIO I/O stop chsa %04x cmd = %02x\n", chsa, cmd);
|
|
chan_end(chsa, SNS_CHNEND|SNS_DEVEND); /* force end */
|
|
return SCPE_IOERR; /* tell chan code to post status */
|
|
}
|
|
|
|
/* Set the number of lines per page on printer */
|
|
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->CNT = 0;
|
|
/* set beginning of form */
|
|
uptr->SNS |= SNS_BEGOF;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* display the number of lines per page */
|
|
t_stat lpr_getlpp(FILE *st, UNIT *uptr, int32 v, CONST void *desc)
|
|
{
|
|
if (uptr == NULL)
|
|
return SCPE_IERR;
|
|
fprintf(st, "linesperpage=%02d", uptr->capac);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* attach a file to the line printer device */
|
|
t_stat lpr_attach(UNIT *uptr, CONST char *file)
|
|
{
|
|
t_stat r;
|
|
uint16 chsa = GET_UADDR(uptr->CMD); /* get address of lpr device */
|
|
CHANP *chp = find_chanp_ptr(chsa); /* get channel prog pointer */
|
|
DEVICE *dptr = get_dev(uptr); /* get device pointer */
|
|
DIB *dibp = 0;
|
|
|
|
if ((r = attach_unit(uptr, file)) != SCPE_OK)
|
|
return r;
|
|
uptr->CMD &= ~(LPR_FULL|LPR_CMDMSK);
|
|
uptr->CNT = 0;
|
|
uptr->SNS = 0;
|
|
/* set beginning of form */
|
|
uptr->SNS |= SNS_BEGOF;
|
|
uptr->capac = 66;
|
|
|
|
/* check for valid configured lpr */
|
|
/* must have valid DIB and Channel Program pointer */
|
|
dibp = (DIB *)dptr->ctxt; /* get the DIB pointer */
|
|
if ((dib_unit[chsa] == NULL) || (dibp == NULL) || (chp == NULL)) {
|
|
sim_debug(DEBUG_CMD, dptr,
|
|
"ERROR===ERROR\nLPR device %s not configured on system, aborting\n",
|
|
dptr->name);
|
|
printf("ERROR===ERROR\nLPR device %s not configured on system, aborting\r\n",
|
|
dptr->name);
|
|
detach_unit(uptr); /* detach if error */
|
|
return SCPE_UNATT; /* error */
|
|
}
|
|
set_devattn(chsa, SNS_DEVEND); /* ready int???? */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* help information for lpr */
|
|
t_stat lpr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
|
{
|
|
fprintf (st, "SEL32 924x High Speed Line Printer\n");
|
|
fprintf (st, "The Line printer can be configured to any number of\n");
|
|
fprintf (st, "lines per page with the:\n");
|
|
fprintf (st, "sim> SET LPRn LINESPERPAGE=n\n\n");
|
|
fprintf (st, "The default is 66 lines per page.\n");
|
|
fprint_set_help(st, dptr);
|
|
fprint_show_help(st, dptr);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* detach a file from the line printer */
|
|
t_stat lpr_detach(UNIT * uptr)
|
|
{
|
|
return detach_unit(uptr);
|
|
}
|
|
|
|
const char *lpr_description (DEVICE *dptr)
|
|
{
|
|
return "SEL32 924x High Speed Line Printer";
|
|
}
|
|
|
|
#endif
|