/*

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: @(#)simulator.cc	1.33	01/19/98 

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

To add simulator commands for a debugger for a particular
board, add an entry to simulatorCommandTables.
*/

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

#include "asupport.h"                /* define dskAssembler */

#include "dsk.h"                     /* define DSK3_ver */

#include "state.h"
#include "pipeline.h"
#include "memmap.h"
#include "cycle.h"
#include "simmisc.h"
#include "loadc30.h"
#include "sim3x.h"                   /* define C30SIM_VERSION_STRING */
#include "simulator.h"

#define C30SIM_MAX_BOARDS 20

#define C30SIM_CHARS_IN_FILE_EXT     4

/* Select default addresses for loading programs */
/* binary file - load into RAM 0 by default (works for C30 and C31) */
/* hex file - specify 0 by default to get the load address from the hex file */
#define C30SIM_BINLOAD_DEFAULT  (C30_PROG_RAM0_ADDRESS + 2)
#define C30SIM_HEXLOAD_DEFAULT  0

struct C30SimCommandTables_t {
    const char* description;
    c30ProcType processor;
    int outputMode;
    C30SimCommandFunctions* commandTablePtr;
};
typedef struct C30SimCommandTables_t C30SimCommandTables;


                              /* Static Data */

/* ANSI C will concatenate two adjacent constant strings into one string */
static const char* StringValueArgFormat =
"%s "
C30_MEMORY_SCANF;

static const char* VersionIdentification =
"The University of Texas at Austin TMS320C30 Simulator Version %s.\n"
"Copyright (c) 1996-1998 The University of Texas.  All Rights Reserved.\n"
"Released as freely distributable software under the GNU license.\n"
"Authored by Chi Duong, Brian Evans, Srikanth Gummadi, and Chris Moy.\n"
"Based in part on the C30 DSK Tools version %4.2lf by Keith Larson.\n";

static const char* CopyrightInformation =
"Freely Distributable C30 Simulator Package\n"
"\n"
"Copyright (c) 1996-1998 The University of Texas\n"
"All Rights Reserved.\n"
"\n"
"This program is free software; you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License as published by\n"
"the Free Software Foundation; either version 2 of the License, or\n"
"(at your option) any later version.\n"
"\n" 
"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
"GNU General Public License for more details.\n"
"\n"
"The GNU Public License is available in the file LICENSE, or you\n"
"can write to the Free Software Foundation, Inc., 59 Temple Place -\n"
"Suite 330, Boston, MA 02111-1307, USA, or you can find it on the\n"
"World Wide Web at http://www.fsf.org.\n";

/* Every message must have a %s format specification in it. */
static const char* ErrorMessages[] = {
  "No error.  Command '%s' was successful.",
  "One or more missing arguments in '%s'.",
  "Extra characters after the command in '%s' were ignored.",
  "Cannot add a breakpoint at address '%s'.",
  "No more breakpoints may be added: '%s' not added.",
  "Invalid or missing address given in '%s'.",
  "No breakpoint at '%s'.",
  "Cannot find a breakpoint at address '%s'.",
  "Invalid or missing filename given in '%s'.",
  "Invalid or missing location '%s'",
  "Invalid or missing value given for '%s'.",
  "Invalid or missing breakpoint '%s'.",
  "Not implemented yet: '%s' ignored.",
  "No previous load command has been given, so '%s' cannot be evaluated",
  "The number of steps %s must be positive.",
  "The run ended because the PC reached an invalid value during '%s'.",
  "Unrecognized command '%s'.",
  "Unrecognized C30 board: cannot evaluate '%s'.",
  "Empty file or file read error while evaluating '%s'.",
  "Error in the assembler: '%s'. See the output file for help.",
  "Invalid command format, see help for correct format of the command: '%s'.",
  "Unsafe filename for security reasons: '%s'.  Please refer to sub-directories only."
};

#define C30SIM_ASSM_DEFAULT_ERROR_LIMIT 5
#define C30SIM_ASSM_DEFAULT_WARN_LIMIT  5

                            /* Static Functions */

/* Check for no arguments */
static int assertNoMoreArguments(const char* input) {
    return ( input && *input ) ? C30SIM_EXTRA_CHARS : C30SIM_NO_ERROR;
}

