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