/***********************************************************************

   Utility to create an assembler include file containing a subset
   of RSX180 kernel symbols. Copyright (C) 2013-2019, Hector Peraza.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

***********************************************************************/

/*
   Create an include file containing a subset of kernel symbol
   definitions for use by privileged tasks.

   The program is called with 3 filename arguments:

     sym2inc symfile datfile incfile

   where

     symfile is the kernel symbol file as generated by the linker
     datfile is a text file containing the names of symbols to filter
             through, with optional comments
     incfile is the output include file
   
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>

struct symbol {
  char name[8], *comment;
  unsigned short value;
  struct symbol *next;
};

struct comment {
  char name[8];
  char comment[128];
  struct comment *next;
};

struct symbol *symtab = NULL;
struct comment *cmnttab = NULL;

unsigned short read_value(FILE *f) {
  int i, c, value;

  value = 0;
  for (i = 0; i < 4; ++i) {
    c = fgetc(f);
    c = toupper(c);
    value <<= 4;
    if ((c >= '0') && (c <= '9')) {
      value += (c - '0');
    } else if ((c >= 'A') && (c <= 'F')) {
      value += (c - 'A' + 10);
    }
  }

  c = fgetc(f);  /* skip following space */
  
  return value;
}

char *read_name(FILE *f) {
  static char str[16];
  int i, c;
  
  for (i = 0; i < 16; ++i) {
    c = fgetc(f);
    if (!isspace(c)) str[i] = c; else break;
  }
  str[i] = '\0';
  if (c == '\r') c = fgetc(f);
  
  return str;
}

char *get_comment(char *sym) {
  struct comment *e;
  
  for (e = cmnttab; e; e = e->next) {
    if (strcmp(sym, e->name) == 0) return e->comment;
  }
  return NULL;
}

int main(int argc, char *argv[]) {
  FILE *f;
  unsigned short value;
  char *name;
  struct symbol *newsym, *e, *prev;
  struct comment *newcmnt;
  int  i, c;
  
  if (argc != 4) {
    fprintf(stderr, "usage: %s symfile datfile incfile\n", argv[0]);
    return 1;
  }

  /* Load the comment table (normally 'system.dat') */
  f = fopen(argv[2], "r");
  if (!f) {
    fprintf(stderr, "%s: could not open %s (%s)\n", argv[0], argv[2], strerror(errno));
    return 1;
  }

  while (1) {
    char str[16];
    //name = read_name(f);
    for (i = 0; i < 16; ++i) {
      c = fgetc(f);
      if (feof(f)) break;
      if (isspace(c) || feof(f)) break;
      str[i] = c;
    }
    if (feof(f)) break;
    str[i] = '\0';
    if (str[0] == '\x1A') break;
    for (;;) {
      c = fgetc(f);
      if (((c != ' ') && (c != '\t')) || feof(f)) break;
    }
    if (c == '\r') c = fgetc(f);
    if (c == '\n') continue;
    if (c == ';') {
      newcmnt = (struct comment *) calloc(1, sizeof(struct comment));
      strncpy(newcmnt->name, str, 8);
      newcmnt->name[7] = '\0';
      i = 0;
      newcmnt->comment[i++] = c;
      for (;;) {
        c = fgetc(f);
        if (feof(f)) break;
        if ((c == '\r') || (c == '\n')) break;
        if (i < 127) newcmnt->comment[i++] = c;
      }
      newcmnt->comment[i] = '\0';
      if (c == '\r') c = fgetc(f);
      newcmnt->next = cmnttab;
      cmnttab = newcmnt;
    } else {
      for (;;) {
        c = fgetc(f);
        if (feof(f)) break;
        if ((c == '\r') || (c == '\n')) break;
      }
      if (c == '\r') c = fgetc(f);
      continue;
    }
  }
  fclose(f);
  
  /* Load the symbol table (normally 'system.sym') */
  f = fopen(argv[1], "r");
  if (!f) {
    fprintf(stderr, "%s: could not open %s (%s)\n", argv[0], argv[1], strerror(errno));
    return 1;
  }

  while (!feof(f)) {
    value = read_value(f);
    name = read_name(f);
    if (name[0] == '\x1A') break;
    newsym = (struct symbol *) calloc(1, sizeof(struct symbol));
    strncpy(newsym->name, name, 8);
    newsym->name[7] = '\0';
    newsym->value = value;
    newsym->comment = get_comment(newsym->name);
#if 0  /* unsorted */
    newsym->next = symtab;
    symtab = newsym;
#elsif 0  /* sort by value */
    for (e = symtab; e; e = e->next) {
      if (e->value > newsym->value) break;
      prev = e;
    }
    if (e == symtab) {
      newsym->next = symtab;
      symtab = newsym;
    } else {
      newsym->next = e;
      prev->next = newsym;
    }
#else  /* sort by name */
    for (e = symtab; e; e = e->next) {
      if (strcmp(e->name, newsym->name) > 0) break;
      prev = e;
    }
    if (e == symtab) {
      newsym->next = symtab;
      symtab = newsym;
    } else {
      newsym->next = e;
      prev->next = newsym;
    }
#endif
  }
  fclose(f);
  
  /* Create the include file (normally 'system.inc') */
  f = fopen(argv[3], "w");
  if (!f) {
    fprintf(stderr, "%s: could not create %s (%s)\n", argv[0], argv[3], strerror(errno));
    return 1;
  }
  
  for (e = symtab; e; e = e->next) {
    if (e->comment) {
//      fprintf(finc, "\tpublic\t%s\r\n", e->name);
      fprintf(f, "%s\tequ\t%05Xh\t%s\r\n", e->name, e->value, e->comment);
    }
  }
//  fprintf(f, "\x1A");

  fclose(f);

  return 0;
}
