Use 'ao-dbg' instead of 's51' to communicate with TeleMetrum
[fw/sdcc] / debugger / mcs51 / sdcdb.c
index 5f764b301ccb459ff742c2a85a8cbb122a88454a..8df52526574ae2c77fc29e30d94becfd3d10f9d0 100644 (file)
    what you give them.   Help stamp out software-hoarding!
 -------------------------------------------------------------------------*/
 
+#define link unix_link
+#define _GNU_SOURCE
+#include <unistd.h>
+#undef link
 #include "sdcdb.h"
 #include "symtab.h"
 #include "simi.h"
 #include "break.h"
 #include "cmd.h"
 #include "newalloc.h"
+#if defined HAVE_LIBREADLINE && HAVE_LIBREADLINE != -1
+#define HAVE_READLINE_COMPLETITION  1
+#endif
+#ifdef HAVE_LIBREADLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif  /* HAVE_LIBREADLINE */
 
 #ifdef SDCDB_DEBUG
 int   sdcdbDebug = 0;
@@ -48,161 +59,219 @@ short showfull = 0;
 char userinterrupt = 0;
 char nointerrupt = 0;
 char contsim = 0;
-char *ssdirl = DATADIR LIB_DIR_SUFFIX ":" DATADIR LIB_DIR_SUFFIX "/small" ;
+char *ssdirl = DATADIR LIB_DIR_SUFFIX ":" DATADIR LIB_DIR_SUFFIX DIR_SEPARATOR_STRING "small" ;
 char *simArgs[40];
 int nsimArgs = 0;
 char model_str[20];
 /* fake filename & lineno to make linker */
 char *filename=NULL;
+char *current_directory;
 int lineno = 0;
 int fatalError = 0;
 
 static void commandLoop(FILE *cmdfile);
