/*------------------------------------------------------------------------------

The input "def" file format has four sections:
   1) Configuration section, containing a single apostrophe-
      delimited string.  This string is put in both the flash
      code and the config file read by MegaTune (which uses it to
      verify that the controller code maps correctly to the
      config file).

   2) The RAM section, which defines all the RAM-resident
      variables.  The first section is "public" and can be
      optionally delimited by the "#PUBLIC" directive, which must
      occur before any variable declarations in the RAM section.
      Public variables are mapped into the MegaTune config file,
      so that they may be displayed in the run-time display.

      The private section, which begins with the the directive
      "#PRIVATE", contains the variables known only to the
      controller and are not listed in the MegaTune config file.

Example input file "mycode.def":

Configuration = 'MyCode 2.3'
Requires = 1.03

RAM = {
#PUBLIC
   secl      byte          ; low seconds - from 0 to 255, then rollover

#ORG $20 ; Start the private data (not known to MegaTune) after the public data.

#PRIVATE
   TPSfuelcut byte = 100T  ; TPS Fuel Cut (percent)

   INTACC1 byte(4)
}

RAM_FLASH = {
#ORG   $E000
#LABEL RamFlashORG
   ; VETABLE and Constants
   VEtableSize const = 64 ; Appears as equ in header.inc
   VE byte(VEtableSize) = { ; 64 bytes for VE Table
      18T, 18T, 18T, 18T, 18T, 18T, 18T, 18T, ; VE (0,n)
      22T, 25T, 30T, 32T, 35T, 35T, 35T, 35T, ; VE (1,n)
      35T, 35T, 35T, 35T, 35T, 35T, 35T, 35T, ; VE (2,n)
      50T, 50T, 50T, 50T, 50T, 50T, 50T, 50T, ; VE (3,n)
      50T, 50T, 50T, 50T, 50T, 50T, 50T, 50T, ; VE (4,n)
      50T, 50T, 50T, 50T, 50T, 50T, 50T, 50T, ; VE (5,n)
      65T, 65T, 65T, 65T, 65T, 65T, 65T, 65T, ; VE (6,n)
      80T, 80T, 80T, 80T, 80T, 80T, 80T, 90T  ; VE (7,n)
      }

   scs1 port($0016) = {  ; SCI Status Register 1
      SCTE(7)            ; SCI transmitter empty bit
      TC  (6)            ; transmission complete bit
   }

   Config11 bits = {
      mapType       (0:1) = 0T
      engineStroke  (  2) = 0T
      injectionType (  3) = 0T
      nCylinders    (4:7) = 7T ; 0 -> 15 maps to 1 -> 16
   }
}

FLASH = { ; The flash-only data, not accessible by MegaTune.
   thing byte
}

Output file "header.inc":

;-------------------------------------------------------------------------------
; RAM variable declarations.
ms_ram_start:
secl              ds          1T
        org       $0020 ; 32T
TPSfuelcut        ds      00001T
INTACC1           ds      00004T
ms_ram_end:

;-------------------------------------------------------------------------------
; RAM-FLASH variable declarations.
ms_rf_start:
VEtableSize       equ     64
VE                ds      00064T
Config11          ds      00001T
ms_rf_end:
;-------------------------------------------------------------------------------
ms_ram_size       equ {ms_ram_end-ms_ram_start}
ms_rf_size        equ {ms_rf_end-ms_rf_start}
ms_total_ram_size equ {ms_rf_end-ms_ram_start}
;-------------------------------------------------------------------------------

Output file "init.inc":

;-------------------------------------------------------------------------------
; Zero out used RAM.
        clra
        ldhx     #ms_ram_start
clr_loram  sta     ,x
           aix     #1
        cphx    #ms_ram_end
        bne     clr_loram

;-------------------------------------------------------------------------------
; Initialize those things not zero.
        mov     #100T,TPSfuelcut

;-------------------------------------------------------------------------------
; Load ram-flash from flash.
        clrh
        clrx
load_ram   lda    ms_rf_start_f,x
           sta    ms_rf_start,x
           aix    #1
        cphx    #ms_rf_size
        bne     load_ram
;-------------------------------------------------------------------------------


Output file "flash.inc" should be included into assembler source
to define the flash-only and ram-flash data.

        org       $E000
ms_rf_start_f:
VE_f:             ; type=byte  entries=64  total bytes=64
                  db      18T,18T,18T,18T,18T,18T,18T,18T
                  db      22T,25T,30T,32T,35T,35T,35T,35T
                  db      35T,35T,35T,35T,35T,35T,35T,35T
                  db      50T,50T,50T,50T,50T,50T,50T,50T
                  db      50T,50T,50T,50T,50T,50T,50T,50T
                  db      50T,50T,50T,50T,50T,50T,50T,50T
                  db      65T,65T,65T,65T,65T,65T,65T,65T
                  db      80T,80T,80T,80T,80T,80T,80T,90T
Config11_f        db      %00111000
ms_rf_end_f:
;-------------------------------------------------------------------------------

;-------------------------------------------------------------------------------
ms_flash_start:
thing             db      0

; Configuration signature, used by config program to confirm
; that .ini matches embedded code.
Configuration db 12T,'MyCode 2.3',13T,10T
ms_flash_end:
;-------------------------------------------------------------------------------


Output file "megatune.ini" used by MegaTune to map the run-time
variables and ram-flash variables to internal variables and
dialog boxes.

#define Configuration "MyCode 2.3" // 10 bytes

#define Rsecl                0 //   1 bytes

#define Rget 1

#define DVE                  0 //  64 bytes
#define DConfig11           64 //   1 bytes

#define Cgetno 65

------------------------------------------------------------------------------*/

