From d5f6e61be5885f73968eccb657712214c5d1b7aa Mon Sep 17 00:00:00 2001 From: Richard Cornwell Date: Mon, 10 Jan 2022 23:06:59 -0500 Subject: [PATCH] KA10: Added KS10 LP20 printer support. --- PDP10/ks10_lp.c | 1613 +++++++++++++++++++++++++-------------------- PDP10/ks10_uba.c | 20 +- PDP10/kx10_defs.h | 10 +- makefile | 3 +- 4 files changed, 930 insertions(+), 716 deletions(-) diff --git a/PDP10/ks10_lp.c b/PDP10/ks10_lp.c index a909558..566afbb 100644 --- a/PDP10/ks10_lp.c +++ b/PDP10/ks10_lp.c @@ -1,707 +1,906 @@ -/* 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 COL u4 -#define POS u5 -#define LINE u6 -#define LPST us9 -#define LPCNT us10 - -/* 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 2 /* 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_PZERO 0020000 /* Page counter zero */ -#define CS1_ERR 0100000 /* Errors */ - -/* 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 */ - -/* LPBA (765404) */ -/* Unibus address */ -#define LPBA u4 /* Save address in u4 */ - -/* LPBC (765406) */ -/* byte count */ -#define LPBC u5 /* Save byte count in u5 */ - -/* LPPAGC (765410) */ -/* Page count */ -#define LINE u6 /* Save line counter in u6 */ - -/* 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 - -#define UNIT_V_CT (UNIT_V_UF + 0) -#define UNIT_UC (1 << UNIT_V_CT) -#define UNIT_CT (3 << UNIT_V_CT) - - - -t_stat lp20_svc (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]; - -#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, 037, 0745, 5, 3, &lp20_read, &lp20_write, &lp20_vect, 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}, - {SAVEDATA(QUEUE, lp20_queue) }, - { 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}, - {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; - int base; - uint16 temp; - int ln; - TMLN *lp; - int i; - - addr &= dibp->uba_mask; - sim_debug(DEBUG_DETAIL, dptr, "DZ%o write %06o %06o %o\n", base, - addr, data, access); - - switch (addr & 016) { - case 000: /* LPCSA */ - break; - - case 002: /* LPCSB */ - break; - - case 004: /* LPBA */ - if (access == BYTE) { - if (addr & 1) - data = data | (uptr->LPBA & 0377); - else - data = (uptr->LPBA & 0177400) | data; - } - uptr->LPBA = (uptr->LPBA & 060000) | (data & 0177777); - break; - - case 006: /* LPBC */ - if (access == BYTE) { - if (addr & 1) - data = data | (uptr->LPBC & 0377); - else - data = (uptr->LPBC & 0177400) | data; - } - uptr->LPBC = (data & 0177777); - break; - - case 010: /* LPPAGC */ - if (access == BYTE) { - if (addr & 1) - data = data | (uptr->LINE & 0377); - else - data = (uptr->LINE & 0177400) | data; - } - uptr->LINE = (data & 0177777); - case 012: /* LPRDAT */ - case 014: /* LPCOL/LPCBUF */ - case 016: /* LPCSUM/LPPDAT */ - } - switch (addr & 06) { - case 0: - if (access == BYTE) { - temp = lp20_csr[base]; - if (addr & 1) - data = data | (temp & 0377); - else - data = (temp & 0177400) | data; - } - if (data & CLR) { - lp20_csr[base] = 0; - lp20_recv[base].in_ptr = lp20_recv[base].out_ptr = 0; - lp20_recv[base].len = 0; - /* Set up the current status */ - ln = base << 3; - for (i = 0; i < 8; i++) { - lp20_flags[ln + i] &= ~LINE_EN; - } - return 0; - } - lp20_csr[base] &= ~(TIE|SAE|RIE|MSE|CLR|MAINT); - lp20_csr[base] |= data & (TIE|SAE|RIE|MSE|MAINT); - break; - - case 2: - ln = (data & 07) + (base << 3); - lp20_ldsc[ln].rcve = (data & RXON) != 0; - break; - - case 4: - temp = 0; - ln = base << 3; - /* Set up the current status */ - for (i = 0; i < 8; i++) { - if (lp20_flags[ln + i] & LINE_EN) - temp |= LINE_ENB << i; - if (lp20_flags[ln + i] & DTR_FLAG) - temp |= DTR << i; - lp20_flags[ln + i] = 0; - } - if (access == BYTE) { - if (addr & 1) - data = data | (temp & 0377); - else - data = (temp & 0177400) | data; - } - for (i = 0; i < 8; i++) { - lp = &lp20_ldsc[ln + i]; - if ((data & (LINE_ENB << i)) != 0) - lp20_flags[ln + i] |= LINE_EN; - if ((data & (DTR << i)) != 0) - lp20_flags[ln + i] |= DTR_FLAG; - if (lp20_flags[ln + i] & DTR_FLAG) - tmxr_set_get_modem_bits(lp, TMXR_MDM_OUTGOING, 0, NULL); - else - tmxr_set_get_modem_bits(lp, 0, TMXR_MDM_OUTGOING, NULL); - sim_debug(DEBUG_DETAIL, dptr, "DZ%o sstatus %07o %o %o\n", base, data, i, lp20_flags[ln+i]); - } - break; - - case 6: - if (access == BYTE && (addr & 1) != 0) { - break; - } - - if ((lp20_csr[base] & TRDY) == 0) - break; - - ln = ((lp20_csr[base] & TLINE) >> TLINE_V) + (base << 3); - lp = &lp20_ldsc[ln]; - - if ((lp20_flags[ln] & LINE_EN) != 0 && lp->conn) { - int32 ch = data & 0377; - /* Try and send character */ - t_stat r = tmxr_putc_ln(lp, ch); - /* If character did not send, queue it */ - if (r == SCPE_STALL) - lp20_xmit[ln] = TRDY | ch; - } - break; - } - - lp20_csr[base] &= ~TRDY; - if ((lp20_csr[base] & MSE) == 0) - return 0; - ln = ((lp20_csr[base] & TLINE) >> TLINE_V) + (base << 3); - /* See if there is another line ready */ - for (i = 0; i < 8; i++) { - ln = (ln & 070) | ((ln + 1) & 07); - lp = &lp20_ldsc[ln]; - /* Connected and empty xmit_buffer */ - if ((lp20_flags[ln] & LINE_EN) != 0 && lp->conn && lp20_xmit[ln] == 0) { - lp20_csr[base] &= ~(TLINE); - lp20_csr[base] |= TRDY | ((ln & 07) << TLINE_V); - break; - } - } - lp20_checkirq(dibp); - return 0; -} - -int -lp20_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access) -{ - struct pdp_dib *dibp = (DIB *)dptr->ctxt; - int base; - uint16 temp; - int ln; - TMLN *lp; - int i; - - addr &= dibp->uba_mask; - switch (addr & 016) { - case 000: /* LPCSA */ - *data = lp20_csr; - break; - - case 002: /* LPCSB */ - *data = 0; - break; - - case 004: /* LPBA */ - *data = uptr->LPBA & 0177777; - break; - - case 006: /* LPBC */ - *data = uptr->LPBC; - break; - - case 010: /* LPPAGC */ - case 012: /* LPRDAT */ - case 014: /* LPCOL/LPCBUF */ - case 016: /* LPCSUM/LPPDAT */ - } - sim_debug(DEBUG_DETAIL, dptr, "DZ%o read %06o %06o %o\n", base, - addr, *data, access); - return 0; -} - - -void -lp20_printline(UNIT *uptr, int nl) { - int trim = 0; - - /* Trim off trailing blanks */ - while (uptr->COL >= 0 && lp20_buffer[uptr->COL - 1] == ' ') { - uptr->COL--; - trim = 1; - } - lp20_buffer[uptr->COL] = '\0'; - sim_debug(DEBUG_DETAIL, &lp20_dev, "LP output %d %d [%s]\n", uptr->COL, nl, - lp20_buffer); - /* Stick a carraige return and linefeed as needed */ - if (uptr->COL != 0 || trim) - lp20_buffer[uptr->COL++] = '\r'; - if (nl != 0) { - lp20_buffer[uptr->COL++] = '\n'; - uptr->LINE++; - } - if (nl > 0 && lp20_vfu[uptr->LINE] == 010000) { - lp20_buffer[uptr->COL++] = '\f'; - uptr->LINE = 1; - } else if (nl < 0 && uptr->LINE >= (int32)uptr->capac) { - uptr->LINE = 1; - } - - sim_fwrite(&lp20_buffer, 1, uptr->COL, uptr->fileref); - uptr->pos += uptr->COL; - uptr->COL = 0; - return; -} - - -/* Unit service */ -void -lp20_output(UNIT *uptr, char c) { - - if (c == 0) - return; - if (uptr->COL == 132) - lp20_printline(uptr, 1); - if ((uptr->flags & UNIT_UC) && (c & 0140) == 0140) - c &= 0137; - else if (c >= 040 && c < 0177) { /* If printable */ - lp20_buffer[uptr->COL++] = c; - } if (c == 011) { /* Tab */ - lp20_buffer[uptr->COL++] = ' '; - while ((uptr->COL & 07) != 0) - lp20_buffer[uptr->COL++] = ' '; - } - return; -} - -t_stat lp20_svc (UNIT *uptr) -{ - char ch; - uint16 ram_ch; - uint16 data1[5]; - - if ((uptr->flags & UNIT_ATT) == 0) - return SCPE_OK; - if (dte_dev.flags & TYPE_RSX20 && uptr->LPST & HDSFLG) { - data1[0] = 0; - - data1[1] = (uptr->LINE == 1) ? 01<<8: 0; - sim_debug(DEBUG_DETAIL, &dte_dev, "LPT status %06o \n", uptr->LPST); - if (uptr->LPST & EOFFLG) { - data1[0] |= 040 << 8; - uptr->LPCNT = 0; - } - if (uptr->LPST & INTFLG) { - data1[1] |= 02 << 8; - uptr->LPCNT = 0; - } - data1[2] = 0110200; - if (dte_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMLPT, 4, data1) == 0) - sim_activate(uptr, 1000); - uptr->LPST &= ~(HDSFLG); - } - - if (empty(&lp20_queue)) - return SCPE_OK; - while (not_empty(&lp20_queue)) { - ch = lp20_queue.buff[lp20_queue.out_ptr]; - inco(&lp20_queue); - ram_ch = lp20_ram[(int)ch]; - - /* If previous was delimiter or translation do it */ - if (uptr->LPST & DELFLG || (ram_ch &(LP20_RAM_DEL|LP20_RAM_TRN)) != 0) { - ch = ram_ch & LP20_RAM_CHR; - uptr->LPST &= ~DELFLG; - if (ram_ch & LP20_RAM_DEL) - uptr->LPST |= DELFLG; - } - /* Flag if interrupt set */ - if (ram_ch & LP20_RAM_INT) - uptr->LPST |= HDSFLG|INTFLG; - /* Check if paper motion */ - if (ram_ch & LP20_RAM_PI) { - int lines = 0; /* Number of new lines to output */ - /* Print any buffered line */ - lp20_printline(uptr, (ram_ch & 037) != 020); - sim_debug(DEBUG_DETAIL, &lp20_dev, "LP deque %02x %04x\n", - ch, 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 = 1; - 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 = 1; - } - lines++; - uptr->LINE++; - ram_ch--; - } - } - for(;lines > 0; lines--) { - sim_fwrite("\r\n", 1, 2, uptr->fileref); - uptr->pos+=2; - } - } else if (ch != 0) { - sim_debug(DEBUG_DETAIL, &lp20_dev, "LP deque %02x '%c' %04x\n", - ch, ch, ram_ch); - lp20_output(uptr, ch); - } - } - if (empty(&lp20_queue)) { - data1[0] = 0; - if (dte_queue(PRI_EMLBE, PRI_EMLPT, 1, data1) == 0) - sim_activate(uptr, 1000); - if (dte_dev.flags & TYPE_RSX20) { - if (uptr->LINE == 0) { - uptr->LPST |= HDSFLG; - sim_activate(uptr, 1000); - } - } - } - return SCPE_OK; -} - -/* Reset routine */ - -t_stat lp20_reset (DEVICE *dptr) -{ - UNIT *uptr = &lp20_unit; - int i; - uptr->POS = 0; - uptr->COL = 0; - uptr->LINE = 1; - /* Clear RAM & VFU */ - for (i = 0; i < 256; i++) { - lp20_ram[i] = 0; - lp20_vfu[i] = 0; - } - - /* Load default VFU into VFU */ - memcpy(&lp20_vfu, lp20_dvfu, sizeof(lp20_dvfu)); - 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; - sim_cancel (&lp20_unit); /* deactivate unit */ - return SCPE_OK; -} - -/* Attach routine */ - -t_stat lp20_attach (UNIT *uptr, CONST char *cptr) -{ - sim_switches |= SWMASK ('A'); /* Position to EOF */ - return attach_unit (uptr, cptr); -} - -/* Detach routine */ - -t_stat lp20_detach (UNIT *uptr) -{ - 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. The POS register specifies\n"); -fprintf (st, "the number of the next data item to be written. Thus, by changing POS, the\n"); -fprintf (st, "user can backspace or advance the printer.\n"); -fprintf (st, "The Line printer can be configured to any number of lines per page with the:\n"); -fprintf (st, " sim> SET %s0 LINESPERPAGE=n\n\n", dptr->name); -fprintf (st, "The default is 66 lines per page.\n\n"); -fprintf (st, "The device address of the Line printer can be changed\n"); -fprintf (st, " sim> SET %s0 DEV=n\n\n", dptr->name); -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 +/* 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 0030000 /* 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, char 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; +uint16 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; + int base; + uint16 temp; + int ln; + TMLN *lp; + int i; + + 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_GOE); + lp20_cs2 |= data & CS2_GOE; + } else { + lp20_cs2 &= ~(CS2_TEST); + lp20_cs2 |= data & CS2_TEST; + } + } 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_buf = data & 0377; + else + lp20_col = (data >> 8) & 0377; + } else { + lp20_buf = data & 0377; + lp20_col = (data >> 8) & 0377; + } + break; + + case 016: /* LPCSUM/LPPDAT */ + temp = lp20_ram[(int)lp20_buf]; + if (access == BYTE) { + if (addr & 1) + data = data | (temp & 0377); + else + data = (temp & 0177400) | data; + } + lp20_ram[(int)lp20_buf] = data; + 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; + int base; + uint16 temp; + int ln; + TMLN *lp; + int i; + + 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 */ + *data = lp20_ram[lp20_buf]; + break; + + case 014: /* LPCOL/LPCBUF */ + *data = ((uint16)lp20_col) << 8; + *data |= 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, char c) { + + if (c == 0) + return; + 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; + } else { + lp20_cs1 &= ~(CS1_DONE|CS1_ONL); + lp20_cs2 |= CS2_OFFL; + } + if (lp20_cs2 & CS2_ERR) + lp20_cs1 |= CS1_ERR; + if ((lp20_cs1 & CS1_IE) && (irq || (lp20_cs1 & CS1_DONE))) + uba_set_irq(dibp); + else + uba_clr_irq(dibp); +} + +/* + * 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); + } + 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; + uint16 par; + + if (fnc != FNC_TEST && (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, &lp20_buf) == 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 &= 0377; + lp20_ba = (lp20_ba + 1) & 0777777; + lp20_wcnt = (lp20_wcnt + 1) & 07777; + if (lp20_wcnt == 0) { + lp20_cs1 &= ~CS1_GO; + } + lp20_chksum += (uint8)(lp20_buf & 0377); + 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]; + /* Check parity on RAM */ + ram_ch &= ~(LP20_RAM_RAP); + par = (ram_ch >> 8) ^ (ram_ch >> 4) ^ ram_ch; + par = (par >> 2) ^ par; + par ^= par >> 1; + if ((par & 1) == 0) + ram_ch |= LP20_RAM_RAP; + if (ram_ch != lp20_ram[(int)lp20_buf]) + lp20_cs2 |= CS2_RPE; + /* 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 = 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, 1, 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; + } 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 & 0377) << 8); + lp20_index++; + } else { + lp20_ram[lp20_index] = (lp20_ram[lp20_index] & 0177400) | (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_OVFU |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_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_cs1 |= CS1_ONL; + lp20_cs2 &= ~CS2_OFFL; + } + return r; +} + +/* Detach routine */ + +t_stat lp20_detach (UNIT *uptr) +{ + sim_cancel (uptr); /* deactivate unit */ + lp20_cs1 &= ~(CS1_DONE|CS1_ONL|CS1_GO); + lp20_cs2 |= CS2_OFFL; + lp20_update_chkirq (uptr, 1, 0); + 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 diff --git a/PDP10/ks10_uba.c b/PDP10/ks10_uba.c index 3039401..f9fc64a 100644 --- a/PDP10/ks10_uba.c +++ b/PDP10/ks10_uba.c @@ -208,8 +208,8 @@ int uba_write_npr(t_addr addr, uint16 ctl, uint64 data) { int ubm = uba_device[ctl]; - t_addr oaddr = addr; uint32 map = uba_map[ubm][(077) & (addr >> 11)]; + t_addr oaddr = addr; if ((addr & 0400000) != 0) return 0; if ((map & MAP_VALID) == 0) @@ -227,6 +227,7 @@ uba_read_npr_byte(t_addr addr, uint16 ctl, uint16 *data) { int ubm = uba_device[ctl]; uint32 map = uba_map[ubm][(077) & (addr >> 11)]; + t_addr oaddr = addr; uint64 wd; if ((addr & 0400000) != 0) return 0; @@ -234,10 +235,12 @@ uba_read_npr_byte(t_addr addr, uint16 ctl, uint16 *data) return 0; addr = (map & PAGE_MASK) | (addr >> 2) & 0777; wd = M[addr]; - if (addr & 02) + sim_debug(DEBUG_DATA, &cpu_dev, "RD NPR B %08o %08o %012llo ", oaddr, addr, wd); + if ((oaddr & 02) == 0) wd >>= 18; - if (addr & 01) + if ((oaddr & 01)) wd >>= 8; + sim_debug(DEBUG_DATA, &cpu_dev, "%03llo ", wd & 0377); *data = (uint16)(wd & 0377); return 1; } @@ -247,6 +250,7 @@ uba_write_npr_byte(t_addr addr, uint16 ctl, uint16 data) { int ubm = uba_device[ctl]; uint32 map = uba_map[ubm][(077) & (addr >> 11)]; + t_addr oaddr = addr; uint64 wd; uint64 msk; uint64 buf; @@ -258,11 +262,11 @@ uba_write_npr_byte(t_addr addr, uint16 ctl, uint16 data) msk = 0377; buf = (uint64)(data & msk); wd = M[addr]; - if (addr & 02) { + if ((oaddr & 02) == 0) { buf <<= 18; msk <<= 18; } - if (addr & 01) { + if ((oaddr & 01)) { buf <<= 8; msk <<= 8; } @@ -277,6 +281,7 @@ uba_read_npr_word(t_addr addr, uint16 ctl, uint16 *data) { int ubm = uba_device[ctl]; uint32 map = uba_map[ubm][(077) & (addr >> 11)]; + t_addr oaddr = addr; uint64 wd; if ((addr & 0400000) != 0) return 0; @@ -284,7 +289,7 @@ uba_read_npr_word(t_addr addr, uint16 ctl, uint16 *data) return 0; addr = (map & PAGE_MASK) | (addr >> 2) & 0777; wd = M[addr]; - if (addr & 02) + if ((oaddr & 02) == 0) wd >>= 18; *data = (uint16)(wd & 0177777); return 1; @@ -295,6 +300,7 @@ uba_write_npr_word(t_addr addr, uint16 ctl, uint16 data) { int ubm = uba_device[ctl]; uint32 map = uba_map[ubm][(077) & (addr >> 11)]; + t_addr oaddr = addr; uint64 wd; uint64 msk; uint64 buf; @@ -306,7 +312,7 @@ uba_write_npr_word(t_addr addr, uint16 ctl, uint16 data) msk = 0177777; buf = (uint64)(data & msk); wd = M[addr]; - if (addr & 02) { + if ((oaddr & 02) == 0) { buf <<= 18; msk <<= 18; } diff --git a/PDP10/kx10_defs.h b/PDP10/kx10_defs.h index b5cda04..35499fc 100644 --- a/PDP10/kx10_defs.h +++ b/PDP10/kx10_defs.h @@ -460,6 +460,10 @@ extern void restore_pi_hold(); extern void set_pi_hold(); extern UNIT cpu_unit[]; extern UNIT ten11_unit[]; +#if KS +extern DEVICE lp20_dev; +extern DEVICE ch11_dev; +#endif #if KL extern DEVICE dte_dev; extern DEVICE lp20_dev; @@ -739,9 +743,13 @@ extern void ka10_lights_clear_aux (int); #define NUM_DEVS_NIA 1 #define NUM_DEVS_DN 0 #elif KS -#define NUM_DEVS_LP20 0 +#define NUM_DEVS_LP20 1 #define NUM_DEVS_DZ 4 #define NUM_DEVS_TCU 1 +#if KS_ITS +#define NUM_DEVS_IMP 0 /* KS_ITS */ +#define NUM_DEVS_CH11 0 /* KS_ITS */ +#endif #endif #if KA | KI #define NUM_DEVS_RC 1 diff --git a/makefile b/makefile index 38a3b6e..970a531 100644 --- a/makefile +++ b/makefile @@ -2155,7 +2155,8 @@ KL10_OPT = -DKL=1 -DUSE_INT64 -I $(KL10D) ${NETWORK_OPT} KS10D = ${SIMHD}/PDP10 KS10 = ${KS10D}/kx10_cpu.c ${KS10D}/kx10_sys.c ${KS10D}/kx10_disk.c \ ${KS10D}/ks10_cty.c ${KS10D}/ks10_uba.c ${KS10D}/kx10_rh.c \ - ${KS10D}/kx10_rp.c ${KS10D}/kx10_tu.c ${KS10D}/ks10_dz.c ${KS10D}/ks10_tcu.c + ${KS10D}/kx10_rp.c ${KS10D}/kx10_tu.c ${KS10D}/ks10_dz.c \ + ${KS10D}/ks10_tcu.c ${KS10D}/ks10_lp.c KS10_OPT = -DKS=1 -DUSE_INT64 -I $(KS10D) ${NETWORK_OPT} ATT3B2D = ${SIMHD}/3B2