From 4b4274f9bd02af8b2434802aa5588741a6144cee Mon Sep 17 00:00:00 2001 From: sbosse Date: Mon, 16 Mar 2026 11:12:07 +0100 Subject: [PATCH] Mon 16 Mar 11:09:06 CET 2026 --- src/net0.c | 245 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 src/net0.c diff --git a/src/net0.c b/src/net0.c new file mode 100644 index 0000000..2a15d1f --- /dev/null +++ b/src/net0.c @@ -0,0 +1,245 @@ +/* + Very simple network module w/o routing and no output buffering supporting only one port per node. + Only one message can be received (MSGCOMMAND, MSGPROGRAMx, MSGUSER) and is handled directly. + Two input buffers: Code & Data +*/ + +#include "config.h" +#include "types.h" +#include "net.h" +#include "printf.h" +#include "log.h" +#include "context.h" +#include "event.h" + +/* IO multiplexer provided by host application */ +extern int availbyte(int fd); +extern char inbyte(int fd); +extern int outbyte(int fd,char x); + +#if HAS_NET>0 && HAS_NET_ROUTING == 0 +port_t *Port=NULL; + +void NetInit(int (**handlers)(),port_t *P, char *buffer) { + // shared buffer [rx|inp] + if (!buffer) buffer = (char *)MEMALLOC(PORTRXBUFFERSIZE); + // only one port, this buffer is shared by rx and input buffers + if (!P) P = (port_t *)MEMALLOC(sizeof(port_t)); + // no tx buffer + Port=P; + memset(P,0,sizeof(port_t)); + log(LOGINFO,"NetInit P=$%x B=$%x\n",P,buffer); + P->rxState=LSTART; + if (handlers) { + P->handlers=handlers; + } + P->rxBuffer=&buffer[0]; // low-water + P->inputBuffer=&buffer[PORTRXBUFFERSIZE/2]; // high-water + P->inputBottom=P->inputTop=PORTRXBUFFERSIZE/2-1; +} + +int PortReceiver() { + int n=0; + message_type_t t; + port_t *P=Port; + // Timeout handling + int tm=MILLIS(); + if (P->rxCurrent && tm>P->rxLast+NETTIMEOUT) { + // reset receiver + P->rxTop=P->rxStart; + P->rxState=LSTART; + P->rxCurrent=0; + } + while (availbyte(P->rd)>0) { + char ch=inbyte(P->rd); + n++; +#if DEBUG > 0 + log(LOGDEBUG1,"PortReceiver%d State=%d type=%d rx[%d:%d] '%c'\n",0, + P->rxState,P->rxCurrent,P->rxTop,P->inputTop,ch=='\n'?'^':ch); +#endif + if (ch==0) return -1; + switch (P->rxState) { + case LSTART: + t=-1; + switch (ch) { + case ']': t=MSGCOMMAND; break; + case '[': t=MSGPROGRAM; + // stop current program in context 0 (if any is running)? + P->inputBottom=P->inputTop=PORTRXBUFFERSIZE/2-1; /*new prog: get full rx buffer space, stop VM */ + break; + case '!': t=MSGDATA; break; + case '$': t=MSGUSER; break; + case '@': t=MSGNET; break; + case '%': t=MSGEVENT; break; + case '#': t=MSGCONSOLE; break; + } + if (t>=0) { + P->rxCurrent=t; + P->rxState=LPARSING; + P->rxStart=P->rxTop; + } + break; + case LPARSING: + switch (ch) { + case '[': + switch (P->rxCurrent) { + case MSGPROGRAM: + if (P->rxBottom==P->rxTop) { + // [[ + P->rxCurrent=MSGPROGRAMPART; + continue; + break; + } + break; + case MSGCOMMAND: + P->rxState=LEND; + continue; + } + case ']': + switch (P->rxCurrent) { + case MSGPROGRAM: + P->rxState=LEND; + continue; + break; + case MSGPROGRAMPART: + P->rxState=LPREEND; // wait for second ] + continue; + break; + } + case '$': + switch (P->rxCurrent) { + case MSGUSER: + P->rxState=LEND; + continue; + break; + } + case '%': + switch (P->rxCurrent) { + case MSGEVENT: + P->rxState=LEND; + continue; + break; + } + default: + // store message body + switch (P->rxCurrent) { + case MSGUSER: + // cyclic downwards + P->inputBuffer[P->inputTop]=ch; + RINGDECR(P->inputTop,PORTRXBUFFERSIZE/2); + break; + default: + // all others (linear upwards, no ring logic needed) + P->rxBuffer[P->rxTop++]=ch; + break; + } + } + break; + case LPREEND: + // check for second delimiter char + switch (P->rxCurrent) { + case MSGPROGRAMPART: + if (ch==']') { + P->rxState=LEND; + } else { + // Error, reset receiver + P->rxTop=P->rxStart; + P->rxState=LSTART; + P->rxCurrent=0; + continue; + } + break; + } + break; + case LEND: + Notify(NOTIFYMSG,P->rxCurrent,(void*)&P); + if (ch!='\n') { + // Error, reset receiver +#if DEBUG > 0 + log(LOGDEBUG1,"PortReceiver%d No LEND msg(%d)\n",0,P->rxCurrent); +#endif + P->rxTop=P->rxStart; + P->rxState=LSTART; + P->rxCurrent=0; + continue; + } else { + // process now, call handler if existing! + switch (P->rxCurrent) { + case MSGUSER: + P->inputBuffer[P->inputTop]=ch; + RINGDECR(P->inputTop,PORTRXBUFFERSIZE/2); + break; + default: + P->rxBuffer[P->rxTop++]=0; + } +#if DEBUG > 0 + log(LOGDEBUG1,"PortReceiver%d LEND msg(%d)\n",0,P->rxCurrent); +#endif + if (P->handlers && P->handlers[P->rxCurrent]) + P->handlers[P->rxCurrent](P,P->rxCurrent); + else { + // reset buffer pointer + P->rxTop=P->rxStart; + log(LOGINFO,"PortReceiver%d No Handler for (%d)\n",0,P->rxCurrent); + } + } + switch (P->rxCurrent) { + case MSGUSER: + // processed by VM program, ring buffer pointers are modified by ? operations (or VM reset) + break; + default: + // store and process; TODO: delayed processing (e.g., incremental chunk compilations) + // all others, already processed by handler + P->rxStart=P->rxTop; // =P->rxBottom doen in handler + break; + } + P->rxState=LSTART; + P->rxCurrent=0; + break; + } + } + if (n) P->rxLast=tm; + return 0; +} +/*********** + VM program API +***********/ + +/* + Read one byte from input buffer redirected from (host) inbyte(-id) via (VM) INBYTE(id) +*/ +int PortAvailByte(int id) { + port_t *P=Port; +#if DEBUG > 0 + log(LOGDEBUG2,"PortAvailByte(%d) bot=%d top=%d\n",id,P->inputBottom,P->inputTop); +#endif + return P->inputBottom!=P->inputTop; +} + +int PortInByte(int id) { + port_t *P=Port; +#if DEBUG > 0 + log(LOGDEBUG2,"PortInByte(%d) bot=%d top=%d byte='%c'\n",id,P->inputBottom,P->inputTop,P->inputBuffer[P->inputBottom]); +#endif + if (P->inputBottom==P->inputTop) return -1; + char b=P->inputBuffer[P->inputBottom]; + RINGDECR(P->inputBottom,PORTRXBUFFERSIZE/2); + if (P->inputBuffer[P->inputBottom]==EOM) { + // skip EOM, point to next input message + RINGDECR(P->inputBottom,PORTRXBUFFERSIZE/2); + //if (P->inputBottom==P->inputTop) + // P->inputBottom=P->inputTop=PORTRXBUFFERSIZE/2-1; + // input buffer is empty, reset pointers to high water position + // else + } + return b; +} +/* + Write one byte to output port; either directly or buffered in tx buffer if port is busy (?can this happen here?); +*/ +int PortOutByte(int od, char ch) { + port_t *P=Port; + outbyte(Port->wr,ch); + return 1; +} +#endif