#include <vector>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <ctype.h>
#include <string>

using namespace std;

#define VERSION 1.03

//------------------------------------------------------------------------------

ostream &dosEndL(ostream &stream)
{
#ifdef unix
   stream << "\r\n";
#else
   stream << endl;
#endif
   return stream;
}

ostream &sep(ostream &stream)
{
   stream << ";";
   for (int i = 0; i < 79; i++) stream << "-";
   stream << dosEndL;
   return stream;
}

//------------------------------------------------------------------------------

void Usage(string msg)
{
   cerr << "ERROR:  " << msg << endl;
   exit(1);
}

//------------------------------------------------------------------------------

bool number(string s, int &n)
{
   n = 0;

   // Prefixes: % = binary, $ = hexadecimal, ! = decimal.
   // Suffixes: Q = binary, H = hexadecimal, T = decimal.

   int  base = 10;
   char ch   = s[0];

   switch (ch) {
      case '%': base =  2; s[0] = ' '; break;
      case '$': base = 16; s[0] = ' '; break;
      case '!': base = 10; s[0] = ' '; break;

      case '0': case '1': case '2': case '3': case '4':
      case '5': case '6': case '7': case '8': case '9':
      case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
      case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
         ch = s[s.size()-1];
         switch (ch) {
            case 'Q': case 'q': base =  2; break;
            case 'H': case 'h': base = 16; break;
            case 'T': case 't': base = 10; break;

            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
            case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
            case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
               // Everthing is ok so far.
               break;

            default:
               return false;
               break;
         }
         break;
      default:
         return false;
         break;
   }

   n = strtol(s.c_str(), NULL, base);
   return true;
}

//------------------------------------------------------------------------------

#ifdef i386
extern "C" int sprintf(char *, char *, ...);
#endif
char *str(int n)
{
   static char s[100];
   sprintf(s, "%d", n);
   return s;
}

//==============================================================================

class subDataObject { // Bit field on a bits/port dataObject.
   string _name;
   int    _firstBit;
   int    _lastBit;
   int    _iVal;

public:
   subDataObject(string &n, int f, int l=-1, int i=0)
    : _name     (n),
      _firstBit (f),
      _lastBit  (l),
      _iVal     (i)
   {
      if (_lastBit < _firstBit) _lastBit = _firstBit;
      // Check for iVal bigger than bit field,
      // first and last in range 0-7.
   }

