TARGETS= hostside
CFLAGS= -Wall -Werror -Wstrict-prototypes -Wmissing-prototypes \
- -Wpointer-arith -Wwrite-strings $(OPTIMISE)
+ -Wpointer-arith -Wwrite-strings -g $(OPTIMISE)
OPTIMISE= -O2
all: $(TARGETS)
-hostside: serialio.o nmra.o main.o
+hostside: serialio.o nmra.o main.o encode.o
$(CC) $(CFLAGS) -o $@ $+
+encode.o: nmra-packets.h
+main.o: nmra-packets.h
%.c: hostside.h
clean:
#include <assert.h>
#include "hostside.h"
+#include "nmra.h"
-#define XCONST(d) const Byte c[2]= { d }; XMIT;
+#define CONST(...) \
+ static const Byte m[]= { __VA_ARGS__ }; \
+ memcpy(c,m,sizeof(m)); \
+ c+=sizeof(m)
-static Byte *addr(int addr, Byte *ap) {
+#define FUNCS(v) \
+ nmra_errchk(cn, bitmap, !(bitmap & ~0x1fffu)); \
+ *c++= (v)
+
+#define nmra_errchk(cn, v, truth) \
+ if (!truth) nmra_errchk_fail(cn, v, #v, #truth);
+
+static void nmra_errchk_fail(const char *m, long v,
+ const char *vn, const char *truth) {
+ fprintf(stderr,"nmra encode %s: %s %ld (0x%lx) fails condition %s\n",
+ m, vn, (unsigned long)v, (unsigned long)v, truth);
+ exit(15);
+}
+
+#define ADDR c= encode_addr(cn, addr, c);
+
+static Byte *encode_addr(const char *cn, int addr, Byte *ap) {
/* encodes decoder address */
- assert(addr>0 && addr<=0x3ff);
+ nmra_errchk(cn, addr, addr>0 && addr<=0x3ff);
if (addr < 0x3f) {
/* Short addresses: S9.2 B l.41 which is the same as RP9.2.1 C l.65
* first sentence. */
- *--ap= addr;
+ *ap++= addr;
} else {
/* Long addresses: RP9.2.1 C l.65-69. */
- *--ap= addr;
- *--ap= (addr >> 8) | 0xc0;
+ *ap++= addr;
+ *ap++= (addr >> 8) | 0xc0;
}
return ap;
}
-#define ADDR Byte *c= addr(addr, p->d);
-#define ENCD p->l= c - p->d; assert(p->l <= sizeof(p->d));
-
-void enco_nmra_speed28(Nmra *p, int addr, int speed, int reverse) {
- /* Baseline Speed and Direction, S9.2 B (for short addresses
- * only), which is also identical to Multi-Function Decoder
- * Speed and Direction RP9.2.1 C l.215- (defined for both
- * addresses), so actually the address format and instruction
- * format are independent.
- */
- int adj;
- ADDR;
-
- assert(speed>=0 && speed<=28);
- *c++= 0x40 | (reverse ? 0 : 0x20);
- if (speed) {
- adj= speed + 3;
- *c |= adj & 1 ? 0x10 : 0;
- *c |= adj >> 1;
- }
- c++;
- ENCD;
-}
-
-void enco_nmra_estop1(Nmra *p, int addr) {
- /* Baseline Speed and direction Forwards E-Stop(I) S9.2 B table l.56 */
- ADDR;
- *c++= 0x71;
- ENCD;
-}
-
-void enco_nmra_estop(void) {
- /* Baseline Broadcast stop Forwards(I) Emergency S9.2 B l.98- */
- XCONST(0x00, 0x71);
-}
-void enco_nmra_reset(void) {
- /* Baseline Decoder Reset S9.2 B l.77- */
- XCONST(0x00, 0x00);
-}
-void enco_nmra_idle(void) {
- /* Baseline Idle S9.2 B l.87- */
- XCONST(0xff, 0x00);
-}
-
-void enco_nmra_speed126(int addr, int speed, int reverse) {
- /* Advanced Operations 128 Speed Step Control
- * (actually speeds 0..126) RP9.2.1 C l.200- */
- CMD(2);
- ADDR;
- assert(speed>=0 && speed<=126);
- c[0]= 0x3f;
- c[1]= (speed ? speed + 1 : 0) | (reverse ? 0 : 0x80);
- ENCD;
-}
-
-void enco_nmra_funcs0to4(unsigned bitmap) {
- /* Function Group One RP9.2.1 C l.234- */
- CMD(1);
- ADDR;
- FUNCS;
- c[0]= 0x80 | ((bitmap >> 1) & 0x0f) | ((bitmap << 4 & 0x10));
- ENCD;
-}
-void enco_nmra_funcs5to9(unsigned bitmap) {
- /* Function Group Two RP9.2.1 C l.246- */
- CMD(1);
- ADDR;
- FUNCS;
- c[0]= 0xa0 | ((bitmap >> 5) & 0x0f);
- ENCD;
-}
-void enco_nmra_funcs9to12(unsigned bitmap) {
- /* Function Group Two RP9.2.1 C l.246- */
- CMD(1);
- ADDR;
- FUNCS;
- c[0]= 0xb0 | ((bitmap >> 9) & 0x0f);
- ENCD;
-}
+#define Aint(v) , int v
+#define Abitmap(v) , unsigned v
+#define Abyte(v) , Byte v
+#define Anone
-void enco_nmra_cv_write(int addr, int cv, Byte value) {
- /* Configuration Variable Access Long Form RP9.2.1 C l.286- */
- int adj;
- CMD(3);
- ADDR;
- assert(cv>=1 && cv<=1024);
- adj= cv - 1;
- c[0]= 0xec | (adj >> 8);
- c[1]= adj;
- c[2]= value;
- ENCD;
+#define NMRA(n,al,body) \
+void enco_nmra_##n(Nmra *p al) { \
+ static const char cn[]= #n; \
+ Byte *c= p->d; \
+ (void)cn; \
+ do { body } while(0); \
+ p->l= c - p->d; \
+ assert(p->l <= sizeof(p->d)); \
}
+#include "nmra-packets.h"
#ifndef HOSTSIDE_H
#define HOSTSIDE_H
+#include <stdio.h>
+
typedef unsigned char Byte;
#define COMMAND_ENCODED_MAX 16
void xmit_nmra(const Nmra*);
void xmit_command(const Byte *command, int length);
-void enco_nmra_speed28(Nmra *p, int train, int speed, int reverse);
-void enco_nmra_speed126(Nmra *p, int addr, int speed, int reverse);
- /* 0<=speed<=28 or <=126; reverse: 0 forwards, non-0 backwards */
-void enco_nmra_funcs0to4(Nmra *p, unsigned bitmap);
-void enco_nmra_funcs5to8(Nmra *p, unsigned bitmap);
-void enco_nmra_funcs9to12(Nmra *p, unsigned bitmap);
- /* bit 0 is FL aka F0; bits 1-12 are F1-F12; do not call with bits
- * outside 0-12 set; bits in 0-12 but not relevant for the relevant
- * command are ignored. */
-void enco_nmra_cv_write(Nmra *p, int addr, int cv, Byte value);
-
-void enco_nmra_idle(Nmra *p);
-void enco_nmra_reset(Nmra *p);
-void enco_nmra_estop(Nmra *p);
-void enco_nmra_estop1(Nmra *p);
-
extern FILE *dump_stream;
void dump(const char *what, const Byte *data, int length);
void sysfatal(const char *m);
#include <string.h>
#include <assert.h>
#include <stdlib.h>
+#include <limits.h>
#include <unistd.h>
#include "hostside.h"
+#include "nmra.h"
FILE *dump_stream= 0;
-int main(int argc, const char **argv) {
- int l, i;
- char hbuf[3], *ep;
- Byte buf[NMRA_PACKET_MAX + COMMAND_ENCODED_MAX];
+static int repeat_delay= -1, iterations= -1;
+static const char *serial_port;
+
+static struct {
+ Nmra nmra;
+ Byte buf[COMMAND_ENCODED_MAX]; /* padding prevents overruns */
+} buf;
- int repeat_delay= -1, iterations= -1;
- const char *serial_port, *data_string, *instruction;
+static void badusage(const char *why) {
+ fprintf(stderr,"bad usage: %s\n",why); exit(8);
+}
+
+static long argnumber(const char ***argv, long min, long max) {
+ long l;
+ const char *arg;
+ char *ep;
- assert(argc>=4 && argc<=7);
- serial_port= argv[1];
- data_string= argv[2];
- instruction= argv[3];
- if (argc>4) serial_fudge_delay= atoi(argv[4]);
- if (argc>5) repeat_delay= atoi(argv[5]);
- if (argc>6) iterations= atoi(argv[6]);
+ if (!(arg= *(*argv)++)) badusage("missing numeric arg value");
+ l= strtoul(arg,&ep,0); if (*ep) badusage("bad numeric arg value");
+ if (l<min || l>max) badusage("numeric arg value out of range");
+ return l;
+}
- serial_open(serial_port);
+static void pahex(const char **argv) {
+ const char *data_string;
+ char hbuf[3], *ep;
+ int i, l;
+ Byte *c;
+
+ c= buf.nmra.d;
- l= strlen(data_string);
- assert(!(l&1));
- l >>= 1;
- assert(l<=sizeof(buf));
-
- for (i=0; i<l; i++) {
- hbuf[0]= data_string[i*2];
- hbuf[1]= data_string[i*2+1];
- hbuf[2]= 0;
- buf[i]= strtoul(hbuf,&ep,16);
- assert(ep==&hbuf[2]);
+ while ((data_string= *argv++)) {
+ l= strlen(data_string);
+ if (l&1) badusage("hex argument odd length");
+ l >>= 1;
+
+ for (i=0; i<l; i++) {
+ if (c >= buf.nmra.d + sizeof(buf.nmra.d)) badusage("too much hex");
+
+ hbuf[0]= data_string[i*2];
+ hbuf[1]= data_string[i*2+1];
+ hbuf[2]= 0;
+ *c++= strtoul(hbuf,&ep,16);
+ if (ep!=&hbuf[2]) badusage("bad hex in hex argument");
+ }
}
+ buf.nmra.l= c - buf.nmra.d;
+}
- for (;;) {
+int main(int argc, const char **argv) {
+ void (*xmitter)(const Byte *d, int l);
+ const char *arg;
- if (*instruction == 'd') {
- dump_stream= stdout;
- instruction++;
+ if (!*argv++) badusage("need argv[0]");
+
+ while ((arg=*argv++) && *arg=='-') {
+ arg++;
+ switch (*arg++) {
+ case 's': serial_port= arg; break;
+ case 'i': iterations= atoi(arg); break;
+ case 'f': serial_fudge_delay= atoi(arg); break;
+ case 'w': repeat_delay= atoi(arg); break;
+ case 'd': dump_stream= stdout; goto noarg;
+ default: badusage("uknown option");
+ noarg: if (*arg) badusage("option with value where forbidden");
}
+ }
+
+ if (!serial_port) {
+ if (!arg) badusage("need serial port as -s or arg");
+ serial_port= arg;
+ arg= *argv++;
+ }
+
+ if (!arg) badusage("need arg");
- switch (*instruction) {
- case 'c': xmit_command(buf,l); break;
- case 'r': xmit_nmra_raw(buf,l); break;
- case 'n': xmit_nmra(buf,l); break;
- default: abort();
+ #define BARE(f) \
+ if (!strcmp(arg,#f)) { \
+ pahex(argv); \
+ xmitter= xmit_##f; \
+ } else
+ BARE(command)
+ BARE(nmra_raw)
+ BARE(nmra_bytes)
+ #undef BARE
+ {
+ #define Aint(x) , argnumber(&argv,INT_MIN,INT_MAX)
+ #define Abitmap(x) , argnumber(&argv,0,~0u)
+ #define Abyte(x) , argnumber(&argv,0,0xff)
+ #define Anone
+ #define NMRA(n,al,body) \
+ if (!strcmp(arg,#n)) { \
+ enco_nmra_##n(&buf.nmra al); \
+ } else
+#include "nmra-packets.h"
+ {
+ badusage("unknown instruction");
}
- assert(!instruction[1]);
+ if (*argv) badusage("too many args for packet type");
+ xmitter= xmit_nmra_bytes;
+ }
+ serial_open(serial_port);
+
+ for (;;) {
+ xmitter(buf.nmra.d,buf.nmra.l);
+
if (repeat_delay < 0) break;
if (iterations >= 0 && !iterations--) break;
if (repeat_delay > 0) usleep(repeat_delay);
-
-
+/*
+ * strange macros - do not use directly, usually
+ */
+
NMRA(speed28, Aint(addr) Aint(speed) Aint(reverse), {
+ /* 0<=speed<=28 or <=126; reverse: 0 forwards, non-0 backwards */
int adj;
-
- assert(speed>=0 && speed<=28);
+ ADDR;
+ nmra_errchk(cn, speed, speed>=0 && speed<=28);
*c++= 0x40 | (reverse ? 0 : 0x20);
if (speed) {
adj= speed + 3;
}
c++;
});
+NMRA(estop1, Aint(addr), {
+ /* Baseline Speed and direction Forwards E-Stop(I) S9.2 B table l.56 */
+ ADDR;
+ *c++= 0x71;
+});
+NMRA(speed126, Aint(addr) Aint(speed) Aint(reverse), {
+ /* Advanced Operations 128 Speed Step Control
+ * (actually speeds 0..126) RP9.2.1 C l.200- */
+ ADDR;
+ nmra_errchk(cn, speed, speed>=0 && speed<=126);
+ *c++= 0x3f;
+ *c++= (speed ? speed + 1 : 0) | (reverse ? 0 : 0x80);
+});
-NMRA(
-
-
+NMRA(estop, Anone, {
+ /* Baseline Broadcast stop Forwards(I) Emergency S9.2 B l.98- */
+ CONST(0x00, 0x71);
+});
+NMRA(reset, Anone, {
+ /* Baseline Decoder Reset S9.2 B l.77- */
+ CONST(0x00, 0x00);
+});
+NMRA(idle, Anone, {
+ /* Baseline Idle S9.2 B l.87- */
+ CONST(0xff, 0x00);
+});
- /* Baseline Speed and Direction, S9.2 B (for short addresses
- * only), which is also identical to Multi-Function Decoder
- * Speed and Direction RP9.2.1 C l.215- (defined for both
- * addresses), so actually the address format and instruction
- * format are independent.
- */
+/* For functions:
+ * bit 0 is FL aka F0; bits 1-12 are F1-F12; do not call with bits
+ * outside 0-12 set; bits in 0-12 but not relevant for the relevant
+ * command are ignored. */
+NMRA(funcs0to4, Aint(addr) Abitmap(bitmap), {
+ /* Function Group One RP9.2.1 C l.234- */
+ FUNCS(0x80 | ((bitmap >> 1) & 0x0f) | ((bitmap << 4 & 0x10)));
+});
+NMRA(funcs5to9, Aint(addr) Abitmap(bitmap), {
+ /* Function Group Two RP9.2.1 C l.246- */
+ FUNCS(0xa0 | ((bitmap >> 5) & 0x0f));
+});
+NMRA(funcs9to12, Aint(addr) Abitmap(bitmap), {
+ /* Function Group Two RP9.2.1 C l.246- */
+ FUNCS(0xb0 | ((bitmap >> 9) & 0x0f));
+});
+NMRA(cvwrite, Aint(addr) Aint(cv) Abyte(value), {
+ /* Configuration Variable Access Long Form RP9.2.1 C l.286- */
+ int adj;
ADDR;
+ nmra_errchk(cn, cv, cv>=1 && cv<=1024);
+ adj= cv - 1;
+ *c++= 0xec | (adj >> 8);
+ *c++= adj;
+ *c++= value;
+});
- ENCD;
-}
+#undef Aint
+#undef Abitmap
+#undef Abyte
+#undef Anone
+#undef NMRA
--- /dev/null
+/**/
+
+#ifndef NMRA_H
+#define NMRA_H
+
+#define Aint(v) , int v
+#define Abitmap(v) , unsigned v
+#define Abyte(v) , Byte v
+#define Anone
+#define NMRA(n,al,body) void enco_nmra_##n(Nmra *packet_r al)
+#include "nmra-packets.h"
+
+#endif /*NMRA_H*/
}
void dump(const char *what, const Byte *data, int length) {
- printf("%-25s %3d ",what,length);
+ if (!dump_stream) return;
+ fprintf(dump_stream,"%-25s %3d ",what,length);
while (length>0) {
- printf(" %02x", *data);
+ fprintf(dump_stream," %02x", *data);
data++;
length--;
}
- printf("\n");
+ fprintf(dump_stream,"\n");
}
void xmit_command(const Byte *command, int length) {