/*

Freely Distributable C30 Simulator Package

Copyright (c) 1996-1998 The University of Texas
All Rights Reserved.

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.
  
The GNU Public License is available in the file LICENSE, or you
can write to the Free Software Foundation, Inc., 59 Temple Place -
Suite 330, Boston, MA 02111-1307, USA, or you can find it on the
World Wide Web at http://www.fsf.org.

Authors: Chi Duong, Brian Evans, and Chris Moy
Version: @(#)c30sim.cc	1.51	01/19/98

Department of Electrical and Computer Engineering
The University of Texas, Austin, TX 78712-1084

Simple command-line front end to the C30 simulator.
*/

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

#include "simulator.h"
#include "ut_cmds.h"
#include "dsk_cmds.h"
#include "loadc30.h"
#include "memmap.h"

#define C30SIM_PROMPT "c30>"

#define C30SIM_DEFAULT_BOARD     "ut"

#define C30SIM_OPTION_BOARD "board="
#define C30SIM_OPTION_PROC  "processor="
#define C30SIM_OPTION_HELP  "help"

#define C30SIM_OPTION_BOARD_LEN (sizeof(C30SIM_OPTION_BOARD) - 1)
#define C30SIM_OPTION_PROC_LEN  (sizeof(C30SIM_OPTION_PROC) - 1)
#define C30SIM_OPTION_HELP_LEN  (sizeof(C30SIM_OPTION_HELP) - 1)

/*
Parse the arguments on the command line.
Currently, we only check to see if the user passed a filename of an
object file to simulate.
*/
static int parseCommandLine(int argc, char** argv, c30SimState* c30simptr) {
  const char* boardName = C30SIM_DEFAULT_BOARD;
  int firstBadArgFlag = TRUE;
  int helpFlag = FALSE;
  const char* processorName = 0;
  c30ProcType processor = __c3x;

  /* Parse options */
  /* -processor=X sets the type of C3x processor: C30, C31, C32, ... */
  /* -board=X sets the type of board and processor: UT, DSK, ... */
  while (argc > 1 && *argv[1] == '-') {
    int recognizedOptionFlag = FALSE;
    const char* optionName = &argv[1][1];
    switch (*optionName) {
      case 'b':                            /* -board */
        if ( strncmp(optionName, C30SIM_OPTION_BOARD,
                     C30SIM_OPTION_BOARD_LEN) == 0 ) {
          recognizedOptionFlag = TRUE;
          boardName = &optionName[C30SIM_OPTION_BOARD_LEN];
        }
        break;

      case 'h':
        if ( strncmp(optionName, C30SIM_OPTION_HELP,
                     C30SIM_OPTION_HELP_LEN) == 0 ) {
          recognizedOptionFlag = TRUE;
          helpFlag = TRUE;
        }
	break;

      case 'p':
        if ( strncmp(optionName, C30SIM_OPTION_PROC,
                     C30SIM_OPTION_PROC_LEN) == 0 ) {
          recognizedOptionFlag = TRUE;
          processorName = &optionName[C30SIM_OPTION_PROC_LEN];
        }
        break;
    }
    if (!recognizedOptionFlag) {
      if (firstBadArgFlag) {
        fprintf(stderr, "c30sim: illegal options --");
        firstBadArgFlag = FALSE;
      }
      fprintf(stderr, " %s", optionName);
    }
    argc--;
    argv++;
  }
  if (!firstBadArgFlag) {
    helpFlag = TRUE;
    fprintf(stderr, "\n");
    fflush(stderr);
  }
  if (helpFlag) {
    fprintf(stderr, "Usage: %s\n",
            "c30sim [-board=dsk|ut] [-processor=c30|c31|c32] [-help] loadfile");
    fflush(stderr);
    return FALSE;
  }

  /* Determine the type of processor */
  if (processorName) {
    if (!C30SimProcessorType(processorName, &processor)) {
      fprintf(stderr, "Unknown processor '%s'\n", processorName);
      fflush(stderr);
      return FALSE;
    }
  }

  /* Determine the type of board */
  int board = C30SimFindBoardIdNum(boardName);
  if (board < 0) {
    fprintf(stderr, "Unknown C30 board.\n");
    fflush(stderr);
    return FALSE;
  }

  /* Determine compatibility between the processor and board */
  if (!C30SimProcessorBoardCompatibility(processor, board)) {
    fprintf(stderr, "Processor is incompatible with the board.\n");
    fflush(stderr);
    return FALSE;
  }

  /* Pick member of the c3x family the processor is a genearic c3x processor */
  if (processor == __c3x) {
    C30SimBoardProcessor(board, &processor);
    if (processor == __c3x) processor = __c30;
  }

  /* Initialize the C30 simulator (board and processor) */
  if (!C30SimInitSimState(c30simptr, processor, board)) {
    fprintf(stderr, "Error initializing the C30 simulator.\n");
    fflush(stderr);
    return FALSE;
  }

  /* Load the program given by the first argument */
  int retval = TRUE;
  if (argc > 2) {    /* use C30 address given by next argument */
    int loadCommandLength =
        strlen(argv[1]) + strlen(argv[2]) + 2;
    char* loadCommandString = (char *) malloc(loadCommandLength);
    sprintf(loadCommandString, "%s %s", argv[1], argv[2]);
    retval = C30SimLoadCommand(loadCommandString, c30simptr);
    free(loadCommandString);
  }
  else if (argc == 1) {
    retval = C30SimLoadCommand(argv[1], c30simptr);
  }
  return retval;
}