   string name() { return _name; }
   int    size() { return _lastBit-_firstBit+1; }
   int    mask() { return ((1 << size()) - 1) << _firstBit; }
   int    ofs () { return _firstBit; }

   int initialValue(int baseByte) {
      baseByte &= ~mask();
      baseByte |= (_iVal << _firstBit);
      return baseByte;
   }
};

//------------------------------------------------------------------------------

class dataObject {
public:
   typedef enum { byte, word, dword, equ, bits, port, org, label } doType;
   // dword not supported in assembler.

private:
   string name;
   doType type;
   int    count;
   int    offset;    // Offset into this block of variables.
   vector<string> initialValues;
   vector<subDataObject> bitFields;

   int    _orgAddr;  // Value of preceding ORG directive, if any.
   string _orgLabel;
   string _equString;
   string _label;    // Value of any LABEL directive.

   static int   typeSize[6];
   static char *typeName[6];
   static char *typeDecl[6];

   string rpad(string s, int len)
   {
      s += " "; // Make sure there is at least one so as to separate it.
      while (s.size() < len) s += " ";
      return s;
   }

   string binary(int v)
   {
      if (v < 0 || v > 255) Error("binary value out of range");
      string iv = "%";
      for (int b = 0x80; b > 0; b >>= 1) {
         iv += (b & v) ? "1" : "0";
      }
      return iv;
   }

   void setInitialValue()
   {
      if (type == bits && initialValues.size() == 0) {
         int n = bitFields.size();
         int v = 0;
         for (int i = 0; i < n; i++) {
            v = bitFields[i].initialValue(v);
         }
         addInitialValue(binary(v));
      }
   }

   //---------------------------------------------------------------------------

public:
   dataObject(string n)
    : name      (n),
      count     (1),
      type      (byte),
      offset    (0),
      _orgAddr  (-1),
      _orgLabel (""),
      _label    (""),
      _equString("")
   { }

   void setType        (doType t) { type   = t; }
   void setCount       (int    n) { count  = n; }
   void setOffset      (int    o) { offset = o; }
   void setORG         (int    o) { _orgAddr   = o; count = 0; }
   void setORG         (string s) { _orgLabel  = s; count = 0; }
   void setEqu         (string s) { _equString = s; }
   void setLabel       (string s) { _label     = s; count = 0; }
   void addLabel       (string s) { _label     = s; } // For internal labels.
   void addInitialValue(string v) { initialValues.push_back(v); }

   void addBitField    (string n, int first, int last, int iVal)
   {
      bitFields.push_back(subDataObject(n, first, last, iVal));
   }

   int  Offset()   { return type != org ? offset : _orgAddr; }
   int  Size()     { return typeSize[type] * count; }
   bool zeroPage() { return Offset() < 256;  }
   bool   typeIs(doType t)  { return type == t; }

   bool nameIs(string s) { return s == name; }
   string initialValue(int i) { return initialValues[i]; }

   //---------------------------------------------------------------------------

   void Error(string msg)
   {
      cerr << "ERROR in \"" << name << "\": " << msg << endl;
      exit(1);
   }

   //---------------------------------------------------------------------------

   void writeDD(ostream &o)
   {
      if (type == org || type == label) return;
      o << name << "=" << Offset() << "," << Size()
        << dosEndL;
   }

   //---------------------------------------------------------------------------

   void writeOrg(ostream &o, bool writeOrgs=true, string suffix="")
   {
      if (type == org && writeOrgs) {
         o << rpad("", 18) << "org     ";
         if (_orgAddr < 0)
            o << _orgLabel;
         else
            o << "$" << setw(4) << setfill('0') << hex << _orgAddr
              << dec << setfill(' ') << " ; " << _orgAddr << "T";
         o << dosEndL;
         if (_label != "") o << _label+suffix << ":" << dosEndL;
      }
      else if (type == label || _label != "") {
       //o << rpad(_label+suffix, 18) << "equ     *" << dosEndL;
         o << _label+suffix << ":" << dosEndL;
      }
   }

