Mon 16 Mar 11:09:06 CET 2026

This commit is contained in:
sbosse 2026-03-16 11:13:05 +01:00
parent 0ce3ca9e74
commit d402bb4128

200
src/net.h Normal file
View File

@ -0,0 +1,200 @@
#ifndef _NET_H
#define _NET_H
#if HAS_NET > 0
/*
==== Communication Ports ====
Link: Connection between ports
Logical/virtual ports are buffered async. IO devices:
- External access via buffers only,
- Internal reading and writing from physical serial channels in the background (via main IO loop).
Ports and links between ports operate message-based. VM send messages to port.
Serial Channel -> PortReceiver(L) -> AllocateMessage(M) -> Process(M) -> FinalizeMessage(M=
VM IN/OUT API via generic INBYTE/OUTBYTE passed to port InByte(M),OutByte(M)
Outgoing messages: PortOutByte -> AllocateMessage(M) -> PortRoute(P)
INBYTE/OUTBYTE: IO multiplexer: Real file desciptor IO and logical port access (via port structures and buffers)
*/
#ifndef PORTBUFFERSIZE
#define PORTBUFFERSIZE 2048
#endif
#ifndef EOM
#define EOM '\n'
#endif
// Maximal number of ports
#ifndef NUMPORTS
#define NUMPORTS 4
#endif
#ifndef NETTIMEOUT
// rx timeout in ms
#define NETTIMEOUT 200
#endif
#define RINGDECR(index,size) index=(index==0?size-1:index-1)
#define RINGINCR(index,size) index=((index+1)%size)
#define RINGLEN(start,end,size) end<start?end+size-start:end-start+1
#define MSGRX 0x1000
#define MSGTX 0x2000
#define MSGTYPEMASK 0x1F
#define MSGTYPE(t) t&MSGTYPEMASK
#define MSGDIR(t) t&(MSGRX|MSGTX)
typedef enum {
MSGCOMMAND = 1, // ]command[ : one single program line executed immed. if VM is PIDLE
MSGPROGRAM, // [program] : one single (multi-line) main program, compiled, reset VM, run
MSGPROGRAMPART, // [[program]] : one part of a main program, compiled
MSGDATA, // !data! : not passed to VM program, must be processed by host application
MSGEVENT, // %event% : raises VM event
MSGUSER, // $message$ : passed to VM program, can be processed by ? operator
MSGNET, // Prefix control message (for routing), @dx,dy@, @^@, @~@, @*@, @id..@, @locate..@
MSGCONSOLE, // #message# : like MSGDATA
MSGMAXNUM,
MSGSKIP, // A virtual marker that requires a ring buffer wrap around (to bottom of buffer),
// i.e., the next message starts at 0 (remaining space cannot hold buffer mapped binary structures, which must be linear in memory)
MSGFREE // free message block in rx/tx buffer
} message_type_t;
typedef enum {
LDEAD=0,
LIDLE,
LSTART, // waiting for first message delimiter
LPARSING, // processing content of a message
LPREEND, // awaiting second message delimiter?
LEND // finalize, go to start
} port_state_t;
#if HAS_NET_ROUTING > 0
/*
Message handled by ports (input as well as output), administrative message header stored in rx/tx buffers
*/
typedef struct {
index_t type; //message_type_t
index_t start;
index_t end; // wrap around in ring buffer?
index_t port;
} message_t;
/*
Network module with extended routing, ingoing (rx) and outgoing (tx) messages are buffered.
*/
/*
Network control message (prefix of another message or stand-alone)
It is directly mappend in rx/tx buffer (parsed in place(
*/
typedef enum {
NCMUP = 'u',
NCMDOWN = 'd',
NCMMULTICAST = 'm',
NCMUNICAST = '1',
NCMID = 'i',
NCMDELTA = 'D'
} control_type_t;
/*
(Optional) Message prefix for routing
@u@ | @u,hopx,hopy@
@d@ | @d,hopx,hopy@
@D,dx,dy,dx0,dy0@
@m,range@
Some messages have default routing strategies:
!! => Up
[] => Down (always broadcast)
%% => Down (always broadcast)
## => Up
*/
#ifndef MAXNCMARGS
#define MAXNCMARGS 8
#endif
typedef struct __attribute__((packed)) {
char type; // control_type_t
int8_t narg; // number of args
int8_t args[MAXNCMARGS]; // dynamic array
} message_control_t;
typedef struct {
/* linear buffer */
char *rxBuffer;
/* ring buffer */
char *txBuffer;
index_t rxBottom;
index_t rxTop; // points to next free message block
index_t txBottom;
index_t txTop;
char busy;
index_t rd; // unix: fd
index_t wr; // unix: fd
port_state_t state; // message parser state
message_t *rxCurrent; // currently processed (non-fixed) message
message_t *txCurrent;
int (**handlers)(); // int foo(port_t *,message_t *)
index_t index;
} port_t;
extern port_t **Ports;
message_t *CurrentInputMessage[NUMPORTS]; // for PLX ? operator
message_t *CurrentOutputMessage[NUMPORTS]; // for PLX ! operator
int PortsDown[NUMPORTS],PortsUp[NUMPORTS]; // keep track for up and down ports
/* if buffer==NULL the rx+tx buffer is allocated, else buffer=char[NUMPORTS*PORTBUFFERSIZE] */
void NetInit(int (**handlers)(),port_t **ports, char *buffer);
message_t *AllocateMessage(port_t *P,message_type_t t);
message_t *FinalizeMessage(message_t *m);
void FreeMessage(message_t *m);
#if HAS_NET_ROUTING > 0
int NetControlMessage(port_t *P, char type,index_t a, index_t b);
#endif
int PortReceiver(port_t *L);
/*
Read one byte from message
*/
int PortInByte(message_t *m);
/*
Add one byte to message
*/
int PortOutByte(message_t *m, char ch);
void PrintPortMessages(port_t *L);
#else /* ! HAS_NET_ROUTING: NET0 */
/* net0: only 1 port */
typedef struct {
/* shared buffer=[rxBuffer,inputBuffer] */
/* linear buffer, grows to top */
char *rxBuffer; // all messages not passed to the VM input layer
/* circular buffer, grows to bottom, wrap around at PORTBUFFERSIZE/2, reset to high water if top==bottom */
char *inputBuffer; // all messages passed to the VM input layer
index_t rxBottom;
index_t rxTop; // points to next free byte
index_t rxStart; // begin of current message in buffer (rx/input)
int rxLast; // timestamp of last received char
index_t inputBottom;
index_t inputTop; // points to next free byte
message_type_t rxCurrent; // current received message
char busy;
index_t rd; // unix: fd
index_t wr; // unix: fd
port_state_t rxState; // receiver message parser state
int (**handlers)(); // int foo(port_t *,message_type_t)
} port_t;
extern port_t *Port;
/* if buffer==NULL the rx+in buffer is allocated, else buffer=char[NUMPORTS*PORTBUFFERSIZE] */
void NetInit(int (**handlers)(),port_t *port, char *buffer);
int PortAvailByte(int id);
int PortInByte(int id);
int PortOutByte(int od, char ch);
int PortReceiver();
#endif
#endif /* !HAS_NET */
#endif /* _NET_H */