/*
The main program contains the command-line interface.
It also adds and deletes breakpoints, which could be be moved
to a different part of the program or to the GUI.
If arguments are passed to main, the first is a program file
name to load, the second is a c30 memory location in which
to begin loading. If a location is not specified, the program
is loaded at the beginning of ROM. If a file is not given,
nothing happens; the prompt appears. We flush all output
streams after writing to guarantee proper behavior if this
simulator is used as a child process.
*/
int main(int argc, char *argv[] )  {
  c30SimState c30sim;

  /* Initialize the possible syntaxes for the simulator commands */
  C30SimAddBoard("ut", C30SimUTCommandFunctionsList, __c3x, C30_EMULATOR);
  C30SimAddBoard("dsk", C30SimDSKCommandFunctionsList, __c31, DSK_DEBUGGER);

  /* Parse options and arguments, and initialize the C30 simulator state */
  if (!parseCommandLine(argc, argv, &c30sim)) {
    exit(1);
  }

  /* Print welcome message. */
  C30SimVersionCommand("", &c30sim);

  /* Primary command loop: read user command, process it, and repeat */
  while (!feof(stdin)) {
    char inputBuffer[C30SIM_MAX_BUFFER_LENGTH];
    char* input = 0;

    /* Display the simulator prompt, and flush standard output */
    printf(C30SIM_PROMPT);
    fflush(stdout);

    /* Read the user input */
    input =
      C30ReadStringFromStream(inputBuffer, C30SIM_MAX_BUFFER_LENGTH, stdin);

    /* Ignore empty lines */
    if (*input) {
      char* lowerCasePtr = 0;
      int status;

      while (*input && isspace(*input)) input++;
      lowerCasePtr = input;
      while (*lowerCasePtr && !isspace(*lowerCasePtr))
        *lowerCasePtr++ = tolower(*lowerCasePtr);
 
      status = C30SimEvaluateCommand(input, &c30sim);
      if ( status < 0) {                                  /* quit requested */
        break;
      }
      else if ( status > 0 ) {
        fprintf(stderr, C30SimCommandErrorMessage(status), input);
        fprintf(stderr, "\n");
        fflush(stderr);
      }
    }
  }

  C30SimEndSimState(&c30sim);

  exit(0);
}
