Added error messages displaying using source file name and line number
[zilutils.git] / zilasm / main.cpp
1 /*
2  * main.c
3  *
4  * Copyright (C) 2015 Alexander Andrejevic <theflash AT sdf DOT lonestar DOT org>
5  * Copyright (C) 2015, 2019, 2020 Jason Self <j@jxself.org>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>
19  *
20  * SPDX-License-Identifier: AGPL-3.0-or-later
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <string.h>
27 #include <string>
28 #include <list>
29 #include <stack>
30 using namespace std;
31
32
33 extern "C"
34 {
35 #include <strings.h>
36 }
37 #include <getopt.h>
38 #include <time.h>
39 #include <ctype.h>
40 #include "config.h"
41 #include "header.h"
42 extern "C"
43 {
44 #include "opcodes.h"
45 }
46 #include "parser.h"
47
48
49
50 const int DEFAULT_ZVERSION = 6;
51
52 enum
53 { ZVERSION = 11, ZORKID, ZSERIAL };
54
55 enum
56 { FAIL = -1, OK = 0, NEED_RESTART = 1 };
57
58 static struct option const long_options[] = {
59   {"help", no_argument, NULL, 'h'},
60   {"version", no_argument, NULL, 'V'},
61   {"output", required_argument, NULL, 'o'},
62   {"zversion", required_argument, NULL, ZVERSION},
63   {"zorkid", required_argument, NULL, ZORKID},
64   {"serial", required_argument, NULL, ZSERIAL},
65   {NULL, 0, NULL, 0}
66 };
67
68
69 typedef struct
70 {
71   int todo;
72 } Opcode_dict;
73
74
75 struct
76 {
77   int zversion;                 /* 0 - 8     */
78   int zorkid;                   /* 0 - 65535 */
79   char zserial[7];              /* YYMMDD    */
80   Opcode_dict *opcode_dict;
81 } Config;
82
83
84 struct String_Table_Elem
85 {
86   string value;                 // value in ASCII format
87   int index;
88 };
89
90
91 class CMain
92 {
93 public:
94   CMain ();
95   int assembly ();
96   void fill_config (void);
97   void get_arguments (int argc, char *argv[], char *envp[]);
98
99   char *get_output_file_name ();
100 private:
101   char *m_output_file;
102     list < String_Table_Elem > m_string_table;
103   int m_code_size;
104
105
106   char *build_output_filename (const char basename[], const char *suffix);
107   void fill_zserial (void);
108   void new_file_suffix (char *result, size_t maxlen, const char *src,
109                         const char *newsuffix);
110
111   void output_code_section ();
112
113   void parse_intarg (int *dest, const char name[], int min, int max,
114                      int defval);
115   void parse_zserial (void);
116   void print_usage (int failed);
117   void print_version ();
118   void wrong_arg (const char *err, ...);
119
120 };
121
122
123 CMain::CMain ():m_output_file (NULL)
124 {
125 }
126
127 int
128 CMain::assembly ()
129 {
130   FILE *file = fopen (m_output_file, "wb");
131   if (file)
132     {
133       program_header_reset (6);
134       int size = sizeof (Program_header);
135
136       Program_header.mode = 0;
137       Program_header.release = 1;       // game version
138
139       int code_start_offset = 64;
140       Program_header.startPC = code_start_offset >> 2;
141
142       m_code_size = 0;
143       ZMemblock *zmem_code = zmem_init (65536);
144
145       /// write zero number of local variables
146       zmem_putbyte (zmem_code, 0);      // number of local variables
147       ++m_code_size;
148
149       // write instructions' codes
150       for (int i = 0; i < g_number_of_instructions; ++i)
151         {
152           for (int j = 0; j < g_codes[i]->used_size; ++j)
153             {
154               zmem_putbyte (zmem_code, g_codes[i]->contents[j]);
155               ++m_code_size;
156             }
157         }
158
159       if (m_code_size & 7)
160         m_code_size += 8 - (m_code_size & 7);
161
162       Program_header.dynamic_size = 8;
163
164       //Program_header.h_file_size = 33;  //sizeof(Program_header) + zmb->used_size;
165
166       //m_code_size = 8;
167
168       Word stringTableOffset = m_code_size;
169       Program_header.H_STRINGS_OFFSET = (64 + stringTableOffset) >> 3;
170
171       int stringTableSize = 64;
172       Program_header.h_file_size =
173         (code_start_offset + m_code_size + stringTableSize) >> 3;
174       ZMemblock *zmb = zmem_init (Program_header.h_file_size * 8);
175
176       for (int i = 0; i < m_code_size; ++i)
177         zmem_putbyte (zmb, zmem_code->contents[i]);
178
179       zmem_destroy (zmem_code);
180
181       //zmem_putbyte(zmb, 0); // number of local variables
182       //zmem_putbyte(zmb, 141); // print addr command
183
184       //Word offset = 0;
185       //zmem_putbyte(zmb, (offset >> 8) & 255);
186       //zmem_putbyte(zmb, offset & 255);
187
188       //zmem_putbyte(zmb, 186); // quit command
189
190       // output zeros until string table begins
191       while (zmb->used_size < stringTableOffset)
192         zmem_putbyte (zmb, 0);
193       //
194       //// fill string table with one string
195       //add_string_to_string_table("Hello, World!", zmb);
196
197       outputToFile (&Program_header, file);
198       fwrite (zmb->contents, zmb->allocated_size, 1, file);
199       fclose (file);
200     }
201
202   return OK;
203 }
204
205
206 char *
207 CMain::get_output_file_name ()
208 {
209   return m_output_file;
210 }
211
212
213 void
214 CMain::get_arguments (int argc, char *argv[], char *envp[])
215 {
216   int opt = 0;
217   while ((opt = getopt_long (argc, argv, "hVo:", long_options, NULL)) != -1)
218     {
219       switch (opt)
220         {
221         case 'h':
222           print_usage (0);
223         case 'V':
224           print_version ();
225         case 'o':
226           if (m_output_file)
227             wrong_arg ("Output file must be given once\n");
228           m_output_file = optarg;
229           break;
230         case ZVERSION:
231           parse_intarg (&Config.zversion, "zversion", 1, 8, 1);
232           break;
233         case ZORKID:
234           parse_intarg (&Config.zorkid, "zorkid", 0, 0xFFFF, 0);
235           break;
236         case ZSERIAL:
237           parse_zserial ();
238           break;
239         default:
240           wrong_arg (0);
241         }
242     }
243
244   int first_input_file = optind;
245   if (first_input_file >= argc)
246     wrong_arg ("Missing input file\n");
247   if (!m_output_file)
248     m_output_file = build_output_filename (argv[first_input_file], ".dat");
249
250   // TODO: Everything :)
251
252
253   printf ("Input files:\n");
254   for (int i = optind; i < argc; i++)
255     printf ("\t%s\n", argv[i]);
256
257   printf ("Output file: %s\n\n", m_output_file);
258
259   printf ("Config:\n"
260           "- ZVersion: %d\n"
261           "- ZorkID:   %d\n"
262           "- ZSerial:  %s\n", Config.zversion, Config.zorkid, Config.zserial);
263
264 }
265
266
267 void
268 CMain::wrong_arg (const char *err, ...)
269 {
270   if (err)
271     {
272       va_list ap;
273       va_start (ap, err);
274       vfprintf (stderr, err, ap);
275       va_end (ap);
276     }
277   fprintf (stderr, "Try `" PACKAGE_NAME " --help' for more information.\n");
278   exit (1);
279 }
280
281
282 void
283 CMain::print_version ()
284 {
285   printf (PACKAGE_STRING "\n"
286           "License AGPLv3+: GNU AGPL version 3 or later\n"
287           "<http://gnu.org/licenses/agpl.html>\n"
288           "This is free software: you are free to change and redistribute it.\n"
289           "There is NO WARRANTY, to the extent permitted by law.\n");
290   exit (0);
291 }
292
293
294 void
295 CMain::print_usage (int failed)
296 {
297   printf ("Usage: " PACKAGE_NAME " [OPTION...] [FILES...]\n"
298           "\n"
299           "--version  Display program version and exit\n"
300           "--help     Display this help\n"
301           "\n"
302           "--zversion (accepts numbers 1 - 8, defaults to %d if not specified)\n"
303           "--zorkid   (integer between 0 and 65535, defaults to 0 if not specified)\n"
304           "--serial   (six characters of ASCII, defaults to current date\n"
305           "            in the form YYMMDD if not specified)\n",
306           DEFAULT_ZVERSION);
307   exit (failed);
308 }
309
310 void
311 CMain::fill_zserial (void)
312 {
313   time_t t;
314   struct tm *timeinfo;
315   time (&t);
316   timeinfo = localtime (&t);
317   strftime (Config.zserial, sizeof (Config.zserial), "%y%m%d", timeinfo);
318 }
319
320
321 void
322 CMain::fill_config (void)
323 {
324   bzero (&Config, sizeof (Config));
325   Config.zversion = DEFAULT_ZVERSION;
326   fill_zserial ();
327 }
328
329
330 void
331 CMain::parse_intarg (int *dest, const char name[], int min, int max,
332                      int defval)
333 {
334   if (!optarg)
335     {
336       *dest = defval;
337       return;
338     }
339   int n = atoi (optarg);
340   if (n >= min && n <= max)
341     {
342       *dest = n;
343       return;
344     }
345   wrong_arg ("Wrong %s value %s, must be integer between %d and %d\n",
346              name, optarg, min, max);
347 }
348
349
350 void
351 CMain::parse_zserial (void)
352 {
353   if (!optarg)
354     {
355       fill_zserial ();
356       return;
357     }
358   size_t n = strlen (optarg);
359   if (n == sizeof (Config.zserial) - 1)
360     {
361       char *p = optarg;
362       while (*p && isalnum (*p))
363         p++;
364       if (!*p)
365         {                       /* ..optarg contains alphanumeric only? */
366           strncpy (Config.zserial, optarg, sizeof (Config.zserial));
367           return;
368         }
369     }
370   wrong_arg ("Wrong zserial value %s, must be 6 ascii characters\n", optarg);
371 }
372
373
374 void
375 CMain::new_file_suffix (char *result, size_t maxlen, const char *src,
376                         const char *newsuffix)
377 {
378   strncpy (result, src, maxlen);
379   char *p = strrchr (result, '.');
380   if (p && strchr (p, '/'))
381     p = NULL;
382   if (p)
383     {
384       strncpy (p, newsuffix, maxlen - (p - result));
385     }
386   else
387     {
388       strncat (result, newsuffix, maxlen);
389     }
390   result[maxlen] = 0;
391 }
392
393
394 char *
395 CMain::build_output_filename (const char basename[], const char *suffix)
396 {
397   int n = strlen (basename) + strlen (suffix);
398   char *ofile = (char *) malloc (n + 1);        /* todo!!! check for NULL. free. */
399   new_file_suffix (ofile, n, basename, suffix);
400   return ofile;
401 }
402
403
404 int
405 init_assembly (void)
406 {
407   /* TODO */
408   return OK;
409 }
410
411
412 void
413 CMain::output_code_section ()
414 {
415
416 }
417
418
419
420 int
421 main (int argc, char *argv[], char *envp[])
422 {
423   CMain main;
424
425   main.fill_config ();
426   main.get_arguments (argc, argv, envp);
427   init_opcodes (Config.zversion, 0);
428
429   init_parser ();
430
431         //for (int i = optind; i < argc; i++)
432         //      parse_file(argv[i]);
433   
434   string file_name = argv[optind];
435   Parsing_Context pc;
436
437 #ifdef WIN32
438   char delimeter = '\\';
439 #else
440   char delimeter = '/';
441 #endif
442
443   if (file_name.rfind(delimeter) == string::npos)
444   {
445           pc.current_directory = "";
446           pc.current_file_name = file_name;
447   }
448   else
449   {
450           pc.current_directory = file_name.substr(0, file_name.rfind(delimeter)+1);
451           pc.current_file_name = file_name.substr(file_name.rfind(delimeter)+1);
452   }
453   g_parsing_contexts.push(pc);
454
455   parse_file();// argv[optind]);
456   if ( !g_haveErrors )
457         main.assembly ();
458
459   /* TODO! List global symbols */
460   /* TODO! Find abbreviations */
461   relase_parser ();
462   return 0;
463 }