From 53e81819e0424ae062fe74f4ec92351c9a05a1b6 Mon Sep 17 00:00:00 2001 From: Lars Brinkhoff Date: Mon, 18 Oct 2021 21:56:15 +0200 Subject: [PATCH] H316: Fix reading messages from the host interface. The 1822 IMP-host interface carefully handshakes every bit transferred. Thus the DMC facility can read messages piecewise. The IMP uses this to read a 32-bit leader first, and process the rest or the message in another round. The IMP expects the 1822 end of message bit not to be set until after the entire message has been read. --- H316/h316_hi.c | 62 ++++++++++++++++++++++++++++--------------------- H316/h316_imp.h | 23 ++++++++++-------- 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/H316/h316_hi.c b/H316/h316_hi.c index 152eca29..3f61b21a 100644 --- a/H316/h316_hi.c +++ b/H316/h316_hi.c @@ -119,7 +119,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, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 0, HI_TXBPS} +#define HI_HIDB(N) {FALSE, FALSE, 0, {0}, 0, 0, 0, 0, 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); @@ -228,6 +228,7 @@ void hi_reset_rx (uint16 host) PHIDB(host)->iloop = PHIDB(host)->error = PHIDB(host)->enabled = FALSE; PHIDB(host)->ready = TRUE; // XXX PHIDB(host)->eom = FALSE; + PHIDB(host)->rxsize = PHIDB(host)->rxnext = 0; PHIDB(host)->rxtotal = 0; CLR_RX_IRQ(host); CLR_RX_IEN(host); } @@ -401,7 +402,6 @@ void hi_poll_rx (uint16 line) // the receive operation. If a packet is waiting but no receive is pending // then the packet is discarded... uint16 next, last, maxbuf; uint16 *pdata; int16 count; - uint16 *tmp = NULL; uint16 i; // If the modem isn't attached, then the read never completes! @@ -420,39 +420,47 @@ void hi_poll_rx (uint16 line) // struct // uint16 flags; // uint16 data [MAXDATA - 1]; - // Read the packet into a temp buffer for disassembly. - tmp = (uint16 *)malloc (MAXDATA * sizeof (*tmp)); + + if (PHIDB(line)->rxnext < PHIDB(line)->rxsize) { + // There is data left from a previously recieved UDP packet. + count = PHIDB(line)->rxsize - PHIDB(line)->rxnext; + } else if (!PHIDB(line)->eom && (PHIDB(line)->rxdata[0] & PFLG_FINAL) + && PHIDB(line)->rxsize > 1) { + // No data left, time to signal end of message if the UDP packet said so. + PHIDB(line)->eom = TRUE; + } else { + // Get a new UDP packet. + count = udp_receive(PDEVICE(line), PHIDB(line)->link, PHIDB(line)->rxdata, MAXDATA); + PHIDB(line)->eom = FALSE; + PHIDB(line)->rxsize = count; + if (count == 0) { return; } + if (count < 0) { hi_link_error(line); return; } + // Exclude the flags from the count. + PHIDB(line)->rxnext = 1; count--; + if (count == 0) return; + PHIDB(line)->rxtotal++; + } - count = udp_receive(PDEVICE(line), PHIDB(line)->link, tmp, maxbuf+1); - if (count == 0) {free (tmp); return; } - if (count < 0) {free (tmp); hi_link_error(line); return; } + // We really got a packet! Update the DMC pointers to reflect the actual + // size of the packet received. + if (count > maxbuf) { + // Limit data to the DMC word count. Keep the UDP packet for next round. + 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++]; - PHIDB(line)->eom = !! tmp[0] & PFLG_FINAL; - for (i = 0; i < count - 1; i ++) - * (pdata + i) = tmp [i + 1]; - free (tmp); - tmp = NULL; // Now would be a good time to worry about whether a receive is pending! if (!PHIDB(line)->rxpending) { sim_debug(IMP_DBG_WARN, PDEVICE(line), "data received with no input pending\n"); return; } - // Exclude the flags from the count. - count--; - - // We really got a packet! Update the DMC pointers to reflect the actual - // size of the packet received. If the packet length would have exceeded the - // receiver buffer, then that sets the error flag too. - if (count > maxbuf) { - sim_debug(IMP_DBG_WARN, PDEVICE(line), "receiver overrun (length=%d maxbuf=%d)\n", count, maxbuf); - PHIDB(line)->rxerror = TRUE; count = maxbuf; - } - hi_update_dmc(PDIB(line)->rxdmc, count); - hi_debug_msg (line, next, count, "received"); - // Assert the interrupt request and we're done! - SET_RX_IRQ(line); PHIDB(line)->rxpending = FALSE; PHIDB(line)->rxtotal++; + SET_RX_IRQ(line); PHIDB(line)->rxpending = FALSE; sim_debug(IMP_DBG_IOT, PDEVICE(line), "receive done (message #%d, intreq=%06o)\n", PHIDB(line)->rxtotal, dev_ext_int); } @@ -584,7 +592,7 @@ t_stat hi_rx_service (UNIT *uptr) // queue entry expires. It just polls the receiver and reschedules itself. // That's it! uint16 line = uptr->hline; - hi_poll_rx(line); + if (PHIDB(line)->rxpending) hi_poll_rx(line); sim_activate(uptr, uptr->wait); return SCPE_OK; } diff --git a/H316/h316_imp.h b/H316/h316_imp.h index 8aadb1ce..e73fbb3d 100644 --- a/H316/h316_imp.h +++ b/H316/h316_imp.h @@ -136,6 +136,16 @@ #define rxint inum // dib->rxint #define txint inum2 // dib->txint +// This constant determines the longest possible IMP data payload that can be +// sent. Most IMP messages are trivially small - 68 words or so - but, when one +// IMP asks for a reload the neighbor IMP sends the entire memory image in a +// single message! That message is about 14K words long. +// The next thing you should worry about is whether the underlying IP network +// can actually send a UDP packet of this size. It turns out that there's no +// simple answer to that - it'll be fragmented for sure, but as long as all +// the fragments arrive intact then the destination should reassemble them. +#define MAXDATA 16384 // longest possible IMP packet (in H316 words) + // Modem interface data block .... // One of these is allocated to every modem interface to keep track of the // current state, COM port, UDP connection , etc... @@ -162,6 +172,9 @@ struct _HIDB { t_bool rxpending; // TRUE if a read is pending on this line t_bool rxerror; // TRUE if any modem error detected uint32 rxtotal; // total host messages received + uint16 rxdata[MAXDATA]; // UDP packet received. + uint16 rxnext; // Index to next word in UDP packet. + uint16 rxsize; // Size of UDP packet. // Transmitter (IMP -> HOST) data ... uint32 txdelay; // RTC ticks until TX done interrupt uint32 txtotal; // total host messages sent @@ -193,16 +206,6 @@ typedef struct _HIDB HIDB; extern uint32 rtc_interval; extern t_stat mi_tx_service (uint32 quantum); -// This constant determines the longest possible IMP data payload that can be -// sent. Most IMP messages are trivially small - 68 words or so - but, when one -// IMP asks for a reload the neighbor IMP sends the entire memory image in a -// single message! That message is about 14K words long. -// The next thing you should worry about is whether the underlying IP network -// can actually send a UDP packet of this size. It turns out that there's no -// simple answer to that - it'll be fragmented for sure, but as long as all -// the fragments arrive intact then the destination should reassemble them. -#define MAXDATA 16384 // longest possible IMP packet (in H316 words) - // Prototypes for UDP modem/host interface emulation routines ... #define NOLINK (-1) t_stat udp_create (DEVICE *pdtr, const char *premote, int32 *plink);