1 /* ======================================================================== */
2 /*  Saucer Control Table Compiler                                           */
3 /* ======================================================================== */
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <ctype.h>
9 
10 #define MAX_MMR (256)
11 #define MAX_VEL (256)
12 #define MAX_SCP (256)
13 
14 /* movement range table */
15 typedef struct mmr_t
16 {
17     int min;
18     int max;
19     int cnt;
20     int vel[MAX_VEL];
21     int dst[MAX_VEL];
22     int tic[MAX_VEL];
23 } mmr_t;
24 
25 /* top level of the saucer control program */
26 typedef struct scp_t
27 {
28     int min_dly;
29     int dly_mag;
30     int dxt_num;
31     int dyt_num;
32 
33 } scp_t;
34 
35 
36 int max_scp = 0, cur_scp = 0;
37 int max_dxt = 0, cur_dxt = 0;
38 int max_dyt = 0, cur_dyt = 0;
39 
40 scp_t scp[MAX_SCP];
41 mmr_t dxt[MAX_MMR], dyt[MAX_MMR];
42 
43 
44 char *file = NULL;
45 int line_no = 0;
46 char orig[1024];
47 char line[1024];
48 
49 /* ======================================================================== */
50 /*  CANONICALIZE_LINE   -- Delete whitespace, strip comments, capitalize    */
51 /* ======================================================================== */
canonicalize_line(void)52 void canonicalize_line(void)
53 {
54     char *s1, *s2;
55 
56     s1 = s2 = line;
57 
58     strcpy(orig, line);
59 
60     while (*s2)
61     {
62         if (*s2 == ';' || *s2 == '#' || *s2 == '\n' || *s2 == '\r')
63             break;
64 
65         if (!isspace(*s2))
66         {
67             if (isalpha(*s2)) *s1++ = toupper(*s2);
68             else              *s1++ = *s2;
69         }
70 
71         s2++;
72     }
73     *s1++ = ';';
74     *s1 = 0;
75 }
76 
77 /* ======================================================================== */
78 /*  Syntax is simple and line-oriented.                                     */
79 /*                                                                          */
80 /*      SCP <#>      -- Sets the SCP currently being defined.               */
81 /*      MINDLY <#>   -- Sets minimum firing delay                           */
82 /*      DLYMAG <#>   -- Sets the magnitude of random component of fire dly. */
83 /*      SETDXT       -- binds current SCP to a given DXT                    */
84 /*      SETDYT       -- binds current SCP to a given DYT                    */
85 /*                                                                          */
86 /*      DXT <#>      -- Sets the DXT currently being defined                */
87 /*      MVX sx,ex,t  -- Adds a dxt entry based on start/end and total time  */
88 /*      VELX vx,t    -- Adds a dxt entry based on velocity and total time.  */
89 /*      RNGX x1,x2   -- Sets the bounding box                               */
90 /*                                                                          */
91 /*      DYT <#>      -- Sets the DYT currently being defined.               */
92 /*      MVY sy,ey,t  -- Adds a dyt entry based on start/end and total time  */
93 /*      VELY vy,t    -- Adds a dyt entry based on velocity and total time.  */
94 /*      RNGY y1,y2   -- Sets the bounding box                               */
95 /*                                                                          */
96 /* ======================================================================== */
97 
do_scp(int new_scp)98 void do_scp(int new_scp)
99 {
100     if (new_scp < 0 || new_scp >= MAX_SCP)
101     {
102         fprintf(stderr, "SCP must be in range %d to %d\n%s:%d>> %s\n",
103                 0, MAX_SCP-1, file, line_no, orig);
104         exit(1);
105     }
106 
107 
108     cur_scp = new_scp;
109 
110     if (cur_scp > max_scp)
111         max_scp = cur_scp;
112 }
113 
114 
do_mindly(int dly)115 void do_mindly(int dly)
116 {
117     if (dly < 0 || dly >= 255)
118     {
119         fprintf(stderr, "MINDLY must be in range %d to %d\n%s:%d>> %s\n",
120                 0, 255, file, line_no, orig);
121         exit(1);
122     }
123 
124     scp[cur_scp].min_dly = dly;
125 }
126 
do_dlymag(int dly)127 void do_dlymag(int dly)
128 {
129     if (dly < 0 || dly >= 255)
130     {
131         fprintf(stderr, "DLYMAG must be in range %d to %d\n%s:%d>> %s\n",
132                 0, 255, file, line_no, orig);
133         exit(1);
134     }
135 
136     scp[cur_scp].dly_mag = dly;
137 }
138 
do_rngx(int x1,int x2)139 void do_rngx(int x1, int x2)
140 {
141     if (x2 < x1) { int t = x2; x2 = x1; x1 = t; }
142 
143     if (x1 < 0 || x1 > 159 || x2 < 0 || x2 > 159)
144     {
145         fprintf(stderr, "RNGY X coord must be in range %d to %d\n%s:%d>> %s\n",
146                 0, 159, file, line_no, orig);
147         exit(1);
148     }
149 
150     dxt[cur_dxt].min = x1 * 256;
151     dxt[cur_dxt].max = x2 * 256 + 255;
152 }
153 
do_rngy(int y1,int y2)154 void do_rngy(int y1, int y2)
155 {
156     if (y2 < y1) { int t = y2; y2 = y1; y1 = t; }
157 
158     if (y1 < 0 || y1 > 63 || y2 < 0 || y2 > 63)
159     {
160         fprintf(stderr, "RNGY Y coord must be in range %d to %d\n%s:%d>> %s\n",
161                 0, 63, file, line_no, orig);
162         exit(1);
163     }
164 
165     dyt[cur_dyt].min = y1 * 256;
166     dyt[cur_dyt].max = y2 * 256 + 255;
167 }
168 
do_setdxt(int dxt)169 void do_setdxt(int dxt)
170 {
171     if (dxt < 0 || dxt >= MAX_MMR)
172     {
173         fprintf(stderr, "SETDXT must be in range %d to %d\n%s:%d>> %s\n",
174                 0, MAX_MMR - 1, file, line_no, orig);
175         exit(1);
176     }
177 
178     if (dxt > max_dxt) max_dxt = dxt;
179 
180     scp[cur_scp].dxt_num = dxt;
181 }
182 
do_setdyt(int dyt)183 void do_setdyt(int dyt)
184 {
185     if (dyt < 0 || dyt >= MAX_MMR)
186     {
187         fprintf(stderr, "SETDYT must be in range %d to %d\n%s:%d>> %s\n",
188                 0, MAX_MMR - 1, file, line_no, orig);
189         exit(1);
190     }
191 
192     if (dyt > max_dyt) max_dyt = dyt;
193 
194     scp[cur_scp].dyt_num = dyt;
195 }
196 
do_dxt(int dxt)197 void do_dxt(int dxt)
198 {
199     if (dxt < 0 || dxt >= MAX_MMR)
200     {
201         fprintf(stderr, "DXT must be in range %d to %d\n%s:%d>> %s\n",
202                 0, MAX_MMR - 1, file, line_no, orig);
203         exit(1);
204     }
205 
206     cur_dxt = dxt;
207 }
208 
do_dyt(int dyt)209 void do_dyt(int dyt)
210 {
211     if (dyt < 0 || dyt >= MAX_MMR)
212     {
213         fprintf(stderr, "DYT must be in range %d to %d\n%s:%d>> %s\n",
214                 0, MAX_MMR - 1, file, line_no, orig);
215         exit(1);
216     }
217 
218     cur_dyt = dyt;
219 }
220 
insert_mmr(mmr_t * mmr,char axis,int v,int t)221 void insert_mmr(mmr_t *mmr, char axis, int v, int t)
222 {
223     int idx;
224 
225     if (v > 127 || v <= 0)
226     {
227         fprintf(stderr,"MV%c has out-of-range velocity %d\n%s:%d>> %s\n",
228                 axis, v, file, line_no, orig);
229         exit(1);
230     }
231 
232     if (mmr->cnt >= MAX_VEL)
233     {
234         fprintf(stderr,"Too many D%cT entries for SCP %d\n%s:%d>> %s\n",
235                 axis, cur_scp, file, line_no, orig);
236         exit(1);
237     }
238 
239     idx = mmr->cnt++;
240 
241     mmr->vel[idx] = v;
242     mmr->dst[idx] = v * t * 8;
243     mmr->tic[idx] = t;
244 }
245 
calc_vel(int d,int dt)246 int calc_vel(int d, int dt)
247 {
248     return (d * 32) / dt;  /* round down */
249 }
250 
do_mvx(int sx,int ex,int dt)251 void do_mvx(int sx, int ex, int dt)
252 {
253     int vx, dx;
254 
255     if (sx > ex) { int t = sx; sx = ex; ex = t; }
256 
257     if (sx < 0 || sx > 159 || ex < 0 || ex > 159)
258     {
259         fprintf(stderr, "MVX X coord must be in range %d to %d\n%s:%d>> %s\n",
260                 0, 159, file, line_no, orig);
261         exit(1);
262     }
263 
264     if (sx == ex)
265     {
266         fprintf(stderr,"MVX must have different start/end coord\n%s:%d>> %s\n",
267                 file, line_no, orig);
268         exit(1);
269     }
270 
271     if (dt < 1)
272     {
273         fprintf(stderr,"MVX tick count must be >= 1\n%s:%d>> %s\n",
274                 file, line_no, orig);
275         exit(1);
276     }
277 
278     dx = ex - sx;
279     vx = calc_vel(dx, dt);
280 
281     insert_mmr(&(dxt[cur_dxt]), 'X', vx, dt);
282 }
283 
do_mvy(int sy,int ey,int dt)284 void do_mvy(int sy, int ey, int dt)
285 {
286     int vy, dy;
287 
288     if (sy > ey) { int t = sy; sy = ey; ey = t; }
289 
290     if (sy < 0 || sy > 63 || ey < 0 || ey > 63)
291     {
292         fprintf(stderr, "MVY Y coord must be in range %d to %d\n%s:%d>> %s\n",
293                 0, 63, file, line_no, orig);
294         exit(1);
295     }
296 
297     if (sy == ey)
298     {
299         fprintf(stderr,"MVY must have different start/end coord\n%s:%d>> %s\n",
300                 file, line_no, orig);
301         exit(1);
302     }
303 
304     if (dt < 1)
305     {
306         fprintf(stderr,"MVY tick count must be >= 1\n%s:%d>> %s\n",
307                 file, line_no, orig);
308         exit(1);
309     }
310 
311     dy = ey - sy;
312     vy = calc_vel(dy, dt);
313 
314     insert_mmr(&(dyt[cur_dyt]), 'Y', vy, dt);
315 }
316 
do_velx(int vx,int dt)317 void do_velx(int vx, int dt)
318 {
319     int dx;
320 
321     dx = (dt*vx + 31) >> 5;
322 
323     if (dx < 0 || dx > 159)
324     {
325         fprintf(stderr, "VELX results in out-of-range dx: %d\n%s:%d>> %s\n",
326                 dx, file, line_no, orig);
327         exit(1);
328     }
329 
330     insert_mmr(&(dxt[cur_dxt]), 'X', vx, dt);
331 }
332 
do_vely(int vy,int dt)333 void do_vely(int vy, int dt)
334 {
335     int dy;
336 
337     dy = (dt*vy + 31) >> 5;
338 
339     if (dy < 0 || dy > 63)
340     {
341         fprintf(stderr, "VELY results in out-of-range dy: %d\n%s:%d>> %s\n",
342                 dy, file, line_no, orig);
343         exit(1);
344     }
345 
346 
347     insert_mmr(&(dyt[cur_dyt]), 'Y', vy, dt);
348 }
349 
350 
351 
352 /* ======================================================================== */
353 /*  HANDLE_LINE -- parses canonicalized line and dispatches to handler.     */
354 /* ======================================================================== */
handle_line(void)355 void handle_line(void)
356 {
357     int w, x, y;
358 
359     if (line[0] == ';')
360         return;
361 
362     if      (sscanf(line,"SCP%d;",         &w)          == 1) do_scp(w);
363     else if (sscanf(line,"MINDLY%d;",      &w)          == 1) do_mindly(w);
364     else if (sscanf(line,"DLYMAG%d;",      &w)          == 1) do_dlymag(w);
365     else if (sscanf(line,"SETDXT%d;",      &w)          == 1) do_setdxt(w);
366     else if (sscanf(line,"SETDYT%d;",      &w)          == 1) do_setdyt(w);
367 
368     else if (sscanf(line,"DXT%d;",         &w)          == 1) do_dxt(w);
369     else if (sscanf(line,"MVX%d,%d,%d;",   &w,&x,&y)    == 3) do_mvx(w,x,y);
370     else if (sscanf(line,"VELX%d,%d;",     &w,&x)       == 2) do_velx(w,x);
371     else if (sscanf(line,"RNGX%d,%d;",     &w,&x)       == 2) do_rngx(w,x);
372 
373     else if (sscanf(line,"DYT%d;",         &w)          == 1) do_dyt(w);
374     else if (sscanf(line,"MVY%d,%d,%d;",   &w,&x,&y)    == 3) do_mvy(w,x,y);
375     else if (sscanf(line,"VELY%d,%d;",     &w,&x)       == 2) do_vely(w,x);
376     else if (sscanf(line,"RNGY%d,%d;",     &w,&x)       == 2) do_rngy(w,x);
377     else
378     {
379         fprintf(stderr, "Could not parse this line:\n%s:%d>> %s\n",
380                 file, line_no, orig);
381         exit(1);
382     }
383 }
384 
handle_file(FILE * f)385 void handle_file(FILE *f)
386 {
387     while (fgets(line, 1024, f))
388     {
389         line_no++;
390         canonicalize_line();
391         handle_line();
392     }
393 }
394 
395 /* ======================================================================== */
396 /*  GENERATE_TABLES                                                         */
397 /*  For each Saucer Control Program, emits an entry in top-level table      */
398 /*  and corresponding MMR.                                                  */
399 /*                                                                          */
400 /*  SCTBL.top   Top level table which contains:                             */
401 /*               -- Aggressiveness (firing parameters)                      */
402 /*               -- Pointer to Up/Down parameter set table                  */
403 /*               -- Pointer to Left/Right parameter set table               */
404 /*                                                                          */
405 /*  SCTBL.dxt   Delta-X table                                               */
406 /*               -- Min X in 8Q8                                            */
407 /*               -- Max X in 8Q8                                            */
408 /*               -- # of entries                                            */
409 /*                                                                          */
410 /*              and then a series of entries of the form                    */
411 /*                                                                          */
412 /*               -- Rightward velocity (biased 2Q6)                         */
413 /*               -- Number of 30Hz ticks                                    */
414 /*               -- Final delta-X for this move (8.8)                       */
415 /*                                                                          */
416 /*  SCTBL.dxy   Delta-Y table                                               */
417 /*              Same format of Delta-X table, only on Y axis.               */
418 /* ======================================================================== */
emit_toplev(FILE * fo)419 void emit_toplev(FILE *fo)
420 {
421     int i;
422 
423 
424     fprintf(fo, "        ;; top-level SCTBL\n");
425     fprintf(fo, "@@top:\n");
426 
427     for (i = 0; i <= max_scp; i++)
428     {
429         fprintf(fo, "@@%.2X    DECLE   $%.4X, $%.4X, @@dxt_%.2X, @@dyt_%.2X\n",
430                 i,
431                 scp[i].dly_mag, scp[i].min_dly,
432                 scp[i].dxt_num, scp[i].dyt_num);
433     }
434 
435     fprintf(fo, "\n");
436 }
437 
438 
emit_mmrtbl(FILE * fo,mmr_t * mmr,int max,char axis)439 void emit_mmrtbl(FILE *fo, mmr_t *mmr, int max, char axis)
440 {
441     int i, j;
442     char l_axis = tolower(axis), u_axis = toupper(axis);
443 
444     for (i = 0; i <= max; i++)
445     {
446         if (mmr[i].cnt == 0)
447             continue;
448 
449         fprintf(fo, "        ;; D%cT table #%d\n@@d%ct_%.2X:\n",
450                 u_axis, i, l_axis, i);
451 
452 
453         fprintf(fo, "        DECLE   $%.4X, $%.4X, %d\n",
454                 mmr[i].min, mmr[i].max, mmr[i].cnt);
455 
456         for (j = 0; j < mmr[i].cnt; j++)
457         {
458             fprintf(fo,
459                     "        DECLE   ($%.2X SHL 8) + ($%.2X), $%.4X\n",
460                     mmr[i].tic[j], mmr[i].vel[j], mmr[i].dst[j]);
461         }
462         fprintf(fo, "\n");
463     }
464 }
465 
generate_tables(FILE * fo)466 void generate_tables(FILE *fo)
467 {
468     fprintf(fo,
469             ";; Generated by SCTC\n"
470             "SCTBL   PROC\n");
471 
472     emit_toplev(fo);
473     emit_mmrtbl(fo, dxt, max_dxt, 'x');
474     emit_mmrtbl(fo, dyt, max_dyt, 'y');
475 
476     fprintf(fo, "        ENDP\n");
477 }
478 
479 
480 /* ======================================================================== */
481 /*  MAIN -- where the action is.                                            */
482 /* ======================================================================== */
main(int argc,char * argv[])483 int main(int argc, char *argv[])
484 {
485     int i;
486     FILE *fi;
487 
488     for (i = 1; i < argc; i++)
489     {
490         fi = fopen(argv[i], "r");
491         if (!fi)
492         {
493             fprintf(stderr, "ERROR:  could not open %s for reading\n",argv[i]);
494             exit(1);
495         }
496 
497         file = argv[i];
498         line_no = 0;
499 
500         handle_file(fi);
501         fclose(fi);
502     }
503 
504     generate_tables(stdout);
505 
506     return 0;
507 }
508 
509