diff --git a/PDP10/ks10_cty.c b/PDP10/ks10_cty.c index d3a265a..587a05f 100644 --- a/PDP10/ks10_cty.c +++ b/PDP10/ks10_cty.c @@ -192,6 +192,7 @@ t_stat cty_reset (DEVICE *dptr) M[CTY_OUT] = 0; M[KLINK_IN] = 0; M[KLINK_OUT] = 0; + M[CTY_SWITCH] = 0; return SCPE_OK; } diff --git a/PDP10/ks10_dz.c b/PDP10/ks10_dz.c index ad9df29..cc6834a 100644 --- a/PDP10/ks10_dz.c +++ b/PDP10/ks10_dz.c @@ -137,13 +137,13 @@ REG dz_reg[] = { MTAB dz_mod[] = { {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "addr", "addr", &uba_set_addr, uba_show_addr, - NULL, "Sets address of RH11" }, + NULL, "Sets address of DZ11" }, {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "vect", "vect", &uba_set_vect, uba_show_vect, - NULL, "Sets vect of RH11" }, + NULL, "Sets vect of DZ11" }, {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "br", "br", &uba_set_br, uba_show_br, - NULL, "Sets br of RH11" }, + NULL, "Sets br of DZ11" }, {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ctl", "ctl", &uba_set_ctl, uba_show_ctl, - NULL, "Sets br of RH11" }, + NULL, "Sets uba of DZ11" }, { MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, &dz_desc, "Disconnect a specific line" }, { UNIT_ATT, UNIT_ATT, "SUMMARY", NULL, @@ -184,11 +184,15 @@ dz_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access) TMLN *lp; int i; + if ((dptr->units[0].flags & UNIT_DIS) != 0) + return 1; addr &= dibp->uba_mask; + sim_debug(DEBUG_DETAIL, dptr, "DZ%o write %06o %06o %o l=%o\n", base, + addr, data, access, dz_desc.lines); if (addr < 010 || addr > 047) return 1; base = ((addr & 070) - 010) >> 3; - if ((int32)(addr & 070) >= dz_desc.lines) + if (((base + 1) * 8) > dz_desc.lines) return 1; sim_debug(DEBUG_DETAIL, dptr, "DZ%o write %06o %06o %o\n", base, addr, data, access); @@ -304,11 +308,13 @@ dz_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access) TMLN *lp; int i; + if ((dptr->units[0].flags & UNIT_DIS) != 0) + return 1; addr &= dibp->uba_mask; if (addr < 010 || addr > 047) return 1; base = ((addr & 070) - 010) >> 3; - if ((int32)(addr & 070) >= dz_desc.lines) + if (((base + 1) * 8) > dz_desc.lines) return 1; switch (addr & 06) { case 0: diff --git a/PDP10/ks10_lp.c b/PDP10/ks10_lp.c new file mode 100644 index 0000000..1e51f72 --- /dev/null +++ b/PDP10/ks10_lp.c @@ -0,0 +1,630 @@ +/* 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 + +#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 +*/ + +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, + NULL, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &lp20_help, NULL, NULL, &lp20_description +}; + +int +dz_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 & 06) { + case 0: + if (access == BYTE) { + temp = dz_csr[base]; + if (addr & 1) + data = data | (temp & 0377); + else + data = (temp & 0177400) | data; + } + if (data & CLR) { + dz_csr[base] = 0; + dz_recv[base].in_ptr = dz_recv[base].out_ptr = 0; + dz_recv[base].len = 0; + /* Set up the current status */ + ln = base << 3; + for (i = 0; i < 8; i++) { + dz_flags[ln + i] &= ~LINE_EN; + } + return 0; + } + dz_csr[base] &= ~(TIE|SAE|RIE|MSE|CLR|MAINT); + dz_csr[base] |= data & (TIE|SAE|RIE|MSE|MAINT); + break; + + case 2: + ln = (data & 07) + (base << 3); + dz_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 (dz_flags[ln + i] & LINE_EN) + temp |= LINE_ENB << i; + if (dz_flags[ln + i] & DTR_FLAG) + temp |= DTR << i; + dz_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 = &dz_ldsc[ln + i]; + if ((data & (LINE_ENB << i)) != 0) + dz_flags[ln + i] |= LINE_EN; + if ((data & (DTR << i)) != 0) + dz_flags[ln + i] |= DTR_FLAG; + if (dz_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, dz_flags[ln+i]); + } + break; + + case 6: + if (access == BYTE && (addr & 1) != 0) { + break; + } + + if ((dz_csr[base] & TRDY) == 0) + break; + + ln = ((dz_csr[base] & TLINE) >> TLINE_V) + (base << 3); + lp = &dz_ldsc[ln]; + + if ((dz_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) + dz_xmit[ln] = TRDY | ch; + } + break; + } + + dz_csr[base] &= ~TRDY; + if ((dz_csr[base] & MSE) == 0) + return 0; + ln = ((dz_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 = &dz_ldsc[ln]; + /* Connected and empty xmit_buffer */ + if ((dz_flags[ln] & LINE_EN) != 0 && lp->conn && dz_xmit[ln] == 0) { + dz_csr[base] &= ~(TLINE); + dz_csr[base] |= TRDY | ((ln & 07) << TLINE_V); + break; + } + } + dz_checkirq(dibp); + return 0; +} + +int +dz_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 & 06) { + case 0: + *data = dz_csr[base]; + break; + + case 2: + *data = 0; + if ((dz_csr[base] & MSE) == 0) + return 0; + dz_csr[base] &= ~(SA|RDONE); + if (!empty(&dz_recv[base])) { + *data = dz_recv[base].buff[dz_recv[base].out_ptr]; + inco(&dz_recv[base]); + dz_recv[base].len = 0; + } + if (!empty(&dz_recv[base])) + dz_csr[base] |= RDONE; + dz_checkirq(dibp); + break; + + case 4: + temp = 0; + ln = base << 3; + /* Set up the current status */ + for (i = 0; i < 8; i++) { + sim_debug(DEBUG_DETAIL, dptr, "DZ%o status %o %o\n", base, i, dz_flags[ln+i]); + if (dz_flags[ln + i] & LINE_EN) + temp |= LINE_ENB << i; + if (dz_flags[ln + i] & DTR_FLAG) + temp |= DTR << i; + } + *data = temp; + break; + + case 6: + temp = (uint16)dz_ring[base]; + ln = base << 3; + for (i = 0; i < 8; i++) { + lp = &dz_ldsc[ln + i]; + if (lp->conn) + temp |= CO << i; + } + dz_ring[base] = 0; + *data = temp; + break; + } + 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 diff --git a/PDP10/ks10_tcu.c b/PDP10/ks10_tcu.c new file mode 100644 index 0000000..0f02a8e --- /dev/null +++ b/PDP10/ks10_tcu.c @@ -0,0 +1,137 @@ +/* ks10_tcu.c: PDP-10 + + 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_TCU +#define NUM_DEVS_TCU 0 +#endif + +#if (NUM_DEVS_TCU > 0) + +#define UNIT_V_Y2K (UNIT_V_UF + 0) /* Y2K compliant OS */ +#define UNIT_Y2K (1u << UNIT_V_Y2K) + + +int tcu_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access); +int tcu_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access); +t_stat tcu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *tcu_description (DEVICE *dptr); +DIB tcu_dib = { 0760770, 07, 0, 0, 3, &tcu_read, &tcu_write, NULL, 0, 0 }; + +UNIT tcu_unit = {UDATA (NULL, UNIT_IDLE+UNIT_DISABLE, 0)}; + +MTAB tcu_mod[] = { + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "addr", "addr", &uba_set_addr, uba_show_addr, + NULL, "Sets address of TCU" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ctl", "ctl", &uba_set_ctl, uba_show_ctl, + NULL, "Sets uba of TCU" }, + { UNIT_Y2K, 0, "non Y2K OS", "NOY2K", NULL }, + { UNIT_Y2K, UNIT_Y2K, "Y2K OS", "Y2K", NULL }, + { 0 } + }; + +DEVICE tcu_dev = { + "TIM", &tcu_unit, NULL, tcu_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, NULL, NULL, NULL, NULL, + &tcu_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &tcu_help, NULL, NULL, &tcu_description + }; + + +/* Time can't be set in device */ +int +tcu_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access) +{ + if ((dptr->units[0].flags & UNIT_DIS) != 0) + return 1; + + return 0; +} + +/* Read current time of day */ +int +tcu_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access) +{ + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + time_t curtim; + struct tm *tptr; + + if ((dptr->units[0].flags & UNIT_DIS) != 0) + return 1; + + /* Get time */ + curtim = sim_get_time (NULL); + tptr = localtime (&curtim); + if (tptr == NULL) + return 0; + if ((tptr->tm_year > 99) && !(dptr->units[0].flags & UNIT_Y2K)) + tptr->tm_year = 99; /* Y2K prob? */ + + switch (addr & 06) { + + case 0: /* year/month/day */ + *data = (((tptr->tm_year) & 0177) << 9) | + (((tptr->tm_mon + 1) & 017) << 5) | + ((tptr->tm_mday) & 037); + break; + + case 2: /* hour/minute */ + *data = (((tptr->tm_hour) & 037) << 8) | + ((tptr->tm_min) & 077); + break; + + case 4: /* second */ + *data = (tptr->tm_sec) & 077; + break; + + case 6: /* status */ + *data = 0200; + break; + } + + sim_debug(DEBUG_DETAIL, dptr, "TCU read %06o %06o %o\n", + addr, *data, access); + return 0; +} + + +t_stat tcu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +return SCPE_OK; +} + +const char *tcu_description (DEVICE *dptr) +{ +return "TCU150 Time of day clock"; +} + +#endif diff --git a/PDP10/ks10_uba.c b/PDP10/ks10_uba.c index b5e1b8f..f675776 100644 --- a/PDP10/ks10_uba.c +++ b/PDP10/ks10_uba.c @@ -303,6 +303,28 @@ uba_clr_irq(DIB *idev) clr_interrupt(idev->uba_ctl<<2); } +void +uba_reset() +{ + DEVICE *dptr; + int i; + + /* Clear the Unibus map */ + for (i = 0; i < 64; i++) { + uba_map[0][i] = 0; + uba_map[1][i] = 0; + } + uba_status[0] = 0; + uba_status[1] = 0; + + for(i = 0; (dptr = sim_devices[i]) != NULL; i++) { + DIB *dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + continue; + /* Clear any pending interrupt */ + dibp->uba_irq_pend = 0; + } +} t_addr uba_get_vect(t_addr addr, int lvl, int *dev, int *new_lvl) diff --git a/PDP10/kx10_cpu.c b/PDP10/kx10_cpu.c index b50709c..74b79a7 100644 --- a/PDP10/kx10_cpu.c +++ b/PDP10/kx10_cpu.c @@ -1781,7 +1781,7 @@ load_tlb(int uf, int page, int wr) sim_interval--; data = M[dbr + pg]; if ((page & 02) == 0) - data &= ~0020000000000LL; + data &= ~(0020000LL << 18); else data &= ~0020000LL; M[dbr + pg] = data; @@ -1791,21 +1791,25 @@ load_tlb(int uf, int page, int wr) pg = 0; switch(data >> 16) { case 0: - fault_data = (data >> 16) << 30; + fault_data = (data >> 16) << 28; + if (wr) { + fault_data |= 010000LL << 18; + } page_fault = 1; return 0; /* No access */ case 1: /* Read Only */ case 2: /* R/W First */ if (wr) { - fault_data = (data >> 16) << 30; + fault_data = (data >> 16) << 28; + fault_data |= 010000LL << 18; page_fault = 1; return 0; } pg = KL_PAG_A; - if (data & 0400000) + if (data & (2LL << 16)) pg |= KL_PAG_S; /* Indicate writable */ break; - case 3: pg = KL_PAG_A|KL_PAG_W|KL_PAG_S; break; /* R/W */ + case 3: pg = KL_PAG_A|KL_PAG_W; break; /* R/W */ } pg |= (data & 001777) << 1; /* Create 2 page table entries. */ @@ -2071,8 +2075,6 @@ int page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int if (data == 0) { data = load_tlb(uf | upmp, page, wr); if (data == 0 && page_fault) { - if (fault_data != 0) - return 0; fault_data |= ((uint64)addr); if (uf) /* U */ fault_data |= SMASK; @@ -2121,6 +2123,11 @@ int page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int /* Check for access error */ if ((data & KL_PAG_A) == 0 || (wr & ((data & KL_PAG_W) == 0))) { #if KS_ITS + /* Access bits: + * KL_PAG_A means valid page. + * KL_PAG_S means read write first + * KL_PAG_W means read/write + */ if (QITS) { /* Remap the flag bits */ fault_data = (uint64)addr; @@ -2130,13 +2137,14 @@ int page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int } else { e_tlb[page] = 0; } - if ((data & KL_PAG_A) != 0) { - if ((data & KL_PAG_S) != 0) { - fault_data |= 004000LL << 18; - if ((data & KL_PAG_W) != 0) - fault_data |= 002000LL << 18; - } else { - fault_data |= 002000LL << 18; + /* Check if accessable */ + if (wr) { + if ((data & KL_PAG_A) != 0) { + if ((data & KL_PAG_S) != 0) { + fault_data |= 004000LL << 18; /* PF2.9 */ + } else if ((data & KL_PAG_W) == 0) { + fault_data |= 002000LL << 18; /* PF2.8 */ + } } } if (wr) @@ -2170,15 +2178,6 @@ int page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int /* * Register access on KS 10 */ -#if 0 -uint64 get_reg(int reg) { - return FM[fm_sel|(reg & 017)]; -} - -void set_reg(int reg, uint64 value) { - FM[fm_sel|(reg & 017)] = value; -} -#endif #define get_reg(reg) FM[fm_sel|((reg) & 017)] #define set_reg(reg, value) FM[fm_sel|((reg) & 017)] = (value) @@ -2709,18 +2708,9 @@ int page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int /* * Register access on KL 10 */ -#if 0 -uint64 get_reg(int reg) { - return FM[fm_sel|(reg & 017)]; -} - -void set_reg(int reg, uint64 value) { - FM[fm_sel|(reg & 017)] = value; -} -#else #define get_reg(reg) FM[fm_sel|((reg) & 017)] + #define set_reg(reg, value) FM[fm_sel|((reg) & 017)] = (value) -#endif int Mem_read(int flag, int cur_context, int fetch, int mod) { t_addr addr; @@ -11403,6 +11393,9 @@ fetch_opr: /* BLKI RDERA */ case 2: /* PAG */ case 3: /* CCA */ + MB = 0; + if (Mem_write(pi_cycle, 0)) + goto last; break; case 4: /* TIM */ @@ -11674,6 +11667,14 @@ last: #if KS_ITS if (QITS) { AB = eb_ptr + 0440; + if (PIH != 0) { +fprintf(stderr, "PIH = %03o\n\r", PIH); + for(f = 0100; f != 0; f >>= 1) { + if (f & PIH) + break; + AB += 3; + } + } } else #endif AB = ub_ptr + 0500; @@ -12105,12 +12106,10 @@ do_byte_setup(int n, int wr, int *pos, int *sz) set_reg(n+2, val2); /* Read final value */ -// ptr_flg = 1; if (Mem_read(0, 0, 0, wr)) { ptr_flg = BYF5 = 0; return 1; } - // ptr_flg = 0; return 0; } @@ -12129,7 +12128,7 @@ load_byte(int n, uint64 *data, uint64 fill, int cnt) } /* Fetch Pointer word */ -ptr_flg = 1; + ptr_flg = 1; if (do_byte_setup(n, 0, &p, &s)) goto back; @@ -13233,7 +13232,7 @@ pag_reload = ac_stack = 0; fm_sel = small_user = user_addr_cmp = page_enable = 0; #else fm_sel = prev_ctx = user_addr_cmp = page_enable = t20_page = 0; -//irq_enable = irq_flags = 0; +irq_enable = irq_flags = 0; #if KL sect = cur_sect = pc_sect = 0; #endif @@ -13245,6 +13244,7 @@ exec_map = 0; for(i=0; i < 128; dev_irq[i++] = 0); #if KS int_cur = int_val = 0; +uba_reset(); #endif sim_brk_types = SWMASK('E') | SWMASK('W') | SWMASK('R'); diff --git a/PDP10/kx10_defs.h b/PDP10/kx10_defs.h index b9dbf9b..01573a6 100644 --- a/PDP10/kx10_defs.h +++ b/PDP10/kx10_defs.h @@ -518,6 +518,7 @@ extern DEVICE mtc_dev; extern DEVICE dsk_dev; extern DEVICE dcs_dev; extern DEVICE dz_dev; +extern DEVICE tcu_dev; #if KS @@ -579,6 +580,7 @@ void uba_set_parity(uint16 ctl); uint16 uba_rh_vect(struct pdp_dib *dibp); int uba_rh_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access); int uba_rh_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access); +void uba_reset(); t_stat uba_set_addr(UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat uba_show_addr (FILE *st, UNIT *uptr, int32 val, CONST void *desc); @@ -736,6 +738,7 @@ extern void ka10_lights_clear_aux (int); #elif KS #define NUM_DEVS_LP20 0 #define NUM_DEVS_DZ 4 +#define NUM_DEVS_TCU 1 #endif #if KA | KI #define NUM_DEVS_RC 1 diff --git a/PDP10/kx10_rh.c b/PDP10/kx10_rh.c index 1a76c3a..800d5a6 100644 --- a/PDP10/kx10_rh.c +++ b/PDP10/kx10_rh.c @@ -228,13 +228,13 @@ uba_rh_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access) { switch(addr) { case 000: /* CS1 */ if (access == BYTE) { - if (addr & 1) { - rhc->dev_read(dptr, rhc, 0, &temp); - data = data | (rhc->cs1 & 0377) | (temp & 076); - } else { - data = ((rhc->cda & 0600000) >> 8) | data; - } - } + if (addr & 1) { + rhc->dev_read(dptr, rhc, 0, &temp); + data = data | (rhc->cs1 & 0377) | (temp & 076); + } else { + data = ((rhc->cda & 0600000) >> 8) | data; + } + } rhc->cs1 &= ~(CS1_IE); rhc->cs1 |= data & (CS1_IE); rhc->cda = ((data << 8) & 0600000) | (rhc->cda & 0177777); diff --git a/PDP10/kx10_rp.c b/PDP10/kx10_rp.c index da6af3e..c40131c 100644 --- a/PDP10/kx10_rp.c +++ b/PDP10/kx10_rp.c @@ -365,7 +365,7 @@ MTAB rp_mod[] = { {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "br", "br", &uba_set_br, uba_show_br, NULL, "Sets br of RH11" }, {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ctl", "ctl", &uba_set_ctl, uba_show_ctl, - NULL, "Sets br of RH11" }, + NULL, "Sets uba of RH11" }, #endif {0} }; @@ -1146,6 +1146,8 @@ rp_boot(int32 unit_num, DEVICE * rptr) regs[RPDC] = (int32)((rp_buf[0][04] >> 24) << DC_V_CY); len = (int)(((rp_buf[0][05] & 077) * 4) & RMASK); } +if (len == 0) + len = 4; /* Read len sectors into address 1000 */ addr = 01000; for (; len > 0; len--) { @@ -1165,7 +1167,7 @@ rp_boot(int32 unit_num, DEVICE * rptr) } } } - /* Start location, and set up load infor */ + /* Start location, and set up load info */ word = 01000; M[036] = rhc->dib->uba_addr | (rhc->dib->uba_ctl << 18); M[037] = unit_num; @@ -1216,7 +1218,6 @@ t_stat rp_attach (UNIT *uptr, CONST char *cptr) { t_stat r; DEVICE *rptr; -// DIB *dib; int ctlr; uint16 *regs = (uint16 *)(uptr->up7); @@ -1230,7 +1231,6 @@ t_stat rp_attach (UNIT *uptr, CONST char *cptr) #if KS ctlr = 0; #else -// dib = (DIB *) rptr->ctxt; for (ctlr = 0; rh[ctlr].dev_num != 0; ctlr++) { if (rh[ctlr].dev == rptr) break; diff --git a/PDP10/kx10_sys.c b/PDP10/kx10_sys.c index f2d900e..9c7f87b 100644 --- a/PDP10/kx10_sys.c +++ b/PDP10/kx10_sys.c @@ -221,6 +221,9 @@ DEVICE *sim_devices[] = { #if NUM_DEVS_DZ > 0 &dz_dev, #endif +#if NUM_DEVS_TCU > 0 + &tcu_dev, +#endif #if NUM_DEVS_DN > 0 &dn_dev, #endif diff --git a/PDP10/kx10_tu.c b/PDP10/kx10_tu.c index d522642..5cbb020 100644 --- a/PDP10/kx10_tu.c +++ b/PDP10/kx10_tu.c @@ -229,7 +229,7 @@ MTAB tu_mod[] = { {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "br", "br", &uba_set_br, uba_show_br, NULL, "Sets br of RH11" }, {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ctl", "ctl", &uba_set_ctl, uba_show_ctl, - NULL, "Sets br of RH11" }, + NULL, "Sets uba of RH11" }, #endif {0} }; diff --git a/makefile b/makefile index 17ef818..38a3b6e 100644 --- a/makefile +++ b/makefile @@ -2155,7 +2155,7 @@ 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}/kx10_rp.c ${KS10D}/kx10_tu.c ${KS10D}/ks10_dz.c ${KS10D}/ks10_tcu.c KS10_OPT = -DKS=1 -DUSE_INT64 -I $(KS10D) ${NETWORK_OPT} ATT3B2D = ${SIMHD}/3B2