#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "ktypes.h"
#include "registers.h"
#include "memory.h"
#include "code.h"
#include "setup.h"
#include "fedex.h"

/* ----------------------------------------------------- */

// kbox.c

// KBOX is main driver program for KCODE interpreter.
// It is comprised of two large components:

//    the first component oversees
//        the reading of the source file
//        the setup of memory allocations,
//            register contents, label locations,
//            and listing of instructions

//     the second component is the actual
//        "fetch-decode-execute" cycle

/* ----------------------------------------------------- */

// The first component is a rather lengthy process which
// requires reading every line in the source code.
// Support for this component may be found in two files
//     setup.h and setup.c.
// Even with of the work channeled to these helpful
// procedures, reading and processing the source file is
// quite detailed:

//     - is the line of code a directive or an instruction
//     - if it is a directive, is it data or code segment
//     - if it is a label directive,
//         what is its position in the instruction set
//     - if it is an instruction, what are its components

//     - after reading the source file,
//         the important registers must be set
//     - and the user is given the option to view setup
//         prior to execution of the instruction set

/* ----------------------------------------------------- */

// The second component appears trivial in comparison!
// The "fetch-decode-execute" cycle is essentially
// an infinite loop.
// The actual procedure that does all the work is found
// in two files
//     fedex.h and fedex.c.
// Furthermore, the deceptively short fragment
// of coding below expands into a huge implementation
// file where the sole procedure single-handedly
// simulates every KCODE instruction!

/* ----------------------------------------------------- */

int data_definition = 0;
int code_definition = 0;
int data_done = 0;
int code_done = 0;

int main (int argc,char** argv)
{
  if (argc < 2)
  {
    printf ("a source file must be specified!\n");
    exit (EXIT_FAILURE);
  }

// setup code

  initializeMemory ();
  openFile (argv[1]);
  while (!done)
  {
    readLine ();
    if (isBlankLine())
      continue;
    str64 code = getCode ();
    if (strcmp(code,"_DATA_SEGMENT") == 0)
    {
      if (code_definition && !code_done)
        code_done = 1;
      if (data_definition || data_done)
      {
        printf (" ... redefinition _DATA_SEGMENT!\n");
        exit (EXIT_FAILURE);
      }
      data_definition = 1;
    }
    else if (strcmp(code,"_DEFINE") == 0)
    {
      if (data_definition && !data_done)
      {
        str64 label = getOperand ();
        str64 op2 = getOperand ();
        klunk value = literal2klunk(op2);
        _DEFINE (label,value);
      }
      else
        printf (" ... move _DEFINE into _DATA_SEGMENT!\n");
    }
    else if (strcmp(code,"_RESERVE") == 0)
    {
      if (data_definition && !data_done)
      {
        str64 label = getOperand ();
        klunk size = atoll(getOperand());
        _RESERVE (label,size);
      }
      else
        printf (" ... move _RESERVE into _DATA_SEGMENT!\n");
    }

    else if (strcmp(code,"_CODE_SEGMENT") == 0)
    {
      if (data_definition && !data_done)
      data_done = 1;
      if (code_definition || code_done)
      {
        printf (" ... redefinition _DATA_SEGMENT!\n");
        exit (EXIT_FAILURE);
      }
      code_definition = 1;
    }
    else if (strcmp(code,"_GLOBAL") == 0)
    {
      if (code_definition && !code_done)
      {
        str64 label = getOperand ();
        _GLOBAL (label);
      }
      else
        printf (" ... move _GLOBAL into _CODE_SEGMENT!\n");
    }
    else if (strcmp(code,"_EXTERN") == 0)
    {
      if (code_definition && !code_done)
      {
        str64 label = getOperand ();
        _EXTERN (label);
      }
      else
        printf (" ... move _EXTERN into _CODE_SEGMENT!\n");
    }
    else if (strcmp(code,"_LABEL") == 0)
    {
      if (code_definition && !code_done)
      {
        str64 label = getOperand ();
        _LABEL (label);
      }
      else
        printf (" ... move _LABEL into _CODE_SEGMENT!\n");
    }

    else if (code[0] == '_')
    {
      printf (" ... invalid directive: %s\n",code);
    }

    else // code is an instruction!
    {
      str64 oper1 = getOperand ();
      str64 oper2 = getOperand ();
      str64 oper3 = getOperand ();
      addInstruction (code,oper1,oper2,oper3);
    }
  }
  closeFile ();

  if (!data_definition)
    printf (" ... no _DATA_SEGMENT found!\n");
  if (!code_definition)
    printf (" ... no _CODE_SEGMENT found!\n");
  *addrReg("FP") = MAXMEM;
  *addrReg("SP") = MAXMEM;
  *addrReg("RP") = -1;
  *addrReg("HP") = getNextAddress ();
  *addrReg("ZR") = 0;
  PC = 0;
  printf (" ... do you wish to view setup (Y or N)? ");
  fgets(buffer,BUFFER_SIZE,stdin);
  char ans = toupper(buffer[0]);
  if (ans == 'Y')
  {
    displayMemory ();
    displayMap ();
    displayNext ();
    displayGlobals ();
    displayExterns ();
    displayLabels ();
    displayCode ();
    printf ("KEY REGISTERS\n");
    printf ("%15s %15d\n","FP:",*addrReg("FP"));
    printf ("%15s %15d\n","SP:",*addrReg("SP"));
    printf ("%15s %15d\n","RP:",*addrReg("RP"));
    printf ("%15s %15d\n","HP:",*addrReg("HP"));
    printf ("%15s %15d\n","ZR:",*addrReg("ZR"));
    printf ("%15s %15d\n","PC:",PC);
    printf ("\n");
  }
  printf (" ... do you wish to view execution (Y or N)? ");
  fgets(buffer,BUFFER_SIZE,stdin);
  ans = toupper(buffer[0]);
  if (ans == 'Y')
    debug = 1;
  else
    debug = 0;

// execute code

  while (PC < INSTRUCTION_COUNTER)
  {
    FEDEX (debug);
  }

  exit (EXIT_SUCCESS);
}

/* ----------------------------------------------------- */
