simh-testsetgenerator/PDP10/ks10_lp.c
Richard Cornwell 91aee9b39f KA10: Release 3 of PDP10 simulators. KS10 support added.
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.
2022-02-19 20:25:44 -05:00

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