   //---------------------------------------------------------------------------

   void writeDecl(ostream &o, string suffix)
   {
      setInitialValue();
      int n = initialValues.size();

      writeOrg(o, true, suffix);
      if (type == org || type == label || type == equ) return;

      if (n == 1 && initialValues[0][0] == '\'') {
         // Make sure that memory is the right size to hold the string.
         if (initialValues[0].size()-2 < Size())
            Error("The declared value, "+initialValues[0]+", is too small to fill declared size of "+str(Size())+" bytes");
         if (initialValues[0].size()-2 > Size())
            Error("The declared value, "+initialValues[0]+", is too large to fit in declared size of "+str(Size())+" bytes");
      }

      if (n <= 1) {
         o << rpad(name+suffix, 18)
           << rpad(typeDecl[type], 8)
           << string(n == 0 ? "0" : initialValues[0])
           ;
      }
      else {
         if (n != count)
            Error("Different number of values and initializers");

         o << rpad(name+suffix+":", 18)
           << "; type="        << typeName[type]
           << "  entries="     << count
           << "  total bytes=" << Size()
           ;
         for (int i = 0; i < n; i++) {
            if (i % 8 == 0) o << dosEndL << setw(18) << "" << rpad(typeDecl[type], 8);
            else            o << ",";
            o << initialValues[i];
         }
      }
      o << dosEndL;
   }

   //---------------------------------------------------------------------------
   //--  Casm08 has a 16-character limit on names, figure that out for the
   //--  bit field names.

   void writeDefn(ostream &o, bool writeOrgs=true)
   {
      int i;
      writeOrg(o, writeOrgs);

      switch (type) {
         case equ:
            o << rpad(name, 18)
              << rpad("equ", 8)
              << setw(10) << initialValues[0]
              << dosEndL;
            break;

         case byte:
         case word:
         case dword:
            o << rpad(name, 18)
              << rpad("ds", 8)
              << setw( 9) << Size() << "T"
              << dosEndL;
            break;

         case port:
            o << rpad(name, 18)
              << rpad("equ", 13)
              << "$" << setw(4) << setfill('0') << hex << count
              << dec << setfill(' ')
              << dosEndL;
            for (i = 0; i < bitFields.size(); i++) {
               if (bitFields[i].size() == 1) {
                  o << rpad(name+"_b_"+bitFields[i].name(), 28)
                    << rpad("equ", 8)
                    << setw( 9) << bitFields[i].ofs() << "T"
                    << dosEndL;
               }
            }
            break;

         case bits:
            o << rpad(name, 18)
              << rpad("ds", 8)
              << setw( 9) << Size() << "T"
              << dosEndL;
            for (i = 0; i < bitFields.size(); i++) {
               o << rpad(name+"_m_"+bitFields[i].name(), 28)
                 << rpad("equ", 8)
                 << binary(bitFields[i].mask())
                 << dosEndL;
            }
            for (i = 0; i < bitFields.size(); i++) {
               if (bitFields[i].size() == 1) {
                  o << rpad(name+"_b_"+bitFields[i].name(), 28)
                    << rpad("equ", 8)
                    << setw( 9) << bitFields[i].ofs() << "T"
                    << dosEndL;
               }
            }
#if 0
; Usage Goto_Mode D
$MACRO Goto_Mode
        cmp     #'%1'
        bne     skip
        jmp     MODE_%1
Skip:
$MACROEND
        lda     Config13
        bit     #c13_bc
        bne     DoBaroCorr
#endif
            break;
      }
   }

   //---------------------------------------------------------------------------