static int assertMoreArguments(const char* input) {
    return ( input && *input ) ? C30SIM_NO_ERROR : C30SIM_MISSING_ARGUMENTS;
}


/*
Check for 'string=number' and return the number of tokens found.
Mimic sscanf(input, "%s=%d", string, numPtr), which does not work
under GCC 2.7.2 under Solaris 2.5.1.
*/
static int parseStringEqualNumber(const char* input, char* string,
                                  uint32* numPtr) {
    int numItems = 0;
    while (*input && isspace(*input)) input++;
    if (*input) {
      const char* equalCharPtr = strchr(input, '=');
      if (equalCharPtr) {
        int numChars = equalCharPtr - input;    /* don't copy equal sign */
        strncpy(string, input, numChars);
        string[numChars] = 0;
        equalCharPtr++;
        while (*equalCharPtr && isspace(*equalCharPtr)) equalCharPtr++;
        numItems = sscanf(equalCharPtr, C30_MEMORY_SCANF, numPtr) + 1;
      }
    }
    return numItems;
}

/* Check for 'string number' and return the number of tokens found. */
static int parseStringNumber(const char* input, char* string, uint32* numPtr) {
    return sscanf(input, StringValueArgFormat, string, numPtr);
}

/* Modify a memory location or register.  Handle the PC as a special case. */
static int modifyCommand(const char* input,
                         c30SimState *c30simptr, int equalSignFlag) {
    int retval = assertMoreArguments(input);
    if ( retval == C30SIM_NO_ERROR ) {
      char locationString[C30SIM_MAX_BUFFER_LENGTH];
      uint32 value = 0;
      int numFields = 0;

      locationString[0] = 0;

      if (equalSignFlag)
        numFields = parseStringEqualNumber(input, locationString, &value);
      else
        numFields = parseStringNumber(input, locationString, &value);

      if (numFields == 2) {
        state* statePtr = &c30simptr->st;
        uint32* location = C30SimGetLoc(locationString, statePtr);
        if (location) {
          if (location == &statePtr->pc) {
            C30SimUpdateProgramCounter(&c30simptr->pipe, statePtr, value);
          }
          else {
            *location = value;
          }
        }
        else {
          retval = C30SIM_INVALID_LOCATION;
        }
      }
      else if (numFields == 1) {
        retval = C30SIM_INVALID_VALUE;
      }
      else {
        retval = C30SIM_INVALID_LOCATION;
      }
    }
    return retval;
}

/*
Extract the file extension and make the extension lower case.
The caller controls the number of maximum number of characters
in the extension (including the period) by the argument maxlen.
*/
static void extractExtension(const char *prog, char* extension, int maxlen) {
    int fileNameLen = strlen(prog);
    int extPosInFilename = 0;
    int extLength = 0;
    int i;

    extension[0] = 0;

    for (i = fileNameLen - 1; i >= 0; i--) {
      extLength++;
      if (prog[i] == '.') {
        extPosInFilename = i;
        break;
      }
    }

    if ((extLength > 0) && (extLength <= maxlen)) {
      strcpy(extension, &prog[extPosInFilename]);
      for (i = 0; i < maxlen; i++) {
        char c = extension[i];
        if (isupper(c)) {
          extension[i] = tolower(c);
        }
      }
    }

    return;
}

                  /* Visible Supporting Functions */

/*
Clear breakpoints in the C30 simulator state.
*/
int C30SimClearBreakpoints(c30SimState* c30simptr) {
    int i;
    uint32* breakpoints = c30simptr->breakpoints;
    for (i = 0; i < C30SIM_BREAKSMAX; i++) *breakpoints++ = 0;
    c30simptr->numberOfBreakpoints = 0;
    return TRUE;
}

/*
Find a breakpoint: returns -1 if no breakpoints were found.
*/
int C30SimFindBreakpoint(c30SimState* c30simptr, uint32 address) {
    int i, numBreaks = c30simptr->numberOfBreakpoints;
    uint32* breakpoints = c30simptr->breakpoints;
    for (i = 0; i < numBreaks; i++) {
        if (*breakpoints++ == address) return i;
    }
    return -1;
}

