From feb155fbc49333e879ab082d481e6dcce27d2d91 Mon Sep 17 00:00:00 2001 From: Lars Brinkhoff Date: Mon, 9 Dec 2024 13:37:22 +0100 Subject: [PATCH] H316: Convert IMP long leaders to short, and vice versa. This adds the modifiers CONVERT and NOCOVERT to the HI units. When enabled for a unit, 1822 messages will transparently be converted. IMP-to-host messages are converted from the old, short (32-bit) format to the new, long (96-bit) format. Host-to-IMP messages are converted in the other direction. The motivation for this feature, is that the currently running IMP software is from 1974 and only supports short leaders. Some operating systems are from a later era, and only support long leaders. --- H316/h316_hi.c | 175 +++++++++++++++++++++++++++++++++++++++++++++--- H316/h316_imp.h | 3 + 2 files changed, 170 insertions(+), 8 deletions(-) diff --git a/H316/h316_hi.c b/H316/h316_hi.c index 00c451912..f8d6239b9 100644 --- a/H316/h316_hi.c +++ b/H316/h316_hi.c @@ -105,6 +105,8 @@ void hi_rx_local (uint16 line, uint16 txnext, uint16 txcount); t_stat hi_reset (DEVICE *dptr); t_stat hi_attach (UNIT *uptr, CONST char *cptr); t_stat hi_detach (UNIT *uptr); +t_stat hi_set_convert (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat hi_show_convert (FILE *st, UNIT *uptr, int32 val, CONST void *desc); @@ -115,7 +117,7 @@ t_stat hi_detach (UNIT *uptr); // Host interface data blocks ... // The HIDB is our own internal data structure for each host. It keeps data // about the TCP/IP connection, buffers, etc. -#define HI_HIDB(N) {FALSE, FALSE, 0, {0}, 0, 0, 0, 0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 0, HI_TXBPS} +#define HI_HIDB(N) {FALSE, FALSE, 0, {0}, 0, 0, 0, FALSE, 0, 0, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 0, HI_TXBPS} HIDB hi1_db = HI_HIDB(1), hi2_db = HI_HIDB(2); HIDB hi3_db = HI_HIDB(3), hi4_db = HI_HIDB(4); @@ -161,8 +163,9 @@ REG hi3_reg[] = HI_REG(3), hi4_reg[] = HI_REG(4); // Host Device Modifiers ... // These are the modifiers simh uses for the "SET MIxn" and "SHOW MIx" commands. -#define HI_MOD(N) { \ - { 0 } \ +#define HI_MOD(N) { \ + { MTAB_XTD|MTAB_VDV, 0, NULL, "CONVERT", &hi_set_convert, &hi_show_convert, NULL }, \ + { MTAB_XTD|MTAB_VDV, 1, NULL, "NOCONVERT", &hi_set_convert, &hi_show_convert, NULL }, \ } MTAB hi1_mod[] = HI_MOD(1), hi2_mod[] = HI_MOD(2); MTAB hi3_mod[] = HI_MOD(3), hi4_mod[] = HI_MOD(4); @@ -196,6 +199,25 @@ UNIT *const hi_units [HI_NUM] = {&hi1_unit, &hi2_unit, &hi3_unit, &hi4_unit}; DIB *const hi_dibs [HI_NUM] = {&hi1_dib, &hi2_dib, &hi3_dib, &hi4_dib }; HIDB *const hi_hidbs [HI_NUM] = {&hi1_db, &hi2_db, &hi3_db, &hi4_db }; +// Old message type field set to this to signal new format leader. +#define NEW_FORMAT_FLAG (15 << 8) + +// 1822 message types (or subtypes). +#define LEADER_REGULAR 000 +#define LEADER_UNCONTROLLED 003 +#define LEADER_NOP 004 + +// 1822 new leader flags. +#define NLEADER_TRACE 010 +#define NLEADER_OCTAL 004 +#define NLEADER_FOR_IMP 252 +#define NLEADER_PRIORITY 0200 + +// 1822 old leader flags. +#define OLEADER_PRIORITY 010 +#define OLEADER_FOR_IMP 004 +#define OLEADER_TRACE 002 +#define OLEADER_OCTAL 001 //////////////////////////////////////////////////////////////////////////////// @@ -235,6 +257,7 @@ void hi_reset_tx (uint16 host) { PHIDB(host)->iloop = PHIDB(host)->enabled = PHIDB(host)->full = FALSE; PHIDB(host)->txtotal = 0; + PHIDB(host)->txfirst = TRUE; CLR_TX_IRQ(host); CLR_TX_IEN(host); } @@ -309,6 +332,123 @@ void hi_debug_msg (uint16 line, uint16 next, uint16 count, const char *ptext) ///////////////// T R A N S M I T A N D R E C E I V E ////////////////// //////////////////////////////////////////////////////////////////////////////// +// Convert 1822 long header from host, to short leader for IMP. +static int16 hi_convert_long_to_short(uint16 line, int16 count) +{ + uint16 nflags, mtype, htype, host, imp, id, stype, length; + uint16 oflags; + uint16 *data = PHIDB(line)->rxdata; + + if (count == 0) + return 0; + + if (count < 7 || (data[1] & 0x0F00) != NEW_FORMAT_FLAG) + return count; // This is not a long leader message. + + nflags = (data[2] & 0x0F00) >> 8; + mtype = data[2] & 0xFF; + htype = (data[3] & 0xFF00) >> 8; + host = data[3] & 0xFF; + imp = data[4]; + id = (data[5] & 0xFFF0) >> 4; + stype = data[5] & 0x000F; + length = data[6]; + + // Keep track of padding. + if (mtype == LEADER_NOP) + PHIDB(line)->padding = stype; + + // Sorry, can't handle these addresses. + if (host > 3) + return 0; + if (imp > 63) + return 0; + + if (mtype == LEADER_REGULAR && stype == LEADER_UNCONTROLLED) + mtype = LEADER_UNCONTROLLED, stype = 0; + else if (mtype == LEADER_NOP) + stype = 0; + + oflags = 0; + if (nflags & NLEADER_TRACE) + oflags |= OLEADER_TRACE; + if (nflags & NLEADER_OCTAL) + oflags |= OLEADER_OCTAL; + if (host >= NLEADER_FOR_IMP) { + oflags |= OLEADER_FOR_IMP; + host -= NLEADER_FOR_IMP; + } + if (htype & NLEADER_PRIORITY) + oflags |= OLEADER_PRIORITY; + + oflags <<= 12; + mtype <<= 8; + host <<= 6; + id <<= 4; + data[1] = oflags | mtype | host | imp; + data[2] = id | stype; + + if (mtype == LEADER_REGULAR) { + count -= 4 + PHIDB(line)->padding; + memmove(&data[3], &data[7 + PHIDB(line)->padding], 2 * count); + } else { + count = 3; + } + + return count; +} + +// Convert 1822 short header from IMP, to long leader for host. +static uint16 hi_convert_short_to_long(uint16 line, uint16 *data, uint16 count) +{ + uint16 oflags, mtype, host, imp, id, stype; + uint16 nflags = 0, htype = 0, length = 0; + + oflags = (data[1] & 0xF000) >> 12; + mtype = (data[1] & 0x0F00) >> 8; + host = (data[1] & 0x00C0) >> 6; + imp = data[1] & 0x003F; + id = (data[2] & 0xFFF0) >> 4; + stype = data[2] & 0x000F; + + if (mtype == LEADER_REGULAR) { + memmove(&data[7 + PHIDB(line)->padding], &data[3], 2 * (count - 3)); + memset(&data[7], 0, 2 * PHIDB(line)->padding); + length = 16 * (count - 3); + count += PHIDB(line)->padding; + } + count += 4; + + if (oflags & OLEADER_PRIORITY) + htype |= NLEADER_PRIORITY; + htype |= 7; + if (oflags & OLEADER_FOR_IMP) + host += NLEADER_FOR_IMP; + if (oflags & OLEADER_TRACE) + nflags |= NLEADER_TRACE; + if (oflags & OLEADER_OCTAL) + nflags |= OLEADER_OCTAL; + + if (mtype == LEADER_REGULAR) + stype = 0; + else if (mtype == LEADER_UNCONTROLLED) + mtype = LEADER_REGULAR, stype = LEADER_UNCONTROLLED; + else if (mtype == LEADER_NOP) + stype = 0; + + nflags <<= 8; + htype <<= 8; + id <<= 4; + data[1] = NEW_FORMAT_FLAG; + data[2] = nflags | mtype; + data[3] = htype | host; + data[4] = imp; + data[5] = id | stype; + data[6] = length; + + return count; +} + // Start the transmitter ... void hi_start_tx (uint16 line, uint16 flags) { @@ -337,7 +477,7 @@ void hi_start_tx (uint16 line, uint16 flags) // uint16 flags; // uint16 data [MAXDATA - 1]; // Put the packet into a temp buffer for assembly. - uint16 *tmp = (uint16 *)malloc ((count + 1) * sizeof (*tmp)); + static uint16 tmp[1500]; uint16 i; tmp [0] = flags; @@ -345,8 +485,11 @@ void hi_start_tx (uint16 line, uint16 flags) tmp [0] |= PFLG_READY; for (i = 0; i < count; i ++) tmp [i + 1] = M [next+i]; - ret = udp_send(PDEVICE(line), PHIDB(line)->link, tmp, count + 1); - free (tmp); + count++; + if (PHIDB(line)->convert && PHIDB(line)->txfirst) + count = hi_convert_short_to_long(line, tmp, count); + PHIDB(line)->txfirst = !!(flags & PFLG_FINAL); + ret = udp_send(PDEVICE(line), PHIDB(line)->link, tmp, count); if (ret != SCPE_OK && ret != 66) hi_link_error(line); } @@ -426,6 +569,7 @@ void hi_poll_rx (uint16 line) } else { // Get a new UDP packet. count = udp_receive(PDEVICE(line), PHIDB(line)->link, PHIDB(line)->rxdata, MAXDATA); + if (PHIDB(line)->convert) count = hi_convert_long_to_short(line, count); PHIDB(line)->eom = FALSE; PHIDB(line)->rxsize = count; if (count == 0) { return; } @@ -433,7 +577,7 @@ void hi_poll_rx (uint16 line) // Make note of the host ready bit. PHIDB(line)->ready = !! (PHIDB(line)->rxdata[0] & PFLG_READY); // Exclude the flags from the count. - PHIDB(line)->rxnext = 1; count--; + PHIDB(line)->rxnext = 1; count--; if (count == 0) return; PHIDB(line)->rxtotal++; } @@ -445,10 +589,10 @@ void hi_poll_rx (uint16 line) count = maxbuf; } hi_update_dmc(PDIB(line)->rxdmc, count); - hi_debug_msg (line, next, count, "received"); for (i = 0; i < count; i ++) * (pdata + i) = PHIDB(line)->rxdata[PHIDB(line)->rxnext++]; + hi_debug_msg (line, next, count, "received"); // Now would be a good time to worry about whether a receive is pending! if (!PHIDB(line)->rxpending) { @@ -682,4 +826,19 @@ t_stat hi_detach (UNIT *uptr) } +t_stat hi_set_convert (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + PHIDB(uptr->hline)->convert = !val; + return SCPE_OK; +} + +t_stat hi_show_convert (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + if (PHIDB(uptr->hline)->convert) + fprintf (st, "Convert short leaders"); + else + fprintf (st, "Do not convert short leaders"); + return SCPE_OK; +} + #endif // #ifdef VM_IMPTIP from the very top diff --git a/H316/h316_imp.h b/H316/h316_imp.h index ffdf104d7..cfc60f157 100644 --- a/H316/h316_imp.h +++ b/H316/h316_imp.h @@ -176,9 +176,12 @@ struct _HIDB { uint16 rxdata[MAXDATA]; // UDP packet received. uint16 rxnext; // Index to next word in UDP packet. uint16 rxsize; // Size of UDP packet. + uint16 padding; // Padding for long leaders. + t_bool convert; // Convert between 1822 short/long messages. // Transmitter (IMP -> HOST) data ... uint32 txdelay; // RTC ticks until TX done interrupt uint32 txtotal; // total host messages sent + t_bool txfirst; // First packet in a series // Other data ... t_bool iloop; // local loop back enabled t_bool enabled; // TRUE if the host is enabled