   void writeInit(ostream &o) // Assume this is for RAM only variables.
   {
      if (type == org || type == label || type == equ) return;

      setInitialValue();
      int n = initialValues.size();
      if (n == 1) {
         int iv;
         number(initialValues[0], iv);
         if (iv != 0) {
            if (zeroPage())
               o << "        mov     #" << initialValues[0] << "," << name << dosEndL;
            else
               o << "        lda     #" << initialValues[0] << dosEndL
                 << "        sta     " << name << dosEndL;
         }
      }
      else if (n > 1) { // So make a table and copy it.
         Usage("Can't initialize non-scalar RAM variables yet, \""+name+"\"");
      }
   }
};

int   dataObject::typeSize[6] = { 1,      2,      4,       0,       1,      0      };
char *dataObject::typeName[6] = { "byte", "word", "dword", "const", "bits", "port" };
char *dataObject::typeDecl[6] = { "db",   "dw",   "???",   "equ",   "db",   "equ"  };

//==============================================================================

class data {
   vector<dataObject> Data;
   int _privateAfter;

public:
   data() : _privateAfter(0) { }

   dataObject &addData(string name) { Data.push_back(dataObject(name)); return Data[Data.size()-1]; }
   dataObject & operator [](int i) { return Data[i]; }
   int size() { return Data.size(); }

   int  privateAfter()      { return _privateAfter; }
   void privateAfter(int p) { _privateAfter = p;    }

   dataObject *findEqu(string name) {
      int n = size();
      for (int i = 0; i < n; i++) {
         if (Data[i].typeIs(dataObject::equ) && Data[i].nameIs(name)) {
            return &Data[i];
         }
      }
      return NULL;
   }
};

//==============================================================================

class parsedData {
   int      lNo;
   ifstream &input;
   string    token;
   char     *buffer;

   string configuration;
   data      rm;         // Pure RAM data
   data      fl;         // Pure flash data
   data      hr;         // Pure RAM above $200
   data      rf;         // Flashed RAM data
   data     *current;

   void Error(string msg)
   {
      cerr << "ERROR at line " << lNo << ": " << msg << endl;
      exit(1);
   }

   void Scan()
   {
      token = "";
      if (*buffer) {
         while (*buffer && (isspace(*buffer) || *buffer == ';')) {
            if (*buffer == ';') {
               while (*buffer && *buffer != '\n') buffer++;
            }
            if (*buffer == '\n') lNo++;
            buffer++;
         }

         if (*buffer) {
            if (isalnum(*buffer) || strchr("%$!_", *buffer)) {
               token += *buffer++;
               while (isalnum(*buffer) || *buffer == '_') token += *buffer++;
            }
            else if (*buffer == '\'') {
               token += *buffer++;
               while (*buffer != '\'') token += *buffer++;
               token += *buffer++;
            }
            else
               token += *buffer++;
         }
      }
   }

   bool tokenIs(string couldBe, bool scanPast=true)
   {
      if (token != couldBe)
         return false;
      else {
         if (scanPast) Scan();
         return true;
      }
   }

   void Check(string shouldBe)
   {
      if (!tokenIs(shouldBe)) {
         Error("Expected token to be \""+shouldBe+"\", not \""+token+"\"");
      }
   }

   void CheckValue()
   {
      if (strchr("'%$!0123456789", token[0]) == NULL) {
         Error("Expected a character string or numeric value, not \""+token+"\"");
      }
      Scan();
   }

   int CheckNumber()
   {
      int n;
      if (!number(token, n))
         Error("Expected a number, not \""+token+"\"");
      Scan(); // Past the number.
      return n;
   }

   void parseConfiguration()
   {
      Scan(); // "Configuration" keyword.
      Check("=");
      configuration = token;
      Scan();
   }

   void parseRequires()
   {
      Scan(); // Past "Requires".
      Check("=");
      string sno = token;
      Scan();
      Check(".");
      sno += "." + token;
      double vno = strtod(sno.c_str(), NULL);
      if (vno > VERSION) Error("Input file requires version "+sno+" or greater");
      Scan();
   }

