chiark / gitweb /
b14ed033052579e1c4e53162f871744372c3bccc
[trains.git] / hostside / commands.c
1 /**/
2
3 #include <setjmp.h>
4 #include <stdlib.h>
5 #include <errno.h>
6 #include <assert.h>
7 #include <string.h>
8
9 #include "hostside.h"
10 #include "auproto-pic.h"
11
12 #define NMRA_MAX_NARGS 10
13
14 typedef struct ManualRetransmitNode ManualRetransmitNode;
15 struct ManualRetransmitNode {
16   ManualRetransmitNode *back, *next;
17   char *name;
18   int lname;
19   RetransmitNode rn;
20 };
21
22 #define bogus_volatile /*empty*/
23 #ifdef __GNUC__
24 #if __GNUC__ == 2
25 #undef bogus_volatile
26 #define bogus_volatile volatile
27 #endif
28 #endif
29
30 struct NmraParseEncodeCaller {
31   ParseState *ps;
32   unsigned long arg[NMRA_MAX_NARGS];
33   jmp_buf jb;
34 };
35
36 unsigned long nmra_argnumber(NmraParseEncodeCaller *pec, int argi) {
37   return pec->arg[argi];
38 }
39
40 void nmra_problem(NmraParseEncodeCaller *pec, const char *problem) {
41   badcmd(pec->ps,problem);
42   longjmp(pec->jb, 1);
43 }
44
45 static int cmd_nmra_command(ParseState *ps, RetransmitNode *rn) {
46   int hex;
47   const char *cmdarg;
48   int lcmdarg;
49   bogus_volatile int argc, checksum;
50   NmraParseEncodeCaller pec;
51   Nmra nmra;
52   char *ep;
53
54   assert(ps->remain);
55   switch (ps->remain[0]) {
56   case '_':  ps->remain++; return ps_needhextoend(ps, rn->pi.d, &rn->pi.l);
57   case '=':  hex=1;  checksum=1;  break;
58   case ':':  hex=1;  checksum=0;  break;
59   default:   hex=0;  checksum=1;  break;
60   }
61
62   if (hex) {
63     ps->remain++;
64     nmra.l= NMRA_PACKET_MAX - checksum;
65     if (!ps_needhextoend(ps, nmra.d, &nmra.l))
66       return 0;
67   } else {
68     if (!ps_needword(ps)) return 0;
69     cmdarg= ps->thisword;
70     lcmdarg= ps->lthisword;
71     pec.ps= ps;
72     argc= 0;
73
74     while (ps_word(ps)) {
75       if (argc >= NMRA_MAX_NARGS) {
76         badcmd(ps,"far too many nmra args");
77         return 0;
78       }
79
80       errno=0; pec.arg[argc++]= strtoul(ps->thisword, &ep, 0);
81       if (errno || ep != ps->thisword + ps->lthisword)
82         { badcmd(ps,"bad numeric argument for nmra"); return 0; }
83     }
84     if (setjmp(pec.jb))
85       return 0;
86   
87     nmra_parse_encode(&nmra, cmdarg,lcmdarg, argc, &pec);
88   }
89   if (checksum)
90     nmra_addchecksum(&nmra);
91
92   nmra_encodeforpic(&nmra, &rn->pi);
93   return 1;
94 }
95   
96 static void cmd_nmra(ParseState *ps, const CmdInfo *ci) {
97   static struct { ManualRetransmitNode *head, *tail; } mrns;
98   
99   ManualRetransmitNode *mrn;
100   RetransmitNode *rn, rn_buf;
101   
102   if (ps->remain && ps->remain[0]=='*') {
103     const char *mrname;
104     int lmrname;
105
106     ps_word(ps);
107     mrname= ps->thisword+1;
108     lmrname= ps->lthisword-1;
109
110     for (mrn= mrns.head;
111          !(mrn->lname == lmrname &&
112            !memcmp(mrn->name, mrname, lmrname));
113          mrn= mrn->next);
114     if (mrn) {
115       retransmit_cancel(&mrn->rn);
116     } else {
117       mrn= mmalloc(sizeof(*mrn));
118       mrn->name= mmalloc(lmrname);
119       memcpy(mrn->name, mrname, lmrname);
120       mrn->lname= lmrname;
121     }
122   } else {
123     mrn= 0;
124   }
125
126   if (!ps->remain) {
127     if (!mrn) {
128       badcmd(ps,"nmra must have slot to cancel or data to send");
129       return;
130     }
131     free(mrn->name);
132     free(mrn);
133     return;
134   }
135
136   rn= mrn ? &mrn->rn : &rn_buf;
137   rn->pi.l= sizeof(rn->pi.d);
138   
139   if (!cmd_nmra_command(ps, rn)) {
140     if (mrn) { free(mrn->name); free(mrn); }
141     return;
142   }
143
144   if (mrn)
145     retransmit_queue(&mrn->rn);
146   else
147     serial_transmit(&rn->pi);
148 }
149
150 static void cmd_noop(ParseState *ps, const CmdInfo *ci) {
151   oprintf(&ps->cl->ch,"noop successful\n");
152 }
153
154 typedef struct PicCmdInfo PicCmdInfo;
155 struct PicCmdInfo {
156   const char *name;
157   Byte opcode;
158   int argbits;
159 };
160
161 static void cmd_pic(ParseState *ps, const CmdInfo *ci) {
162   const PicInsnInfo *pii;
163   PicInsn pi;
164   long arg;
165
166   if (ps->remain && ps->remain[0]=='=') {
167     ps->remain++;
168     pi.l= sizeof(pi.d);
169     if (!ps_needhextoend(ps, pi.d, &pi.l)) return;
170   } else {
171     pii= some_needword_lookup(ps,pic_command_infos,"pic command");
172     if (!pii) return;
173   
174     if (pii->argbits) {
175       if (!ps_neednumber(ps, &arg, 0, (1L << pii->argbits) - 1,
176                          "pic object number"))
177         return;
178     } else {
179       arg= 0;
180     }
181     if (!ps_neednoargs(ps))
182       return;
183     enco_pic_anyinsn(&pi, pii, arg);
184   }
185   serial_transmit(&pi);
186 }
187
188 const CmdInfo toplevel_cmds[]= {
189   { "pic",        cmd_pic         },
190   { "nmra",       cmd_nmra,       },
191   { "noop",       cmd_noop        },
192   { 0 }
193 };