1 /* ======================================================================== */
2 /*  World assembler for Lunar MP.                                           */
3 /*  This is my THIRD(!) attempt at a world encoding.                        */
4 /*                                                                          */
5 /*  Syntax:                                                                 */
6 /*      [loc] LABEL 'label_name'  -- Align to word and emit asm label.      */
7 /*      loc CUE num         -- Cue creeps with cue 'num'   1 unit           */
8 /*      loc CUE 'label'     -- Cue creeps with cue 'label' 1 unit           */
9 /*      loc ROCK1           -- Small rock                  2 units          */
10 /*      loc ROCK2           -- Medium rock                 2 units          */
11 /*      loc ROCK3           -- Large rock                  3 units          */
12 /*      loc CRAT1           -- Small crater                2 units          */
13 /*      loc CRAT2           -- Large crater                3 units          */
14 /*      loc FILLTO          -- Fill up to but not including 'loc'           */
15 /*      loc LEVEL 'char'    -- Cue a level marker          1 unit           */
16 /*      loc CAUTION num     -- Cue a caution light         1 unit           */
17 /*      loc EXIT num        -- Tell some creeps to exit    1 unit           */
18 /*      loc EXIT 'label'    -- Tell some creeps to exit    1 unit           */
19 /*                                                                          */
20 /*                                                                          */
21 /*  Numbers are all in decimal.  See design notes below for details.        */
22 /*  Blank lines are ignored.  Anything after a # mark is ignored.           */
23 /*  Whitespace is compressed, and everything outside quotes is case-        */
24 /*  insensitive.  (Note, whitespace isn't really allowed in quotes,         */
25 /*  though I don't really check for it.)                                    */
26 /*                                                                          */
27 /*  Although the objects allow specifying a location, the assembler         */
28 /*  requires you to state the objects in monotonically increasing order.    */
29 /*  All objects (including cues) have an implicit width that must be        */
30 /*  taken into account when specifying object location.  No two objects     */
31 /*  may be co-located.  This includes cues which have a minimum width       */
32 /*  of 1 (to stagger the processing burden).                                */
33 /* ======================================================================== */
34 
35 /* ======================================================================== */
36 /*  WORLD DATA encoding format -- design ramblings.                         */
37 /*                                                                          */
38 /*  The world data format is really quite simple:                           */
39 /*                                                                          */
40 /*      Bits 0..7    Encoded object/cue number.                             */
41 /*      Bits 8..14   Delay until next word is processed.                    */
42 /*      Bit  15      Object or cue?  0 == object, 1 == cue.                 */
43 /*                                                                          */
44 /*  For objects, the lower 8 bits are an index into the tables RCS1/RCS2.   */
45 /*  For cues, the lower 8 bits are an index into the SPAWNS table.  (The    */
46 /*  details of the SPAWNS table TBD.)                                       */
47 /*                                                                          */
48 /*  This format is a much simpler format than previous attempts.  Why?      */
49 /*  I noticed that the compression ratio achieved by the more clever        */
50 /*  encodings was pretty lackluster (about 20% for attempt #2), and the     */
51 /*  work required to decode it was non-trivial.  At the very least, there   */
52 /*  was too much state to carry around.  With this scheme, the only state   */
53 /*  required is a pointer to the current word, and a down-counter.  :-)     */
54 /* ======================================================================== */
55 
56 
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <ctype.h>
61 
62 
63 #define Q(x) #x
64 #define X(x) x
65 
66 #define MAX_LEVEL (4096)
67 #define FL_NOP (0)
68 #define FL_OBJ (1)
69 #define FL_CUE (2)
70 
71 #define CMD_BLANK (7)
72 
73 typedef struct
74 {
75     char          *label;
76     char          *cuelbl;
77     unsigned char  flag;
78     unsigned short count;
79     int            object;
80 } level_t;
81 
82 FILE *f_out;
83 
84 
85 int cur_pos   = 0;
86 int last_pos  = 0;
87 level_t level[MAX_LEVEL];
88 
89 
90 /* ======================================================================== */
91 /*  MOVE_TO -- Add blanks to the run of blanks.  This doesn't actually      */
92 /*               emit any blanks until something non-blank is emitted.      */
93 /* ======================================================================== */
move_to(int new_loc)94 void move_to(int new_loc)
95 {
96     if (new_loc < 0 || new_loc >= MAX_LEVEL)
97     {
98         fprintf(stderr, "ERROR:  Location %d must be in the range 0 to %d\n",
99                 new_loc, MAX_LEVEL);
100         exit(1);
101     }
102     cur_pos = new_loc;
103     if (cur_pos > last_pos)
104         last_pos = cur_pos;
105 }
106 
107 /* ======================================================================== */
108 /*  CMD_LABEL -- Record a location as needing a label.                      */
109 /* ======================================================================== */
cmd_label(char * label)110 void cmd_label(char *label)
111 {
112     char *lbl_copy;
113 
114     if (level[cur_pos].label != NULL)
115     {
116         fprintf(stderr, "ERROR:  Attempt to add '%s' as label to '%d',\n"
117                         "        which already has label '%s'\n",
118                         label, cur_pos, level[cur_pos].label);
119         exit(1);
120     }
121 
122     lbl_copy = strdup(label);
123     if (!lbl_copy)
124     {
125         fprintf(stderr, "ERROR:  Out of memory in strdup.  Buy some more.\n");
126         exit(1);
127     }
128     level[cur_pos].label = lbl_copy;
129 }
130 
131 
132 /* ======================================================================== */
133 /*  CMD_CUE  -- Cue some creeps.                                            */
134 /* ======================================================================== */
cmd_cue(int cue)135 void cmd_cue(int cue)
136 {
137     if (level[cur_pos].flag != FL_NOP)
138     {
139         fprintf(stderr, "ERROR:  Trying to add CUE to occupied location %d!\n",
140                 cur_pos);
141         exit(1);
142     }
143 
144     level[cur_pos].cuelbl = NULL;
145     level[cur_pos].object = cue;
146     level[cur_pos].flag   = FL_CUE;
147     level[cur_pos].count  = 1;
148 
149     move_to(cur_pos + 1);
150 }
151 
152 /* ======================================================================== */
153 /*  CMD_CUELBL -- cue, via label (preferred)                                */
154 /* ======================================================================== */
cmd_cuelbl(char * cue)155 void cmd_cuelbl(char *cue)
156 {
157     char *lbl_copy;
158 
159     if (level[cur_pos].flag != FL_NOP)
160     {
161         fprintf(stderr, "ERROR:  Trying to add CUE to occupied location %d!\n",
162                 cur_pos);
163         exit(1);
164     }
165 
166     lbl_copy = strdup(cue);
167     if (!lbl_copy)
168     {
169         fprintf(stderr, "ERROR:  Out of memory in strdup.  Buy some more.\n");
170         exit(1);
171     }
172 
173     level[cur_pos].cuelbl = lbl_copy;
174     level[cur_pos].object = 0;
175     level[cur_pos].flag   = FL_CUE;
176     level[cur_pos].count  = 1;
177 
178     move_to(cur_pos + 1);
179 }
180 
181 /* ======================================================================== */
182 /*  CMD_EXIT -- Cue some creeps to leave                                    */
183 /* ======================================================================== */
cmd_exit(int cue)184 void cmd_exit(int cue)
185 {
186     if (level[cur_pos].flag != FL_NOP)
187     {
188         fprintf(stderr, "ERROR:  Trying to add EXIT to occupied location %d!\n",
189                 cur_pos);
190         exit(1);
191     }
192 
193     level[cur_pos].cuelbl = NULL;
194     level[cur_pos].object = cue + 256;
195     level[cur_pos].flag   = FL_CUE;
196     level[cur_pos].count  = 1;
197 
198     move_to(cur_pos + 1);
199 }
200 
201 /* ======================================================================== */
202 /*  CMD_EXITLBL -- exit, via label (preferred)                              */
203 /* ======================================================================== */
cmd_exitlbl(char * cue)204 void cmd_exitlbl(char *cue)
205 {
206     char *lbl_copy;
207 
208     if (level[cur_pos].flag != FL_NOP)
209     {
210         fprintf(stderr, "ERROR:  Trying to add EXIT to occupied location %d!\n",
211                 cur_pos);
212         exit(1);
213     }
214 
215     lbl_copy = strdup(cue);
216     if (!lbl_copy)
217     {
218         fprintf(stderr, "ERROR:  Out of memory in strdup.  Buy some more.\n");
219         exit(1);
220     }
221 
222     level[cur_pos].cuelbl = lbl_copy;
223     level[cur_pos].object = 256;
224     level[cur_pos].flag   = FL_CUE;
225     level[cur_pos].count  = 1;
226 
227     move_to(cur_pos + 1);
228 }
229 
230 char *cmd_name[9] =
231 {
232     "ROCK1",
233     "ROCK2",
234     "ROCK3",
235     "CRAT1",
236     "CRAT2",
237     "CRAT3",
238     "CRAT4",
239     "BLANK",
240     "CUE",
241 };
242 
243 char *cmd_lbl[15] =
244 {
245     "@@rock1",
246     "@@rock2",
247     "@@rock3",
248     "@@crat1",
249     "@@crat2",
250     "@@crat3",
251     "@@crat4",
252     "@@blank",
253     "@@cue",
254     "@@level",
255     "@@caut0",
256     "@@caut1",
257     "@@caut2",
258     "@@caut3",
259     "@@exit",
260 };
261 
262 int cmd_width[14] = { 2, 2, 3, 2, 3, 5, 5, 1, 1, 1, 1, 1, 1 };
263 
264 /* ======================================================================== */
265 /*  CMD_OBJECT -- Put an object into the level                              */
266 /* ======================================================================== */
cmd_object(int n)267 void cmd_object(int n)
268 {
269     if (level[cur_pos].flag != FL_NOP)
270     {
271         fprintf(stderr, "ERROR:  Trying to add %s to occupied location %d!\n",
272                 cmd_name[n], cur_pos);
273         exit(1);
274     }
275 
276     level[cur_pos].object = n;
277     level[cur_pos].flag   = FL_OBJ;
278 
279     move_to(cur_pos + cmd_width[n]);
280 }
281 
282 /* ======================================================================== */
283 /*  CMD_LEVEL  -- Put an end-of-level marker cue into the level data        */
284 /* ======================================================================== */
cmd_level(int n)285 void cmd_level(int n)
286 {
287     if (level[cur_pos].flag != FL_NOP)
288     {
289         fprintf(stderr, "ERROR:  Trying to add LEVEL to occupied location %d!\n",
290                 cur_pos);
291         exit(1);
292     }
293 
294     if      (n >= 'A' && n <= 'Z') n = n - 'A';
295     else if (n >= 'a' && n <= 'z') n = n - 'a' + 0x20;
296 
297     level[cur_pos].object = n + 32;
298     level[cur_pos].flag   = FL_OBJ;
299 
300     move_to(cur_pos + 1);
301 }
302 
303 /* ======================================================================== */
304 /*  CANONICALIZE -- Canonicalizes an input line.                            */
305 /* ======================================================================== */
canonicalize(char * buf)306 int canonicalize(char *buf)
307 {
308     char *s1, *s2;
309     int in_quote = 0;
310 
311     /* Kill any comments or newlines. */
312     if ((s1 = strchr(buf, '#' )) != NULL) *s1 = 0;
313     if ((s1 = strchr(buf, '\r')) != NULL) *s1 = 0;
314     if ((s1 = strchr(buf, '\n')) != NULL) *s1 = 0;
315 
316     /* Kill leading whitespace. */
317     s1 = s2 = buf;
318 
319     while (*s1 && isspace(*s1)) s1++;
320 
321     if (!*s1) { buf[0] = 0; return 0; }
322 
323     /* Compress remaining whitespace */
324     while (*s1)
325     {
326         while (!isspace(*s1))
327             *s2++ = *s1++;
328 
329         *s2++ = ' ';  /* compressed whitespace */
330 
331         if (!*s1)
332             break;
333 
334         while (isspace(*s1))
335             s1++;
336     }
337 
338     *s2 = 0;
339 
340     if (s2 == buf)
341         return 0;
342 
343 
344     /* Re-scan, uppercasing anything lowercase */
345     s1 = buf;
346     while (*s1)
347     {
348         if (*s1 == '\'')
349             in_quote ^= 1;
350 
351         if (!in_quote && isalpha(*s1))
352             *s1 = toupper(*s1);
353 
354         s1++;
355     }
356 
357     return s1 - buf;
358 }
359 
360 #define MAX_WORD (64)
361 
362 char word1[MAX_WORD];
363 char word2[MAX_WORD];
364 char label[MAX_WORD];
365 int  num1, num2;
366 
367 /* ======================================================================== */
368 /*  PARSE -- Pull a line apart into words.                                  */
369 /* ======================================================================== */
parse(char * buf)370 int parse(char *buf)
371 {
372     char *s1, *s2;
373     int len, lbl, word;
374 
375     word1[0] = word2[0] = label[0] = 0;
376     num1 = num2 = -1;
377     word = 0;
378     lbl  = 0;
379 
380     if (!canonicalize(buf))
381         return 0;
382 
383     s1 = buf;
384 
385     while (*s1)
386     {
387         if (*s1 == ' ')
388         {
389             s1++;
390             continue;
391         }
392         if (isalpha(*s1))
393         {
394             if (word == 2)
395             {
396                 printf("PARSE ERROR: too many words at %s\n", s1);
397                 return -1; /* too many words */
398             }
399             s2 = word == 0 ? word1 : word2;
400             word++;
401 
402             for (len = 0; len < MAX_WORD-1 && *s1 && isalnum(*s1); len++)
403                 *s2++ = *s1++;
404             *s2++ = 0;
405 
406             if (*s1) s1++;
407             continue;
408         }
409 
410         if (*s1 == '\'')
411         {
412             if (lbl == 1)
413             {
414                 printf("PARSE ERROR: too many labels at %s\n", s1);
415                 return -1; /* too many labels */
416             }
417 
418             lbl = 1;
419 
420             s1++;
421             s2 = label;
422             for (len = 0; len < MAX_WORD-1 && *s1 && *s1 != '\''; len++)
423                 *s2++ = *s1++;
424             *s2++ = 0;
425 
426             if (*s1 != '\'')
427             {
428                 printf("PARSE ERROR: bad label syntax\n");
429                 return -1; /* bad label syntax */
430             }
431             s1++;
432             continue;
433         }
434 
435         if (isdigit(*s1))
436         {
437             if      (num1 < 0) num1 = atoi(s1);
438             else if (num2 < 0) num2 = atoi(s1);
439             else
440             {
441                 printf("PARSE ERROR: too many numbers at %s\n", s1);
442                 return -1; /* too many numbers */
443             }
444 
445             while (*s1 && isdigit(*s1))
446                 s1++;
447             continue;
448         }
449 
450         printf("PARSE ERROR:  unexpected character '%c'\n", *s1);
451         return -1;
452     }
453 
454     return 1;
455 }
456 
457 
458 char * keyword[] =
459 {
460     "ROCK1",   /*  0 */
461     "ROCK2",   /*  1 */
462     "ROCK3",   /*  2 */
463     "CRAT1",   /*  3 */
464     "CRAT2",   /*  4 */
465     "FILLTO",  /*  5 */
466     "CUE",     /*  6 */
467     "LABEL",   /*  7 */
468     "LEVEL",   /*  8 */
469     "CAUTION", /*  9 */
470     "EXIT",    /* 10 */
471     "CRAT3",   /* 11 */
472     "CRAT4",   /* 12 */
473 };
474 
475 
476 
477 #define NUM_KEYWORD (sizeof(keyword) / sizeof(char *))
478 
479 /* ======================================================================== */
480 /*  HANDLE_LINE -- Parse the line, and if it's not empty, execute it.       */
481 /* ======================================================================== */
handle_line(char * buf)482 void handle_line(char *buf)
483 {
484     int i, cmd;
485 
486     i = parse(buf);
487 
488     if (i < 0)
489     {
490         printf("PARSE ERROR: \"%s\"\n", buf);
491         exit(1);
492     }
493 
494     if (!i)
495         return;
496 
497     cmd = -1;
498     for (i = 0; i < NUM_KEYWORD; i++)
499         if (!strcmp(keyword[i], word1))
500         {
501             cmd = i;
502             break;
503         }
504 
505     if (num1 >= 0)
506         move_to(num1);
507 
508     /* Check for location argument / required first argument */
509     switch (cmd)
510     {
511         case 0: case 1: case 2: case 3: case 4:  /* ROCKn, CRATn */
512         case 5:  /* FILLTO  */
513         case 11: case 12:
514         {
515             if (num1 < 0)
516             {
517                 fprintf(stderr, "ERROR: Location required for %s.",
518                         keyword[cmd]);
519                 exit(1);
520             }
521 
522             break;
523         }
524         case 6:   /* CUE     */
525         case 10:  /* EXIT    */
526         {
527             if (num1 < 0)
528             {
529                 fprintf(stderr, "ERROR: Location required for %s.",
530                         keyword[cmd]);
531                 exit(1);
532             }
533             if ((num2 < 0) == (label[0] == 0))
534             {
535                 fprintf(stderr, "ERROR: %s requires number or label, "
536                         "but not both\n", keyword[cmd]);
537                 exit(1);
538             }
539             break;
540         }
541         case 9:  /* CAUTION */
542         {
543             if (num1 < 0)
544             {
545                 fprintf(stderr, "ERROR: Location required for %s.",
546                         keyword[cmd]);
547                 exit(1);
548             }
549             if (num2 < 0 || num2 > 3)
550             {
551                 fprintf(stderr, "ERROR: Caution requires number from 0 to 2\n");
552                 exit(1);
553             }
554             break;
555         }
556 
557         case 7: /* LABEL */
558         case 8: /* LEVEL */
559         {
560             if (label[0] == 0)
561             {
562                 printf("No label with LABEL/LEVEL directive\n");
563                 exit(1);
564             }
565 
566             if (cmd == 8 &&
567                 (label[1] != 0 ||
568                  !((label[0] >= 'A' && label[0] <= 'Z') ||
569                    (label[0] >= 'a' && label[0] <= 'z'))
570                  )
571                 )
572             {
573                 printf("Label must be single alpha char for LEVEL directive\n");
574                 exit(1);
575             }
576 
577 
578             break;
579         }
580 
581         default:
582             break;
583     }
584 
585 
586     /* Actually process the command. */
587     switch (cmd)
588     {
589         case 0: case 1: case 2: case 3: case 4:  /* ROCKn, CRATn */
590         {
591             cmd_object(cmd);
592             break;
593         }
594         case 11: case 12:  /* CRAT3/4 */
595         {
596             cmd_object(cmd - 6);
597             break;
598         }
599 
600         case 9:
601         {
602             cmd_object(num2 + 10);
603             break;
604         }
605 
606         case 5: /* FILLTO */
607         {
608             break;
609         }
610 
611         case 6: /* CUE */
612         {
613             if (label[0] != 0)
614             {
615                 cmd_cuelbl(label);
616                 break;
617             }
618 
619             if (num2 < 0 || num2 > 127)
620             {
621                 printf("Numeric argument %d out of range for CUE\n", num1);
622                 exit(1);
623             }
624 
625             cmd_cue(num2);
626             break;
627         }
628 
629 
630         case 10: /* EXIT */
631         {
632             if (label[0] != 0)
633             {
634                 cmd_exitlbl(label);
635                 break;
636             }
637 
638             if (num2 < 0 || num2 > 31)
639             {
640                 printf("Numeric argument %d out of range for EXIT\n", num1);
641                 exit(1);
642             }
643 
644             cmd_exit(num2);
645             break;
646         }
647 
648 
649         case 7: /* LABEL */
650         {
651             cmd_label(label);
652             break;
653         }
654 
655         case 8:
656         {
657             cmd_level(label[0]);
658             break;
659         }
660 
661         default:
662         {
663             printf("Unknown command '%s'\nInput: '%s'\n", word1, buf);
664             exit(1);
665             break;
666         }
667     }
668 }
669 
670 /* ======================================================================== */
671 /*  GENERATE_LEVEL                                                          */
672 /* ======================================================================== */
generate_level(void)673 void generate_level(void)
674 {
675     int i, j, run;
676     int cmd = CMD_BLANK, cue = 0;  /* BLANK */
677     int tot_words = 0;
678     int tot_cmds  = 0;
679     int tot_objs  = 0;
680     int tot_count = 0;
681     char *lbl = level[0].label;
682     char *cuelbl = level[0].cuelbl;
683 
684     if (level[0].flag == FL_NOP)
685     {
686         cmd = CMD_BLANK;
687         cue = 0;
688     } else if (level[0].flag == FL_OBJ)
689     {
690         cmd = level[0].object;
691     } else
692     {
693         cmd = 1000;
694         cue = level[0].object;
695         cuelbl = level[0].cuelbl;
696     }
697 
698     run = 1;
699 
700     /* -------------------------------------------------------------------- */
701     /*  Pass 1:  Trace through level[] and run-length encode.               */
702     /* -------------------------------------------------------------------- */
703     for (i = 1, j = 0; i <= last_pos; i++)
704     {
705         if (i != (last_pos) &&
706             level[i].flag  == FL_NOP &&
707             level[i].label == NULL)
708         {
709             run++;
710             continue;
711         }
712 
713         if (cmd != 1000)
714         {
715             level[j].flag   = FL_OBJ;
716             level[j].object = cmd;
717             level[j].count  = run;
718             level[j].label  = lbl;
719             tot_objs++;
720         } else
721         {
722             level[j].flag   = FL_CUE;
723             level[j].object = cue;
724             level[j].cuelbl = cuelbl;
725             level[j].count  = run;
726             level[j].label  = lbl;
727             tot_cmds++;
728         }
729 
730         run = 1;
731         j++;
732         lbl = level[i].label;
733         if (level[i].flag == FL_OBJ)
734         {
735             cmd = level[i].object;
736         } else if (level[i].flag == FL_CUE)
737         {
738             cmd    = 1000;
739             cue    = level[i].object;
740             cuelbl = level[i].cuelbl;
741         } else
742         {
743             cmd = CMD_BLANK;
744         }
745 
746     }
747 
748     if (cmd != 1000)
749     {
750         level[j].flag   = FL_OBJ;
751         level[j].object = cmd;
752         level[j].count  = run;
753         level[j].label  = lbl;
754         tot_objs++;
755     } else
756     {
757         level[j].flag   = FL_CUE;
758         level[j].cuelbl = cuelbl;
759         level[j].object = cue;
760         level[j].count  = run;
761         level[j].label  = lbl;
762         tot_cmds++;
763     }
764 
765     tot_words = ++j;
766 
767 
768     fprintf(f_out,
769             ";; ====================================="
770             "=================================== ;;\n"
771             "    IF (DEFINED @@rock1) = 0\n"
772             "@@rock1 EQU     ((RCS1.rock1 - RCS1 + 1) SHL 8)\n"
773             "@@rock2 EQU     ((RCS1.rock2 - RCS1 + 1) SHL 8)\n"
774             "@@rock3 EQU     ((RCS1.rock3 - RCS1 + 1) SHL 8)\n"
775             "@@crat1 EQU     ((RCS1.crat1 - RCS1 + 1) SHL 8)\n"
776             "@@crat2 EQU     ((RCS1.crat2 - RCS1 + 1) SHL 8)\n"
777             "@@crat3 EQU     ((RCS1.crat3 - RCS1 + 1) SHL 8)\n"
778             "@@crat4 EQU     ((RCS1.crat4 - RCS1 + 1) SHL 8)\n"
779             "@@blank EQU     ((RCS1.blank - RCS1 + 1) SHL 8)\n"
780             "@@caut0 EQU     $1C00\n"
781             "@@caut1 EQU     $1D00\n"
782             "@@caut2 EQU     $1E00\n"
783             "@@caut3 EQU     $1F00\n"
784             "@@level EQU     $2000\n"
785             "@@exit  EQU     $6000\n"
786             "@@cue   EQU     $8000\n"
787             "    ENDI\n"
788             ";; ====================================="
789             "=================================== ;;\n");
790 
791     /* -------------------------------------------------------------------- */
792     /*  Pass 2:  Emit the run-length encoded data.                          */
793     /* -------------------------------------------------------------------- */
794     for (i = 0; i < tot_words; i++)
795     {
796         if (level[i].label)
797             fprintf(f_out, "%s:\n", level[i].label);
798 
799         if (level[i].flag == FL_CUE && level[i].object < 256)
800         {
801             if (level[i].cuelbl == NULL)
802             {
803                 fprintf(f_out, "    DECLE   %-8s + (%3d - 1) + (%3d SHL 8)     "
804                         "; $%.4X %4d  CUE %d\n",
805                         cmd_lbl[8], level[i].count, level[i].object,
806                         i, tot_count, level[i].object);
807             } else if (level[i].cuelbl != NULL)
808             {
809                 fprintf(f_out, "    DECLE   %-8s + (%3d - 1) + CUE.%-12s"
810                         "; $%.4X %4d  CUE '%s'\n",
811                         cmd_lbl[8], level[i].count, level[i].cuelbl,
812                         i, tot_count, level[i].cuelbl);
813             }
814         } else if (level[i].flag == FL_CUE && level[i].object >= 256)
815         {
816             if (level[i].cuelbl == NULL)
817             {
818                 fprintf(f_out, "    DECLE   %-8s + (%3d - 1) + (%3d SHL 8)     "
819                         "; $%.4X %4d  EXIT %d\n",
820                         cmd_lbl[14], level[i].count, level[i].object - 256,
821                         i, tot_count, level[i].object - 256);
822             } else if (level[i].cuelbl != NULL)
823             {
824                 fprintf(f_out, "    DECLE   %-8s + (%3d - 1) + EXIT.%-11s"
825                         "; $%.4X %4d  EXIT '%s'\n",
826                         cmd_lbl[14], level[i].count, level[i].cuelbl,
827                         i, tot_count, level[i].cuelbl);
828             }
829         }
830         if (level[i].flag == FL_OBJ && level[i].object < 10)
831         {
832             fprintf(f_out, "    DECLE   %-8s + (%3d - 1)                   "
833                     "; $%.4X %4d  %s\n",
834                     cmd_lbl[level[i].object], level[i].count,
835                     i, tot_count, cmd_name[level[i].object]);
836         }
837         if (level[i].flag == FL_OBJ &&
838             level[i].object < 32 &&
839             level[i].object >= 10)
840 
841         {
842             fprintf(f_out, "    DECLE   %-8s + (%3d - 1)                   "
843                     "; $%.4X %4d  CAUTION %d\n",
844                     cmd_lbl[level[i].object], level[i].count,
845                     i, tot_count, level[i].object - 10);
846         }
847         if (level[i].flag == FL_OBJ && level[i].object >= 32)
848         {
849             fprintf(f_out, "    DECLE   %-8s + (%3d - 1) + (%3d SHL 8)     "
850                     "; $%.4X %4d  LEVEL '%c'\n",
851                     cmd_lbl[9], level[i].count, level[i].object - 32,
852                     i, tot_count,
853                     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
854                     "abcdefghijklmnopqrstuvwxyz"[level[i].object - 32]);
855         }
856 
857         if (level[i].flag == FL_NOP)
858         {
859             fprintf(stderr, "ERROR: FL_NOP in pass 2?\n");
860             exit(1);
861         }
862 
863         tot_count += level[i].count;
864     }
865 
866     fprintf(f_out, "                                                   "
867                    ";       %4d  END\n", tot_count);
868 
869     /* -------------------------------------------------------------------- */
870     /*  Print summary information.                                          */
871     /* -------------------------------------------------------------------- */
872 #if 0
873     printf( ";; ====================================="
874             "=================================== ;;\n"
875             ";;  SUMMARY\n"
876             ";;      TOTAL COMMANDS       %8d\n"
877             ";;      TOTAL OBJECTS        %8d\n"
878             ";;      TOTAL WORDS          %8d\n"
879             ";; ====================================="
880             "=================================== ;;\n",
881             tot_cmds, tot_objs, tot_words);
882 #endif
883     fprintf(f_out,
884             "\n\n"
885             ";; ====================================="
886             "=================================== ;;\n"
887             ";;  SUMMARY\n"
888             ";;      TOTAL COMMANDS       %8d\n"
889             ";;      TOTAL OBJECTS        %8d\n"
890             ";;      TOTAL WORDS          %8d\n"
891             ";; ====================================="
892             "=================================== ;;\n",
893             tot_cmds, tot_objs, tot_words);
894 }
895 
896 /* ======================================================================== */
897 /*  MAIN -- where it all happens                                            */
898 /*                                                                          */
899 /*  Usage:  wasm outfile infile [infile [infile [...]]]                     */
900 /* ======================================================================== */
main(int argc,char * argv[])901 int main(int argc, char *argv[])
902 {
903     FILE *f_in;
904     int i;
905     char buf[1024];
906 
907     if (argc < 3)
908     {
909         printf("Usage: wasm outfile infile [infile [infile [...]]]\n");
910         exit(1);
911     }
912 
913 
914     for (i = 2; i < argc; i++)
915     {
916         printf("Processing '%s'\n", argv[i]);
917         f_in = fopen(argv[i], "r");
918         if (!f_in)
919         {
920             printf("could not open '%s' for reading\n", argv[i]);
921             exit(1);
922         }
923 
924         while (fgets(buf, 1024, f_in) != NULL)
925         {
926             handle_line(buf);
927         }
928 
929         fclose(f_in);
930     }
931 
932 
933 
934 
935     f_out = fopen(argv[1], "w");
936     if (!f_out)
937     {
938         printf("could not open '%s' for writing\n", argv[1]);
939         exit(1);
940     }
941 
942 
943     generate_level();
944 
945     fclose(f_out);
946 
947 
948     return 0;
949 }
950 
951