   void parseBitFields(dataObject &o)
   {
      // = {
      //   name (start)
      //   name (start:end) = ivalue 
      // }
      while (!tokenIs("}")) {
         if (!isalpha(token[0])) Error("Expected a bitfield identifier, not \""+token+"\"");
         string name = token;
         Scan(); // Past the name.

         Check("(");
         int start = CheckNumber();
         int end   = start;
         if (tokenIs(":")) {
            end = CheckNumber();
         }
         Check(")");

         int iVal = 0;
         if (tokenIs("=")) { // Optional initializer, default is zero.
            iVal = CheckNumber();
         }
         o.addBitField(name, start, end, iVal);
      }
   }

   void parseData(data &d)
   {
      current = &d;
      Scan(); // Past initial keyword.
      Check("=");
      Check("{");
      while (token != "}") {
         if (token == "#") { // In-block directive
            Scan();
            if (tokenIs("PUBLIC")) {
               if (d.size() != 0)
                  Error("Optional #PUBLIC directive must come before any variable declarations");
               // noise word
            }
            else if (tokenIs("PRIVATE")) {
               if (d.privateAfter() > 0)
                  Error("Optional #PRIVATE must only appear once, after #PUBLIC declarations");
               d.privateAfter(d.size());
            }
            else if (tokenIs("ORG")) {
               dataObject &o = d.addData("");
               o.setType(dataObject::org);
               int n;
               if (number(token, n))
                  o.setORG(n);
               else
                  o.setORG(token);
               Scan();
            }
            else if (tokenIs("LABEL")) {
               dataObject &o = d.addData("");
               o.setType(dataObject::label);
               o.setLabel(token);
               Scan();
            }
            continue; // Skip the rest, since the next token could be "}".
         }

         if (!isalpha(token[0])) Error("Expected an identifier, not \""+token+"\"");
         dataObject &o = d.addData(token);
         Scan(); // Past the name.

              if (tokenIs("byte"))  o.setType(dataObject::byte);
         else if (tokenIs("bits"))  o.setType(dataObject::bits);
         else if (tokenIs("port"))  o.setType(dataObject::port);
         else if (tokenIs("word"))  o.setType(dataObject::word);
         else if (tokenIs("dword")) o.setType(dataObject::dword);
         else if (tokenIs("const")) o.setType(dataObject::equ);
         else Error("Expected type, not \""+token+"\"");

         if (tokenIs("(")) { // Optional count, default is 1.
            if (isalpha(token[0])) {
               // Coerce equates to numbers.
               dataObject *d = current->findEqu(token);
               if (d) token = d->initialValue(0);
            }
            o.setCount(CheckNumber());
            Check(")");
         }

         if (tokenIs("=")) { // Optional initializer, default is zero.
            if (!tokenIs("{")) { // Single value.
               o.addInitialValue(token);
               CheckValue();
            }
            else {
               if (o.typeIs(dataObject::bits) || o.typeIs(dataObject::port)) 
                  parseBitFields(o);
               else {
                  while (1) {
                     o.addInitialValue(token);
                     CheckValue();
                     if (token == ",") {
                           if (o.typeIs(dataObject::equ)) Error("Values of type \"const\" can only have one value");
                        Check(",");
                     }
                     else {
                        Check("}");
                        break;
                     }
                  }
               }
            }
         }
      }
      Check("}");
   }

   void parseStmt()
   {
           if (token == "Configuration") parseConfiguration();
      else if (token == "Requires"     ) parseRequires();
      else if (token == "RAM"          ) parseData(rm);
      else if (token == "FLASH"        ) parseData(fl);
      else if (token == "HIGH_RAM"     ) parseData(hr);
      else if (token == "RAM_FLASH"    ) parseData(rf);
      else Error("Expected token to be keyword, not \""+token+"\"");
   }

   //---------------------------------------------------------------------------

