Added KS10 support. CPU Redid instruction decode to improve performance Triple-I display cleanup. Normalized end of line to DOS/Unix. KL10 FE, Cleanup issues with TTY devices hanging simulator.. Fixed errors in RH20 device. RP and TU drives more independent of RH controller.
896 lines
31 KiB
C
896 lines
31 KiB
C
/* ks10_lp.c: PDP-10 LP20 printer.
|
|
|
|
Copyright (c) 2021, 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.
|
|
|
|
Except as contained in this notice, the name of Richard Cornwell shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Richard Cornwell.
|
|
|
|
*/
|
|
|
|
#include "kx10_defs.h"
|
|
#include "sim_sock.h"
|
|
#include "sim_tmxr.h"
|
|
|
|
#ifndef NUM_DEVS_LP20
|
|
#define NUM_DEVS_LP20 0
|
|
#endif
|
|
|
|
#if (NUM_DEVS_LP20 > 0)
|
|
|
|
#define UNIT_V_CT (UNIT_V_UF + 0)
|
|
#define UNIT_UC (1 << UNIT_V_CT)
|
|
#define UNIT_CT (1 << UNIT_V_CT)
|
|
#define UNIT_V_VFU (UNIT_V_CT + 1)
|
|
#define UNIT_OPT (1 << UNIT_V_VFU)
|
|
|
|
|
|
#define LINE u6
|
|
|
|
/* LPCSRA (765400) */
|
|
#define CS1_GO 0000001 /* Go command */
|
|
#define CS1_PAR 0000002 /* Enable Parity interrupt */
|
|
#define CS1_V_FNC 2 /* Function shift */
|
|
#define CS1_M_FNC 03 /* Function mask */
|
|
#define FNC_PRINT 0 /* Print */
|
|
#define FNC_TEST 1 /* Test */
|
|
#define FNC_DVU 2 /* Load DAVFU */
|
|
#define FNC_RAM 3 /* Load translation RAM */
|
|
#define CS1_UBA 0000060 /* Upper Unibus address */
|
|
#define CS1_IE 0000100 /* Interrupt enable */
|
|
#define CS1_DONE 0000200 /* Done flag */
|
|
#define CS1_INIT 0000400 /* Init */
|
|
#define CS1_ECLR 0001000 /* Clear errors */
|
|
#define CS1_DHOLD 0002000 /* Delimiter hold */
|
|
#define CS1_ONL 0004000 /* Online */
|
|
#define CS1_DVON 0010000 /* DAVFU online */
|
|
#define CS1_UND 0020000 /* Undefined Character */
|
|
#define CS1_PZERO 0040000 /* Page counter zero */
|
|
#define CS1_ERR 0100000 /* Errors */
|
|
#define CS1_MOD (CS1_DHOLD|CS1_IE|(CS1_M_FNC << CS1_V_FNC)|CS1_PAR|CS1_GO)
|
|
|
|
/* LPCSRB (765402) */
|
|
#define CS2_GOE 0000001 /* Go error */
|
|
#define CS2_DTE 0000002 /* DEM timing error */
|
|
#define CS2_MTE 0000004 /* MSYN error */
|
|
#define CS2_RPE 0000010 /* RAM parity error */
|
|
#define CS2_MPE 0000020 /* Memory parity error */
|
|
#define CS2_LPE 0000040 /* LPT parity error */
|
|
#define CS2_DVOF 0000100 /* DAVFU not ready */
|
|
#define CS2_OFFL 0000200 /* Offline */
|
|
#define CS2_TEST 0003400 /* Test mode */
|
|
#define CS2_OVFU 0004000 /* Optical VFU */
|
|
#define CS2_PBIT 0010000 /* data parity bit */
|
|
#define CS2_NRDY 0020000 /* Printer error */
|
|
#define CS2_LA180 0040000 /* LA180 printer */
|
|
#define CS2_VLD 0100000 /* Valid data */
|
|
#define CS2_ECLR (CS2_GOE|CS2_DTE|CS2_MTE|CS2_RPE|CS2_LPE)
|
|
#define CS2_ERR (CS2_ECLR|CS2_OFFL|CS2_DVOF)
|
|
|
|
/* LPBA (765404) */
|
|
/* Unibus address */
|
|
|
|
/* LPBC (765406) */
|
|
/* byte count */
|
|
|
|
/* LPPAGC (765410) */
|
|
/* Page count */
|
|
|
|
/* LPRDAT (765412) */
|
|
/* RAM Data register */
|
|
|
|
/* LPCOLC/LPCBUF (765414) */
|
|
/* Column counter / Character buffer */
|
|
|
|
/* LPCSUM/LPPDAT (765416) */
|
|
/* Checksum / Printer data */
|
|
|
|
|
|
#define EOFFLG 001 /* Tops 20 wants EOF */
|
|
#define HDSFLG 002 /* Tell Tops 20 The current device status */
|
|
#define ACKFLG 004 /* Post an acknowwledge message */
|
|
#define INTFLG 010 /* Send interrupt */
|
|
#define DELFLG 020 /* Previous character was delimiter */
|
|
|
|
#define MARGIN 6
|
|
|
|
|
|
|
|
int lp20_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access);
|
|
int lp20_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access);
|
|
void lp20_printline(UNIT *uptr, int nl);
|
|
void lp20_output(UNIT *uptr, uint8 c);
|
|
void lp20_update_chkirq (UNIT *uptr, int done, int irq);
|
|
void lp20_update_ready(UNIT *uptr, uint16 setrdy, uint16 clrrdy);
|
|
t_stat lp20_svc (UNIT *uptr);
|
|
t_stat lp20_init (UNIT *uptr);
|
|
t_stat lp20_reset (DEVICE *dptr);
|
|
t_stat lp20_attach (UNIT *uptr, CONST char *cptr);
|
|
t_stat lp20_detach (UNIT *uptr);
|
|
t_stat lp20_setlpp(UNIT *, int32, CONST char *, void *);
|
|
t_stat lp20_getlpp(FILE *, UNIT *, int32, CONST void *);
|
|
t_stat lp20_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
|
|
const char *cptr);
|
|
const char *lp20_description (DEVICE *dptr);
|
|
|
|
char lp20_buffer[134 * 3];
|
|
uint16 lp20_cs1;
|
|
uint16 lp20_cs2;
|
|
uint16 lp20_pagcnt;
|
|
uint32 lp20_ba;
|
|
uint16 lp20_wcnt;
|
|
uint8 lp20_col;
|
|
uint8 lp20_chksum;
|
|
uint8 lp20_buf;
|
|
uint8 lp20_data;
|
|
int lp20_odd = 0;
|
|
int lp20_duvfa_state = 0;
|
|
int lp20_index = 0;
|
|
|
|
#define LP20_RAM_RAP 010000 /* RAM Parity */
|
|
#define LP20_RAM_INT 04000 /* Interrrupt bit */
|
|
#define LP20_RAM_DEL 02000 /* Delimiter bit */
|
|
#define LP20_RAM_TRN 01000 /* Translation bite */
|
|
#define LP20_RAM_PI 00400 /* Paper Instruction */
|
|
#define LP20_RAM_CHR 00377 /* Character translation */
|
|
|
|
uint16 lp20_vfu[256];
|
|
uint16 lp20_ram[256];
|
|
uint16 lp20_dvfu[] = { /* Default VFU */
|
|
/* 66 line page with 6 line margin */
|
|
00377, /* Line 0 8 7 6 5 4 3 2 1 */
|
|
00220, /* Line 1 8 5 */
|
|
00224, /* Line 2 8 5 3 */
|
|
00230, /* Line 3 8 5 4 */
|
|
00224, /* Line 4 8 5 3 */
|
|
00220, /* Line 5 8 5 */
|
|
00234, /* Line 6 8 5 4 3 */
|
|
00220, /* Line 7 8 5 */
|
|
00224, /* Line 8 8 5 3 */
|
|
00230, /* Line 9 8 5 4 */
|
|
00264, /* Line 10 8 6 5 3 */
|
|
00220, /* Line 11 8 5 */
|
|
00234, /* Line 12 8 5 4 3 */
|
|
00220, /* Line 13 8 5 */
|
|
00224, /* Line 14 8 5 3 */
|
|
00230, /* Line 15 8 5 4 */
|
|
00224, /* Line 16 8 5 3 */
|
|
00220, /* Line 17 8 5 */
|
|
00234, /* Line 18 8 5 4 3 */
|
|
00220, /* Line 19 8 5 */
|
|
00364, /* Line 20 8 7 6 5 3 */
|
|
00230, /* Line 21 8 5 4 */
|
|
00224, /* Line 22 8 5 3 */
|
|
00220, /* Line 23 8 5 */
|
|
00234, /* Line 24 8 5 4 3 */
|
|
00220, /* Line 25 8 5 */
|
|
00224, /* Line 26 8 5 3 */
|
|
00230, /* Line 27 8 5 4 */
|
|
00224, /* Line 28 8 5 3 */
|
|
00220, /* Line 29 8 5 */
|
|
00276, /* Line 30 8 6 5 4 3 2 */
|
|
00220, /* Line 31 8 5 */
|
|
00224, /* Line 32 8 5 3 */
|
|
00230, /* Line 33 8 5 4 */
|
|
00224, /* Line 34 8 5 3 */
|
|
00220, /* Line 35 8 5 */
|
|
00234, /* Line 36 8 5 4 3 */
|
|
00220, /* Line 37 8 5 */
|
|
00224, /* Line 38 8 5 3 */
|
|
00230, /* Line 39 8 5 4 */
|
|
00364, /* Line 40 8 7 6 5 3 */
|
|
00220, /* Line 41 8 5 */
|
|
00234, /* Line 42 8 5 4 3 */
|
|
00220, /* Line 43 8 5 */
|
|
00224, /* Line 44 8 5 3 */
|
|
00230, /* Line 45 8 5 4 */
|
|
00224, /* Line 46 8 5 3 */
|
|
00220, /* Line 47 8 5 */
|
|
00234, /* Line 48 8 5 4 3 */
|
|
00220, /* Line 49 8 5 */
|
|
00264, /* Line 50 8 6 5 3 */
|
|
00230, /* Line 51 8 5 4 */
|
|
00224, /* Line 52 8 5 3 */
|
|
00220, /* Line 53 8 5 */
|
|
00234, /* Line 54 8 5 4 3 */
|
|
00220, /* Line 55 8 5 */
|
|
00224, /* Line 56 8 5 3 */
|
|
00230, /* Line 57 8 5 4 */
|
|
00224, /* Line 58 8 5 3 */
|
|
00220, /* Line 59 8 5 */
|
|
00020, /* Line 60 5 */
|
|
00020, /* Line 61 5 */
|
|
00020, /* Line 62 5 */
|
|
00020, /* Line 63 5 */
|
|
00020, /* Line 64 5 */
|
|
04020, /* Line 65 12 5 */
|
|
010000, /* End of form */
|
|
};
|
|
|
|
|
|
/* LPT data structures
|
|
|
|
lp20_dev LPT device descriptor
|
|
lp20_unit LPT unit descriptor
|
|
lp20_reg LPT register list
|
|
*/
|
|
|
|
DIB lp20_dib = { 0775400, 017, 0754, 5, 3, &lp20_read, &lp20_write, NULL, 0, 0 };
|
|
|
|
|
|
UNIT lp20_unit = {
|
|
UDATA (&lp20_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 66), 100
|
|
};
|
|
|
|
REG lp20_reg[] = {
|
|
{BRDATA(BUFFER, lp20_buffer, 16, 8, sizeof(lp20_buffer)), REG_HRO},
|
|
{BRDATA(VFU, lp20_vfu, 16, 16, (sizeof(lp20_vfu)/sizeof(uint16))), REG_HRO},
|
|
{BRDATA(RAM, lp20_ram, 16, 16, (sizeof(lp20_ram)/sizeof(uint16))), REG_HRO},
|
|
{ORDATA(CS1, lp20_cs1, 16)},
|
|
{ORDATA(CS2, lp20_cs2, 16)},
|
|
{ORDATA(PAGCNT, lp20_pagcnt, 12)},
|
|
{ORDATA(BA, lp20_ba, 18)},
|
|
{ORDATA(BC, lp20_wcnt, 16)},
|
|
{ORDATA(COL, lp20_col, 8)},
|
|
{ORDATA(CHKSUM, lp20_chksum, 8)},
|
|
{ORDATA(BUF, lp20_buf, 8)},
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB lp20_mod[] = {
|
|
{MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "addr", "addr", &uba_set_addr, uba_show_addr,
|
|
NULL, "Sets address of LP20" },
|
|
{MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "vect", "vect", &uba_set_vect, uba_show_vect,
|
|
NULL, "Sets vect of LP20" },
|
|
{MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "br", "br", &uba_set_br, uba_show_br,
|
|
NULL, "Sets br of LP20" },
|
|
{MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ctl", "ctl", &uba_set_ctl, uba_show_ctl,
|
|
NULL, "Sets uba of LP20" },
|
|
{UNIT_CT, 0, "Lower case", "LC", NULL},
|
|
{UNIT_CT, UNIT_UC, "Upper case", "UC", NULL},
|
|
{UNIT_OPT, 0, "Normal VFU", "NORMAL", NULL},
|
|
{UNIT_OPT, UNIT_OPT, "Optical VFU", "OPTICAL", NULL},
|
|
{MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LINESPERPAGE", "LINESPERPAGE",
|
|
&lp20_setlpp, &lp20_getlpp, NULL, "Number of lines per page"},
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE lp20_dev = {
|
|
"LP20", &lp20_unit, lp20_reg, lp20_mod,
|
|
1, 10, 31, 1, 8, 8,
|
|
NULL, NULL, &lp20_reset,
|
|
NULL, &lp20_attach, &lp20_detach,
|
|
&lp20_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
|
|
NULL, NULL, &lp20_help, NULL, NULL, &lp20_description
|
|
};
|
|
|
|
int
|
|
lp20_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access)
|
|
{
|
|
struct pdp_dib *dibp = (DIB *)dptr->ctxt;
|
|
uint16 temp;
|
|
|
|
addr &= dibp->uba_mask;
|
|
sim_debug(DEBUG_DETAIL, dptr, "LP20 write %06o %06o %o\n",
|
|
addr, data, access);
|
|
|
|
switch (addr & 016) {
|
|
case 000: /* LPCSA */
|
|
if (access == BYTE) {
|
|
if (addr & 1)
|
|
data = data | (lp20_cs1 & 0377);
|
|
else
|
|
data = (lp20_cs1 & 0177400) | data;
|
|
}
|
|
lp20_ba = ((data & CS1_UBA) << 9) | (lp20_ba & 0177777);
|
|
if (data & CS1_INIT) {
|
|
/* Reset controller */
|
|
lp20_init (&lp20_unit);
|
|
}
|
|
if (data & CS1_ECLR) {
|
|
/* Clear errors */
|
|
lp20_cs2 &= ~(CS2_ECLR);
|
|
lp20_cs1 &= ~(CS1_GO);
|
|
lp20_cs1 |= CS1_DONE;
|
|
}
|
|
if (data & CS1_GO) {
|
|
if ((lp20_cs1 & CS1_GO) == 0) {
|
|
lp20_chksum = 0;
|
|
lp20_odd = 0;
|
|
lp20_duvfa_state = 0;
|
|
lp20_index = 0;
|
|
sim_activate(&lp20_unit, 100);
|
|
lp20_cs1 |= CS1_GO;
|
|
}
|
|
} else {
|
|
lp20_cs1 &= ~CS1_GO;
|
|
sim_cancel(&lp20_unit);
|
|
}
|
|
lp20_cs1 &= ~(CS1_MOD);
|
|
lp20_cs1 |= data & CS1_MOD;
|
|
if (lp20_duvfa_state && ((lp20_cs1 >> CS1_V_FNC) & CS1_M_FNC) != FNC_DVU) {
|
|
lp20_update_ready(&lp20_unit, 0, CS1_DVON);
|
|
lp20_duvfa_state = 0;
|
|
}
|
|
break;
|
|
|
|
case 002: /* LPCSB */
|
|
if (access == BYTE) {
|
|
if (addr & 1) {
|
|
lp20_cs2 &= ~(CS2_TEST);
|
|
lp20_cs2 |= data & CS2_TEST;
|
|
} else {
|
|
lp20_cs2 &= ~(CS2_GOE);
|
|
lp20_cs2 |= data & CS2_GOE;
|
|
}
|
|
} else {
|
|
lp20_cs2 &= ~(CS2_TEST|CS2_GOE);
|
|
lp20_cs2 |= data & (CS2_TEST|CS2_GOE);
|
|
}
|
|
break;
|
|
|
|
case 004: /* LPBA */
|
|
lp20_ba = (lp20_ba & 060000) | (data & 0177777);
|
|
break;
|
|
|
|
case 006: /* LPBC */
|
|
lp20_wcnt = (data & 0177777);
|
|
lp20_cs1 &= ~CS1_DONE;
|
|
break;
|
|
|
|
case 010: /* LPPAGC */
|
|
if (access == BYTE) {
|
|
if (addr & 1)
|
|
data = data | (lp20_pagcnt & 0377);
|
|
else
|
|
data = (lp20_pagcnt & 0177400) | data;
|
|
}
|
|
lp20_pagcnt = (data & 0177777);
|
|
lp20_cs1 &= ~CS1_PZERO;
|
|
break;
|
|
|
|
case 012: /* LPRDAT */
|
|
break;
|
|
|
|
case 014: /* LPCOL/LPCBUF */
|
|
if (access == BYTE) {
|
|
if (addr & 1)
|
|
lp20_col = (data >> 8) & 0377;
|
|
else
|
|
lp20_buf = data & 0377;
|
|
} else {
|
|
lp20_buf = data & 0377;
|
|
lp20_col = (data >> 8) & 0377;
|
|
}
|
|
break;
|
|
|
|
case 016: /* LPCSUM/LPPDAT */
|
|
if (access == BYTE) {
|
|
temp = lp20_ram[(int)lp20_buf];
|
|
if (addr & 1)
|
|
data = data | (temp & 0377);
|
|
else
|
|
data = (temp & 0177400) | data;
|
|
}
|
|
lp20_ram[(int)lp20_buf] = data & 07777;
|
|
break;
|
|
}
|
|
lp20_update_chkirq(&lp20_unit, 0, 0);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
lp20_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access)
|
|
{
|
|
struct pdp_dib *dibp = (DIB *)dptr->ctxt;
|
|
uint16 temp;
|
|
int par;
|
|
|
|
addr &= dibp->uba_mask;
|
|
switch (addr & 016) {
|
|
case 000: /* LPCSA */
|
|
*data = lp20_cs1;
|
|
*data &= ~CS1_UBA;
|
|
*data |= (lp20_ba >> 9) & CS1_UBA;
|
|
if ((lp20_cs2 & CS2_ERR) != 0)
|
|
*data |= CS1_ERR;
|
|
break;
|
|
|
|
case 002: /* LPCSB */
|
|
*data = lp20_cs2;
|
|
break;
|
|
|
|
case 004: /* LPBA */
|
|
*data = lp20_ba & 0177777;
|
|
break;
|
|
|
|
case 006: /* LPBC */
|
|
*data = lp20_wcnt;
|
|
break;
|
|
|
|
case 010: /* LPPAGC */
|
|
*data = lp20_pagcnt;
|
|
break;
|
|
|
|
case 012: /* LPRDAT */
|
|
temp = lp20_ram[(int)lp20_buf] & 07777;
|
|
par = (temp >> 8) ^ (temp >> 4) ^ temp;
|
|
par = (par >> 2) ^ par;
|
|
par ^= par >> 1;
|
|
if ((par & 1) != 0)
|
|
temp |= LP20_RAM_RAP;
|
|
break;
|
|
|
|
case 014: /* LPCOL/LPCBUF */
|
|
*data = ((uint16)lp20_col) << 8;
|
|
*data |= (uint16)lp20_buf;
|
|
break;
|
|
|
|
case 016: /* LPCSUM/LPPDAT */
|
|
*data = ((uint16)lp20_chksum) << 8;
|
|
*data |= (uint16)lp20_data;
|
|
break;
|
|
}
|
|
sim_debug(DEBUG_DETAIL, dptr, "LP20 read %06o %06o %o\n",
|
|
addr, *data, access);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
lp20_printline(UNIT *uptr, int nl) {
|
|
int trim = 0;
|
|
|
|
/* Trim off trailing blanks */
|
|
while (lp20_col != 0 && lp20_buffer[lp20_col - 1] == ' ') {
|
|
lp20_col--;
|
|
trim = 1;
|
|
}
|
|
lp20_buffer[lp20_col] = '\0';
|
|
sim_debug(DEBUG_DETAIL, &lp20_dev, "LP output %d %d [%s]\n", lp20_col, nl,
|
|
lp20_buffer);
|
|
/* Stick a carraige return and linefeed as needed */
|
|
if (lp20_col != 0 || trim)
|
|
lp20_buffer[lp20_col++] = '\r';
|
|
if (nl != 0) {
|
|
lp20_buffer[lp20_col++] = '\n';
|
|
uptr->LINE++;
|
|
}
|
|
if (nl > 0 && lp20_vfu[uptr->LINE] == 010000) {
|
|
lp20_buffer[lp20_col++] = '\f';
|
|
uptr->LINE = 0;
|
|
lp20_pagcnt = (lp20_pagcnt - 1) & 07777;
|
|
if (lp20_pagcnt == 0)
|
|
lp20_cs1 |= CS1_PZERO;
|
|
} else if (nl < 0 && uptr->LINE >= (int32)uptr->capac) {
|
|
uptr->LINE = 0;
|
|
lp20_pagcnt = (lp20_pagcnt - 1) & 07777;
|
|
if (lp20_pagcnt == 0)
|
|
lp20_cs1 |= CS1_PZERO;
|
|
}
|
|
|
|
sim_fwrite(&lp20_buffer, 1, lp20_col, uptr->fileref);
|
|
uptr->pos += lp20_col;
|
|
lp20_col = 0;
|
|
return;
|
|
}
|
|
|
|
|
|
/* Unit service */
|
|
void
|
|
lp20_output(UNIT *uptr, uint8 c) {
|
|
|
|
if (c == 0)
|
|
return;
|
|
lp20_data = c & 0377;
|
|
if (lp20_col == 132)
|
|
lp20_printline(uptr, 1);
|
|
/* Map lower to upper case if uppercase only */
|
|
if ((uptr->flags & UNIT_UC) && (c & 0140) == 0140)
|
|
c &= 0137;
|
|
if (c >= 040 && c < 0177) { /* If printable */
|
|
lp20_buffer[lp20_col++] = c;
|
|
return;
|
|
}
|
|
if (c == 012) { /* Line feed? */
|
|
lp20_printline(uptr, 1);
|
|
} else if (c == 014) { /* Form feed, advance to top of form */
|
|
if (lp20_col != 0)
|
|
lp20_printline(uptr, 1);
|
|
sim_fwrite("\f", 1, 1, uptr->fileref);
|
|
uptr->pos += 1;
|
|
lp20_col = 0;
|
|
uptr->LINE = 0;
|
|
} else if (c == 015) { /* Carrage return */
|
|
lp20_col = 0;
|
|
}
|
|
if (c == 011) { /* Tab */
|
|
lp20_buffer[lp20_col++] = ' ';
|
|
while ((lp20_col & 07) != 0)
|
|
lp20_buffer[lp20_col++] = ' ';
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Check if interrupt should be sent.
|
|
* Done set CS1_DONE.
|
|
* Irq set interrupt
|
|
*/
|
|
void
|
|
lp20_update_chkirq (UNIT *uptr, int done, int irq)
|
|
{
|
|
DEVICE *dptr = find_dev_from_unit (uptr);
|
|
struct pdp_dib *dibp = (DIB *)dptr->ctxt;
|
|
if (done)
|
|
lp20_cs1 |= CS1_DONE;
|
|
|
|
if (uptr->flags & UNIT_ATT) {
|
|
lp20_cs1 |= CS1_ONL;
|
|
lp20_cs2 &= ~(CS2_OFFL|CS2_NRDY);
|
|
} else {
|
|
lp20_cs1 &= ~(CS1_ONL|CS1_DONE);
|
|
lp20_cs2 |= CS2_NRDY|CS2_OFFL;
|
|
}
|
|
if ((lp20_cs1 & CS1_IE) && (irq || (lp20_cs1 & CS1_DONE)))
|
|
uba_set_irq(dibp, dibp->uba_vect);
|
|
else
|
|
uba_clr_irq(dibp, dibp->uba_vect);
|
|
}
|
|
|
|
/*
|
|
* Update ready status of printer.
|
|
*/
|
|
|
|
void
|
|
lp20_update_ready(UNIT *uptr, uint16 setrdy, uint16 clrrdy)
|
|
{
|
|
DEVICE *dptr = find_dev_from_unit (uptr);
|
|
struct pdp_dib *dibp = (DIB *)dptr->ctxt;
|
|
uint16 new_cs1 = (lp20_cs1 | setrdy) & ~clrrdy;
|
|
|
|
if ((new_cs1 ^ lp20_cs1) & (CS1_ONL|CS1_DVON) && !sim_is_active(uptr)) {
|
|
if (new_cs1 & CS1_IE)
|
|
uba_set_irq(dibp, dibp->uba_vect);
|
|
}
|
|
if (new_cs1 & CS1_DVON)
|
|
lp20_cs2 &= ~CS2_DVOF;
|
|
if (new_cs1 & CS1_ONL)
|
|
lp20_cs2 &= ~CS2_OFFL;
|
|
else
|
|
lp20_cs2 |= CS2_OFFL;
|
|
lp20_cs1 = new_cs1;
|
|
}
|
|
|
|
t_stat
|
|
lp20_svc (UNIT *uptr)
|
|
{
|
|
DEVICE *dptr = find_dev_from_unit (uptr);
|
|
struct pdp_dib *dibp = (DIB *)dptr->ctxt;
|
|
char ch;
|
|
int fnc = (lp20_cs1 >> CS1_V_FNC) & CS1_M_FNC;
|
|
uint16 ram_ch;
|
|
uint8 data;
|
|
|
|
if (fnc == FNC_PRINT && (uptr->flags & UNIT_ATT) == 0) {
|
|
lp20_cs1 |= CS1_ERR;
|
|
/* Set error */
|
|
lp20_cs1 &= ~CS1_GO;
|
|
lp20_update_chkirq (uptr, 0, 1);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
if (uba_read_npr_byte(lp20_ba, dibp->uba_ctl, &data) == 0) {
|
|
lp20_cs2 |= CS2_MTE;
|
|
lp20_cs1 &= ~CS1_GO;
|
|
lp20_update_chkirq (uptr, 0, 1);
|
|
sim_debug(DEBUG_DETAIL, &lp20_dev, "LP npr failed\n");
|
|
return SCPE_OK;
|
|
}
|
|
|
|
lp20_buf = data;
|
|
lp20_ba = (lp20_ba + 1) & 0777777;
|
|
lp20_wcnt = (lp20_wcnt + 1) & 07777;
|
|
if (lp20_wcnt == 0) {
|
|
lp20_cs1 &= ~CS1_GO;
|
|
}
|
|
lp20_chksum += lp20_buf;
|
|
sim_debug(DEBUG_DETAIL, &lp20_dev, "LP npr %08o %06o %03o %d\n", lp20_ba, lp20_wcnt,
|
|
lp20_buf, fnc);
|
|
switch(fnc) {
|
|
case FNC_PRINT:
|
|
ram_ch = lp20_ram[(int)lp20_buf];
|
|
/* If previous was delimiter or translation do it */
|
|
if (lp20_cs1 & CS1_DHOLD || (ram_ch &(LP20_RAM_DEL|LP20_RAM_TRN)) != 0) {
|
|
ch = ram_ch & LP20_RAM_CHR;
|
|
lp20_cs1 &= ~CS1_DHOLD;
|
|
if (ram_ch & LP20_RAM_DEL)
|
|
lp20_cs1 |= CS1_DHOLD;
|
|
}
|
|
/* Flag if interrupt set */
|
|
if (ram_ch & LP20_RAM_INT) {
|
|
lp20_cs1 &= ~CS1_GO;
|
|
lp20_cs1 |= CS1_UND;
|
|
}
|
|
/* Check if translate flag set */
|
|
if (ram_ch & LP20_RAM_TRN) {
|
|
lp20_buf = (uint8)(ram_ch & 0377);
|
|
}
|
|
/* Check if paper motion */
|
|
if (ram_ch & LP20_RAM_PI) {
|
|
int lines = 0; /* Number of new lines to output */
|
|
/* Print any buffered line */
|
|
if (lp20_col != 0)
|
|
lp20_printline(uptr, 1);
|
|
sim_debug(DEBUG_DETAIL, &lp20_dev, "LP Page Index %02x %04x\n",
|
|
lp20_buf, ram_ch);
|
|
if ((ram_ch & 020) == 0) { /* Find channel mark in output */
|
|
while ((lp20_vfu[uptr->LINE] & (1 << (ram_ch & 017))) == 0) {
|
|
sim_debug(DEBUG_DETAIL, &lp20_dev,
|
|
"LP skip chan %04x %04x %d\n",
|
|
lp20_vfu[uptr->LINE], ram_ch, uptr->LINE);
|
|
if (lp20_vfu[uptr->LINE] & 010000) { /* Hit bottom of form */
|
|
sim_fwrite("\014", 1, 1, uptr->fileref);
|
|
uptr->pos++;
|
|
lines = 0;
|
|
uptr->LINE = 0;
|
|
lp20_pagcnt = (lp20_pagcnt - 1) & 07777;
|
|
if (lp20_pagcnt == 0)
|
|
lp20_cs1 |= CS1_PZERO;
|
|
break;
|
|
}
|
|
lines++;
|
|
uptr->LINE++;
|
|
}
|
|
} else {
|
|
while ((ram_ch & 017) != 0) {
|
|
sim_debug(DEBUG_DETAIL, &lp20_dev,
|
|
"LP skip line %04x %04x %d\n",
|
|
lp20_vfu[uptr->LINE], ram_ch, uptr->LINE);
|
|
if (lp20_vfu[uptr->LINE] & 010000) { /* Hit bottom of form */
|
|
sim_fwrite("\014", 1, 1, uptr->fileref);
|
|
uptr->pos++;
|
|
lines = 0;
|
|
uptr->LINE = 0;
|
|
lp20_pagcnt = (lp20_pagcnt - 1) & 07777;
|
|
if (lp20_pagcnt == 0)
|
|
lp20_cs1 |= CS1_PZERO;
|
|
}
|
|
lines++;
|
|
uptr->LINE++;
|
|
ram_ch--;
|
|
}
|
|
}
|
|
for(;lines > 0; lines--) {
|
|
sim_fwrite("\r\n", 1, 2, uptr->fileref);
|
|
uptr->pos+=2;
|
|
}
|
|
} else if (lp20_buf != 0) {
|
|
sim_debug(DEBUG_DETAIL, &lp20_dev, "LP print %03o '%c' %04o\n",
|
|
lp20_buf, lp20_buf, ram_ch);
|
|
lp20_output(uptr, lp20_buf);
|
|
}
|
|
if (lp20_cs1 & CS1_GO)
|
|
sim_activate(uptr, 600);
|
|
else
|
|
lp20_update_chkirq (uptr, lp20_wcnt == 0, 1);
|
|
return SCPE_OK;
|
|
|
|
case FNC_TEST:
|
|
break;
|
|
|
|
case FNC_DVU:
|
|
if ((uptr->flags & UNIT_OPT) != 0) {
|
|
lp20_output(uptr, lp20_buf);
|
|
break;
|
|
}
|
|
if (lp20_buf >= 0354 && lp20_buf <= 0356) { /* Start of DVU load */
|
|
lp20_duvfa_state = 1;
|
|
lp20_index = 0;
|
|
lp20_odd = 0;
|
|
lp20_cs2 &= ~CS2_DVOF;
|
|
} else if (lp20_buf == 0357) { /* Stop DVU load */
|
|
lp20_duvfa_state = 0;
|
|
lp20_vfu[lp20_index] = 010000;
|
|
if (lp20_odd || lp20_index < 12) {
|
|
lp20_cs1 &= ~CS1_DVON;
|
|
} else {
|
|
lp20_cs1 |= CS1_DVON;
|
|
}
|
|
} else if (lp20_duvfa_state) {
|
|
if (lp20_odd) {
|
|
lp20_vfu[lp20_index] = (lp20_vfu[lp20_index] & 077) | ((lp20_buf & 077) << 6);
|
|
sim_debug(DEBUG_DETAIL, &lp20_dev,
|
|
"LP load DFU %d %04x\n", lp20_index, lp20_vfu[lp20_index]);
|
|
lp20_index++;
|
|
} else {
|
|
lp20_vfu[lp20_index] = (lp20_vfu[lp20_index] & 0007700) | (lp20_buf & 077);
|
|
}
|
|
lp20_odd = !lp20_odd;
|
|
}
|
|
break;
|
|
|
|
case FNC_RAM:
|
|
if (lp20_odd) {
|
|
lp20_ram[lp20_index] = (lp20_ram[lp20_index] & 0377) | ((lp20_buf & 017) << 8);
|
|
lp20_index++;
|
|
} else {
|
|
lp20_ram[lp20_index] = (lp20_ram[lp20_index] & 07400) | (lp20_buf & 0377);
|
|
}
|
|
lp20_odd = !lp20_odd;
|
|
break;
|
|
}
|
|
if (lp20_cs1 & CS1_GO) {
|
|
sim_activate(uptr, 10);
|
|
} else {
|
|
lp20_update_chkirq (uptr, 1, 1);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Init routine */
|
|
t_stat lp20_init (UNIT *uptr)
|
|
{
|
|
lp20_cs1 = (lp20_cs1 & CS1_DVON) | CS1_DONE;
|
|
lp20_cs2 = lp20_cs2 & (CS2_OFFL|CS2_DVOF);
|
|
lp20_col = 0;
|
|
lp20_ba = 0;
|
|
lp20_wcnt = 0;
|
|
lp20_chksum = 0;
|
|
sim_cancel (uptr); /* deactivate unit */
|
|
lp20_update_chkirq (uptr, 1, 0);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Reset routine */
|
|
t_stat lp20_reset (DEVICE *dptr)
|
|
{
|
|
UNIT *uptr = &lp20_unit;
|
|
int i;
|
|
int par;
|
|
lp20_col = 0;
|
|
uptr->LINE = 0;
|
|
lp20_cs1 = 0;
|
|
lp20_cs2 = CS2_OFFL|CS2_DVOF;
|
|
lp20_ba = 0;
|
|
lp20_wcnt = 0;
|
|
/* Clear RAM & VFU */
|
|
for (i = 0; i < 256; i++) {
|
|
lp20_ram[i] = 0;
|
|
lp20_vfu[i] = 0;
|
|
}
|
|
|
|
if ((uptr->flags & UNIT_OPT) != 0) {
|
|
/* Load default VFU into VFU */
|
|
memcpy(&lp20_vfu, lp20_dvfu, sizeof(lp20_dvfu));
|
|
lp20_cs2 |= CS2_OVFU;
|
|
lp20_cs2 &= CS2_DVOF;
|
|
lp20_cs1 |= CS1_DVON;
|
|
}
|
|
lp20_ram[012] = LP20_RAM_TRN|LP20_RAM_PI|7; /* Line feed, print line, space one line */
|
|
lp20_ram[013] = LP20_RAM_TRN|LP20_RAM_PI|6; /* Vertical tab, Skip mod 20 */
|
|
lp20_ram[014] = LP20_RAM_TRN|LP20_RAM_PI|0; /* Form feed, skip to top of page */
|
|
lp20_ram[015] = LP20_RAM_TRN|LP20_RAM_PI|020; /* Carrage return */
|
|
lp20_ram[020] = LP20_RAM_TRN|LP20_RAM_PI|1; /* Skip half page */
|
|
lp20_ram[021] = LP20_RAM_TRN|LP20_RAM_PI|2; /* Skip even lines */
|
|
lp20_ram[022] = LP20_RAM_TRN|LP20_RAM_PI|3; /* Skip triple lines */
|
|
lp20_ram[023] = LP20_RAM_TRN|LP20_RAM_PI|4; /* Skip one line */
|
|
lp20_ram[024] = LP20_RAM_TRN|LP20_RAM_PI|5;
|
|
/* Set parity of default RAM */
|
|
for (i = 0; i < 256; i++) {
|
|
lp20_ram[i] &= ~(LP20_RAM_RAP);
|
|
par = (lp20_ram[i] >> 8) ^ (lp20_ram[i] >> 4) ^ (lp20_ram[i]);
|
|
par = (par >> 2) ^ par;
|
|
par ^= par >> 1;
|
|
if ((par & 1) == 0)
|
|
lp20_ram[i] |= LP20_RAM_RAP;
|
|
}
|
|
sim_cancel (uptr); /* deactivate unit */
|
|
if (uptr->flags & UNIT_ATT) {
|
|
lp20_update_ready(uptr, CS1_ONL, 0);
|
|
lp20_update_chkirq (uptr, 1, 0);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Attach routine */
|
|
|
|
t_stat lp20_attach (UNIT *uptr, CONST char *cptr)
|
|
{
|
|
t_stat r;
|
|
sim_switches |= SWMASK ('A'); /* Position to EOF */
|
|
r = attach_unit (uptr, cptr);
|
|
if (r == SCPE_OK) {
|
|
lp20_update_ready(uptr, CS1_ONL, 0);
|
|
lp20_update_chkirq (uptr, 1, 1);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/* Detach routine */
|
|
|
|
t_stat lp20_detach (UNIT *uptr)
|
|
{
|
|
sim_cancel (uptr); /* deactivate unit */
|
|
lp20_update_ready(uptr, 0, CS1_ONL);
|
|
lp20_update_chkirq (uptr, 1, 1);
|
|
return detach_unit (uptr);
|
|
}
|
|
|
|
/*
|
|
* Line printer routines
|
|
*/
|
|
|
|
t_stat
|
|
lp20_setlpp(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
t_value i;
|
|
t_stat r;
|
|
if (cptr == NULL)
|
|
return SCPE_ARG;
|
|
if (uptr == NULL)
|
|
return SCPE_IERR;
|
|
i = get_uint (cptr, 10, 100, &r);
|
|
if (r != SCPE_OK)
|
|
return SCPE_ARG;
|
|
uptr->capac = (t_addr)i;
|
|
uptr->LINE = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat
|
|
lp20_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 lp20_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
|
|
const char *cptr)
|
|
{
|
|
fprintf (st, "Line Printer (LPT)\n\n");
|
|
fprintf (st, "The line printer (LPT) writes data to a disk file. \n");
|
|
fprintf (st, "The Line printer can be configured to any number of lines per page with the:\n");
|
|
fprintf (st, " sim> SET %s LINESPERPAGE=n\n\n", dptr->name);
|
|
fprintf (st, "The default is 66 lines per page.\n\n");
|
|
fprintf (st, "The LP20 is a unibus device, various parameters can be changed on these devices\n");
|
|
fprintf (st, "\n The address of the device can be set with: \n");
|
|
fprintf (st, " sim> SET LP20 ADDR=octal default address= 775400\n");
|
|
fprintf (st, "\n The interrupt vector can be set with: \n");
|
|
fprintf (st, " sim> SET LP20 VECT=octal default 754\n");
|
|
fprintf (st, "\n The interrupt level can be set with: \n");
|
|
fprintf (st, " sim> SET LP20 BR=# # should be between 4 and 7.\n");
|
|
fprintf (st, "\n The unibus addaptor that the DZ is on can be set with:\n");
|
|
fprintf (st, " sim> SET LP20 CTL=# # can be either 1 or 3\n");
|
|
|
|
fprint_set_help (st, dptr);
|
|
fprint_show_help (st, dptr);
|
|
fprint_reg_help (st, dptr);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
const char *lp20_description (DEVICE *dptr)
|
|
{
|
|
return "LP20 line printer" ;
|
|
}
|
|
|
|
#endif
|