/*
Return the error generated by a simulator command.
*/
const char* C30SimCommandErrorMessage(int errorNumber) {
    const char* errorMessage = "";
    if ( errorNumber > 0 && errorNumber <= C30SIM_MAX_ERROR_NUMBER ) {
      return ErrorMessages[errorNumber];
    }
    return errorMessage;
}


         /* Visible Functions Implementing Simulator Commands */

   /* All commands return 0 on success or an error number on failure */

/*
Assemble a .asm file.
*/
int C30SimAssembleCommand(const char* input, c30SimState */*c30simptr*/) {
    int retval = assertMoreArguments(input);
    int errLimit = C30SIM_ASSM_DEFAULT_ERROR_LIMIT;
    int warnLimit = C30SIM_ASSM_DEFAULT_WARN_LIMIT;
    char thirdInputArg = 0;
    char *errorMessage = 0;
    MSGS assemblerRetval = NO_ERR;

    if ( retval == C30SIM_NO_ERROR ) {
      char inputLimit1[C30SIM_MAX_BUFFER_LENGTH];
      char inputLimit2[C30SIM_MAX_BUFFER_LENGTH];
      char prog[C30SIM_MAX_BUFFER_LENGTH];

      /* get error limit, warning limit, and .asm program filename */
      int numFields = 0;
      numFields = sscanf(input, "%s %s %s", prog, inputLimit1, inputLimit2);

      if (numFields == 0) {
        retval = C30SIM_INVALID_FILENAME;
      }
      else if (!C30SimSafePathName(prog)) {
        retval = C30SIM_UNSAFE_FILENAME;
      }
      else {
        FILE* asm_file = 0;
        char extension[C30SIM_CHARS_IN_FILE_EXT + 1];
        extractExtension(prog, extension, C30SIM_CHARS_IN_FILE_EXT);
        if (strcmp(extension, ".asm") != 0) {
          retval = C30SIM_INVALID_FILENAME;
        }
        else if ((asm_file = fopen(prog, "r")) == NULL) {
          retval = C30SIM_ERROR_READING_FILE;
        }
        else {
          char subDirectory[C30SIM_MAX_BUFFER_LENGTH];
          char* subDirectoryCharPtr = 0;

          fclose(asm_file);
          asm_file = 0;

          /* Extract a sub-directory if there is one */
          strcpy(subDirectory, prog);
          subDirectoryCharPtr = strrchr(subDirectory, '/');
          if (subDirectoryCharPtr) {
            *(subDirectoryCharPtr + 1) = 0;
          }
          else {
            subDirectory[0] = 0;
          }

          switch (numFields) {
            case 3:
              if ((*inputLimit2 == 'e') || (*inputLimit2 == 'E')) {
                thirdInputArg = 'e';
                errLimit = sscanf(&inputLimit2[1], "%d", &errLimit);
              }
              else if ((*inputLimit2 == 'w') || (*inputLimit2 == 'W')) {
                thirdInputArg = 'w';
                warnLimit = sscanf(&inputLimit2[1], "%d", &warnLimit);
              }
              else { 
                retval = C30SIM_INVALID_COMMAND_FORMAT;
                break;
              }
              // fall through
            case 2:
              if (((*inputLimit1 == 'e') || (*inputLimit1 == 'E')) &&
                  ((thirdInputArg == 'w') || (thirdInputArg == 0))) 
                errLimit = sscanf(&inputLimit1[1], "%d",  &errLimit);
              else if (((*inputLimit1 == 'w') || (*inputLimit1 == 'W')) && 
                       ((thirdInputArg == 'e') || (thirdInputArg == 0)))
                warnLimit = sscanf(&inputLimit1[1], "%d",  &warnLimit);
              else { 
                retval = C30SIM_INVALID_COMMAND_FORMAT;
                break;
              }
              // fall through
            case 1:
              assemblerRetval =
                  dskAssembler(errLimit, warnLimit, prog, subDirectory);
              errorMessage = Error_Strg(assemblerRetval);
              if (errorMessage) {
                retval = C30SIM_ERROR_READING_FILE;
                fprintf(stderr, "%s\n", errorMessage);
                fprintf(stderr, "Hint #1: If you have an .include command "
                        "in the assembly program, be sure that the "
                        "included file is in the proper directory.\n");
                fprintf(stderr, "Hint #2: If the error occurred on the "
                        "last line of the file, then check to see "
                        "if you have a spurious end-of-file character "
                        "on that line.\n");
                fflush(stderr);
              }
              else {
                retval = C30SIM_NO_ERROR;
              }
              break;
            default:
              retval = C30SIM_INVALID_COMMAND_FORMAT; 
              break;
          }
        }
      }
    }
    return retval;

}

