]> git.sur5r.net Git - cc65/blob - src/grc65/grc65.c
e685c59cf59b015d81b5cff4cd226e1b8197eb15
[cc65] / src / grc65 / grc65.c
1 /* GEOS resource compiler
2
3    by Maciej 'YTM/Elysium' Witkowiak
4
5    see GEOSLib documentation for license info
6 */
7
8 /* - make it work, then do it better
9    - more or less comments? it was hard to code, should be even harder to
10      understand =D
11    - add loadable icons feature (binary - 63 bytes)
12 */
13
14 /* - err, maybe free allocated memory, huh? (who cares, it's just a little prog...)
15 */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <time.h>
23
24 /* common stuff */
25 #include "fname.h"
26 #include "abend.h"
27 #include "chartype.h"
28 #include "target.h"
29 #include "xmalloc.h"
30
31 /* I hope that no one will be able to create a .grc bigger than this... */
32 #define BLOODY_BIG_BUFFER 65000
33
34 /* there are no 6MB GEOS binaries... I hope! */
35 #define THIS_BUFFER_IS_SOOO_HUGE 6000000
36
37
38 struct menuitem {
39     char *name;
40     char *type;
41     char *target;
42     struct menuitem *next;
43 };
44
45 struct menu {
46     char *name;
47     int top, left;
48     int bot, right;
49     char *type;
50     struct menuitem *item;
51 };
52
53 struct appheader {
54     int year, month, day, hour, min;
55     int mode;
56     int dostype;
57     int geostype;
58     int structure;
59     char *dosname;
60     char *classname;
61     char *version;
62     char *author;
63     char *info;
64     char *icon;
65 };
66
67 const char *mainToken[] = {"MENU", "HEADER", "ICON", "DIALOG", "VLIR", ""};
68
69 const char *hdrFTypes[] = {"APPLICATION", "AUTO_EXEC", "DESK_ACC", "ASSEMBLY",
70                            "DISK_DEVICE", "PRINTER", "SYSTEM", ""};
71
72 const char *hdrFields[] = {"author", "info", "date", "dostype", "mode", "structure", "icon", ""};
73
74 const char *hdrDOSTp[] = {"seq", "SEQ", "prg", "PRG", "usr", "USR", ""};
75
76 const char *hdrStructTp[] = {"seq", "SEQ", "vlir", "VLIR", ""};
77
78 const char *hdrModes[] = {"any", "40only", "80only", "c64only", ""};
79
80 const int BSWTab[] = {0, 0x005, 0x007, 0x00b, 0x011, 0x017, 0x01d, 0x023,
81     0x025, 0x029, 0x02d, 0x033, 0x039, 0x03c, 0x041, 0x043, 0x04a, 0x04f,
82     0x052, 0x056, 0x05a, 0x05f, 0x063, 0x068, 0x06d, 0x072, 0x077, 0x079,
83     0x07c, 0x080, 0x084, 0x088, 0x08e, 0x094, 0x09a, 0x09f, 0x0a4, 0x0a9,
84     0x0ad, 0x0b1, 0x0b6, 0x0bc, 0x0be, 0x0c2, 0x0c8, 0x0cc, 0x0d4, 0x0da,
85     0x0e0, 0x0e5, 0x0eb, 0x0f0, 0x0f5, 0x0f9, 0x0fe, 0x104, 0x10c, 0x112,
86     0x118, 0x11e, 0x121, 0x129, 0x12c, 0x132, 0x13a, 0x13e, 0x143, 0x148,
87     0x14d, 0x152, 0x157, 0x15a, 0x15f, 0x164, 0x166, 0x168, 0x16d, 0x16f,
88     0x177, 0x17c, 0x182, 0x187, 0x18c, 0x18f, 0x193, 0x196, 0x19b, 0x1a1,
89     0x1a9, 0x1af, 0x1b4, 0x1ba, 0x1be, 0x1c0, 0x1c4, 0x1ca, 0x1d2, 0x1dd};
90
91 const unsigned char icon1[] = {255, 255, 255, 128,   0,   1, 128,   0,   1,
92                                128,   0,   1, 128,   0,   1, 128,   0,   1,
93                                128,   0,   1, 128,   0,   1, 128,   0,   1,
94                                128,   0,   1, 128,   0,   1, 128,   0,   1,
95                                128,   0,   1, 128,   0,   1, 128,   0,   1,
96                                128,   0,   1, 128,   0,   1, 128,   0,   1,
97                                128,   0,   1, 128,   0,   1, 255, 255, 255};
98
99 char *ProgName; /* for AbEnd, later remove and use common/cmdline.h */
100
101 char *outputCName = NULL, *outputSName = NULL;
102 FILE *outputCFile, *outputSFile;
103 int CFnum = 0, SFnum = 0;
104 int apple = 0;
105 char outputCMode[2] = "w";
106 char outputSMode[2] = "w";
107
108
109 void printUsage(void) {
110
111     printf("Usage: %s [options] file\n"
112            "Options:\n"
113            "\t-h, -?\t\tthis help\n"
114            "\t-o name\t\tname C output file\n"
115            "\t-s name\t\tname asm output file\n"
116            "\t-t sys\t\tset target system\n",
117            ProgName);
118 }
119
120
121 void printCHeader(void) {
122
123     fprintf(outputCFile,
124         "//\n"
125         "//\tThis file was generated by the GEOS Resource Compiler\n"
126         "//\n"
127         "//\tDO NOT EDIT! Any changes will be lost!\n"
128         "//\n"
129         "//\tEdit proper resource file instead.\n"
130         "//\n\n");
131 }
132
133
134 void printSHeader(void) {
135
136     fprintf(outputSFile,
137         ";\n"
138         ";\tThis file was generated by the GEOS Resource Compiler\n"
139         ";\n"
140         ";\tDO NOT EDIT! Any changes will be lost!\n"
141         ";\n"
142         ";\tEdit proper resource file instead.\n"
143         ";\n\n");
144 }
145
146
147 void openCFile(void) {
148
149     if ((outputCFile = fopen(outputCName,outputCMode)) == 0) {
150         AbEnd("can't open file %s for writing: %s\n", outputCName, strerror(errno));
151     }
152
153     if (CFnum == 0) {
154         outputCMode[0] = 'a';
155         printCHeader();
156         CFnum++;
157     }
158 }
159
160
161 void openSFile(void) {
162
163     if ((outputSFile = fopen(outputSName, outputSMode)) == 0) {
164         AbEnd("can't open file %s for writing: %s\n", outputSName, strerror(errno));
165     }
166
167     if (SFnum == 0) {
168         outputSMode[0] = 'a';
169         printSHeader();
170         SFnum++;
171     }
172 }
173
174
175 int findToken(const char **tokenTbl, const char *token) {
176
177     /* takes as input table of tokens and token, returns position in table or -1 if not found */
178     int a = 0;
179
180     while (strlen(tokenTbl[a]) != 0) {
181         if (strcmp(tokenTbl[a], token) == 0) break;
182         a++;
183     }
184
185     if (strlen(tokenTbl[a]) == 0) a = -1;
186     return a;
187 }
188
189
190 char *nextPhrase() {
191     return strtok(NULL, "\"");
192 }
193
194
195 char *nextWord() {
196     return strtok(NULL, " ");
197 }
198
199
200 void setLen(char *name, unsigned len) {
201     if (strlen(name) > len)
202         name[len] = '\0';
203 }
204
205
206 void fillOut(char *name, int len, char *filler) {
207
208     int a;
209
210     setLen(name, len);
211     fprintf(outputSFile, "\t.byte \"%s\"\n", name);
212     
213     a = strlen(name);
214     if (a < len) {
215         fprintf(outputSFile, "\t.res  (%i - %i), %s\n", len, a, filler);
216     }
217 }
218
219
220 char *bintos(unsigned char a, char out[7]) {
221
222     int i=0;
223
224     for (; i < 8; i++) {
225         out[7 - i] = ((a & 1) == 0) ? '0' : '1';
226         a = a >> 1;
227     }
228     out[i] = '\0';
229
230     return out;
231 }
232
233
234 int getNameSize(const char *word) {
235
236     /* count length of a word using BSW 9 font table */
237     int a = 0, i = 0;
238
239     while (word[i] != '\0') {
240         a += (BSWTab[word[i] - 31] - BSWTab[word[i] - 32]);
241         i++;
242     }
243
244     return a;
245 }
246
247
248 void DoMenu(void) {
249
250     int a, size, tmpsize, item = 0;
251     char *token;
252     char namebuff[255] = "";
253     struct menu myMenu;
254     struct menuitem *curItem, *newItem;
255
256     openCFile();
257
258     myMenu.name = nextWord();
259     myMenu.left = atoi(nextWord());
260     myMenu.top = atoi(nextWord());
261     myMenu.type = nextWord();
262
263     if (strcmp(nextWord(), "{") != 0) {
264         AbEnd("menu '%s' description has no opening bracket!\n", myMenu.name);
265     }
266     curItem = xmalloc(sizeof(struct menuitem));
267     myMenu.item = curItem;
268     do {
269         token = nextWord();
270         if (strcmp(token, "}") == 0) break;
271         if (token[strlen(token) - 1] != '"') {
272             strcpy(namebuff, token);
273             do {
274                 token = nextWord();
275                 strcat(namebuff, " ");
276                 strcat(namebuff, token);
277             } while (token[strlen(token) - 1] != '"');
278             token = xmalloc(strlen(namebuff));
279             strcpy(token, namebuff);
280         }
281         curItem->name = token;
282         curItem->type = nextWord();
283         curItem->target = nextWord();
284         newItem = xmalloc(sizeof(struct menuitem));
285         curItem->next = newItem;
286         curItem = newItem;
287         item++;
288         } while (strcmp(token, "}") != 0);
289     if (item == 0) AbEnd("menu '%s' has 0 items!\n", myMenu.name);
290     if (item > 31) AbEnd("menu '%s' has too many items!\n", myMenu.name);
291
292     curItem->next = NULL;
293
294     /* count menu sizes */
295     size = 0;
296     curItem = myMenu.item;
297     if (strstr(myMenu.type, "HORIZONTAL") != NULL) {
298         /* menu is HORIZONTAL, ysize=15, sum xsize of all items +~8?*/
299         myMenu.bot = myMenu.top + 15;
300         for (a = 0; a != item; a++) {
301             size += getNameSize(curItem->name);
302             curItem = curItem->next;
303         }
304     } else {
305         /* menu is VERTICAL, ysize=item*15, count largest xsize of all items +~8? */
306         myMenu.bot = myMenu.top + (14 * item);
307         for (a = 0; a != item; a++) {
308             tmpsize = getNameSize(curItem->name);
309             size = (size > tmpsize) ? size : tmpsize;
310             curItem = curItem->next;
311         }
312     }
313     myMenu.right = myMenu.left + size - 1;
314
315     curItem = myMenu.item;
316     for (a = 0; a != item; a++) {
317         /* print prototype only if MENU_ACTION or DYN_SUB_MENU are present in type */
318         if ((strstr(curItem->type, "MENU_ACTION") != NULL) || (strstr(curItem->type, "DYN_SUB_MENU") != NULL)) {
319             fprintf(outputCFile,
320                 "void %s (void);\n",
321                 curItem->target);
322         }
323         curItem=curItem->next;
324     }
325
326     fprintf(outputCFile,
327         "\n"
328         "const void %s = {\n"
329         "\t(char)%i, (char)%i,\n"
330         "\t(int)%i, (int)%i,\n"
331         "\t(char)(%i | %s),\n",
332         myMenu.name, myMenu.top, myMenu.bot, myMenu.left, myMenu.right, item, myMenu.type);
333
334     curItem = myMenu.item;
335     for (a = 0; a != item; a++) {
336         fprintf(outputCFile,
337             "\t%s, (char)%s, (int)",
338             curItem->name, curItem->type);
339         if ((strstr(curItem->type, "SUB_MENU") != NULL) && (strstr(curItem->type, "DYN_SUB_MENU") == NULL))
340             fprintf(outputCFile,
341                 "&");
342         fprintf(outputCFile,
343             "%s,\n",
344             curItem->target);
345         curItem = curItem->next;
346     }
347
348     fprintf(outputCFile,
349         "};\n\n");
350
351     if (fclose(outputCFile) != 0)
352         AbEnd("error closing %s: %s\n", outputCName, strerror(errno));
353 }
354
355
356 void DoHeader(void) {
357
358     time_t t;
359     struct tm *my_tm;
360
361     struct appheader myHead;
362     char *token;
363     char i1[9], i2[9], i3[9];
364     int a, b;
365
366     openSFile();
367
368     token = nextWord();
369
370     a = findToken(hdrFTypes, token);
371
372     switch (a) {
373         case 0:
374             myHead.geostype = 6;
375             break;
376         case 1:
377             myHead.geostype = 14;
378             break;
379         default:
380             AbEnd("filetype '%s' is not supported yet\n", token);
381     }
382
383     myHead.dosname = nextPhrase();
384     nextPhrase();
385     myHead.classname = nextPhrase();
386     nextPhrase();
387     myHead.version = nextPhrase();
388
389     /* put default values into myHead here */
390     myHead.author = "cc65";
391     myHead.info = "Program compiled with cc65 and GEOSLib.";
392     myHead.dostype = 128 + 3;
393     myHead.structure = 0;
394     myHead.mode = 0;
395     myHead.icon = NULL;
396
397     t = time(NULL);
398     my_tm = localtime(&t);
399
400     myHead.year = my_tm->tm_year;
401     myHead.month = my_tm->tm_mon+1;
402     myHead.day = my_tm->tm_mday;
403     myHead.hour = my_tm->tm_hour;
404     myHead.min = my_tm->tm_min;
405
406     if (strcmp(nextWord(), "{") != 0) {
407         AbEnd("header '%s' has no opening bracket!\n", myHead.dosname);
408     }
409
410     do {
411         token = nextWord();
412         if (strcmp(token, "}") == 0) break;
413         switch (a = findToken(hdrFields, token)) {
414             case -1:
415                 AbEnd("unknown field '%s' in header '%s'\n", token, myHead.dosname);
416                 break;
417             case 0: /* author */
418                 myHead.author = nextPhrase();
419                 break;
420             case 1: /* info */
421                 myHead.info = nextPhrase();
422                 break;
423             case 2: /* date */
424                 myHead.year = atoi(nextWord());
425                 myHead.month = atoi(nextWord());
426                 myHead.day = atoi(nextWord());
427                 myHead.hour = atoi(nextWord());
428                 myHead.min = atoi(nextWord());
429                 break;
430             case 3: /* dostype */
431                 switch (b = findToken(hdrDOSTp, nextWord())) {
432                     case -1:
433                         AbEnd("unknown dostype in header '%s'\n", myHead.dosname);
434                         break;
435                     default:
436                         myHead.dostype = b / 2 + 128 + 1;
437                         break;
438                 }
439                 break;
440             case 4: /* mode */
441                 switch (b = findToken(hdrModes, nextWord())) {
442                     case -1:
443                         AbEnd("unknown mode in header '%s'\n", myHead.dosname);
444                     case 0:
445                         myHead.mode = 0x40;
446                         break;
447                     case 1:
448                         myHead.mode = 0x00;
449                         break;
450                     case 2:
451                         myHead.mode = 0xc0;
452                         break;
453                     case 3:
454                         myHead.mode = 0x80;
455                         break;
456                 }
457                 break;
458             case 5: /* structure */
459                 switch (b = findToken(hdrStructTp, nextWord())) {
460                     case -1:
461                         AbEnd("unknown structure type in header '%s'\n", myHead.dosname);
462                     case 0:
463                     case 1:
464                         myHead.structure = 0;
465                         break;
466                     case 2:
467                     case 3:
468                         myHead.structure = 1;
469                         break;
470                 }
471                 break;
472             case 6: /* icon */
473                 myHead.icon = nextPhrase();
474                 break;
475         }
476
477     } while (strcmp(token, "}") != 0);
478
479     /* OK, all information is gathered, do flushout */
480
481     fprintf(outputSFile,
482         "\n"
483         "\t\t.segment \"DIRENTRY\"\n\n"
484         "\t.byte %i\n"
485         "\t.word 0\n", myHead.dostype);
486
487     fillOut(myHead.dosname, 16, "$a0");
488
489     fprintf(outputSFile,
490         "\t.word 0\n"
491         "\t.byte %i\n"
492         "\t.byte %i\n"
493         "\t.byte %i, %i, %i, %i, %i\n\n"
494         "\t.word 0\n"
495         "\t.byte \"PRG formatted GEOS file V1.0\"\n\n",
496         myHead.structure, myHead.geostype,
497         myHead.year, myHead.month, myHead.day, myHead.hour, myHead.min);
498
499     fprintf(outputSFile,
500         "\n"
501         "\t\t.segment \"FILEINFO\"\n\n"
502         "\t.import __VLIR0_START__, __STARTUP_RUN__\n\n"
503         "\t.byte 3, 21, 63 | $80\n");
504
505     if (myHead.icon != NULL) {
506         fprintf(outputSFile,
507             "\t.incbin \"%s\", 0, 63\n",
508             myHead.icon);
509     } else {
510         for (a = 0; a != 63; a = a + 3) {
511             fprintf(outputSFile,
512                 "\t.byte %%%s, %%%s, %%%s\n",
513                 bintos(icon1[a], i1), bintos(icon1[a+1], i2), bintos(icon1[a+2], i3));
514         }
515     }
516
517     fprintf(outputSFile,
518         "\t.byte %i, %i, %i\n"
519         "\t.word __VLIR0_START__, __VLIR0_START__ - 1, __STARTUP_RUN__\n\n",
520         myHead.dostype, myHead.geostype, myHead.structure);
521
522     fillOut(myHead.classname, 12, "$20");
523
524     fillOut(myHead.version, 4, "0");
525
526     fprintf(outputSFile,
527         "\t.byte 0, 0, 0\n"
528         "\t.byte %i\n\n", myHead.mode);
529
530     setLen(myHead.author, 62);
531     fprintf(outputSFile,
532         "\t.byte \"%s\"\n"
533         "\t.byte 0\n"
534         "\t.res  (63 - %i)\n\n",
535         myHead.author, (int)(strlen(myHead.author) + 1));
536
537     setLen(myHead.info, 95);
538     fprintf(outputSFile,
539         "\t.byte \"%s\"\n"
540         "\t.byte 0\n\n", myHead.info);
541
542     if (fclose (outputSFile) != 0)
543         AbEnd("error closing %s: %s\n", outputSName, strerror(errno));
544 }
545
546
547 void DoVLIR(void) {
548
549     char *token;
550     int record, lastrecord;
551     int vlirsize, vlirtable[127];
552
553     openSFile();
554
555     vlirsize = strtol(nextWord(), NULL, 0);
556
557     if (strcmp(nextWord(), "{") != 0) {
558         AbEnd ("VLIR description has no opening bracket!\n");
559     }
560
561     lastrecord = -1;
562     memset(vlirtable, 0, sizeof(vlirtable));
563
564     do {
565         token = nextWord();
566         if (strcmp(token, "}") == 0) break;
567
568         record = atoi(token);
569         if (record < 0 || record > 126) {
570             AbEnd("VLIR record %i is out of range 0-126.\n", record);
571         }
572         if (vlirtable[record] == 1) {
573             AbEnd("VLIR record %i is defined twice.\n", record);
574         }
575
576         vlirtable[record] = 1;
577         if (record > lastrecord) lastrecord = record;
578     } while (strcmp(token, "}") != 0);
579
580     if (lastrecord == -1) {
581         AbEnd("There must be at least one VLIR record.\n");
582     }
583     
584     /* always include record 0 */
585     vlirtable[0] = 1;
586
587     /* OK, all information is gathered, do flushout */
588
589     fprintf(outputSFile,
590         "\n"
591         "\t\t.segment \"RECORDS\"\n\n"
592         "\t.export __OVERLAYSIZE__ : absolute = $%04x\n\n",
593         vlirsize);
594
595     for (record = 0; record <= lastrecord; record++) {
596         if (vlirtable[record] == 1) {
597             fprintf(outputSFile,
598                 "\t.import __VLIR%i_START__, __VLIR%i_LAST__\n",
599                 record, record);
600         }
601     }
602     fprintf(outputSFile,
603         "\n");
604
605     for (record = 0; record <= lastrecord; record++) {
606         if (vlirtable[record] == 1) {
607             fprintf(outputSFile,
608                 "\t.byte .lobyte ((__VLIR%i_LAST__ - __VLIR%i_START__ - 1) /    254) + 1\n"
609                 "\t.byte .lobyte ((__VLIR%i_LAST__ - __VLIR%i_START__ - 1) .MOD 254) + 2\n",
610                 record, record, record, record);
611         } else {
612             fprintf(outputSFile,
613                 "\t.byte $00\n"
614                 "\t.byte $FF\n");
615         }
616     }
617     fprintf(outputSFile,
618         "\n");
619
620     if (fclose(outputSFile) != 0)
621         AbEnd("error closing %s: %s\n", outputSName, strerror(errno));
622
623     openCFile();
624
625     fprintf(outputCFile,
626         "extern void _OVERLAYADDR__;\n"
627         "extern void _OVERLAYSIZE__;\n\n"
628         "#define OVERLAY_ADDR (char*)   &_OVERLAYADDR__\n"
629         "#define OVERLAY_SIZE (unsigned)&_OVERLAYSIZE__\n\n");
630
631     if (fclose(outputCFile) != 0)
632         AbEnd("error closing %s: %s\n", outputCName, strerror(errno));
633 }
634
635
636 char *filterInput(FILE *F, char *tbl) {
637
638     /* loads file into buffer filtering it out */
639     int a, prevchar = -1, i = 0, bracket = 0, quote = 1;
640
641     while (1) {
642         a = getc(F);
643         if ((a == '\n') || (a == '\015')) a = ' ';
644         if (a == ',' && quote) a = ' ';
645         if (a == '\042') quote =! quote;
646         if (quote) {
647             if ((a == '{') || (a == '(')) bracket++;
648             if ((a == '}') || (a == ')')) bracket--;
649         }
650         if (a == EOF) {
651             tbl[i] = '\0';
652             xrealloc(tbl, i + 1);
653             break;
654         }
655         if (IsSpace(a)) {
656             if ((prevchar != ' ') && (prevchar != -1)) {
657                 tbl[i++] = ' ';
658                 prevchar = ' ';
659             }
660         } else {
661             if (a == ';' && quote) {
662                 do {
663                     a = getc(F);
664                 } while (a != '\n');
665                 fseek(F, -1, SEEK_CUR);
666             } else {
667                 tbl[i++] = a;
668                 prevchar = a;
669             }
670         }
671     }
672
673     if (bracket != 0) AbEnd("there are unclosed brackets!\n");
674
675     return tbl;
676 }
677
678
679 void processFile(const char *filename) {
680
681     FILE *F;
682
683     char *str;
684     char *token;
685
686     int head = 0; /* number of processed HEADER sections */
687     int vlir = 0; /* number of processed VLIR sections */
688
689     if ((F = fopen(filename, "r")) == 0) {
690         AbEnd("can't open file %s for reading: %s\n", filename, strerror(errno));
691     }
692
693     str = filterInput(F, xmalloc(BLOODY_BIG_BUFFER));
694
695     token = strtok(str, " ");
696
697     do {
698         if (str != NULL) {
699             switch (findToken(mainToken, token)) {
700                 case 0:
701                     DoMenu();
702                     break;
703                 case 1:
704                     if (++head != 1) {
705                         AbEnd("more than one HEADER section, aborting.\n");
706                     } else {
707                         DoHeader();
708                     }
709                     break;
710                 case 2: break; /* icon not implemented yet */
711                 case 3: break; /* dialog not implemented yet */
712                 case 4:
713                     if (++vlir != 1) {
714                         AbEnd("more than one VLIR section, aborting.\n");
715                     } else {
716                         DoVLIR();
717                     }
718                     break;
719                 default:
720                     AbEnd("unknown section %s.\n",token);
721                     break;
722             }
723         }
724         token = nextWord();
725     } while (token != NULL);
726 }
727
728
729 int main(int argc, char *argv[]) {
730
731     int ffile = 0, i = 1;
732
733     ProgName = argv[0];
734
735     while (i < argc) {
736         const char *arg = argv[i];
737
738         if (arg[0] == '-') {
739             switch (arg[1]) {
740                 case 'o':
741                     outputCName = argv[++i];
742                     break;
743                 case 's':
744                     outputSName = argv[++i];
745                     break;
746                 case 't':
747                     switch (FindTarget(argv[++i])) {
748                         case TGT_GEOS: // todo: TGT_GEOS-CBM
749                             apple = 0;
750                             break;
751                         case TGT_COUNT: //todo: TGT_GEOS-APPLE
752                             apple = 1;
753                             break;
754                         default:
755                             AbEnd("unknown target system type %s\n", argv[i]);
756                     }
757                     break;
758                 case 'h':
759                 case '?':
760                     printUsage();
761                     exit(EXIT_SUCCESS);
762                     break;
763                 default:
764                     AbEnd("unknown option %s\n", arg);
765             }
766         } else {
767             ffile++;
768
769             if (outputCName == NULL) outputCName = MakeFilename(arg, ".h");
770             if (outputSName == NULL) outputSName = MakeFilename(arg, ".s");
771
772             processFile(arg);
773         }
774
775         i++;
776     }
777
778     if (ffile == 0) AbEnd("no input file\n");
779
780     return EXIT_SUCCESS;
781 }