/* 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 */ #define DTE_INIT 040000000 /* Recieved an INIT signal last time */ /* 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 */ }; #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}, {HRDATAD(WRU, sim_int_char, 8, "interrupt character") }, { 0 }, }; DEVICE dn_dev = { "DN", dn_unit, dn_reg, dn_mod, 3, 10, 31, 1, 8, 8, NULL, NULL, &dn_reset, NULL, NULL, NULL, &dn_dib, DEV_DIS | DEV_DISABLE | 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, "DN %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, "DN Ring 11 DB\n"); dn_unit[0].STATUS |= DTE_11DB; dn_unit[0].STATUS &= ~(DTE_10DB); 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, "DN %03o CONO %06o %06o\n", dev, (uint32)*data, PC); break; case DATAI: sim_debug(DEBUG_DATAIO, &dn_dev, "DN %03o DATAI %06o\n", dev, (uint32)*data); break; case DATAO: sim_debug(DEBUG_DATAIO, &dn_dev, "DN %03o DATAO %06o\n", dev, (uint32)*data); if (*data == 01365) { dn_unit[0].STATUS |= 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); 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; int i; #if KI_22BIT base = eb_ptr; #endif /* read command */ word = M[SEC_DTCMD + base]; /* Do it */ sim_debug(DEBUG_DETAIL, &dn_dev, "DN secondary %012llo\n", word); for (i = 0; i < 8; i++) sim_debug(DEBUG_DETAIL, &dn_dev, "EB word %o %012llo\n", i, M[eb_ptr + 0150+i]); for (i = 0; i <100; i++) { if (Mem_examine_word(1, i, &word)) break; sim_debug(DEBUG_DETAIL, &dn_dev, "DN1 word %3o %012llo\n", i, word); } switch(word & SEC_CMDMSK) { default: #if 0 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; #endif case SEC_SETPRI: enter_pri: if (Mem_examine_word(1, 0, &word)) return; sim_debug(DEBUG_DETAIL, &dn_dev, "DN word 0 %012llo\n", word); 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; /* Start input process */ M[SEC_DTCMD + base] = 0; M[SEC_DTFLG + base] = FMASK; uptr->STATUS &= ~DTE_11DB; return; #if 0 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; #endif } /* 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; int i; 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); Mem_examine_word(1, 0, &word); dn_proc_num = (word >> 24) & 037; sim_debug(DEBUG_DETAIL, &dn_dev, "DN1 procnum %0o\n", dn_proc_num); dn_base = dn_proc_num + 1; sim_debug(DEBUG_DETAIL, &dn_dev, "DN1 base %0o\n", dn_base); dn_off = dn_base + (word & 0177777); sim_debug(DEBUG_DETAIL, &dn_dev, "DN1 dn_off %0o\n", dn_off); dn_dt10_off = 020; sim_debug(DEBUG_DETAIL, &dn_dev, "DN1 dn_dt_off %0o\n", dn_dt10_off); dn_et10_off = dn_dt10_off + 16; sim_debug(DEBUG_DETAIL, &dn_dev, "DN1 dn_et_off %0o\n", dn_et10_off); dn_et11_off = dn_base + (8 * dn_base); sim_debug(DEBUG_DETAIL, &dn_dev, "DN1 dn_et11_off %0o\n", dn_et11_off); for (i = 0; i < 8; i++) sim_debug(DEBUG_DETAIL, &dn_dev, "EB word %o %012llo\n", i, M[eb_ptr + 0150+i]); for (i = 0; i <200; i++) { if (Mem_examine_word(1, i, &word)) break; sim_debug(DEBUG_DETAIL, &dn_dev, "DN1 word %3o %012llo\n", i, word); } /* 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; } sim_debug(DEBUG_DETAIL, &dn_dev, "DTE: status %06llo %012llo\n", dn_et11_off + PRI_CMTW_STS + M[0155 + eb_ptr], word); if ((word & PRI_CMT_INI) != 0) { word &= ~PRI_CMT_TOT; sim_debug(DEBUG_DETAIL, &dn_dev, "DTE: istatus %06llo %012llo\n", dn_dt10_off + PRI_CMTW_STS + M[0157 + eb_ptr], word); if (Mem_deposit_word(1, dn_dt10_off + PRI_CMTW_STS, &word)) goto error; uptr->STATUS |= DTE_11DN|DTE_10DB|DTE_INIT; set_interrupt(DTE_DEVNUM, uptr->STATUS); return; } if ((uptr->STATUS & DTE_INIT) != 0) { sim_debug(DEBUG_DETAIL, &dn_dev, "DTE: dstatus %06llo %012llo\n", dn_dt10_off + PRI_CMTW_STS + M[0157 + eb_ptr], word); if (Mem_deposit_word(1, dn_dt10_off + PRI_CMTW_STS, &word)) goto error; uptr->STATUS |= DTE_11DN|DTE_10DB; uptr->STATUS &= ~DTE_INIT; set_interrupt(DTE_DEVNUM, uptr->STATUS); 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\r\n"); 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(1, 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 s=%o d=%o\n", dn_in_ptr, in->cnt, in->func, ((in->func & 0377) > PRI_EMLDV)?"***": pri_name[in->func & 0377], in->spare, 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 */ break; case PRI_EM2TI: /* Replay to initial message. */ data1[0] = (6 << 8) | 5; data1[1] = (0 << 8) | 0xc0; data1[2] = (1 << 8) | 0; if (dn_queue(01, PRI_DN60, 3, data1) == 0) return; break; 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: #if 0 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,"DN queue %o\n",ch); } cmd->dptr++; } #endif if (cmd->dptr != cmd->dcnt) return; } break; case PRI_EMSNA: /* Send all (ttys) */ break; case PRI_EMLNC: /* Line-Char */ break; case PRI_EMOPS: break; case PRI_EMRDS: /* Request device status */ 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; int i; /* 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); for (i = 0; i < 8; i++) sim_debug(DEBUG_DETAIL, &dn_dev, "EB Sword %o %012llo\n", i, M[eb_ptr + 0150+i]); for (i = 0; i <200; i++) { uint64 word; if (Mem_examine_word(1, i, &word)) break; sim_debug(DEBUG_DETAIL, &dn_dev, "DN1 Sword %3o %012llo\n", i, word); } /* 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(1, 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 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, "DN 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; } } #endif } /* * 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; int i; /* 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; word |= (word << 18); if (Mem_deposit_word(1, dn_dt10_off + PRI_CMTW_CNT, &word)) goto error; for (i = 0; i < 8; i++) sim_debug(DEBUG_DETAIL, &dn_dev, "EB word %o %012llo\n", i, M[eb_ptr + 0150+i]); for (i = 0; i <200; i++) { if (Mem_examine_word(1, i, &word)) break; sim_debug(DEBUG_DETAIL, &dn_dev, "DN1 word %3o %012llo\n", i, word); } 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) { #if 0 /* 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, "DN outch %o '%c'\n", ch, ((ch > 040 && ch < 0177)? ch: '.')); } cty_done++; #endif 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 = 0154 + 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, "DN keepalive %06o %012llo %06o\n", addr, word, optr->STATUS); } return SCPE_OK; } t_stat dn_reset (DEVICE *dptr) { dn_unit[0].STATUS = 0; //DTE_SEC; dn_unit[1].STATUS = 0; dn_unit[2].STATUS = 0; dn_proc_num = 0; dn_base = dn_proc_num + 1; dn_off = 0; dn_dt10_off = 16; dn_et10_off = 050; dn_et11_off = 033; 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