/*
Adds a breakpoint at the specified location, and maintains the list
of break points in sorted order.
*/
int C30SimBreakAddCommand(const char* input, c30SimState *c30simptr) {
    /* get the breakpoint address */
    int retval = assertMoreArguments(input);
    if (retval == C30SIM_NO_ERROR ) {
      uint32 address = 0;
      if (sscanf(input, C30_MEMORY_SCANF, &address) == 1) {
        uint32* breakpoints = c30simptr->breakpoints;

        /* Check to see if all of the break point entries are full */
        if ( address == 0 ) {
            retval = C30SIM_ZERO_BREAKPOINT;
        }
        else if ( breakpoints[C30SIM_BREAKSMAX - 1] != 0 ) {
            retval = C30SIM_NO_MORE_BREAKPOINTS;
        }
        else {
            int i;
            for (i = 0; i < C30SIM_BREAKSMAX; i++) {
                uint32 breakValue = breakpoints[i];

                /* Do not add multiple copies of the same breakpoint */
                if ( address == breakValue ) {
                    break;
                }
                /* Insert at zero-valued breakpoint */
                else if (breakValue == 0) {
                    breakpoints[i] = address;
                    c30simptr->numberOfBreakpoints++;
                    break;
                }
                /* Insert new breakpoint in descending order */
                else if (address > breakValue) {
                    int j;
                    for (j = C30SIM_BREAKSMAX - 1; j > i; j--) {
                        breakpoints[j] = breakpoints[j-1];
                    }
                    breakpoints[i] = address;
                    c30simptr->numberOfBreakpoints++;
                    break;
                }
            }
        }
      }
      else {
        retval = C30SIM_INVALID_ADDRESS;
      }
    }
    else {
      retval = C30SIM_NO_BREAKPOINT;
    }
    return retval;
}

/*
Look for the breakpoint specified by the string input and delete it
if found in the list of breakpoints in c30simptr.
*/
int C30SimBreakDelCommand(const char* input, c30SimState *c30simptr) {
    int retval = assertMoreArguments(input);
    /* FIXME: Delete all breakpoints if no argument is given. */
    if ( retval == C30SIM_NO_ERROR ) {
      /* get the breakpoint address */
      uint32 address = 0;
      if ( sscanf(input, C30_MEMORY_SCANF, &address) == 1 ) {
        if ( address ) {
            int breakIndex = C30SimFindBreakpoint(c30simptr, address);
            if (breakIndex >= 0) {
                int j;
                int jmax = c30simptr->numberOfBreakpoints - 1;
                uint32* breakpoints = c30simptr->breakpoints;
                for (j = breakIndex; j < jmax; j++) {
                    breakpoints[j] = breakpoints[j+1];
                }
                breakpoints[jmax] = 0;
                c30simptr->numberOfBreakpoints--;
            }
            else {
                retval = C30SIM_UNKNOWN_BREAKPOINT;
            }
        }
        else {
            retval = C30SIM_ZERO_BREAKPOINT;
        }
      }
      else {
        retval = C30SIM_INVALID_ADDRESS;
      }
    }
    else {
      retval = C30SIM_NO_BREAKPOINT;
    }
    return retval;
}

/*
Displays the state of the C30 processor
*/
int C30SimC30StateCommand(const char* input, c30SimState *c30simptr) {
    int retval = assertNoMoreArguments(input);
    if ( retval == C30SIM_NO_ERROR ) {
      C30SimPrintState(&c30simptr->st);
    }
    return retval;
}

int C30SimCopyrightCommand(const char* input, c30SimState* /*c30simptr*/) {
    int retval = assertNoMoreArguments(input);
    printf(CopyrightInformation);
    fflush(stdout);
    return retval;
}

/*
Prints the number of c30 cycles executed since the last reset
*/
int C30SimCycleCommand(const char* input, c30SimState *c30simptr) {
    int retval = assertNoMoreArguments(input);
    if ( retval == C30SIM_NO_ERROR ) {
      printf("cycles=%d\n", C30SimGetCycle(&c30simptr->pipe));
      fflush(stdout);
    }
    return retval;
}

