Updating to reflect the latest work
[zilutils.git] / zilasm / main.cpp
diff --git a/zilasm/main.cpp b/zilasm/main.cpp
new file mode 100644 (file)
index 0000000..22b7e71
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * main.c
+ *
+ * Copyright (C) 2015 Alexander Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2015, 2019 Jason Self <j@jxself.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+extern "C"
+{
+#include <strings.h>
+}
+#include <getopt.h>
+#include <time.h>
+#include <ctype.h>
+#include "config.h"
+#include "header.h"
+extern "C"
+{
+#include "opcodes.h"
+}
+#include "parser.h"
+
+#include <string>
+#include <list>
+using namespace std;
+
+const int DEFAULT_ZVERSION = 6;
+
+enum
+{ ZVERSION = 11, ZORKID, ZSERIAL };
+
+enum
+{ FAIL = -1, OK = 0, NEED_RESTART = 1 };
+
+static struct option const long_options[] = {
+  {"help", no_argument, NULL, 'h'},
+  {"version", no_argument, NULL, 'V'},
+  {"output", required_argument, NULL, 'o'},
+  {"zversion", required_argument, NULL, ZVERSION},
+  {"zorkid", required_argument, NULL, ZORKID},
+  {"serial", required_argument, NULL, ZSERIAL},
+  {NULL, 0, NULL, 0}
+};
+
+
+typedef struct
+{
+  int todo;
+} Opcode_dict;
+
+
+struct
+{
+  int zversion;                        /* 0 - 8     */
+  int zorkid;                  /* 0 - 65535 */
+  char zserial[7];             /* YYMMDD    */
+  Opcode_dict *opcode_dict;
+} Config;
+
+
+struct String_Table_Elem
+{
+  string value;                        // value in ASCII format
+  int index;
+};
+
+
+class CMain
+{
+public:
+  CMain ();
+  int assembly ();
+  void fill_config (void);
+  void get_arguments (int argc, char *argv[], char *envp[]);
+
+  char *get_output_file_name ();
+private:
+  char *m_output_file;
+    list < String_Table_Elem > m_string_table;
+  int m_code_size;
+
+
+  char *build_output_filename (const char basename[], const char *suffix);
+  void fill_zserial (void);
+  void new_file_suffix (char *result, size_t maxlen, const char *src,
+                       const char *newsuffix);
+
+  void output_code_section ();
+
+  void parse_intarg (int *dest, const char name[], int min, int max,
+                    int defval);
+  void parse_zserial (void);
+  void print_usage (int failed);
+  void print_version ();
+  void wrong_arg (const char *err, ...);
+
+};
+
+
+CMain::CMain ():m_output_file (NULL)
+{
+}
+
+int
+CMain::assembly ()
+{
+  FILE *file = fopen (m_output_file, "wb");
+  if (file)
+    {
+      program_header_reset (6);
+      int size = sizeof (Program_header);
+
+      Program_header.mode = 0;
+      Program_header.release = 1;      // game version
+
+      int code_start_offset = 64;
+      Program_header.startPC = code_start_offset >> 2;
+
+      m_code_size = 0;
+      ZMemblock *zmem_code = zmem_init (65536);
+
+      /// write zero number of local variables
+      zmem_putbyte (zmem_code, 0);     // number of local variables
+      ++m_code_size;
+
+      // write instructions' codes
+      for (int i = 0; i < g_numberOfInstructions; ++i)
+       {
+         for (int j = 0; j < g_codes[i]->used_size; ++j)
+           {
+             zmem_putbyte (zmem_code, g_codes[i]->contents[j]);
+             ++m_code_size;
+           }
+       }
+
+      if (m_code_size & 7)
+       m_code_size += 8 - (m_code_size & 7);
+
+      Program_header.dynamic_size = 8;
+
+      //Program_header.h_file_size = 33;  //sizeof(Program_header) + zmb->used_size;
+
+      //m_code_size = 8;
+
+      Word stringTableOffset = m_code_size;
+      Program_header.H_STRINGS_OFFSET = (64 + stringTableOffset) >> 3;
+
+      int stringTableSize = 64;
+      Program_header.h_file_size =
+       (code_start_offset + m_code_size + stringTableSize) >> 3;
+      ZMemblock *zmb = zmem_init (Program_header.h_file_size * 8);
+
+      for (int i = 0; i < m_code_size; ++i)
+       zmem_putbyte (zmb, zmem_code->contents[i]);
+
+      zmem_destroy (zmem_code);
+
+      //zmem_putbyte(zmb, 0); // number of local variables
+      //zmem_putbyte(zmb, 141); // print addr command
+
+      //Word offset = 0;
+      //zmem_putbyte(zmb, (offset >> 8) & 255);
+      //zmem_putbyte(zmb, offset & 255);
+
+      //zmem_putbyte(zmb, 186); // quit command
+
+      // output zeros until string table begins
+      while (zmb->used_size < stringTableOffset)
+       zmem_putbyte (zmb, 0);
+      //
+      //// fill string table with one string
+      //add_string_to_string_table("Hello, World!", zmb);
+
+      outputToFile (&Program_header, file);
+      fwrite (zmb->contents, zmb->allocated_size, 1, file);
+      fclose (file);
+    }
+
+  return OK;
+}
+
+
+char *
+CMain::get_output_file_name ()
+{
+  return m_output_file;
+}
+
+
+void
+CMain::get_arguments (int argc, char *argv[], char *envp[])
+{
+  int opt = 0;
+  while ((opt = getopt_long (argc, argv, "hVo:", long_options, NULL)) != -1)
+    {
+      switch (opt)
+       {
+       case 'h':
+         print_usage (0);
+       case 'V':
+         print_version ();
+       case 'o':
+         if (m_output_file)
+           wrong_arg ("Output file must be given once\n");
+         m_output_file = optarg;
+         break;
+       case ZVERSION:
+         parse_intarg (&Config.zversion, "zversion", 1, 8, 1);
+         break;
+       case ZORKID:
+         parse_intarg (&Config.zorkid, "zorkid", 0, 0xFFFF, 0);
+         break;
+       case ZSERIAL:
+         parse_zserial ();
+         break;
+       default:
+         wrong_arg (0);
+       }
+    }
+
+  int first_input_file = optind;
+  if (first_input_file >= argc)
+    wrong_arg ("Missing input file\n");
+  if (!m_output_file)
+    m_output_file = build_output_filename (argv[first_input_file], ".dat");
+
+  // TODO: Everything :)
+
+
+  printf ("Input files:\n");
+  for (int i = optind; i < argc; i++)
+    printf ("\t%s\n", argv[i]);
+
+  printf ("Output file: %s\n\n", m_output_file);
+
+  printf ("Config:\n"
+         "- ZVersion: %d\n"
+         "- ZorkID:   %d\n"
+         "- ZSerial:  %s\n", Config.zversion, Config.zorkid, Config.zserial);
+
+}
+
+
+void
+CMain::wrong_arg (const char *err, ...)
+{
+  if (err)
+    {
+      va_list ap;
+      va_start (ap, err);
+      vfprintf (stderr, err, ap);
+      va_end (ap);
+    }
+  fprintf (stderr, "Try `" PACKAGE_NAME " --help' for more information.\n");
+  exit (1);
+}
+
+
+void
+CMain::print_version ()
+{
+  printf (PACKAGE_STRING "\n"
+         "License AGPLv3+: GNU AGPL version 3 or later\n"
+         "<http://gnu.org/licenses/agpl.html>\n"
+         "This is free software: you are free to change and redistribute it.\n"
+         "There is NO WARRANTY, to the extent permitted by law.\n");
+  exit (0);
+}
+
+
+void
+CMain::print_usage (int failed)
+{
+  printf ("Usage: " PACKAGE_NAME " [OPTION...] [FILES...]\n"
+         "\n"
+         "--version  Display program version and exit\n"
+         "--help     Display this help\n"
+         "\n"
+         "--zversion (accepts numbers 1 - 8, defaults to %d if not specified)\n"
+         "--zorkid   (integer between 0 and 65535, defaults to 0 if not specified)\n"
+         "--serial   (six characters of ASCII, defaults to current date\n"
+         "            in the form YYMMDD if not specified)\n",
+         DEFAULT_ZVERSION);
+  exit (failed);
+}
+
+void
+CMain::fill_zserial (void)
+{
+  time_t t;
+  struct tm *timeinfo;
+  time (&t);
+  timeinfo = localtime (&t);
+  strftime (Config.zserial, sizeof (Config.zserial), "%y%m%d", timeinfo);
+}
+
+
+void
+CMain::fill_config (void)
+{
+  bzero (&Config, sizeof (Config));
+  Config.zversion = DEFAULT_ZVERSION;
+  fill_zserial ();
+}
+
+
+void
+CMain::parse_intarg (int *dest, const char name[], int min, int max,
+                    int defval)
+{
+  if (!optarg)
+    {
+      *dest = defval;
+      return;
+    }
+  int n = atoi (optarg);
+  if (n >= min && n <= max)
+    {
+      *dest = n;
+      return;
+    }
+  wrong_arg ("Wrong %s value %s, must be integer between %d and %d\n",
+            name, optarg, min, max);
+}
+
+
+void
+CMain::parse_zserial (void)
+{
+  if (!optarg)
+    {
+      fill_zserial ();
+      return;
+    }
+  size_t n = strlen (optarg);
+  if (n == sizeof (Config.zserial) - 1)
+    {
+      char *p = optarg;
+      while (*p && isalnum (*p))
+       p++;
+      if (!*p)
+       {                       /* ..optarg contains alphanumeric only? */
+         strncpy (Config.zserial, optarg, sizeof (Config.zserial));
+         return;
+       }
+    }
+  wrong_arg ("Wrong zserial value %s, must be 6 ascii characters\n", optarg);
+}
+
+
+void
+CMain::new_file_suffix (char *result, size_t maxlen, const char *src,
+                       const char *newsuffix)
+{
+  strncpy (result, src, maxlen);
+  char *p = strrchr (result, '.');
+  if (p && strchr (p, '/'))
+    p = NULL;
+  if (p)
+    {
+      strncpy (p, newsuffix, maxlen - (p - result));
+    }
+  else
+    {
+      strncat (result, newsuffix, maxlen);
+    }
+  result[maxlen] = 0;
+}
+
+
+char *
+CMain::build_output_filename (const char basename[], const char *suffix)
+{
+  int n = strlen (basename) + strlen (suffix);
+  char *ofile = (char *) malloc (n + 1);       /* todo!!! check for NULL. free. */
+  new_file_suffix (ofile, n, basename, suffix);
+  return ofile;
+}
+
+
+int
+init_assembly (void)
+{
+  /* TODO */
+  return OK;
+}
+
+
+void
+CMain::output_code_section ()
+{
+
+}
+
+
+
+int
+main (int argc, char *argv[], char *envp[])
+{
+  CMain main;
+
+  main.fill_config ();
+  main.get_arguments (argc, argv, envp);
+  init_opcodes (Config.zversion, 0);
+
+  init_parser ();
+
+  for (int i = optind; i < argc; i++)
+    parse_file (argv[i]);
+  main.assembly ();
+
+  /* TODO! List global symbols */
+  /* TODO! Find abbreviations */
+  relase_parser ();
+  return 0;
+}