/* 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