diff --git a/PDP10/kl10_dn.c b/PDP10/kl10_dn.c new file mode 100644 index 0000000..4c93228 --- /dev/null +++ b/PDP10/kl10_dn.c @@ -0,0 +1,1184 @@ +/* kl10_dn.c: KL-10 DN network interface. + + Copyright (c) 2019-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" +#include + +#ifndef NUM_DEVS_DN +#define NUM_DEVS_DN 0 +#endif + +#if KL +#if (NUM_DEVS_DN > 0) +#define UNIT_DUMMY (1 << UNIT_V_UF) + +#define DTE_DEVNUM 0204 +#define DEV_V_OS (DEV_V_UF + 1) /* Type RSX10/RSX20 */ +#define DEV_M_OS (1 << DEV_V_OS) +#define TYPE_RSX10 (0 << DEV_V_OS) +#define TYPE_RSX20 (1 << DEV_V_OS) + + + +/* DTE10 CONI bits */ + +#define DTE_RM 00100000 /* Restricted mode */ +#define DTE_D11 00040000 /* Dead-11 */ +#define DTE_11DB 00020000 /* TO11 Door bell request */ +#define DTE_10DB 00001000 /* TO10 Door bell request */ +#define DTE_11ER 00000400 /* Error during TO11 transfer */ +#define DTE_11DN 00000100 /* TO 11 transfer done */ +#define DTE_10DN 00000040 /* TO 10 transfer done */ +#define DTE_10ER 00000020 /* Error during TO10 transfer */ +#define DTE_PIE 00000010 /* PIO enabled */ +#define DTE_PIA 00000007 /* PI channel assigment */ + +/* internal flags */ +#define DTE_11RELD 01000000 /* Reload 11. */ +#define DTE_TO11 02000000 /* Transfer to 11 */ +#define DTE_SEC 04000000 /* In secondary protocol */ +#define DTE_IND 010000000 /* Next transfer will be indirect */ +#define DTE_SIND 020000000 /* Send indirect data next */ + +/* DTE CONO bits */ +#define DTE_CO11DB 0020000 /* Set TO11 Door bell */ +#define DTE_CO11CR 0010000 /* Clear reload 11 button */ +#define DTE_CO11SR 0004000 /* Set reload 11 button */ +#define DTE_CO10DB 0001000 /* Clear TO10 Door bell */ +#define DTE_CO11CL 0000100 /* Clear TO11 done and error */ +#define DTE_CO10CL 0000040 /* Clear TO10 done and error */ +#define DTE_PIENB 0000020 /* Load PI and enable bit */ + +/* DTE DATAO */ +#define DTE_TO10IB 010000 /* Interrupt after transfer */ +#define DTE_TO10BC 007777 /* Byte count for transfer */ + +/* Secondary protocol addresses */ +#define SEC_DTFLG 0444 /* Operation complete flag */ +#define SEC_DTCLK 0445 /* Clock interrupt flag */ +#define SEC_DTCI 0446 /* Clock interrupt instruction */ +#define SEC_DTT11 0447 /* 10 to 11 argument */ +#define SEC_DTF11 0450 /* 10 from 11 argument */ +#define SEC_DTCMD 0451 /* To 11 command word */ +#define SEC_DTSEQ 0452 /* Operation sequence number */ +#define SEC_DTOPR 0453 /* Operational DTE # */ +#define SEC_DTCHR 0454 /* Last typed character */ +#define SEC_DTMTD 0455 /* Monitor tty output complete flag */ +#define SEC_DTMTI 0456 /* Monitor tty input flag */ +#define SEC_DTSWR 0457 /* 10 switch register */ + +#define SEC_PGMCTL 00400 +#define SEC_ENDPASS 00404 +#define SEC_LOOKUP 00406 +#define SEC_RDWRD 00407 +#define SEC_RDBYT 00414 +#define SEC_ESEC 00440 +#define SEC_EPRI 00500 +#define SEC_ERTM 00540 +#define SEC_CLKCTL 01000 +#define SEC_CLKOFF 01000 +#define SEC_CLKON 01001 +#define SEC_CLKWT 01002 +#define SEC_CLKRD 01003 +#define SEC_RDSW 01400 +#define SEC_CLRDDT 03000 +#define SEC_SETDDT 03400 +#define SEC_MONO 04000 +#define SEC_MONON 04400 +#define SEC_SETPRI 05000 +#define SEC_RTM 05400 +#define SEC_CMDMSK 07400 +#define DTE_MON 00000001 /* Save in unit1 STATUS */ +#define SEC_CLK 00000002 /* Clock enabled */ + +/* Primary or Queued protocol addresses */ +#define PRI_CMTW_0 0 +#define PRI_CMTW_PPT 1 /* Pointer to com region */ +#define PRI_CMTW_STS 2 /* Status word */ +#define PRI_CMT_PWF SMASK /* Power failure */ +#define PRI_CMT_L11 BIT1 /* Load 11 */ +#define PRI_CMT_INI BIT2 /* Init */ +#define PRI_CMT_TST BIT3 /* Valid examine bit */ +#define PRI_CMT_QP 020000000LL /* Do Queued protocol */ +#define PRI_CMT_FWD 001000000LL /* Do full word transfers */ +#define PRI_CMT_IP RSIGN /* Indirect transfer */ +#define PRI_CMT_TOT 0200000LL /* TOIT bit */ +#define PRI_CMT_10IC 0177400LL /* TO10 IC for queued transfers */ +#define PRI_CMT_11IC 0000377LL /* TO11 IC for queued transfers */ +#define PRI_CMTW_CNT 3 /* Queue Count */ +#define PRI_CMTW_KAC 5 /* Keep alive count */ +#define PRI_IND_FLG 0100000 /* Flag function as indirect */ + +#define PRI_EM2EI 001 /* Initial message to 11 */ +#define PRI_EM2TI 002 /* Replay to initial message. */ +#define PRI_EMSTR 003 /* String data */ +#define PRI_EMLNC 004 /* Line-Char */ +#define PRI_EMRDS 005 /* Request device status */ +#define PRI_EMOPS 006 +#define PRI_EMHDS 007 /* Here is device status */ +#define PRI_EMRDT 011 /* Request Date/Time */ +#define PRI_EMHDR 012 /* Here is date and time */ +#define PRI_EMFLO 013 /* Flush output */ +#define PRI_EMSNA 014 /* Send all (ttys) */ +#define PRI_EMDSC 015 /* Dataset connect */ +#define PRI_EMHUD 016 /* Hang up dataset */ +#define PRI_EMLBE 017 /* Acknowledge line */ +#define PRI_EMXOF 020 /* XOFF line */ +#define PRI_EMXON 021 /* XON line */ +#define PRI_EMHLS 022 /* Here is line speeds */ +#define PRI_EMHLA 023 /* Here is line allocation */ +#define PRI_EMRBI 024 /* Reboot information */ +#define PRI_EMAKA 025 /* Ack ALL */ +#define PRI_EMTDO 026 /* Turn device On/Off */ +#define PRI_EMEDR 027 /* Enable/Disable line */ +#define PRI_EMLDR 030 /* Load LP RAM */ +#define PRI_EMLDV 031 /* Load LP VFU */ + +#define PRI_EMCTY 001 /* Device code for CTY */ +#define PRI_EMDL1 002 /* DL11 */ +#define PRI_EMDH1 003 /* DH11 #1 */ +#define PRI_EMDLS 004 /* DLS (all ttys combined) */ +#define PRI_EMLPT 005 /* Front end LPT */ +#define PRI_EMCDR 006 /* CDR */ +#define PRI_EMCLK 007 /* Clock */ +#define PRI_EMFED 010 /* Front end device */ +#define PRI_NCL 011 /* NCL device */ +#define PRI_DN60 012 /* DN60 */ +#define PRI_CTYDV 000 /* Line number for CTY */ +#define NUM_DLS 5 /* Number of first DH Line */ + + +extern int32 tmxr_poll; +t_stat dn_devio(uint32 dev, uint64 *data); +t_addr dn_devirq(uint32 dev, t_addr addr); +void dn_second(UNIT *uptr); +void dn_primary(UNIT *uptr); +void dn_transfer(UNIT *uptr); +void dn_function(UNIT *uptr); +void dn_input(); +int dn_start(UNIT *uptr); +int dn_queue(int func, int dev, int dcnt, uint16 *data); +t_stat dni_svc (UNIT *uptr); +t_stat dn_svc (UNIT *uptr); +t_stat dno_svc (UNIT *uptr); +t_stat dnrtc_srv(UNIT * uptr); +t_stat dn_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat dn_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat dn_reset (DEVICE *dptr); +t_stat dn_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *dn_description (DEVICE *dptr); +extern uint64 SW; /* Switch register */ + +extern CONST char *pri_name[]; + +#define STATUS u3 +#define CNT u4 + +extern uint32 eb_ptr; + +struct _dn_queue { + int dptr; /* Pointer to working item */ + uint16 cnt; /* Number of bytes in packet */ + uint16 func; /* Function code */ + uint16 dev; /* Dev code */ + uint16 spare; /* Dev code */ + uint16 dcnt; /* Data count */ + uint16 data[258]; /* Data packet */ + uint16 sdev; /* Secondary device code */ + uint16 sz; /* Byte size */ +} dn_in[32], dn_out[32]; + +int32 dn_in_ptr; +int32 dn_in_cmd; +int32 dn_out_ptr; +int32 dn_out_res; +int32 dn_base; /* Base */ +int32 dn_off; /* Our offset */ +int32 dn_dt10_off; /* Offset to 10 deposit region */ +int32 dn_et10_off; /* Offset to 10 examine region */ +int32 dn_et11_off; /* Offset to 11 examine region */ +int32 dn_proc_num; /* Our processor number */ + +struct _buffer { + int in_ptr; /* Insert pointer */ + int out_ptr; /* Remove pointer */ + char buff[256]; /* Buffer */ +} cty_in, cty_out; +int32 cty_done; + +#define full(q) ((((q)->in_ptr + 1) & 0xff) == (q)->out_ptr) +#define empty(q) ((q)->in_ptr == (q)->out_ptr) +#define not_empty(q) ((q)->in_ptr != (q)->out_ptr) +#define inco(q) (q)->out_ptr = ((q)->out_ptr + 1) & 0xff +#define inci(q) (q)->in_ptr = ((q)->in_ptr + 1) & 0xff + +DIB dn_dib[] = { + { DTE_DEVNUM|000, 1, dn_devio, dn_devirq}, +}; + +MTAB dn_mod[] = { + {MTAB_XTD|MTAB_VDV, TYPE_RSX10, NULL, "RSX10", &dn_set_type, NULL, + NULL, "Sets DTE to RSX10 mode"}, + {MTAB_XTD|MTAB_VDV, TYPE_RSX20, "RSX20", "RSX20", &dn_set_type, &dn_show_type, + NULL, "Sets DTE to RSX20 mode"}, + { 0 } + }; + +UNIT dn_unit[] = { + { UDATA (&dn_svc, TT_MODE_7B, 0), 100}, + { UDATA (&dno_svc, TT_MODE_7B, 0), 100}, + { UDATA (&dni_svc, TT_MODE_7B|UNIT_DIS, 0), 1000 }, + }; + +REG dn_reg[] = { + {SAVEDATA(IN, dn_in) }, + {SAVEDATA(OUT, dn_out) }, + {HRDATA(IN_PTR, dn_in_ptr, 32), REG_HRO}, + {HRDATA(IN_CMD, dn_in_cmd, 32), REG_HRO}, + {HRDATA(OUT_PTR, dn_out_ptr, 32), REG_HRO}, + {HRDATA(OUT_RES, dn_out_res, 32), REG_HRO}, + {HRDATA(BASE, dn_base, 32), REG_HRO}, + {HRDATA(OFF, dn_off, 32), REG_HRO}, + {HRDATA(DTOFF, dn_dt10_off, 32), REG_HRO}, + {HRDATA(ETOFF, dn_et10_off, 32), REG_HRO}, + {HRDATA(E1OFF, dn_et11_off, 32), REG_HRO}, + {HRDATA(PROC, dn_proc_num, 32), REG_HRO}, + {SAVEDATA(CTYIN, cty_in) }, + {SAVEDATA(CTYOUT, cty_out) }, + {HRDATA(DONE, cty_done, 8), REG_HRO}, + {HRDATAD(WRU, sim_int_char, 8, "interrupt character") }, + { 0 }, + }; + + +DEVICE dn_dev = { + "DN", dn_unit, dn_reg, dn_mod, + 4, 10, 31, 1, 8, 8, + NULL, NULL, &dn_reset, + NULL, NULL, NULL, &dn_dib, DEV_DEBUG, 0, dev_debug, + NULL, NULL, &dn_help, NULL, NULL, &dn_description + }; + + +t_stat dn_devio(uint32 dev, uint64 *data) { + uint32 res; + switch(dev & 3) { + case CONI: + *data = (uint64)(dn_unit[0].STATUS) & RMASK; + *data |= DTE_RM; /* Restricted mode */ + sim_debug(DEBUG_CONI, &dn_dev, "CTY %03o CONI %06o\n", dev, (uint32)*data); + break; + case CONO: + res = (uint32)(*data & RMASK); + clr_interrupt(dev); + if (res & DTE_PIENB) { + dn_unit[0].STATUS &= ~(DTE_PIA|DTE_PIE); + dn_unit[0].STATUS |= res & (DTE_PIA|DTE_PIE); + } + if (res & DTE_CO11CL) + dn_unit[0].STATUS &= ~(DTE_11DN|DTE_11ER); + if (res & DTE_CO10CL) { + dn_unit[0].STATUS &= ~(DTE_10DN|DTE_10ER); + dn_start(&dn_unit[0]); + } + if (res & DTE_CO10DB) + dn_unit[0].STATUS &= ~(DTE_10DB); + if (res & DTE_CO11CR) + dn_unit[0].STATUS &= ~(DTE_11RELD); + if (res & DTE_CO11SR) + dn_unit[0].STATUS |= (DTE_11RELD); + if (res & DTE_CO11DB) { + sim_debug(DEBUG_CONO, &dn_dev, "CTY Ring 11 DB\n"); + dn_unit[0].STATUS |= DTE_11DB; + sim_activate(&dn_unit[0], 200); + } + if (dn_unit[0].STATUS & (DTE_10DB|DTE_11DN|DTE_10DN|DTE_11ER|DTE_10ER)) + set_interrupt(dev, dn_unit[0].STATUS); + sim_debug(DEBUG_CONO, &dn_dev, "CTY %03o CONO %06o %06o\n", dev, + (uint32)*data, PC); + break; + case DATAI: + sim_debug(DEBUG_DATAIO, &dn_dev, "CTY %03o DATAI %06o\n", dev, + (uint32)*data); + break; + case DATAO: + sim_debug(DEBUG_DATAIO, &dn_dev, "CTY %03o DATAO %06o\n", dev, + (uint32)*data); + if (*data == 01365) { + dn_unit[0].STATUS |= DTE_SEC|DTE_10ER; + dn_unit[0].STATUS &= ~(DTE_10DB|DTE_IND|DTE_11DB); + break; + } + dn_unit[0].CNT = (*data & (DTE_TO10IB|DTE_TO10BC)); + dn_unit[0].STATUS |= DTE_TO11; + sim_activate(&dn_unit[0], 10); + break; + } + return SCPE_OK; +} + +/* Handle KL style interrupt vectors */ +t_addr +dn_devirq(uint32 dev, t_addr addr) { + return 0152; +} + +/* Handle TO11 interrupts */ +t_stat dn_svc (UNIT *uptr) +{ + /* Did the 10 knock? */ + if (uptr->STATUS & DTE_11DB) { + /* If in secondary mode, do that protocol */ + if (uptr->STATUS & DTE_SEC) + dn_second(uptr); + else + dn_primary(uptr); /* Retrieve data */ + } else if (uptr->STATUS & DTE_TO11) { + /* Does 10 want us to send it what we have? */ + dn_transfer(uptr); + } + return SCPE_OK; +} + +/* Handle secondary protocol */ +void dn_second(UNIT *uptr) { + uint64 word; + int32 ch; + uint32 base = 0; + +#if KI_22BIT + base = eb_ptr; +#endif + /* read command */ + word = M[SEC_DTCMD + base]; + /* Do it */ + sim_debug(DEBUG_DETAIL, &dn_dev, "CTY secondary %012llo\n", word); + switch(word & SEC_CMDMSK) { + default: + case SEC_MONO: /* Ouput character in monitor mode */ + ch = (int32)(word & 0177); + if (full(&cty_out)) { + sim_activate(uptr, 200); + return; + } + if (ch != 0) { + cty_out.buff[cty_out.in_ptr] = ch & 0x7f; + inci(&cty_out); + sim_activate(&dn_unit[1], 200); + } + M[SEC_DTCHR + base] = ch; + M[SEC_DTMTD + base] = FMASK; + break; + + case SEC_SETPRI: +enter_pri: + if (Mem_examine_word(1, 0, &word)) + break; + dn_proc_num = (word >> 24) & 037; + dn_base = dn_proc_num + 1; + dn_off = dn_base + (word & 0177777); + dn_dt10_off = 16; + dn_et10_off = dn_dt10_off + 16; + dn_et11_off = dn_base + 16; + uptr->STATUS &= ~DTE_SEC; + dn_in_ptr = dn_out_ptr = 0; + dn_in_cmd = dn_out_res = 0; + cty_done = 0; + /* Start input process */ + M[SEC_DTCMD + base] = 0; + M[SEC_DTFLG + base] = FMASK; + uptr->STATUS &= ~DTE_11DB; + return; + + case SEC_SETDDT: /* Read character from console */ + if (empty(&cty_in)) { + M[SEC_DTF11 + base] = 0; + M[SEC_DTMTI + base] = FMASK; + break; + } + ch = cty_in.buff[cty_in.out_ptr]; + inco(&cty_in); + M[SEC_DTF11 + base] = 0177 & ch; + M[SEC_DTMTI + base] = FMASK; + break; + + case SEC_CLRDDT: /* Clear DDT input mode */ + uptr->STATUS &= ~DTE_MON; + break; + + case SEC_MONON: + uptr->STATUS |= DTE_MON; + break; + + case SEC_RDSW: /* Read switch register */ + M[SEC_DTSWR + base] = SW; + M[SEC_DTF11 + base] = SW; + break; + + case SEC_PGMCTL: /* Program control: Used by KLDCP */ + switch(word) { + case SEC_ENDPASS: + case SEC_LOOKUP: + case SEC_RDWRD: + case SEC_RDBYT: + break; + case SEC_ESEC: + goto enter_pri; + case SEC_EPRI: + case SEC_ERTM: + break; + } + break; + + case SEC_CLKCTL: /* Clock control: Used by KLDCP */ + break; + } + /* Acknowledge command */ + M[SEC_DTCMD + base] = 0; + M[SEC_DTFLG + base] = FMASK; + uptr->STATUS &= ~DTE_11DB; + if (dn_dev.flags & TYPE_RSX20) { + uptr->STATUS |= DTE_10DB; + set_interrupt(DTE_DEVNUM, dn_unit[0].STATUS); + } +} + + +/* Handle primary protocol */ +void dn_primary(UNIT *uptr) { + uint64 word, iword; + int s; + int cnt; + struct _dn_queue *in; + uint16 data1, *dp; + + if ((uptr->STATUS & DTE_11DB) == 0) + return; + + /* Check if there is room for another packet */ + if (((dn_in_ptr + 1) & 0x1f) == dn_in_cmd) { + /* If not reschedule ourselves */ + sim_activate(uptr, 100); + return; + } + uptr->STATUS &= ~(DTE_11DB); + clr_interrupt(DTE_DEVNUM); + /* Check status word to see if valid */ + if (Mem_examine_word(1, dn_et11_off + PRI_CMTW_STS, &word)) { + uint32 base; +error: + base = 0; +#if KI_22BIT + base = eb_ptr; +#endif + /* If we can't read it, go back to secondary */ + M[SEC_DTFLG + base] = FMASK; + uptr->STATUS |= DTE_SEC; + uptr->STATUS &= ~DTE_11DB; + if (dn_dev.flags & TYPE_RSX20) { + uptr->STATUS |= DTE_10DB; + set_interrupt(DTE_DEVNUM, dn_unit[0].STATUS); + } + sim_debug(DEBUG_DETAIL, &dn_dev, "DTE: error %012llo\n", word); + return; + } + + if ((word & PRI_CMT_QP) == 0) { + goto error; + } + in = &dn_in[dn_in_ptr]; + /* Check if indirect */ + if ((word & PRI_CMT_IP) != 0) { + /* Transfer from 10 */ + if ((uptr->STATUS & DTE_IND) == 0) { + fprintf(stderr, "DTE out of sync\n\r"); + return; + } + /* Get size of transfer */ + if (Mem_examine_word(1, dn_et11_off + PRI_CMTW_CNT, &iword)) + goto error; + sim_debug(DEBUG_EXP, &dn_dev, "DTE: count: %012llo\n", iword); + in->dcnt = (uint16)(iword & 0177777); + /* Read in data */ + dp = &in->data[0]; + for (cnt = in->dcnt; cnt > 0; cnt --) { + /* Read in data */ + s = Mem_read_byte(0, dp, 0); + if (s == 0) + goto error; + in->sz = s; + sim_debug(DEBUG_DATA, &dn_dev, + "DTE: Read Idata: %06o %03o %03o %06o cnt=%o\n", + *dp, *dp >> 8, *dp & 0377, + ((*dp & 0377) << 8) | ((*dp >> 8) & 0377), cnt); + dp++; + if (s <= 8) + cnt--; + } + uptr->STATUS &= ~DTE_IND; + dn_in_ptr = (dn_in_ptr + 1) & 0x1f; + } else { + /* Transfer from 10 */ + in->dptr = 0; + in->dcnt = 0; + /* Read in count */ + if (!Mem_read_byte(1, &data1, 0)) + goto error; + in->cnt = data1; + cnt = in->cnt-2; + if (!Mem_read_byte(1, &data1, 0)) + goto error; + in->func = data1; + cnt -= 2; + if (!Mem_read_byte(1, &data1, 0)) + goto error; + in->dev = data1; + cnt -= 2; + if (!Mem_read_byte(1, &data1, 0)) + goto error; + in->spare = data1; + cnt -= 2; + sim_debug(DEBUG_DATA, &dn_dev, "DTE: Read CMD: %o c=%o f=%o %s d=%o\n", + dn_in_ptr, in->cnt, in->func, + ((in->func & 0377) > PRI_EMLDV)?"***": + pri_name[in->func & 0377], in->dev); + dp = &in->data[0]; + for (; cnt > 0; cnt -=2) { + /* Read in data */ + if (!Mem_read_byte(1, dp, 0)) + goto error; + sim_debug(DEBUG_DATA, &dn_dev, "DTE: Read data: %06o %03o %03o\n", + *dp, *dp >> 8, *dp & 0377); + dp++; + in->dcnt += 2; + } + if (in->func & PRI_IND_FLG) { + uptr->STATUS |= DTE_IND; + in->dcnt = in->data[0]; + in->sdev = (in->dcnt >> 8) & 0377; + in->dcnt &= 0377; + word |= PRI_CMT_TOT; + if (Mem_deposit_word(1, dn_dt10_off + PRI_CMTW_STS, &word)) + goto error; + } else { + dn_in_ptr = (dn_in_ptr + 1) & 0x1f; + } + } + word &= ~PRI_CMT_TOT; + if (Mem_deposit_word(1, dn_dt10_off + PRI_CMTW_STS, &word)) + goto error; + uptr->STATUS |= DTE_11DN; + set_interrupt(DTE_DEVNUM, uptr->STATUS); + dn_function(uptr); +} + +/* Process primary protocol packets */ +void +dn_function(UNIT *uptr) +{ + uint16 data1[32]; + int32 ch; + struct _dn_queue *cmd; + int func; + int dev; + + /* Check if queue is empty */ + while (dn_in_cmd != dn_in_ptr) { + if (((dn_out_res + 1) & 0x1f) == dn_out_ptr) { + sim_debug(DEBUG_DATA, &dn_dev, "DTE: func out full %d %d\n", + dn_out_res, dn_out_ptr); + return; + } + cmd = &dn_in[dn_in_cmd]; + dev = cmd->dev & 0377; + func = cmd->func & 0377; + sim_debug(DEBUG_DATA, &dn_dev, + "DTE: func %o %02o %s dev %o cnt %d dcnt %d\n", + dn_in_cmd, func, (func > PRI_EMLDV) ? "***" : pri_name[func], + cmd->dev, cmd->dcnt, cmd->dptr ); + switch (func) { + case PRI_EM2EI: /* Initial message to 11 */ +// data1[0] = PRI_CTYDV; + // if (dn_queue(PRI_EM2TI, PRI_EMCTY, 1, data1) == 0) + // return; + // data1[0] = 0; + // if (dn_queue(PRI_EMAKA, PRI_EMCLK, 1, data1) == 0) + // return; + break; + + case PRI_EM2TI: /* Replay to initial message. */ + case PRI_EMLBE: /* Acknowledge line */ + /* Should never get these */ + break; + + case PRI_EMHDR: /* Here is date and time */ + /* Ignore this function */ + break; + + case PRI_EMRDT: /* Request Date/Time */ + { + time_t t = sim_get_time(NULL); + struct tm *tm = localtime(&t); + int yr = tm->tm_year + 1900; + int tim = (((tm->tm_hour * 60) + tm->tm_min) * 60) + + tm->tm_sec; + data1[0] = 0177777; + data1[1] = ((yr & 0377) << 8) | ((yr >> 8) & 0377); + data1[2] = (tm->tm_mon) + ((tm->tm_mday - 1) << 8); + data1[3] = (((tm->tm_wday + 6) % 7)) + + (tm->tm_isdst ? 0200 << 8 : 0); + tim >>= 1; + data1[4] = ((tim & 0377) << 8) | ((tim >> 8) & 0377); + if (dn_queue(PRI_EMHDR | PRI_IND_FLG, PRI_EMCLK, 6, data1) == 0) + return; + } + break; + + case PRI_EMSTR: /* String data */ + + + /* Handle terminal data */ + if (dev == PRI_EMDLS) { + int ln = cmd->sdev; + struct _buffer *otty; + if (ln == PRI_CTYDV) + goto cty; + break; + } + + if (dev == PRI_EMCTY) { +cty: + sim_activate(&dn_unit[1], 100); + data1[0] = 0; + if (cmd->sz > 8) + cmd->dcnt += cmd->dcnt; + while (cmd->dptr < cmd->dcnt) { + ch = (int32)(cmd->data[cmd->dptr >> 1]); + if ((cmd->dptr & 1) == 0) + ch >>= 8; + ch &= 0177; + if (ch != 0) { + ch = sim_tt_outcvt( ch, TT_GET_MODE(uptr->flags)); + if (full(&cty_out)) + return; + cty_out.buff[cty_out.in_ptr] = (char)(ch & 0xff); + inci(&cty_out); + sim_debug(DEBUG_DATA, &dn_dev,"CTY queue %o\n",ch); + } + cmd->dptr++; + } + if (cmd->dptr != cmd->dcnt) + return; + } + break; + + case PRI_EMSNA: /* Send all (ttys) */ + /* Handle terminal data */ + if (dev == PRI_EMDLS || dev == PRI_EMCTY) { + struct _buffer *otty; + int ln; + while (cmd->dptr < cmd->dcnt) { + ch = (int32)(cmd->data[cmd->dptr >> 1]); + if ((cmd->dptr & 1) == 0) + ch >>= 8; + ch &= 0177; + if (ch != 0) { + sim_debug(DEBUG_DATA, &dn_dev, "SNA queue %o\n", ch); + ch = sim_tt_outcvt( ch, TT_GET_MODE(uptr->flags)); + if (!(full(&cty_out))) { + cty_out.buff[cty_out.in_ptr] = (char)(ch & 0xff); + inci(&cty_out); + } + } + cmd->dptr++; + } + if (cmd->dptr != cmd->dcnt) + return; + data1[0] = 0; + } + break; + + case PRI_EMLNC: /* Line-Char */ + if (dev == PRI_EMDLS) { + sim_activate(&dn_unit[1], 100); + while (cmd->dptr < cmd->dcnt) { + int ln; + ch = (int32)(cmd->data[cmd->dptr >> 1]); + ln = (ch >> 8); + ch &= 0177; + if (ch != 0 && ln == PRI_CTYDV) { + ch = sim_tt_outcvt( ch, TT_GET_MODE(uptr->flags)); + cty_out.buff[cty_out.in_ptr] = (char)(ch & 0xff); + inci(&cty_out); + if (((cty_out.in_ptr + 1) & 0xff) == cty_out.out_ptr) + return; + sim_debug(DEBUG_DATA, &dn_dev, "CTY queue %o\n", ch); + } + cmd->dptr+=2; + } + if (cmd->dptr != cmd->dcnt) + return; + } + break; + + case PRI_EMOPS: + break; + + case PRI_EMRDS: /* Request device status */ + if (dev == PRI_EMLPT) { + if (cmd->data[0] != 0) { + data1[0] = 2 << 8; + data1[1] = 0; + data1[2] = 0; + if (dn_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMLPT, + 3, data1) == 0) + return; + } else { + data1[0] = 2 << 8; + data1[1] = 0; + data1[2] = 0; + if (dn_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMLPT, + 3, data1) == 0) + return; + } + } + if (dev == PRI_EMCTY) { + data1[0] = 0; + data1[1] = 0; + if (dn_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMCTY, + 3, data1) == 0) + return; + } + if (dev == PRI_EMDH1) { + data1[0] = 0; + data1[1] = 0; + if (dn_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMDH1, + 3, data1) == 0) + return; + } + break; + + case PRI_EMHDS: /* Here is device status */ + break; + case PRI_EMLDV: /* Load LP VFU */ + break; + + case PRI_EMLDR: /* Load LP RAM */ + break; + + + case PRI_EMFLO: /* Flush output */ + break; + + case PRI_EMDSC: /* Dataset connect */ + break; + + case PRI_EMHUD: /* Hang up dataset */ + + case PRI_EMXOF: /* XOFF line */ + break; + + case PRI_EMXON: /* XON line */ + break; + + case PRI_EMHLS: /* Here is line speeds */ + break; + + case PRI_EMHLA: /* Here is line allocation */ + case PRI_EMRBI: /* Reboot information */ + case PRI_EMAKA: /* Ack ALL */ + case PRI_EMTDO: /* Turn device On/Off */ + break; + + case PRI_EMEDR: /* Enable/Disable line */ + break; + default: + break; + } + /* Mark command as finished */ + cmd->cnt = 0; + dn_in_cmd = (dn_in_cmd + 1) & 0x1F; + } +} + +/* + * Handle primary protocol, + * Send to 10 when requested. + */ +void dn_transfer(UNIT *uptr) { + uint16 cnt; + uint16 scnt; + struct _dn_queue *out; + uint16 *dp; + + /* Check if Queue empty */ + if (dn_out_res == dn_out_ptr) + return; + + out = &dn_out[dn_out_ptr]; + uptr->STATUS &= ~DTE_TO11; + clr_interrupt(DTE_DEVNUM); + + /* Compute how much 10 wants us to send */ + scnt = ((uptr->CNT ^ DTE_TO10BC) + 1) & DTE_TO10BC; + /* Check if indirect */ + if ((uptr->STATUS & DTE_SIND) != 0) { + /* Transfer indirect */ + cnt = out->dcnt; + dp = &out->data[0]; + if (cnt > scnt) /* Only send as much as we are allowed */ + cnt = scnt; + for (; cnt > 0; cnt -= 2) { + sim_debug(DEBUG_DATA, &dn_dev, "DTE: Send Idata: %06o %03o %03o\n", + *dp, *dp >> 8, *dp & 0377); + if (Mem_write_byte(0, dp) == 0) + goto error; + dp++; + } + uptr->STATUS &= ~DTE_SIND; + } else { + sim_debug(DEBUG_DATA, &dn_dev, "DTE: %d %d send CMD: [%o] %o %o %o\n", + dn_out_ptr, dn_out_res, scnt, out->cnt, out->func, out->dev); + /* Get size of packet */ + cnt = out->cnt; + if ((out->func & PRI_IND_FLG) == 0) + cnt += out->dcnt; + /* If it will not fit, request indirect */ + if (cnt > scnt) { /* If not enough space request indirect */ + out->func |= PRI_IND_FLG; + cnt = scnt; + } + /* Write out header */ + if (!Mem_write_byte(1, &cnt)) + goto error; + if (!Mem_write_byte(1, &out->func)) + goto error; + cnt -= 2; + if (!Mem_write_byte(1, &out->dev)) + goto error; + cnt -= 2; + if (!Mem_write_byte(1, &out->spare)) + goto error; + cnt -= 2; + if (out->func & PRI_IND_FLG) { + uint16 dwrd = out->dcnt; + sim_debug(DEBUG_DATA, &dn_dev, "DTE: Indirect %o %o\n", cnt, + out->dcnt); + dwrd |= (out->sdev << 8); + if (!Mem_write_byte(1, &dwrd)) + goto error; + uptr->STATUS |= DTE_SIND; + goto done; + } + cnt -= 2; + dp = &out->data[0]; + for (; cnt > 0; cnt -= 2) { + sim_debug(DEBUG_DATA, &dn_dev, "DTE: Send data: %06o %03o %03o\n", + *dp, *dp >> 8, *dp & 0377); + if (!Mem_write_byte(1, dp)) + goto error; + dp++; + } + } + out->cnt = 0; + dn_out_ptr = (dn_out_ptr + 1) & 0x1f; +done: + uptr->STATUS |= DTE_10DN; + set_interrupt(DTE_DEVNUM, uptr->STATUS); +error: + return; +} + +/* Process input from CTY and TTY's to 10. */ +void +dn_input() +{ + uint16 data1; + uint16 dataq[32]; + int n; + int ln; + int save_ptr; + char ch; + UNIT *uptr = &dn_unit[0]; + + if ((uptr->STATUS & DTE_SEC) == 0) { + /* Check if CTY done with input */ + if (cty_done) { + data1 = PRI_CTYDV; + if (dn_queue(PRI_EMLBE, PRI_EMDLS, 1, &data1) == 0) + return; + cty_done--; + } + /* Grab a chunck of input from CTY if any */ + n = 0; + save_ptr = cty_in.out_ptr; + while (not_empty(&cty_in) && n < 32) { + ch = cty_in.buff[cty_in.out_ptr]; + inco(&cty_in); + sim_debug(DEBUG_DETAIL, &dn_dev, "CTY recieve %02x\n", ch); + dataq[n++] = (PRI_CTYDV << 8) | ch; + } + if (n > 0 && dn_queue(PRI_EMLNC, PRI_EMDLS, n, dataq) == 0) { + /* Restore the input pointer */ + cty_in.out_ptr = save_ptr; + return; + } + } +} + +/* + * Queue up a packet to send to 10. + */ +int +dn_queue(int func, int dev, int dcnt, uint16 *data) +{ + uint16 *dp; + struct _dn_queue *out; + + /* Check if room in queue for this packet. */ + if (((dn_out_res + 1) & 0x1f) == dn_out_ptr) { + sim_debug(DEBUG_DATA, &dn_dev, "DTE: %d %d out full\n", dn_out_res, dn_out_ptr); + return 0; + } + out = &dn_out[dn_out_res]; + out->cnt = 10; + out->func = func; + out->dev = dev; + out->dcnt = (dcnt-1)*2; + out->spare = 0; + sim_debug(DEBUG_DATA, &dn_dev, "DTE: %d %d queue resp: %o (%o) f=%o %s d=%o\n", + dn_out_ptr, dn_out_res, out->cnt, out->dcnt, out->func, + (out->func > PRI_EMLDV)? "***":pri_name[out->func], out->dev); + for (dp = &out->data[0]; dcnt > 0; dcnt--) { + *dp++ = *data++; + } + /* Advance pointer to next function */ + dn_out_res = (dn_out_res + 1) & 0x1f; + return 1; +} + + +/* + * If anything in queue, start a transfer, if one is not already + * pending. + */ +int +dn_start(UNIT *uptr) +{ + uint64 word; + int dcnt; + + /* Check if queue empty */ + if (dn_out_ptr == dn_out_res) + return 1; + + /* If there is interrupt pending, just return */ + if ((uptr->STATUS & (DTE_IND|DTE_10DB|DTE_11DB)) != 0) + return 1; + if (Mem_examine_word(1, dn_et11_off + PRI_CMTW_STS, &word)) { +error: + /* If we can't read it, go back to secondary */ + uptr->STATUS |= DTE_SEC|DTE_10ER; + set_interrupt(DTE_DEVNUM, uptr->STATUS); + return 0; + } + /* Bump count of messages sent */ + word = (word & ~(PRI_CMT_10IC|PRI_CMT_IP)) | ((word + 0400) & PRI_CMT_10IC); + word &= ~PRI_CMT_FWD; + if ((uptr->STATUS & DTE_SIND) != 0) + word |= PRI_CMT_IP; + if (Mem_deposit_word(1, dn_dt10_off + PRI_CMTW_STS, &word)) + goto error; + dcnt = dn_out[dn_out_ptr].cnt; + if ((dn_out[dn_out_ptr].func & PRI_IND_FLG) == 0) + dcnt += dn_out[dn_out_ptr].dcnt; + /* Tell 10 something is ready */ + if ((uptr->STATUS & DTE_SIND) != 0) { + dcnt = dn_out[dn_out_ptr].dcnt; + } + sim_debug(DEBUG_DATA, &dn_dev, "DTE: start: %012llo %o\n", word, dcnt); + word = (uint64)dcnt; + if (Mem_deposit_word(1, dn_dt10_off + PRI_CMTW_CNT, &word)) + goto error; + uptr->STATUS |= DTE_10DB; + set_interrupt(DTE_DEVNUM, uptr->STATUS); + return 1; +} + + +/* Check for input from CTY and put on queue. */ +t_stat dni_svc (UNIT *uptr) +{ + int32 ch; + uint32 base = 0; + UNIT *optr = &dn_unit[0]; + +#if KI_22BIT + base = eb_ptr; +#endif + sim_clock_coschedule (uptr, tmxr_poll); + dn_input(); + if ((optr->STATUS & (DTE_SEC)) == 0) { + dn_function(uptr); /* Process queue */ + dn_start(optr); + } + + +#if 0 + /* If we have room see if any new lines */ + while (!full(&cty_in)) { + ch = sim_poll_kbd (); + if (ch & SCPE_KFLAG) { + ch = 0177 & sim_tt_inpcvt(ch, TT_GET_MODE (uptr->flags)); + cty_in.buff[cty_in.in_ptr] =ch & 0377; + inci(&cty_in); + sim_debug(DEBUG_DETAIL, &dn_dev, "CTY char %o '%c'\n", ch, + ((ch > 040 && ch < 0177)? ch: '.')); + } else + break; + } + + /* If Monitor input, place in buffer */ + if ((optr->STATUS & (DTE_SEC|DTE_MON)) == (DTE_SEC|DTE_MON) && + not_empty(&cty_in) && M[SEC_DTMTI + base] == 0) { + ch = cty_in.buff[cty_in.out_ptr]; + inco(&cty_in); + M[SEC_DTF11 + base] = ch; + M[SEC_DTMTI + base] = FMASK; + if (dn_dev.flags & TYPE_RSX20) { + uptr->STATUS |= DTE_10DB; + set_interrupt(DTE_DEVNUM, dn_unit[0].STATUS); + } + } +#endif + return SCPE_OK; +} + +/* Handle output of characters to CTY. Started whenever there is output pending */ +t_stat dno_svc (UNIT *uptr) +{ + /* Flush out any pending CTY output */ + while(not_empty(&cty_out)) { + char ch = cty_out.buff[cty_out.out_ptr]; + if (ch != 0) { + if (sim_putchar_s(ch) != SCPE_OK) { + sim_activate(uptr, 1000); + return SCPE_OK;; + } + } + inco(&cty_out); + sim_debug(DEBUG_DETAIL, &dn_dev, "CTY outch %o '%c'\n", ch, + ((ch > 040 && ch < 0177)? ch: '.')); + } + cty_done++; + return SCPE_OK; +} + + +/* Handle FE timer interrupts. And keepalive counts */ +t_stat +dnrtc_srv(UNIT * uptr) +{ + UNIT *optr = &dn_unit[0]; + + sim_activate_after(uptr, 1000000/60); + + /* Update out keep alive timer if in secondary protocol */ + if ((optr->STATUS & DTE_SEC) == 0) { + int addr = 0144 + eb_ptr; + uint64 word; + + (void)Mem_examine_word(1, dn_et11_off + PRI_CMTW_STS, &word); + addr = (M[addr+1] + dn_off + PRI_CMTW_KAC) & RMASK; + word = M[addr]; + word = (word + 1) & FMASK; + M[addr] = word; + sim_debug(DEBUG_EXP, &dn_dev, "CTY keepalive %06o %012llo %06o\n", + addr, word, optr->STATUS); + } + + return SCPE_OK; +} + + +t_stat dn_reset (DEVICE *dptr) +{ + dn_unit[0].STATUS = DTE_SEC; + dn_unit[1].STATUS = 0; + dn_unit[2].STATUS = 0; + dn_unit[3].STATUS = 0; + cty_done = 0; + sim_activate(&dn_unit[3], 1000); + sim_activate(&dn_unit[2], 1000); + return SCPE_OK; +} + + +t_stat +dn_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + DEVICE *dptr; + dptr = find_dev_from_unit (uptr); + if (dptr == NULL) + return SCPE_IERR; + dptr->flags &= ~DEV_M_OS; + dptr->flags |= val; + return SCPE_OK; +} + +t_stat +dn_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + DEVICE *dptr; + + if (uptr == NULL) + return SCPE_IERR; + + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + fprintf (st, "%s", (dptr->flags & TYPE_RSX20) ? "RSX20" : "RSX10"); + return SCPE_OK; +} + + +t_stat dn_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprint_reg_help (st, &dn_dev); +return SCPE_OK; +} + +const char *dn_description (DEVICE *dptr) +{ + return "DN Network interface"; +} + + +#endif +#endif diff --git a/PDP10/kx10_defs.h b/PDP10/kx10_defs.h index 40b38cf..92ef34b 100644 --- a/PDP10/kx10_defs.h +++ b/PDP10/kx10_defs.h @@ -204,6 +204,9 @@ extern DEBTAB crd_debug[]; #define FPMMASK 00000000000077777777777LL #define FPRBIT2 00000000000100000000000LL #define FPRBIT1 00000000000200000000000LL +#if KS +#define IOCTL 00000017000000LL +#endif /* IRQ Flags in APR */ #if KL @@ -507,6 +510,7 @@ extern DEVICE dpk_dev; extern DEVICE wcnsls_dev; /* MIT Spacewar Consoles */ extern DEVICE ocnsls_dev; /* Old MIT Spacewar Consoles */ extern DEVICE ai_dev; +extern DEVICE dn_dev; extern DEVICE dct_dev; /* PDP6 devices. */ extern DEVICE dtc_dev; extern DEVICE mtc_dev; @@ -530,6 +534,8 @@ typedef struct pdp_dib DIB; void cty_wakeup(); void cty_interrupt(); +t_stat cty_reset (DEVICE *dptr); + #define WORD 0 #define BYTE 1 @@ -686,6 +692,7 @@ extern void ka10_lights_clear_aux (int); #define NUM_DEVS_TTY 1 #define NUM_LINES_TTY 64 #define NUM_DEVS_NIA 1 +#define NUM_DEVS_DN 0 #elif KS #define NUM_DEVS_LP20 0 #define NUM_DEVS_DZ 0 diff --git a/PDP10/kx10_sys.c b/PDP10/kx10_sys.c index 69875be..6c0c6d3 100644 --- a/PDP10/kx10_sys.c +++ b/PDP10/kx10_sys.c @@ -214,6 +214,9 @@ DEVICE *sim_devices[] = { #endif #if NUM_DEVS_AI > 0 &ai_dev, +#endif +#if NUM_DEVS_DN > 0 + &dn_dev, #endif NULL }; diff --git a/makefile b/makefile index aae99ad..4853757 100644 --- a/makefile +++ b/makefile @@ -2138,7 +2138,7 @@ KL10 = ${KL10D}/kx10_cpu.c ${KL10D}/kx10_sys.c ${KL10D}/kx10_df.c \ ${KL10D}/kx10_rp.c ${KL10D}/kx10_tu.c ${KL10D}/kx10_rs.c \ ${KL10D}/kx10_imp.c ${KL10D}/kl10_fe.c ${KL10D}/ka10_pd.c \ ${KL10D}/ka10_ch10.c ${KL10D}/kx10_lp.c ${KL10D}/kl10_nia.c \ - ${KL10D}/kx10_disk.c + ${KL10D}/kx10_disk.c ${KL10D}/kl10_dn.c KL10_OPT = -DKL=1 -DUSE_INT64 -I $(KL10D) ${NETWORK_OPT} KS10D = ${SIMHD}/PDP10