/*
Disables printing of the instruction in the pipeline decode stage.
*/
int C30SimDasmOffCommand(const char* input, c30SimState *c30simptr) {
    int retval = assertNoMoreArguments(input);
    if ( retval == C30SIM_NO_ERROR ) {
      c30simptr->pipe.dasmPrint = FALSE;
    }
    return retval;
}

/*
Enables printing of the instruction in the pipeline decode stage.
*/
int C30SimDasmOnCommand(const char* input, c30SimState *c30simptr) {
    int retval = assertNoMoreArguments(input);
    if ( retval == C30SIM_NO_ERROR ) {
      c30simptr->pipe.dasmPrint = TRUE;
    }
    return retval;
}

/*
Placeholder function.  Returns success.
*/
int C30SimDummyCommand(const char* /*input*/, c30SimState* /*c30simptr*/ ) {
    return C30SIM_NO_ERROR;
}

/*
Return a status that indicates a requet to exit the simulator.
*/
int C30SimExitCommand(const char* /*input*/, c30SimState* /*c30simptr*/ ) {
    return C30SIM_EXIT_PROGRAM;
}

/*
Load a C30 file according to the extension.  Return status.
*/
static int loadC30File(c30SimState *c30simptr) {
    int retval = C30SIM_NO_ERROR;
    uint32 addr = c30simptr->currentAddr;
    uint32 pcValue = 0;
    const char* prog = c30simptr->currentProg;
    uint32 wordsRead = 0;
    char extension[C30SIM_CHARS_IN_FILE_EXT + 1];

    extractExtension(prog, extension, C30SIM_CHARS_IN_FILE_EXT);
    if (strcmp(extension, ".hex") == 0) {
      if (addr == 0) addr = C30SIM_HEXLOAD_DEFAULT;
      wordsRead = C30SimLoadHex(&c30simptr->st, prog, &addr, &pcValue);
      if (wordsRead == 0) retval = C30SIM_ERROR_READING_FILE;
    }
    else if (strcmp(extension, ".c30") == 0) {
      if (addr == 0) addr = C30SIM_BINLOAD_DEFAULT;
      wordsRead = C30SimLoadC30(&c30simptr->st, prog, &addr, &pcValue);
      if (wordsRead == 0) retval = C30SIM_ERROR_READING_FILE;
    }
    else {
      retval = C30SIM_INVALID_FILENAME;
    }

    /* If the load was successful, set the program counter and inform user */
    if (wordsRead != 0) {
      C30SimUpdateProgramCounter(&c30simptr->pipe, &c30simptr->st, pcValue);
      printf("%ld words read from file '%s'.\nPC="
             C30_MEMORY_PRINTF
             "\n",
             (long)wordsRead, prog, pcValue);
      fflush(stdout);
    }

    return retval;
}
   
/*
Loads the specified C30 program beginning at specified location.
*/
int C30SimLoadCommand(const char* input, c30SimState *c30simptr) {
    int retval = assertMoreArguments(input);
    if ( retval == C30SIM_NO_ERROR ) {
      uint32 address = 0;                  /* must be zero for load routines */
      char prog[C30SIM_MAX_BUFFER_LENGTH];

      /* get the c30 program filename and destination address */
      int numFields = sscanf(input, StringValueArgFormat, prog, &address);
      if (numFields == 0) {
        retval = C30SIM_INVALID_FILENAME;
      }
      else {
        /* Save in case of reload */
        strncpy(c30simptr->currentProg, prog, C30SIM_MAX_BUFFER_LENGTH);
        c30simptr->currentProg[C30SIM_MAX_BUFFER_LENGTH - 1] = 0;
        c30simptr->currentAddr = address;
        retval = loadC30File(c30simptr);
      }
    }

    return retval;
}

/*
Stores the specified value in a C30 register or memory address.
*/
int C30SimModifyCommand(const char* input, c30SimState *c30simptr) {
    return modifyCommand(input, c30simptr, FALSE);
}

/*
Placeholder for a function that will be implemented.
*/
int C30SimNotAvailCommand(const char* /*input*/, c30SimState* /*c30simptr*/ ) {
    return C30SIM_NOT_IMPLEMENTED;
}