   void parse()
   {
      streambuf *pfb    = input.rdbuf();
      int       nChars  = pfb->in_avail() + 50000;
      char      *buf    = new char[nChars+1];
      nChars = pfb->sgetn(buf, nChars);
    //nChars = input.readsome(buf, nChars);
      buf[nChars]    = '\0';

      buffer = buf;
      Scan(); // Prime the pump
      while (*buffer) {
         parseStmt();
      }
      if (rm.privateAfter() == 0) rm.privateAfter(rm.size());

      delete [] buf;
   }

public:
   parsedData(ifstream &in)
    : lNo        (1),
      input      (in)
   {
      parse();
      if (configuration.size() == 0)
         Error("You must have a \"Configuration='string'\" statement in your file.");
   }

   //---------------------------------------------------------------------------

   void computeOffsets()
   {
      int i;
      //------------------------------------------------------------------------
      //--  Zip the offsets in the stuff that will reside in RAM.
      int nRM = rm.size();
      for (i = 1; i < nRM; i++) rm[i].setOffset(rm[i-1].Offset()+rm[i-1].Size());

      int nRF   = rf.size();
      int rfOfs = rf[0].Size();
      for (i = 1; i < nRF; i++) {
         rf[i].setOffset(rf[i-1].Offset()+rf[i-1].Size());
         rfOfs += rf[i].Size();
      }

      int nHR = hr.size();
      if (!hr[0].typeIs(dataObject::org)) hr[0].setOffset(rm[nRM-1].Offset()+rm[nRM-1].Size() + rfOfs);
      for (i = 1; i < nHR; i++) hr[i].setOffset(hr[i-1].Offset()+hr[i-1].Size());
      cout << "   Free RAM $" << setw(4) << setfill('0') << hex
           << hr[nHR-1].Offset()+hr[nHR-1].Size() << dec << endl;
   }

   //---------------------------------------------------------------------------

   void writeDataDictionary()
   {
      int i;

      ofstream dd("megatune.xxx");
      char quoteConfig[128];
      strcpy(quoteConfig, configuration.c_str());
      quoteConfig[0] = '"';
      quoteConfig[configuration.size()-1] = '"';
      int rmPA = rm.privateAfter();
      dd     << "[Configuration]" << dosEndL
             << quoteConfig << dosEndL
             << dosEndL
             << "[Run Time]" << dosEndL
             << "blockSize=" << rm[rmPA-1].Offset()+rm[rmPA-1].Size() << dosEndL;
      for (i = 0; i < rmPA; i++) rm[i].writeDD(dd);
      int nRF = rf.size();
      dd     << dosEndL
             << "[Variables]" << dosEndL
             << "blockSize=" << rf[nRF-1].Offset()+rf[nRF-1].Size() << dosEndL;
      for (i = 0; i < nRF; i++) rf[i].writeDD(dd);
   }

   //---------------------------------------------------------------------------

   void writeHeader()
   {
      int i;

      ofstream header("header.inc");
      header << sep
             <<"; FLASH equates." << dosEndL;
      int nFL = fl.size();
      for (i = 0; i < nFL; i++) if (fl[i].typeIs(dataObject::equ)) fl[i].writeDefn(header, false);

      header << sep
             << "; RAM variable declarations." << dosEndL;
      int nRM = rm.size();
      if (nRM) rm[0].addLabel("ms_ram_start");
      // Only write orgs for RAM section, the ones in RAM_FLASH
      // are written to flash include file.
      for (i = 0; i < nRM; i++) rm[i].writeDefn(header);
      header << "ms_ram_end:" << dosEndL
             << dosEndL
             << sep
             << "; RAM-FLASH variable declarations." << dosEndL;

      int nRF = rf.size();
      if (nRF) rf[0].addLabel("ms_rf_start");
      for (i = 0; i < nRF; i++) rf[i].writeDefn(header, false);
      header << "ms_rf_end:"                                      << dosEndL
             << sep;

      int nHR = hr.size();
      if (nHR) hr[0].addLabel("ms_hr_start");
      else     header << "ms_hr_start:" << dosEndL;
      for (i = 0; i < nHR; i++) {
         hr[i].writeDefn(header);
         // Following only true for 908GP32...
         //if (hr[i].Offset() + hr[i].Size() > 0x240) hr[i].Error("HIGH_RAM overflow, it's only 64 bytes long");
      }
      header << "ms_hr_end:"                                      << dosEndL
             << sep;

      header << "ms_ram_size       equ {ms_ram_end-ms_ram_start}" << dosEndL
             << "ms_rf_size        equ {ms_rf_end-ms_rf_start}"   << dosEndL
             << "ms_hr_size        equ {ms_hr_end-ms_hr_start}"   << dosEndL
             << "ms_total_ram_size equ {ms_rf_end-ms_ram_start}"  << dosEndL
             << sep << dosEndL;
   }

