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