/*
Prints the contents of the C30 register or memory address
*/
int C30SimReadCommand(const char* input, c30SimState *c30simptr) {
    int retval = assertMoreArguments(input);
    /* get pointer to location of the C30 address from which to read */
    if ( retval == C30SIM_NO_ERROR ) {
      state* statePtr = &c30simptr->st;
      uint32* locPtr = C30SimGetLoc(input, statePtr);
      if (locPtr) {
        uint32 value = *locPtr;
        /* if location is one of R exponents, truncate value to be a char */
        if ((locPtr == &statePtr->Sregs[__rx0]) ||
            (locPtr == &statePtr->Sregs[__rx1]) ||
            (locPtr == &statePtr->Sregs[__rx2]) ||
            (locPtr == &statePtr->Sregs[__rx3]) ||
            (locPtr == &statePtr->Sregs[__rx4]) ||
            (locPtr == &statePtr->Sregs[__rx5]) ||
            (locPtr == &statePtr->Sregs[__rx6]) ||
            (locPtr == &statePtr->Sregs[__rx7]))  
          value >>=  24;  /* exponent is stored in the 8MSBs of rxn */
        printf(C30_MEMORY_PRINTF_NEWLINE, value);     /* print value in hex */
        fflush(stdout);
      }
      else {
        retval = C30SIM_INVALID_VALUE;
      }
    }
    return retval;
}

/*
Executes the last load command.
*/
int C30SimReloadCommand(const char* input, c30SimState *c30simptr) {
    int retval = assertNoMoreArguments(input);
    if ( retval == C30SIM_NO_ERROR ) {
      if (c30simptr->currentProg && c30simptr->currentProg[0]) {
        retval = loadC30File(c30simptr);
      }
      else {
        retval = C30SIM_NO_LOAD_COMMAND;
      }
    }
    return retval;
}

/*
Resets the C30 processor and clears the breakpoints.
*/
int C30SimResetCommand(const char* input, c30SimState *c30simptr) {
    int retval = assertNoMoreArguments(input);
    if ( retval == C30SIM_NO_ERROR ) {
      C30SimReset(&c30simptr->st, &c30simptr->pipe);
      C30SimClearBreakpoints(c30simptr);
    }
    return retval;
}

/*
Runs the current program until a breakpoint is reached
This routine returns 0 if the run stopped because the program counter
assumed an invalid value, and non-zero otherwise.
*/
int C30SimRunCommand(const char* input, c30SimState *c30simptr) {
    int retval = assertNoMoreArguments(input);
    if ( retval == C30SIM_NO_ERROR ) {
      retval = C30SimRun(&c30simptr->st, &c30simptr->pipe, -1,
                         c30simptr->breakpoints,
                         c30simptr->numberOfBreakpoints);
    }
    return retval;
}

/*
Runs the current program and ignores breakpoint settings.
*/
int C30SimRunFreeCommand(const char* input, c30SimState *c30simptr) {
    int retval = assertNoMoreArguments(input);
    if ( retval == C30SIM_NO_ERROR ) {
      uint32 emptyTable = 0;
      retval = C30SimRun(&c30simptr->st, &c30simptr->pipe, -1, &emptyTable, 0);
    }
    return retval;
}

/*
Runs until the current program hits the specified breakpoint.
*/
int C30SimRunUntilCommand(const char* input, c30SimState *c30simptr) {
    uint32 stopAddr = 0;
    int retval = assertMoreArguments(input);
    if ( retval == C30SIM_NO_ERROR ) {
      if (sscanf(input, C30_MEMORY_SCANF, &stopAddr) == 1) {
        retval = C30SimRun(&c30simptr->st, &c30simptr->pipe, -1, &stopAddr, 1);
      }
      else {
        retval = C30SIM_INVALID_BREAKPOINT;
      }
    }
    else {
      retval = C30SIM_NO_BREAKPOINT;
    }
    return retval;
}