   //---------------------------------------------------------------------------

   void writeInit()
   {
      int i;
      ofstream init("init.inc");
      init   << sep
             << "; Zero out used RAM."                            << dosEndL
             << "        clra"                                    << dosEndL
             << "        ldhx     #ms_ram_start"                  << dosEndL
             << "clr_loram  sta     ,x"                           << dosEndL
             << "           aix     #1"                           << dosEndL
             << "        cphx    #ms_ram_end"                     << dosEndL
             << "        bne     clr_loram"                       << dosEndL
             << dosEndL;
      int nHR = hr.size();
      if (nHR)
      init   << "        ldhx     #ms_hr_start"                   << dosEndL
             << "clr_hiram  sta     ,x"                           << dosEndL
             << "           aix     #1"                           << dosEndL
             << "        cphx    #ms_hr_end"                      << dosEndL
             << "        bne     clr_hiram"                       << dosEndL
             << dosEndL;
      init   << sep
             << "; Initialize those things not zero." << dosEndL;
      int nRM = rm.size();
      for (i = 0; i < nRM; i++) rm[i].writeInit(init);
      for (i = 0; i < nHR; i++) hr[i].writeInit(init);
      init   << dosEndL
             << sep
             << "; Load ram-flash from flash."                    << dosEndL
             << "        clrh"                                    << dosEndL
             << "        clrx"                                    << dosEndL
             << "load_ram   lda    ms_rf_start_f,x"               << dosEndL
             << "           sta    ms_rf_start,x"                 << dosEndL
             << "           aix    #1"                            << dosEndL
             << "        cphx    #ms_rf_size"                     << dosEndL
             << "        bne     load_ram"                        << dosEndL
             << sep;
   }

   //---------------------------------------------------------------------------

   void writeFlash()
   {
      int i;

      ofstream flash("flash.inc");
   // flash   << "        org       $E000" << dosEndL; // Default.
      int nRF = rf.size();
   // rf[0].addLabel("ms_rf_start"); already done above in writeHeader
      for (i = 0; i < nRF; i++) rf[i].writeDecl(flash, "_f");
      flash  << "ms_rf_end_f:" << dosEndL
             << sep << dosEndL
             << sep;
      int nFL = fl.size();
      if (nFL) fl[0].addLabel("ms_flash_start");
      for (i = 0; i < nFL; i++) fl[i].writeDecl(flash, "");
      flash  << dosEndL
             << "; Configuration signature, used by config program to confirm" << dosEndL
             << "; that .ini matches embedded code." << dosEndL
             << "Configuration db " << configuration.size() << "T," << configuration << ",13T,10T" << dosEndL
             << "ms_flash_end:" << dosEndL
             << sep
             << dosEndL;
   }

   //---------------------------------------------------------------------------

   void writeFiles()
   {
      computeOffsets();
      writeDataDictionary();
      writeHeader();
      writeInit();
      writeFlash();
   }
};

//==============================================================================

int main(int argc, char *argv[])
{
   cout << "MS PreProcessor " << VERSION << endl;
   if (argc != 2) Usage("Incorrect number of arguments");
   ifstream input(argv[1]);
   if (!input) Usage("Couldn't open input file");
   //input.unsetf(ios_base::skipws);

   parsedData p(input);
   p.writeFiles();
   return 0;
}

//------------------------------------------------------------------------------