+#ifdef HAVE_READLINE_COMPLETITION
+char *completionCmdSource(const char *text, int state);
+char *completionCmdFile(const char *text, int state);
+char *completionCmdInfo(const char *text, int state);
+char *completionCmdShow(const char *text, int state);
+char *completionCmdListSymbols(const char *text, int state);
+char *completionCmdPrintType(const char *text, int state);
+char *completionCmdPrint(const char *text, int state);
+char *completionCmdDelUserBp(const char *text, int state);
+char *completionCmdUnDisplay(const char *text, int state);
+char *completionCmdSetUserBp(const char *text, int state);
+char *completionCmdSetOption(const char *text, int state);
+#else
+#define completionCmdSource NULL
+#define completionCmdFile NULL
+#define completionCmdInfo NULL
+#define completionCmdShow NULL
+#define completionCmdListSymbols NULL
+#define completionCmdPrintType NULL
+#define completionCmdPrint NULL
+#define completionCmdDelUserBp NULL
+#define completionCmdUnDisplay NULL
+#define completionCmdSetUserBp NULL
+#define completionCmdSetOption NULL
+#endif /* HAVE_READLINE_COMPLETITION */
 
 /* command table */
 struct cmdtab
 {
     char      *cmd ;  /* command the user will enter */
     int (*cmdfunc)(char *,context *);   /* function to execute when command is entered */
+#ifdef HAVE_READLINE_COMPLETITION
+    rl_compentry_func_t *completion_func;
+#else
+    void *dummy;
+#endif  /* HAVE_READLINE_COMPLETITION */
     char *htxt ;    /* short help text */
 
 } cmdTab[] = {
     /* NOTE:- the search is done from the top, so "break" should
        precede the synonym "b" */
     /* break point */
-    { "break"    ,  cmdSetUserBp  ,
+    { "break"    ,  cmdSetUserBp  , completionCmdSetUserBp,
       "{b}reak\t\t\t [LINE | FILE:LINE | FILE:FUNCTION | FUNCTION | *<address>]\n",
     },
-    { "tbreak"   ,  cmdSetTmpUserBp ,
+    { "tbreak"   ,  cmdSetTmpUserBp , completionCmdSetUserBp/*same as "break"*/,
       "tbreak\t\t\t [LINE | FILE:LINE | FILE:FUNCTION | FUNCTION | *<address>]\n",
     },
-    { "b"        ,  cmdSetUserBp  , NULL },
+    { "b"        ,  cmdSetUserBp  , completionCmdSetUserBp ,  NULL,},
 
-    { "jump"   ,  cmdJump ,
+    { "jump"   ,  cmdJump , NULL,
       "jump\t\t\tContinue program being debugged at specified line or address\n [LINE | FILE:LINE | *<address>]\n",
     },
-    { "clear"    ,  cmdClrUserBp  ,
+    { "clear"    ,  cmdClrUserBp  , completionCmdSetUserBp/*same as "break"*/,
       "{cl}ear\t\t\t [LINE | FILE:LINE | FILE:FUNCTION | FUNCTION]\n"
     },
-    { "cl"       ,  cmdClrUserBp  , NULL },
+    { "cl"       ,  cmdClrUserBp  , completionCmdSetUserBp/*same as "break"*/ , NULL,},
 
-    { "continue" ,  cmdContinue   ,
+    { "continue" ,  cmdContinue   ,  NULL,
       "{c}ontinue\t\t Continue program being debugged, after breakpoint.\n"
     },
-    { "condition" ,  cmdCondition   ,
+    { "condition" ,  cmdCondition   ,  completionCmdDelUserBp/*same as "delete"*/,
       "condition brkpoint_number expr\t\tSet condition for breakpoint.\n"
     },
-    { "ignore" ,  cmdIgnore  ,
-      "brkpoint_number count\t\tSet ignore count for breakpoint.\n"
+    { "ignore" ,  cmdIgnore  ,  completionCmdDelUserBp/*same as "delete"*/,
+      "ignore brkpoint_number count\t\tSet ignore count for breakpoint.\n"
     },
-    { "commands" ,  cmdCommands  ,
+    { "commands" ,  cmdCommands  ,  completionCmdDelUserBp/*same as "delete"*/,
       "commands [brkpoint_number]\t\tSetting commands for breakpoint.\n"
     },
-    { "c"        ,  cmdContinue   , NULL },
+    { "c"        ,  cmdContinue   , NULL ,  NULL,},
 
-    { "disassemble",cmdDisasmF    , "disassemble [startaddr [endaddress]]\tdisassemble asm commands\n" },
-    { "delete" ,  cmdDelUserBp  ,
+    { "disassemble",cmdDisasmF    ,  NULL, "disassemble [startaddr [endaddress]]\tdisassemble asm commands\n" },
+    { "delete" ,  cmdDelUserBp  , completionCmdDelUserBp,
       "{d}elete n\t\t clears break point number n\n"
     },
-    { "display"    ,  cmdDisplay     ,
+    { "display"    ,  cmdDisplay     , completionCmdPrint/*same as "print"*/,
       "display [/<fmt>] [<variable>]\t print value of given variable each time the program stops\n"
     },
-    { "undisplay"  ,  cmdUnDisplay   ,
+    { "undisplay"  ,  cmdUnDisplay   , completionCmdUnDisplay,
       "undisplay [<variable>]\t dont display this variable or all\n"
     },
-    { "down"     ,  cmdDown      ,
+    { "down"     ,  cmdDown      , NULL,
       "down\t\tSelect and print stack frame called by this one.\nAn argument says how many frames down to go.\n"
     },
-    { "up"       ,  cmdUp      ,
+    { "up"       ,  cmdUp      , NULL,
       "up\t\tSelect and print stack frame that called this one.\nAn argument says how many frames up to go.\n"
     },
-    { "d"        ,  cmdDelUserBp  , NULL },
+    { "d"        ,  cmdDelUserBp  , completionCmdDelUserBp, NULL },
 
-    { "info"     ,  cmdInfo       ,
-      "info <break stack frame registers all-registers>\n"
+    { "info"     ,  cmdInfo       , completionCmdInfo,
+      "info <break stack frame registers all-registers line source functions symbols variables>\n"
       "\t list all break points, call-stack, frame or register information\n"
     },
 
-    { "listasm"  ,  cmdListAsm    ,
+    { "listasm"  ,  cmdListAsm    , NULL,
       "listasm {la}\t\t list assembler code for the current C line\n"
     },
-    { "la"       ,  cmdListAsm    , NULL },
-    { "ls"       ,  cmdListSymbols  , "ls,lf,lm\t\t list symbols,functions,modules\n" },
-    { "lf"       ,  cmdListFunctions, NULL },
-    { "lm"       ,  cmdListModules  , NULL },
-    { "list"     ,  cmdListSrc    ,
+    { "la"       ,  cmdListAsm    , NULL, NULL },
+    { "ls"       ,  cmdListSymbols  , completionCmdListSymbols, "ls,lf,lm\t\t list symbols,functions,modules\n" },
+    { "lf"       ,  cmdListFunctions, completionCmdListSymbols, NULL },
+    { "lm"       ,  cmdListModules  , completionCmdListSymbols, NULL },
+    { "list"     ,  cmdListSrc    , completionCmdSetUserBp/*same as "break"*/,
       "{l}ist\t\t\t [LINE | FILE:LINE | FILE:FUNCTION | FUNCTION]\n"
     },
-    { "l"        ,  cmdListSrc    , NULL },
-    { "show"     ,  cmdShow       ,
+    { "l"        ,  cmdListSrc    , completionCmdSetUserBp/*same as "break"*/, NULL },
+    { "show"     ,  cmdShow       , completionCmdShow,
       "show"
       " <copying warranty>\t copying & distribution terms, warranty\n"
     },
-    { "set"      ,  cmdSetOption  , "set <srcmode>\t\t toggle between c/asm.\nset variable <var> = >value\t\tset variable to new value\n" },
-    { "stepi"    ,  cmdStepi      ,
+    { "set"      ,  cmdSetOption  , completionCmdSetOption, "set <srcmode>\t\t toggle between c/asm.\nset variable <var> = >value\t\tset variable to new value\n" },
+    { "stepi"    ,  cmdStepi      , NULL,
       "stepi\t\t\tStep one instruction exactly.\n"
     },
-    { "step"     ,  cmdStep       ,
+    { "step"     ,  cmdStep       , NULL,
       "{s}tep\t\t\tStep program until it reaches a different source line.\n"
     },
-    { "source"   ,  cmdSource      ,
+    { "source"   ,  cmdSource      , completionCmdSource,
       "source <FILE>\t\t\tRead commands from a file named FILE.\n"
     },
-    { "s"        ,  cmdStep       , NULL },
-    { "nexti"    ,  cmdNexti      ,
+    { "s"        ,  cmdStep       , NULL, NULL },
+    { "nexti"    ,  cmdNexti      , NULL,
       "nexti\t\t\tStep one instruction, but proceed through subroutine calls.\n"
     },
-    { "next"     ,  cmdNext       ,
+    { "next"     ,  cmdNext       , NULL,
       "{n}ext\t\t\tStep program, proceeding through subroutine calls.\n"
     },
-    { "n"        ,  cmdNext       , NULL },
-    { "run"      ,  cmdRun        ,
+    { "n"        ,  cmdNext       , NULL, NULL },
+    { "run"      ,  cmdRun        , NULL,
       "{r}un\t\t\tStart debugged program. \n"
     },
-    { "r"        ,  cmdRun        , NULL },
-    { "ptype"    ,  cmdPrintType  ,
+    { "r"        ,  cmdRun        , NULL, NULL },
+    { "ptype"    ,  cmdPrintType  , completionCmdPrintType,
       "{pt}ype <variable>\tprint type information of a variable\n"
     },
-    { "pt"       ,  cmdPrintType  , NULL },
-    { "print"    ,  cmdPrint      ,
+    { "pt"       ,  cmdPrintType  , NULL, NULL },
+    { "print"    ,  cmdPrint      , completionCmdPrintType,
       "{p}rint <variable>\t print value of given variable\n"
     },
-    { "output"   ,  cmdOutput      ,
+    { "output"   ,  cmdOutput      , completionCmdPrint/*same as "print"*/,
       "output <variable>\t print value of given variable without $ and newline \n"
     },
-    { "p"        ,  cmdPrint      , NULL },
-    { "file"     ,  cmdFile       ,
+    { "p"        ,  cmdPrint      , completionCmdPrintType, NULL },
+    { "file"     ,  cmdFile       , completionCmdFile,
       "file <filename>\t\t load symbolic information from <filename>\n"
     },
-    { "frame"    ,  cmdFrame      ,
+    { "frame"    ,  cmdFrame      , NULL,
       "{fr}ame\t\t print information about the current Stack\n"
     },
-    { "finish"   ,  cmdFinish     ,
+    { "finish"   ,  cmdFinish     , NULL,
       "{fi}nish\t\t execute till return of current function\n"
     },
-    { "fi"       ,  cmdFinish     , NULL },
-    { "where"    ,  cmdWhere      , "where\t\t print stack\n" },
-    { "fr"       ,  cmdFrame      , NULL },
-    { "f"        ,  cmdFrame      , NULL },
-    { "x /i"     ,  cmdDisasm1    , "x\t\t disassemble one asm command\n" },
-    { "!"        ,  cmdSimulator  ,
+    { "fi"       ,  cmdFinish     , NULL, NULL },
+    { "where"    ,  cmdWhere      , NULL, "where\t\t print stack\n" },
+    { "fr"       ,  cmdFrame      , NULL, NULL },
+    { "f"        ,  cmdFrame      , NULL, NULL },
+    { "x /i"     ,  cmdDisasm1    , NULL, "x\t\t disassemble one asm command\n" },
+    { "!"        ,  cmdSimulator  , NULL,
       "!<simulator command>\t send a command directly to the simulator\n"
     },
-    { "."        ,  cmdSimulator  ,
+    { "."        ,  cmdSimulator  , NULL,
       ".{cmd}\t switch from simulator or debugger command mode\n"
     },
-    { "help"     ,  cmdHelp       ,
+    { "help"     ,  cmdHelp       , NULL,
       "{h|?}elp\t [CMD_NAME | 0,1,2,3(help page)] (general help or specific help)\n"
     },
-    { "?"        ,  cmdHelp       , NULL },
-    { "h"        ,  cmdHelp       , NULL },
+    { "?"        ,  cmdHelp       , NULL, NULL },
+    { "h"        ,  cmdHelp       , NULL, NULL },
 
-    { "quit"     ,  cmdQuit       ,
+    { "quit"     ,  cmdQuit       , NULL,
       "{q}uit\t\t\t \"Watch me now. Iam going Down. My name is Bobby Brown\"\n"
     },
-    { "q"        ,  cmdQuit       , NULL }
+    { "q"        ,  cmdQuit       , NULL, NULL }
 };
 
+/*-----------------------------------------------------------------*/
+/* trimming functions                                              */
+/*-----------------------------------------------------------------*/
+char *trim_left(char *s)
+{
+    while (isspace(*s))
+        ++s;
+
+    return s;
+}
+
+char *trim_right(char *s)
+{
+    char *p = &s[strlen(s) - 1];
+
+    while (p >= s && isspace(*p))
+      --p;
+    *++p = '\0';
+
+    return s;
+}
+
+char *trim(char *s)
+{
+    return trim_right(trim_left(s));
+}
+
 /*-----------------------------------------------------------------*/
 /* gc_strdup - make a string duplicate garbage collector aware     */
 /*-----------------------------------------------------------------*/
@@ -318,7 +387,7 @@ char *searchDirsFname (char *fname)
 {
     char *dirs , *sdirs;
     FILE *rfile = NULL;
-    char buffer[128];
+    char buffer[1024];
 
     /* first try the current directory */
     if ((rfile = fopen(fname,"r"))) {
@@ -360,7 +429,7 @@ FILE *searchDirsFopen(char *fname)
 {
     char *dirs , *sdirs;
     FILE *rfile = NULL;
-    char buffer[128];
+    char buffer[1024];
 
     /* first try the current directory */
     if ((rfile = fopen(fname,"r")))
@@ -433,7 +502,7 @@ static void loadModules (void)
         switch (loop->type) {
         /* for module records do */
         case MOD_REC:
-            currMod = parseModule(loop->line,TRUE);
+            currMod = parseModule(loop->line, TRUE);
             currModName = currMod->name ;
 
             currMod->cfullname = searchDirsFname(currMod->c_name);
@@ -624,38 +693,17 @@ static void functionPoints (void)
     }
 }
 
-
-/*-----------------------------------------------------------------*/
-/* setEntryExitBP - set the entry & exit Break Points for functions*/
-/*-----------------------------------------------------------------*/
-DEFSETFUNC(setEntryExitBP)
-{
-    function *func = item;
-
-    if (func->sym && func->sym->addr && func->sym->eaddr) {
-
-        /* set the entry break point */
-        setBreakPoint (func->sym->addr , CODE , FENTRY ,
-            fentryCB ,func->mod->c_name , func->entryline);
-
-        /* set the exit break point */
-        setBreakPoint (func->sym->eaddr , CODE , FEXIT  ,
-            fexitCB  ,func->mod->c_name , func->exitline );
-    }
-
-    return 0;
-}
-
 /*-----------------------------------------------------------------*/
 /* cmdFile - load file into the debugger                           */
 /*-----------------------------------------------------------------*/
 int cmdFile (char *s,context *cctxt)
 {
     FILE *cdbFile;
-    char buffer[128];
+    char buffer[1024];
     char *bp;
 
-    while (isspace(*s)) s++;
+    s = trim_left(s);
+
     if (!*s) {
         fprintf(stdout,"No exec file now.\nNo symbol file now.\n");
         return 0;
@@ -690,7 +738,11 @@ int cmdFile (char *s,context *cctxt)
     specialFunctionRegs();
 
     /* start the simulator & setup connection to it */
+#ifdef _WIN32
+    if (INVALID_SOCKET == sock)
+#else
     if ( sock == -1 )
+#endif
         openSimulator((char **)simArgs,nsimArgs);
     fprintf(stdout,"%s",simResponse());
     /* now send the filename to be loaded to the simulator */
@@ -702,7 +754,7 @@ int cmdFile (char *s,context *cctxt)
     /*set the break points
        required by the debugger . i.e. the function entry
        and function exit break points */
-    applyToSet(functions,setEntryExitBP);
+//    applyToSet(functions,setEntryExitBP);
 
     setMainContext();
     return 0;
@@ -714,13 +766,8 @@ int cmdFile (char *s,context *cctxt)
 int cmdSource (char *s, context *cctxt)
 {
     FILE *cmdfile;
-    char *bp = s+strlen(s) -1;
-
-    while (isspace(*s))
-      ++s;
 
-    while (isspace(*bp)) bp--;
-    *++bp = '\0';
+    s = trim(s);
 
     if (!( cmdfile = searchDirsFopen(s)))
     {
@@ -741,8 +788,8 @@ int cmdHelp (char *s, context *cctxt)
     int endline = 999;
     int startline = 0;
 
-    while (isspace(*s))
-        ++s;
+    s = trim_left(s);
+
     if (isdigit(*s)) {
         endline = ((*s - '0') * 20) + 20;
         if (endline > 0)
@@ -783,6 +830,16 @@ int cmdHelp (char *s, context *cctxt)
 static char cmdbuff[MAX_CMD_LEN];
 static int sim_cmd_mode = 0;
 
+char *
+canonname(char *file)
+{
+    static char        buffer[1024];
+    if (*file == '/')
+       return file;
+    sprintf(buffer,"%s/%s", current_directory, file);
+    return buffer;
+}
+
 /*-----------------------------------------------------------------
  interpretCmd - interpret and do the command.  Return 0 to continue,
    return 1 to exit program.
@@ -795,7 +852,7 @@ int interpretCmd (char *s)
 
     /* if nothing & previous command exists then
        execute the previous command again */
-    if (*s == '\n' && pcmd)
+    if ((*s == '\n' || *s == '\0') && pcmd)
         strcpy(s,pcmd);
 
     /* if previous command exists & is different
@@ -808,8 +865,8 @@ int interpretCmd (char *s)
     } else
         pcmd = strdup(s);
 
-    /* lookup the command table and do the task required */
-    strtok(s,"\n");
+    /* trim trailing blanks */
+    s = trim_right(s);
 
     if (sim_cmd_mode) {
         if (strcmp(s,".") == 0) {
@@ -848,11 +905,11 @@ int interpretCmd (char *s)
                 showfull = 0;
                 if (srcMode == SRC_CMODE)
                     fprintf(stdout,"\032\032%s:%d:1:beg:0x%08x\n",
-                        currCtxt->func->mod->cfullname,
+                        canonname(currCtxt->func->mod->cfullname),
                         currCtxt->cline+1,currCtxt->addr);
                 else
                     fprintf(stdout,"\032\032%s:%d:1:beg:0x%08x\n",
-                        currCtxt->func->mod->afullname,
+                        canonname(currCtxt->func->mod->afullname),
                         currCtxt->asmline,currCtxt->addr);
                 displayAll(currCtxt);
             }
@@ -897,13 +954,533 @@ void stopCommandList()
     stopcmdlist = 1;
 }
 
+#ifdef HAVE_READLINE_COMPLETITION
+// helper function for doing readline completion.
+// input: toknum=index of token to find (0=first token)
+// output: *start=first character index of the token,
+//                or the index of '\0'
+//         *end=first blank character right after the token,
+//                or the index of '\0'
+// return value: 0=token not found, 1=token found
+int completionHelper_GetTokenNumber(int toknum, int *start, int *end)
+{
+    int tok_index;
+    const char *p = rl_line_buffer;
+
+    tok_index = 0;
+    *start = *end = 0;
+    while (p[*end] != 0)
+    {
+        // start = skip blanks from end
+        *start = *end;
+        while (p[*start] && isspace( p[*start] ))
+            (*start)++;
+
+        // end = skip non-blanks from start
+        *end = *start;
+        while (p[*end] && !isspace( p[*end] ))
+            (*end)++;
+
+        if (tok_index == toknum)
+            return 1;   // found
+
+        tok_index++;
+    }
+
+    return 0;   // not found
+}
+
+// helper function for doing readline completion.
+// returns the token number that we were asked to complete.
+// 0=first token (command name), 1=second token...
+int completionHelper_GetCurrTokenNumber()
+{
+    int toknum, start, end;
+
+    toknum = start = end = 0;
+    while (1)
+    {
+        if (!completionHelper_GetTokenNumber(toknum, &start, &end))
+            return toknum;
+
+        if (rl_point <= end)
+            return toknum;
+
+        toknum++;
+    }
+}
+
+// exapmle for vallist on entry:
+//          "copying\0warranty\0";
+char *completionCompleteFromStrList(const char *text, int state, char *vallist)
+{
+    static char *ptr;
+    int len;
+
+    if (state == 0)
+        ptr = vallist;
+    else
+        ptr += strlen(ptr)+1;
+
+    len = strlen(text);
+    while (*ptr)
+    {
+        if ( (len < strlen(ptr)) &&
+              !strncmp(text, ptr, len) )
+            return strdup(ptr);
+
+        ptr += strlen(ptr)+1;
+    }
+
+    return NULL;
+}
+
+// readline library completion function.
+// completes from the list of all sdcdb command.
+char *completionCommandsList(const char *text, int state)
+{
+    static int i = 0;
+
+    if (state == 0) // new completion?
+    {   // yes, only complete if this is the first token on the line.
+        int ok = 0; // try to complete this request?
+        char *p = rl_line_buffer;
+
+        // skip blanks
+        while (p && isspace(*p))
+        {
+            if (p-rl_line_buffer == rl_point)
+                ok = 1;
+            p++;
+        }
+
+        while (p && !isspace(*p))
+        {
+            if (p-rl_line_buffer == rl_point)
+                ok = 1;
+            p++;
+        }
+
+        if (p-rl_line_buffer == rl_point)
+            ok = 1;
+
+        if ( !ok )
+            return NULL; // no more completions
+
+        i = 0;  // ok, gonna complete. initialize static variable.
+    }
+    else i++;
+
+    for (; i < (sizeof(cmdTab)/sizeof(struct cmdtab)) ; i++)
+    {
+        int len = strlen(text);
+        if (len <= strlen(cmdTab[i].cmd))
+        {
+            if (strncmp(text,cmdTab[i].cmd,len) == 0)
+                return strdup(cmdTab[i].cmd);
+        }
+    }
+
+    return NULL; // no more completions
+}
+
+// readline library completion function.
+// completes from the list of symbols.
+char *completionSymbolName(const char *text, int state)
+{
+    static symbol *sy;
+
+    if (state == 0) // new completion?
+        sy = setFirstItem(symbols); // yes
+    else
+      sy = setNextItem(symbols);
+
+    for (; sy != NULL; )
+    {
+        int len = strlen(text);
+        if (len <= strlen(sy->name))
+        {
+            if (strncmp(text,sy->name,len) == 0)
+                return strdup(sy->name);
+        }
+
+        sy = setNextItem(symbols);
+    }
+    return NULL;
+}
+
+// readline library completion function.
+// completes from the list known functions.
+// module_flag - if false, ignore function module name
+//               if true, compare against module_name:fnction_name
+char *completionFunctionName(const char *text, int state, int module_flag)
+{
+    static function *f;
+
+    if (state == 0) // new completion?
+        f = setFirstItem(functions); // yes
+    else
+      f = setNextItem(functions);
+
+    for (; f != NULL; )
+    {
+        int text_len = strlen(text);
+
+        if (!module_flag)
+        {
+            if (text_len <= strlen(f->sym->name) &&
+                !strncmp(text,f->sym->name,text_len))
+                return strdup(f->sym->name);
+        }
+        else
+        {
+            int modname_len = strlen(f->mod->c_name);
+            int funcname_len = strlen(f->sym->name);
+            char *functext = malloc(modname_len+funcname_len+2);
+            //assert(functext);
+            strcpy(functext,f->mod->c_name);
+            strcat(functext,":");
+            strcat(functext,f->sym->name);
+            if (text_len <= strlen(functext) &&
+                !strncmp(text,functext,text_len))
+                return functext;
+            else
+                free(functext);
+        }
+        f = setNextItem(functions);
+    }
+    return NULL;
+}
+
+// readline library completion function.
+// completes from the list known modules.
+char *completionModuleName(const char *text, int state)
+{
+    static module *m;
+
+    if (state == 0) // new completion?
+        m = setFirstItem(modules); // yes
+    else
+      m = setNextItem(modules);
+
+    for (; m != NULL; )
+    {
+        int len = strlen(text);
+        if ( (len <= strlen(m->c_name)) &&
+             !strncmp(text,m->c_name,len) )
+            return strdup(m->c_name);
+
+        if ( (len <= strlen(m->asm_name)) &&
+             (strncmp(text,m->asm_name,len) == 0) )
+            return strdup(m->asm_name);
+
+        m = setNextItem(modules);
+    }
+    return NULL;
+}
+
+// readline completion function for "file" command
+char *completionCmdFile(const char *text, int state)
+{
+    if (state == 0)
+    {
+        if (completionHelper_GetCurrTokenNumber() != 1)
+            return NULL;
+    }
+
+    // we use filename_completion_function() from the readline library.
+    return rl_filename_completion_function(text, state);
+}
+
+// readline completion function for "source" command
+char *completionCmdSource(const char *text, int state)
+{
+    return completionCmdFile(text, state);
+}
+
+// readline completion function for "info" command
+char *completionCmdInfo(const char *text, int state)
+{
+    static char *ptr;
+
+    if (state == 0)
+    {
+        if (completionHelper_GetCurrTokenNumber() != 1)
+            return NULL;
+    }
+
+    return completionCompleteFromStrList(text, state,
+            "break\0stack\0frame\0registers\0all-registers\0"
+            "line\0source\0functions\0symbols\0variables\0");
+}
+
+// readline completion function for "show" command
+char *completionCmdShow(const char *text, int state)
+{
+    static char *ptr;
+
+    if (state == 0)
+    {
+        if (completionHelper_GetCurrTokenNumber() != 1)
+            return NULL;
+    }
+    return completionCompleteFromStrList(text, state, "copying\0warranty\0");
+}
+
+// readline completion function for "la" command
+char *completionCmdListSymbols(const char *text, int state)
+{
+    static char *ptr;
+
+    if (state == 0)
+    {
+        if (completionHelper_GetCurrTokenNumber() != 1)
+            return NULL;
+    }
+    return completionCompleteFromStrList(text, state, "v1\0v2\0");
+}
+
+char *completionCmdPrintType(const char *text, int state)
+{
+    if (state == 0)
+    {
+        if (completionHelper_GetCurrTokenNumber() != 1)
+            return NULL;
+    }
+    return completionSymbolName(text, state);
+}
+
+char *completionCmdPrint(const char *text, int state)
+{
+    if (state == 0)
+    {
+        int i = completionHelper_GetCurrTokenNumber();
+        if (i != 1 && i != 2)
+            return NULL;
+    }
+    return completionSymbolName(text, state);
+}
+
+char *completionCmdDelUserBp(const char *text, int state)
+{
+    static breakp *bp;
+    static int k;
+
+    if (state == 0)
+    {
+        if (completionHelper_GetCurrTokenNumber() != 1)
+            return NULL;
+
+        if (!userBpPresent)
+            return NULL;
+
+        bp = hTabFirstItem(bptable,&k);
+    }
+    else
+        bp = hTabNextItem(bptable,&k);
+
+    for ( ; bp ; bp = hTabNextItem(bptable,&k))
+    {
+        if (bp->bpType == USER || bp->bpType == TMPUSER)
+        {
+            char buff[20];
+            sprintf(buff, "%d", bp->bpnum);
+            return strdup(buff);
+        }
+    }
+
+    return NULL;
+}
+
+// readline completion function for "undisplay" command
+char *completionCmdUnDisplay(const char *text, int state)
+{
+    static dsymbol *dsym;
+
+    if (state == 0)
+    {
+        if (completionHelper_GetCurrTokenNumber() != 1)
+            return NULL;
+        dsym = setFirstItem(dispsymbols);
+    }
+
+    if (dsym)
+    {
+        char buff[30];
+        sprintf(buff, "%d", dsym->dnum);
+        dsym = setNextItem(dispsymbols);
+        return strdup(buff);
+    }
+    return NULL;
+}
+
+char *completionCmdSetUserBp(const char *text, int state)
+{
+    static int internal_state; // 0=calling completionFunctionName(text, state, 0)
+                               // 1=calling completionFunctionName(text, 1, 1)
+    if (state == 0)
+    {
+        if (completionHelper_GetCurrTokenNumber() != 1)
+            return NULL;
+
+        internal_state = 0;
+    }
+    if (internal_state == 0)
+    {
+        char *p = completionFunctionName(text, state, 0);
+        if (p)
+            return p;
+        internal_state = 1;
+        return completionFunctionName(text, 0, 1);
+    }
+    else
+    {
+        return completionFunctionName(text, 1, 1);
+    }
+}
+
+char *completionCmdSetOption(const char *text, int state)
+{
+    static char *ptr;
+    static int currtok;
+
+    if (state == 0)
+    {
+        int start,end;
+
+        currtok = completionHelper_GetCurrTokenNumber();
+
+        if (currtok == 2 || currtok == 3)
+        {
+            // make sure token 1 == "variable"
+            completionHelper_GetTokenNumber(1, &start, &end);
+            if (end - start != 8 ||
+                strncmp(rl_line_buffer+start,"variable",8))
+                return NULL;
+        }
+        else if (currtok != 1)
+        {
+            return NULL;
+        }
+    }
+
+    switch (currtok)
+    {
+        case 1:
+            return completionCompleteFromStrList(text, state,
+#ifdef SDCDB_DEBUG
+                "debug\0"
+#endif
+                "srcmode\0listsize\0variable\0");
+        case 2:
+            return completionSymbolName(text, state);
+
+        case 3:
+            return completionCompleteFromStrList(text, state, "=\0");
+    }
+}
+
+// our main readline completion function
+// calls the other completion functions as needed.
+char *completionMain(const char *text, int state)
+{
+    static rl_compentry_func_t *compl_func;
+    int i, start, end, len;
+
+    if (state == 0) // new completion?
+    {
+        compl_func = NULL;
+
+        if (completionHelper_GetCurrTokenNumber() == 0)
+            compl_func = &completionCommandsList;
+        else
+        {   // not completing first token, find the right completion
+            // function according to the first token the user typed.
+            completionHelper_GetTokenNumber(0, &start, &end);
+            len = end-start;
+
+            for (i=0; i < (sizeof(cmdTab)/sizeof(struct cmdtab)) ; i++)
+            {
+                if (!strncmp(rl_line_buffer+start,cmdTab[i].cmd,len) &&
+                    cmdTab[i].cmd[len] == '\0')
+                {
+                    compl_func = cmdTab[i].completion_func;
+                    break;
+                }
+            }
+        }
+        if (!compl_func)
+            return NULL;
+    }
+
+    return (*compl_func)(text,state);
+}
+#endif  /* HAVE_READLINE_COMPLETITION */
+
 /*-----------------------------------------------------------------*/
 /* commandLoop - the main command loop or loop over command file   */
 /*-----------------------------------------------------------------*/
 static void commandLoop(FILE *cmdfile)
 {
     char *line, save_ch, *s;
+    char *line_read;
+
+#ifdef HAVE_LIBREADLINE
+    FILE *old_rl_instream, *old_rl_outstream;
     actualcmdfile = cmdfile;
+
+#ifdef HAVE_READLINE_COMPLETITION
+    rl_completion_entry_function = completionMain;
+#endif  /* HAVE_READLINE_COMPLETITION */
+    rl_readline_name = "sdcdb"; // Allow conditional parsing of the ~/.inputrc file.
+
+    // save readline's input/output streams
+    // this is done to support nested calls to commandLoop()
+    // i wonder if it works...
+    old_rl_instream = rl_instream;
+    old_rl_outstream = rl_outstream;
+
+    // set new streams for readline
+    if ( cmdfile == stdin )
+        rl_instream = rl_outstream = NULL;  // use stdin/stdout pair
+    else
+        rl_instream = rl_outstream = cmdfile;
+
+      while (1)
+      {
+          if ( cmdfile == stdin )
+          {
+              if (sim_cmd_mode)
+                  line_read = (char*)readline ("(sim) ");
+              else
+                  line_read = (char*)readline ("(sdcdb) ");
+          }
+          else
+              line_read = (char*)readline ("");
+
+        if (line_read)
+        {
+            /* If the line has any text in it,
+               save it on the history. */
+            if (line_read && *line_read)
+              add_history (line_read);
+
+             // FIX: readline returns malloced string.
+             //   should check the source to verify it can be used
+             //    directly. for now - just copy it to cmdbuff.
+            strcpy(cmdbuff,line_read);
+#if defined(_WIN32) || defined(HAVE_RL_FREE)
+            rl_free(line_read);
+#else
+            free(line_read);
+#endif
+            line_read = NULL;
+        }
+        else
+            break;  // EOF
+#else
+    actualcmdfile = cmdfile;
+
     while (1)
     {
         if ( cmdfile == stdin )
@@ -914,14 +1491,14 @@ static void commandLoop(FILE *cmdfile)
                 fprintf(stdout,"(sdcdb) ");
             fflush(stdout);
         }
-
         //fprintf(stderr,"commandLoop actualcmdfile=%p cmdfile=%p\n",
         //        actualcmdfile,cmdfile);
         if (fgets(cmdbuff,sizeof(cmdbuff),cmdfile) == NULL)
             break;
+#endif  /* HAVE_LIBREADLINE */
 
-        if (interpretCmd(cmdbuff))
-            break;
+          if (interpretCmd(cmdbuff))
+              break;
 
         while ( actualcmds )
         {
@@ -951,6 +1528,11 @@ static void commandLoop(FILE *cmdfile)
             }
         }
     }
+#ifdef HAVE_LIBREADLINE
+    // restore readline's input/output streams
+    rl_instream = old_rl_instream;
+    rl_outstream = old_rl_outstream;
+#endif  /* HAVE_LIBREADLINE */
 }
 
 /*-----------------------------------------------------------------*/
@@ -1014,7 +1596,7 @@ static void parseCmdLine (int argc, char **argv)
             }
 
             if (strncmp(argv[i],"-cd=",4) == 0) {
-                chdir(argv[i][4]);
+                chdir(argv[i]+4);
                 continue;
             }
 
@@ -1071,8 +1653,10 @@ static void parseCmdLine (int argc, char **argv)
 
             /* serial port */
             if ( (strcmp(argv[i],"-S") == 0) ||
-                (strcmp(argv[i],"-s") == 0)) {
-                simArgs[nsimArgs++] = strdup(argv[i]);
+                (strcmp(argv[i],"-s") == 0) ||
+                (strcmp(argv[i],"-T") == 0) ||
+                (strcmp(argv[i],"--tty") == 0)) {
+               simArgs[nsimArgs++] = "--tty";
                 simArgs[nsimArgs++] = strdup(argv[++i]);
                 continue ;
             }
@@ -1095,7 +1679,15 @@ static void parseCmdLine (int argc, char **argv)
                 continue ;
             }
 
-            filename = strtok(argv[i],".");
+            if (-1 != access(argv[i], 0)) {
+                /* file exists: strip the cdb or ihx externsion */
+                char *p = strrchr(argv[i], '.');
+
+                if (NULL != p &&
+                    (0 == strcmp(p, ".cdb") || 0 == strcmp(p, ".ihx")))
+                    *p = '\0';
+            }
+            filename = argv[i];
 
         }
     }
@@ -1167,7 +1759,8 @@ int main ( int argc, char **argv)
     printVersionInfo();
     printf("WARNING: SDCDB is EXPERIMENTAL.\n");
 
-    simArgs[nsimArgs++] = "s51";
+    current_directory = get_current_dir_name();
+    simArgs[nsimArgs++] = "ao-dbg";
     simArgs[nsimArgs++] = "-P";
     simArgs[nsimArgs++] = "-r 9756";
     /* parse command line */