/*
Displays the state of the C30 simulator
*/
int C30SimSimStateCommand(const char* input, c30SimState *c30simptr) {
    int retval = assertNoMoreArguments(input);
    if ( retval == C30SIM_NO_ERROR ) {
      uint32* breakpoints = c30simptr->breakpoints;
      int i;
      printf("breakpoints={ ");
      for (i = 0; i < c30simptr->numberOfBreakpoints; i++) {
          printf(C30_MEMORY_PRINTF, breakpoints[i]);
          putchar(' ');
      }
      printf("}\n");
      printf("program=%s\n", c30simptr->currentProg);
      C30SimPrintRegister("loadAddress", c30simptr->currentAddr);
      printf("processor=");
      C30SimPrintProcessorType(c30simptr->st.c30proc);
      printf("\n");
      printf("board=%s\n", C30SimBoardName(c30simptr->board));
      fflush(stdout);
    }
    return retval;
}

/*
Executes one c30 cycle.
*/
int C30SimStepCommand(const char* input, c30SimState *c30simptr) {
    int retval = C30SIM_NONPOSITIVE_STEPS;
    int numberOfSteps = 1;
    if (input && *input && sscanf(input, "%d", &numberOfSteps) != 1) {
        numberOfSteps = 1;
    }
    if (numberOfSteps > 0) {
      int runFlag = C30SimRun(&c30simptr->st, &c30simptr->pipe, numberOfSteps,
                      c30simptr->breakpoints, c30simptr->numberOfBreakpoints);
      retval = (runFlag) ? C30SIM_NO_ERROR : C30SIM_INVALID_PC;
    }
    return retval;
}

/*
Displays changes in the C30 processor state since the last time
update was called.
*/
int C30SimUpdateCommand(const char* input, c30SimState *c30simptr) {
    int retval = assertNoMoreArguments(input);
    if ( retval == C30SIM_NO_ERROR ) {
      C30SimPrintChangesInState(&c30simptr->oldst, &c30simptr->st);
      c30simptr->oldst = c30simptr->st;
    }
    return retval;
}

int C30SimVersionCommand(const char* input, c30SimState* /*c30simptr*/) {
    int retval = assertNoMoreArguments(input);
    printf(VersionIdentification, C30SIM_VERSION_STRING, DSK3_ver);
    fflush(stdout);
    return retval;
}

/* Indexed syntax for simulators by the board type */
static C30SimCommandTables simulatorCommandTables[C30SIM_MAX_BOARDS + 1] = {
    { "", __c3x, C30_EMULATOR, 0 }
};

static numberCommandTables = 0;

/*
Add a new board.
*/
int C30SimAddBoard(const char* boardName, C30SimCommandFunctions* newCommands,
                   c30ProcType processor, int outputMode) {
    int addedFlag = (numberCommandTables < C30SIM_MAX_BOARDS);
    if (addedFlag) {
      C30SimCommandTables* tempEntryPtr =
          &simulatorCommandTables[numberCommandTables];
      simulatorCommandTables[numberCommandTables + 1] = *tempEntryPtr;
      tempEntryPtr->description = boardName;
      tempEntryPtr->processor = processor;
      tempEntryPtr->outputMode = outputMode;
      tempEntryPtr->commandTablePtr = newCommands;
      numberCommandTables++;
    }
    return addedFlag;
}

/*
Find the index of the C30 board.
Return -1 if the passed description is not found.
*/
int C30SimFindBoardIdNum(const char* boardDesc) {
    int boardIndex = 0, foundIndex = -1;
    C30SimCommandTables* simulatorTablePtr = simulatorCommandTables;

    char boardDescLowerCase[C30SIM_MAX_BUFFER_LENGTH];
    char* lowerCasePtr = boardDescLowerCase;
    while (*boardDesc) {
      *lowerCasePtr++ = tolower(*boardDesc);
      boardDesc++;
    }
    *lowerCasePtr = 0;

    while (simulatorTablePtr->description && *simulatorTablePtr->description) {
      if ( strcmp(boardDescLowerCase, simulatorTablePtr->description) == 0 ) {
        foundIndex = boardIndex;
        break;
      }
      simulatorTablePtr++;
      boardIndex++;
    }
    return foundIndex;
}

C30SimCommandFunctions* C30SimBoardCommandTable(int boardId) {
    return simulatorCommandTables[boardId].commandTablePtr;
}

/*
Check to see if the processor is compatible with the board
*/
int C30SimProcessorBoardCompatibility(c30ProcType processor, int board) {
    int retval = FALSE;
    if ((board >= 0) && (board < numberCommandTables)) {
      retval = (C30SimProcessorCompatibility(processor,
                                simulatorCommandTables[board].processor));
    }
    return retval;
}

