Mon 16 Mar 11:09:06 CET 2026

This commit is contained in:
sbosse 2026-03-16 11:12:07 +01:00
parent 5f8d72f1d2
commit 4b4274f9bd

245
src/net0.c Normal file
View File

@ -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