/*
Return the processor associated with the board.
*/
int C30SimBoardProcessor(int board, c30ProcType* processorPtr) {
    int retval = ((board >= 0) && (board < numberCommandTables));
    if (retval) {
      *processorPtr = simulatorCommandTables[board].processor;
    }
    return retval;
}

/*
Return the name of the board.
*/
const char* C30SimBoardName(int board) {
    const char* retval = "<unknown>";
    if ((board >= 0) && (board < numberCommandTables)) {
      retval = simulatorCommandTables[board].description;
    }
    return retval;
}

/*
Initialize the state of the C30 simulator
*/
int C30SimInitSimState(
            c30SimState* c30simptr, c30ProcType processor, int board) {
    int retval = C30SimProcessorBoardCompatibility(processor, board);
    if (retval) {
      C30SimInit(&c30simptr->st, &c30simptr->pipe, processor);
      c30simptr->st.outputMode = simulatorCommandTables[board].outputMode;

      /* initialize simulator data structures */
      C30SimClearBreakpoints(c30simptr);
      c30simptr->currentProg[0] = 0;
      c30simptr->currentAddr = C30_PROG_RAM0_ADDRESS + 2;
      c30simptr->oldst = c30simptr->st;
      c30simptr->processor = processor;
      c30simptr->board = board;
    }
    return retval;
}

/*
Delete the state of the C30 simulator.
*/
int C30SimEndSimState(c30SimState* c30simptr) {
    return C30SimEnd(&c30simptr->st, &c30simptr->pipe);
}

C30SimCommandFunctions* C30SimSearchForFunction(
            const char* input, C30SimCommandFunctions* commandInfo) {
    C30SimCommandFunctions* simFunctionPtr = 0;
    if (commandInfo) {
      int commandLength = commandInfo->length;
      while (commandLength) {
        const char* commandName = commandInfo->name;

        /* Avoid the function call to strncmp when possible for speed */
        if ((*input == *commandName) &&
            (strncmp(input, commandName, commandLength) == 0) &&
            ((input[commandLength] == 0) || isspace(input[commandLength]))) {
          simFunctionPtr = commandInfo;
          break;
        }
        commandInfo++;
        commandLength = commandInfo->length;
      }
    }
    return simFunctionPtr;
}

/*
Prints out the list of commands, or help on a specific command.
*/
int C30SimHelpCommand(const char* input, c30SimState* c30simptr) {
    C30SimCommandFunctions* commandInfo =
        C30SimBoardCommandTable(c30simptr->board);
    C30SimCommandFunctions* funcCommandInfo =
        C30SimSearchForFunction(input, commandInfo);
    if ( funcCommandInfo ) {
      printf("%s %s\n", funcCommandInfo->name, funcCommandInfo->comment);
    }
    else {
      printf("commands={ ");
      while (commandInfo->length) {
        printf("%s ", commandInfo->name);
        commandInfo++;
      }
      printf("}\n");
    }
    fflush(stdout);
    return C30SIM_NO_ERROR;
}

/*
Evaluates the user command and returns 0 on successful evaluation,
a negative number if the command requested to end the program, or
a positive error number if an error occurred.
*/
int C30SimEvaluateCommand(const char* input, c30SimState* c30simptr) {
  int status = C30SIM_NO_ERROR;
  C30SimCommandFunctions* commandInfo =
      C30SimBoardCommandTable(c30simptr->board);
  if ( commandInfo ) {
    C30SimCommandFunctions* funcCommandInfo =
      C30SimSearchForFunction(input, commandInfo);
    if (funcCommandInfo) {
      const char* restOfArgs = &input[funcCommandInfo->length];
      while (*restOfArgs && isspace(*restOfArgs)) restOfArgs++;
      status = (*funcCommandInfo->function)(restOfArgs, c30simptr);
    }
    else if (strchr(input, '=') != 0) {
      status = modifyCommand(input, c30simptr, TRUE);
    }
    else {
      status = C30SIM_UNRECOGNIZED_COMMAND;
    }
  }
  else {
    status = C30SIM_UNRECOGNIZED_BOARD;
  }

  return status;
}
