From: oharboe Date: Sun, 6 Jul 2008 19:17:43 +0000 (+0000) Subject: src/helper/configuration.h X-Git-Tag: v0.1.0~542 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=dc796a209113c3fdc27de0211edcaa67faed7b5f;p=openocd src/helper/configuration.h - Log output handlers now get a "const char *line" - Added "const" to parameter. src/helper/command.c src/helper/command.h - New function: 'command_output_text()' - Log output handlers now get a "const char *line" src/helper/options.c - Log output handlers now get a "const char *line" src/server/telnet_server.c - DO not transmit NULL bytes via TELNET. - Log output handlers now get a "const char *line" src/server/gdb_server.c - Log output handlers now get a "const char *line" *** THIS INCLUDES PORTIONS OF A PATCH FROM Oyvind sent previously to the mailing list for TCL users try src/target/target.c *** THIS INCLUDES PORTIONS OF A PATCH FROM Oyvind sent previously to the mailing list for TCL users try src/target/target.h *** THIS INCLUDES PORTIONS OF A PATCH FROM Oyvind sent previously to the mailing list for TCL users try src/openocd.c - **MAJOR** Work: New TCL/Jim function: mem2array - **MAJOR** Work: Redirect Tcl/Jim stdio output to remote users. - Previously: TCL output did not go to GDB. - Previously: TCL output did not go to TELNET - Previously: TCL output only goes to control console. - This fixes that problem. + Created callbacks: +openocd_jim_fwrite() +openocd_jim_fread() +openocd_jim_vfprintf() +openocd_jim_fflush() +openocd_jim_fgets() src/Makefile.am - New TCL files. - Future note: This should be more automated. As the list of 'tcl' files grows maintaning this list will suck. src/Jim.c - ** THIS INCLUDES A PREVIOUS PATCH I SENT EARLIER ** - that impliments many [format] specifies JIM did not support. - Jim_FormatString() - **MAJOR** work. - Previously only supported "%s" "%d" and "%c" - And what support existed had bugs. - NEW: *MANY* formating parameters are now supported. - TODO: The "precision" specifier is not supported. ** NEW ** This patch. - Jim_StringToWide() test if no conversion occured. - Jim_StringToIndex() test if no conversion occured. - Jim_StringToDouble() test if no conversion occured. ** NEW ** This Patch. Major Work. - Previously output from JIM did not get sent to GDB - Ditto: Output to Telnet session. - Above items are now fixed - By introducing callbacks new function pointers in the "interp" structure. - Helpers that call the callbacks. - New function: Jim_fprintf() - New function: Jim_vfprintf() - New function: Jim_fwrite() - New function: Jim_fread() - New function: Jim_fflush() - New function: Jim_fgets() By default: the output is to STDIO as previous. The "openocd.c" - redirects the output as needed. - Jim_Panic() - Send panic to both STDERR and the interps specified STDERR output as a 2nd choice. - Now JIM's "stdin/stdout/stderr" paramters are "void *" and are no longer "FILE *". src/Jim.h - **MAJOR** - New JIM STDIO callbacks in the "interp" structure. - change: "stdin/stdout/stderr" are now "void *" cookies. - New JIM stdio helper functions. git-svn-id: svn://svn.berlios.de/openocd/trunk@755 b42882b7-edfa-0310-969c-e2dbd0fdcd60 --- diff --git a/src/Makefile.am b/src/Makefile.am index 462b5ee4..924040e6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,4 +81,20 @@ openocd_LDADD = $(top_builddir)/src/xsvf/libxsvf.a \ -nobase_dist_pkglib_DATA = tcl/commands.tcl +nobase_dist_pkglib_DATA = \ + tcl/commands.tcl \ + tcl/bitsbytes.tcl \ + tcl/chip/atmel/at91/aic.tcl \ + tcl/chip/atmel/at91/at91sam7x128.tcl \ + tcl/chip/atmel/at91/at91sam7x256.tcl \ + tcl/chip/atmel/at91/pmc.tcl \ + tcl/chip/atmel/at91/rtt.tcl \ + tcl/chip/atmel/at91/usarts.tcl \ + tcl/cpu/arm/arm7tdmi.tcl \ + tcl/cpu/arm/arm920.tcl \ + tcl/cpu/arm/arm946.tcl \ + tcl/cpu/arm/arm966.tcl \ + tcl/memory.tcl \ + tcl/mmr_helpers.tcl \ + tcl/readable.tcl + diff --git a/src/helper/command.c b/src/helper/command.c index ed0a82e6..386572ed 100644 --- a/src/helper/command.c +++ b/src/helper/command.c @@ -621,7 +621,7 @@ int command_print_help(command_context_t* context, char* name, char** args, int return command_print_help_match(context, context->commands, name, args, argc); } -void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv) +void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv) { context->output_handler = output_handler; context->output_handler_priv = priv; @@ -720,3 +720,10 @@ int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **arg return retval; } +/* + * Local Variables: ** + * tab-width: 4 ** + * c-basic-offset: 4 ** + * End: ** + */ + diff --git a/src/helper/command.h b/src/helper/command.h index 8d50d6e6..8db1675f 100644 --- a/src/helper/command.h +++ b/src/helper/command.h @@ -48,7 +48,7 @@ typedef struct command_context_s * Returning ERROR_COMMAND_SYNTAX_ERROR will have the effect of * printing out the syntax of the command. */ - int (*output_handler)(struct command_context_s *context, char* line); + int (*output_handler)(struct command_context_s *context, const char* line); void *output_handler_priv; } command_context_t; @@ -67,7 +67,7 @@ typedef struct command_s extern command_t* register_command(command_context_t *context, command_t *parent, char *name, int (*handler)(struct command_context_s *context, char* name, char** args, int argc), enum command_mode mode, char *help); extern int unregister_command(command_context_t *context, char *name); extern int unregister_all_commands(command_context_t *context); -extern void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv); +extern void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv); extern command_context_t* copy_command_context(command_context_t* context); extern command_context_t* command_init(); extern int command_done(command_context_t *context); @@ -86,3 +86,11 @@ extern int command_run_file(command_context_t *context, FILE *file, enum command extern int fast_and_dangerous; #endif /* COMMAND_H */ + +/* + * Local Variables: ** + * tab-width: 4 ** + * c-basic-offset: 4 ** + * End: ** + */ + diff --git a/src/helper/configuration.h b/src/helper/configuration.h index 894d874c..1d315cc2 100644 --- a/src/helper/configuration.h +++ b/src/helper/configuration.h @@ -27,7 +27,7 @@ extern int parse_cmdline_args(struct command_context_s *cmd_ctx, int argc, char extern int parse_config_file(struct command_context_s *cmd_ctx); extern void add_config_command (const char *cfg); extern void add_script_search_dir (const char *dir); -extern int configuration_output_handler(struct command_context_s *context, char* line); +extern int configuration_output_handler(struct command_context_s *context, const char* line); extern FILE *open_file_from_path (char *file, char *mode); extern char *find_file(char *name); diff --git a/src/helper/options.c b/src/helper/options.c index ab36a286..afff55ba 100644 --- a/src/helper/options.c +++ b/src/helper/options.c @@ -45,7 +45,7 @@ static struct option long_options[] = {0, 0, 0, 0} }; -int configuration_output_handler(struct command_context_s *context, char* line) +int configuration_output_handler(struct command_context_s *context, const char* line) { LOG_INFO_N(line); diff --git a/src/jim.c b/src/jim.c index a466a08b..4cf4f3de 100644 --- a/src/jim.c +++ b/src/jim.c @@ -363,7 +363,7 @@ int Jim_StringToWide(const char *str, jim_wide *widePtr, int base) #else *widePtr = strtol(str, &endptr, base); #endif - if (str[0] == '\0') + if ((str[0] == '\0') || (str == endptr) ) return JIM_ERR; if (endptr[0] != '\0') { while(*endptr) { @@ -380,7 +380,7 @@ int Jim_StringToIndex(const char *str, int *intPtr) char *endptr; *intPtr = strtol(str, &endptr, 10); - if (str[0] == '\0') + if ( (str[0] == '\0') || (str == endptr) ) return JIM_ERR; if (endptr[0] != '\0') { while(*endptr) { @@ -437,7 +437,7 @@ int Jim_StringToDouble(const char *str, double *doublePtr) char *endptr; *doublePtr = strtod(str, &endptr); - if (str[0] == '\0' || endptr[0] != '\0') + if (str[0] == '\0' || endptr[0] != '\0' || (str == endptr) ) return JIM_ERR; return JIM_OK; } @@ -460,13 +460,16 @@ static jim_wide JimPowWide(jim_wide b, jim_wide e) void Jim_Panic(Jim_Interp *interp, const char *fmt, ...) { va_list ap; - FILE *fp = interp ? interp->stderr_ : stderr; va_start(ap, fmt); - fprintf(fp, JIM_NL "JIM INTERPRETER PANIC: "); - vfprintf(fp, fmt, ap); - fprintf(fp, JIM_NL JIM_NL); + /* + * Send it here first.. Assuming STDIO still works + */ + fprintf(stderr, JIM_NL "JIM INTERPRETER PANIC: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, JIM_NL JIM_NL); va_end(ap); + #ifdef HAVE_BACKTRACE { void *array[40]; @@ -481,6 +484,13 @@ void Jim_Panic(Jim_Interp *interp, const char *fmt, ...) fprintf(fp,"[backtrace] of 'nm ' in the bug report." JIM_NL); } #endif + + /* This may actually crash... we do it last */ + if( interp && interp->cookie_stderr ){ + Jim_fprintf( interp, interp->cookie_stderr, JIM_NL "JIM INTERPRETER PANIC: "); + Jim_vfprintf( interp, interp->cookie_stderr, fmt, ap ); + Jim_fprintf( interp, interp->cookie_stderr, JIM_NL JIM_NL ); + } abort(); } @@ -2136,21 +2146,36 @@ static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr) } /* This is the core of the [format] command. - * TODO: Export it, make it real... for now only %s and %% - * specifiers supported. */ + * TODO: Lots of things work - via a hack + * However, no format item can be >= JIM_MAX_FMT + */ Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv) { - const char *fmt; + const char *fmt, *_fmt; int fmtLen; Jim_Obj *resObjPtr; + fmt = Jim_GetString(fmtObjPtr, &fmtLen); + _fmt = fmt; resObjPtr = Jim_NewStringObj(interp, "", 0); while (fmtLen) { const char *p = fmt; char spec[2], c; jim_wide wideValue; + double doubleValue; + /* we cheat and use Sprintf()! */ +#define JIM_MAX_FMT 2048 + char sprintf_buf[JIM_MAX_FMT]; + char fmt_str[100]; + char *cp; + int width; + int ljust; + int zpad; + int spad; + int altfm; + int forceplus; while (*fmt != '%' && fmtLen) { fmt++; fmtLen--; @@ -2159,38 +2184,191 @@ Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, if (fmtLen == 0) break; fmt++; fmtLen--; /* skip '%' */ - if (*fmt != '%') { + zpad = 0; + spad = 0; + width = -1; + ljust = 0; + altfm = 0; + forceplus = 0; + next_fmt: + if( fmtLen <= 0 ){ + break; + } + switch( *fmt ){ + /* terminals */ + case 'b': /* binary - not all printfs() do this */ + case 's': /* string */ + case 'i': /* integer */ + case 'd': /* decimal */ + case 'x': /* hex */ + case 'X': /* CAP hex */ + case 'c': /* char */ + case 'o': /* octal */ + case 'u': /* unsigned */ + case 'f': /* float */ + break; + + /* non-terminals */ + case '0': /* zero pad */ + zpad = 1; + *fmt++; fmtLen--; + goto next_fmt; + break; + case '+': + forceplus = 1; + *fmt++; fmtLen--; + goto next_fmt; + break; + case ' ': /* sign space */ + spad = 1; + *fmt++; fmtLen--; + goto next_fmt; + break; + case '-': + ljust = 1; + *fmt++; fmtLen--; + goto next_fmt; + break; + case '#': + altfm = 1; + *fmt++; fmtLen--; + goto next_fmt; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + width = 0; + while( isdigit(*fmt) && (fmtLen > 0) ){ + width = (width * 10) + (*fmt - '0'); + fmt++; fmtLen--; + } + goto next_fmt; + case '*': + /* suck up the next item as an integer */ + *fmt++; fmtLen--; + objc--; + if( objc <= 0 ){ + goto not_enough_args; + } + if( Jim_GetWide(interp,objv[0],&wideValue )== JIM_ERR ){ + Jim_FreeNewObj(interp, resObjPtr ); + return NULL; + } + width = wideValue; + if( width < 0 ){ + ljust = 1; + width = -width; + } + objv++; + goto next_fmt; + break; + } + + + if (*fmt != '%') { if (objc == 0) { + not_enough_args: Jim_FreeNewObj(interp, resObjPtr); Jim_SetResultString(interp, - "not enough arguments for all format specifiers", -1); + "not enough arguments for all format specifiers", -1); return NULL; } else { objc--; } } + + /* + * Create the formatter + * cause we cheat and use sprintf() + */ + cp = fmt_str; + *cp++ = '%'; + if( altfm ){ + *cp++ = '#'; + } + if( forceplus ){ + *cp++ = '+'; + } else if( spad ){ + /* PLUS overrides */ + *cp++ = ' '; + } + if( ljust ){ + *cp++ = '-'; + } + if( zpad ){ + *cp++ = '0'; + } + if( width > 0 ){ + sprintf( cp, "%d", width ); + /* skip ahead */ + cp = strchr(cp,0); + } + *cp = 0; + + /* here we do the work */ + /* actually - we make sprintf() do it for us */ switch(*fmt) { case 's': - Jim_AppendObj(interp, resObjPtr, objv[0]); - objv++; + *cp++ = 's'; + *cp = 0; + /* BUG: we do not handled embeded NULLs */ + snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, Jim_GetString( objv[0], NULL )); break; case 'c': + *cp++ = 'c'; + *cp = 0; if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) { Jim_FreeNewObj(interp, resObjPtr); return NULL; } c = (char) wideValue; - Jim_AppendString(interp, resObjPtr, &c, 1); + snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, c ); break; + case 'f': + case 'F': + case 'g': + case 'G': + case 'e': + case 'E': + *cp++ = *fmt; + *cp = 0; + if( Jim_GetDouble( interp, objv[0], &doubleValue ) == JIM_ERR ){ + Jim_FreeNewObj( interp, resObjPtr ); + return NULL; + } + snprintf( sprintf_buf, JIM_MAX_FMT, fmt_str, doubleValue ); + break; + case 'b': case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + /* jim widevaluse are 64bit */ + if( sizeof(jim_wide) == sizeof(long long) ){ + *cp++ = 'l'; + *cp++ = 'l'; + } else { + *cp++ = 'l'; + } + *cp++ = *fmt; + *cp = 0; if (Jim_GetWide(interp, objv[0], &wideValue) == JIM_ERR) { Jim_FreeNewObj(interp, resObjPtr); return NULL; } - Jim_AppendObj(interp, resObjPtr, objv[0]); + snprintf(sprintf_buf, JIM_MAX_FMT, fmt_str, wideValue ); break; case '%': - Jim_AppendString(interp, resObjPtr, "%" , 1); + sprintf_buf[0] = '%'; + sprintf_buf[1] = 0; + objv--; /* undo the objv++ below */ break; default: spec[0] = *fmt; spec[1] = '\0'; @@ -2200,6 +2378,16 @@ Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, "bad field specifier \"", spec, "\"", NULL); return NULL; } + /* force terminate */ +#if 0 + printf("FMT was: %s\n", fmt_str ); + printf("RES was: |%s|\n", sprintf_buf ); +#endif + + sprintf_buf[ JIM_MAX_FMT - 1] = 0; + Jim_AppendString( interp, resObjPtr, sprintf_buf, strlen(sprintf_buf) ); + /* next obj */ + objv++; fmt++; fmtLen--; } @@ -3910,7 +4098,7 @@ int Jim_Collect(Jim_Interp *interp) Jim_AddHashEntry(&marks, &objPtr->internalRep.refValue.id, NULL); #ifdef JIM_DEBUG_GC - fprintf(interp->stdout_, + Jim_fprintf(interp,interp->cookie_stdout, "MARK (reference): %d refcount: %d" JIM_NL, (int) objPtr->internalRep.refValue.id, objPtr->refCount); @@ -3949,7 +4137,7 @@ int Jim_Collect(Jim_Interp *interp) * was found. Mark it. */ Jim_AddHashEntry(&marks, &id, NULL); #ifdef JIM_DEBUG_GC - fprintf(interp->stdout_,"MARK: %d" JIM_NL, (int)id); + Jim_fprintf(interp,interp->cookie_stdout,"MARK: %d" JIM_NL, (int)id); #endif p += JIM_REFERENCE_SPACE; } @@ -3969,7 +4157,7 @@ int Jim_Collect(Jim_Interp *interp) * this reference. */ if (Jim_FindHashEntry(&marks, refId) == NULL) { #ifdef JIM_DEBUG_GC - fprintf(interp->stdout_,"COLLECTING %d" JIM_NL, (int)*refId); + Jim_fprintf(interp,interp->cookie_stdout,"COLLECTING %d" JIM_NL, (int)*refId); #endif collected++; /* Drop the reference, but call the @@ -4057,9 +4245,14 @@ Jim_Interp *Jim_CreateInterp(void) i->freeFramesList = NULL; i->prngState = NULL; i->evalRetcodeLevel = -1; - i->stdin_ = stdin; - i->stdout_ = stdout; - i->stderr_ = stderr; + i->cookie_stdin = stdin; + i->cookie_stdout = stdout; + i->cookie_stderr = stderr; + i->cb_fwrite = ((size_t (*)( const void *, size_t, size_t, void *))(fwrite)); + i->cb_fread = ((size_t (*)( void *, size_t, size_t, void *))(fread)); + i->cb_vfprintf = ((int (*)( void *, const char *fmt, va_list ))(vfprintf)); + i->cb_fflush = ((int (*)( void *))(fflush)); + i->cb_fgets = ((char * (*)( char *, int, void *))(fgets)); /* Note that we can create objects only after the * interpreter liveList and freeList pointers are @@ -4128,23 +4321,23 @@ void Jim_FreeInterp(Jim_Interp *i) if (i->liveList != NULL) { Jim_Obj *objPtr = i->liveList; - fprintf(i->stdout_,JIM_NL "-------------------------------------" JIM_NL); - fprintf(i->stdout_,"Objects still in the free list:" JIM_NL); + Jim_fprintf( i, i->cookie_stdout,JIM_NL "-------------------------------------" JIM_NL); + Jim_fprintf( i, i->cookie_stdout,"Objects still in the free list:" JIM_NL); while(objPtr) { const char *type = objPtr->typePtr ? objPtr->typePtr->name : ""; - fprintf(i->stdout_,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL, + Jim_fprintf( i, i->cookie_stdout,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL, objPtr, type, objPtr->bytes ? objPtr->bytes : "(null)", objPtr->refCount); if (objPtr->typePtr == &sourceObjType) { - fprintf(i->stdout_, "FILE %s LINE %d" JIM_NL, + Jim_fprintf( i, i->cookie_stdout, "FILE %s LINE %d" JIM_NL, objPtr->internalRep.sourceValue.fileName, objPtr->internalRep.sourceValue.lineNumber); } objPtr = objPtr->nextObjPtr; } - fprintf(stdout, "-------------------------------------" JIM_NL JIM_NL); + Jim_fprintf( i, i->cookie_stdout, "-------------------------------------" JIM_NL JIM_NL); Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?"); } /* Free all the freed objects. */ @@ -4330,22 +4523,22 @@ int Jim_GetExitCode(Jim_Interp *interp) { return interp->exitCode; } -FILE *Jim_SetStdin(Jim_Interp *interp, FILE *fp) +void *Jim_SetStdin(Jim_Interp *interp, void *fp) { - if (fp != NULL) interp->stdin_ = fp; - return interp->stdin_; + if (fp != NULL) interp->cookie_stdin = fp; + return interp->cookie_stdin; } -FILE *Jim_SetStdout(Jim_Interp *interp, FILE *fp) +void *Jim_SetStdout(Jim_Interp *interp, void *fp) { - if (fp != NULL) interp->stdout_ = fp; - return interp->stdout_; + if (fp != NULL) interp->cookie_stdout = fp; + return interp->cookie_stdout; } -FILE *Jim_SetStderr(Jim_Interp *interp, FILE *fp) +void *Jim_SetStderr(Jim_Interp *interp, void *fp) { - if (fp != NULL) interp->stderr_ = fp; - return interp->stderr_; + if (fp != NULL) interp->cookie_stderr = fp; + return interp->cookie_stderr; } /* ----------------------------------------------------------------------------- @@ -8478,7 +8671,7 @@ int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr) Jim_IncrRefCount(objv[1]); if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) { /* Report the error to stderr. */ - fprintf(interp->stderr_, "Background error:" JIM_NL); + Jim_fprintf( interp, interp->cookie_stderr, "Background error:" JIM_NL); Jim_PrintErrorMessage(interp); } Jim_DecrRefCount(interp, objv[0]); @@ -8509,10 +8702,12 @@ int Jim_EvalFile(Jim_Interp *interp, const char *filename) maxlen = totread+buflen+1; prg = Jim_Realloc(prg, maxlen); } + /* do not use Jim_fread() - this is really a file */ if ((nread = fread(prg+totread, 1, buflen, fp)) == 0) break; totread += nread; } prg[totread] = '\0'; + /* do not use Jim_fclose() - this is really a file */ fclose(fp); scriptObjPtr = Jim_NewStringObjNoAlloc(interp, prg, totread); @@ -9011,8 +9206,8 @@ static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, } } str = Jim_GetString(argv[1], &len); - fwrite(str, 1, len, interp->stdout_); - if (!nonewline) fprintf(interp->stdout_, JIM_NL); + Jim_fwrite(interp, str, 1, len, interp->cookie_stdout); + if (!nonewline) Jim_fprintf( interp, interp->cookie_stdout, JIM_NL); return JIM_OK; } @@ -11744,9 +11939,9 @@ void Jim_PrintErrorMessage(Jim_Interp *interp) { int len, i; - fprintf(interp->stderr_, "Runtime error, file \"%s\", line %d:" JIM_NL, - interp->errorFileName, interp->errorLine); - fprintf(interp->stderr_, " %s" JIM_NL, + Jim_fprintf(interp, interp->cookie_stderr, "Runtime error, file \"%s\", line %d:" JIM_NL, + interp->errorFileName, interp->errorLine); + Jim_fprintf(interp,interp->cookie_stderr, " %s" JIM_NL, Jim_GetString(interp->result, NULL)); Jim_ListLength(interp, interp->stackTrace, &len); for (i = 0; i < len; i+= 3) { @@ -11761,7 +11956,7 @@ void Jim_PrintErrorMessage(Jim_Interp *interp) Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr, JIM_NONE); line = Jim_GetString(objPtr, NULL); - fprintf(interp->stderr_, + Jim_fprintf( interp, interp->cookie_stderr, "In procedure '%s' called at file \"%s\", line %s" JIM_NL, proc, file, line); } @@ -11772,7 +11967,7 @@ int Jim_InteractivePrompt(Jim_Interp *interp) int retcode = JIM_OK; Jim_Obj *scriptObjPtr; - fprintf(interp->stdout_, "Welcome to Jim version %d.%d, " + Jim_fprintf(interp,interp->cookie_stdout, "Welcome to Jim version %d.%d, " "Copyright (c) 2005-8 Salvatore Sanfilippo" JIM_NL, JIM_VERSION / 100, JIM_VERSION % 100); Jim_SetVariableStrWithStr(interp, "jim_interactive", "1"); @@ -11786,12 +11981,12 @@ int Jim_InteractivePrompt(Jim_Interp *interp) if (retcode != 0) { if (retcode >= 2 && retcode <= 6) - fprintf(interp->stdout_, "[%s] . ", retcodestr[retcode]); + Jim_fprintf(interp,interp->cookie_stdout, "[%s] . ", retcodestr[retcode]); else - fprintf(interp->stdout_, "[%d] . ", retcode); + Jim_fprintf(interp,interp->cookie_stdout, "[%d] . ", retcode); } else - fprintf(interp->stdout_, ". "); - fflush(interp->stdout_); + Jim_fprintf( interp, interp->cookie_stdout, ". "); + Jim_fflush( interp, interp->cookie_stdout); scriptObjPtr = Jim_NewStringObj(interp, "", 0); Jim_IncrRefCount(scriptObjPtr); while(1) { @@ -11799,7 +11994,7 @@ int Jim_InteractivePrompt(Jim_Interp *interp) char state; int len; - if (fgets(buf, 1024, interp->stdin_) == NULL) { + if ( Jim_fgets(interp, buf, 1024, interp->cookie_stdin) == NULL) { Jim_DecrRefCount(interp, scriptObjPtr); goto out; } @@ -11807,8 +12002,8 @@ int Jim_InteractivePrompt(Jim_Interp *interp) str = Jim_GetString(scriptObjPtr, &len); if (Jim_ScriptIsComplete(str, len, &state)) break; - fprintf(interp->stdout_, "%c> ", state); - fflush(stdout); + Jim_fprintf( interp, interp->cookie_stdout, "%c> ", state); + Jim_fflush( interp, interp->cookie_stdout); } retcode = Jim_EvalObj(interp, scriptObjPtr); Jim_DecrRefCount(interp, scriptObjPtr); @@ -11819,11 +12014,91 @@ int Jim_InteractivePrompt(Jim_Interp *interp) exit(Jim_GetExitCode(interp)); } else { if (reslen) { - fwrite(result, 1, reslen, interp->stdout_); - fprintf(interp->stdout_, JIM_NL); + Jim_fwrite( interp, result, 1, reslen, interp->cookie_stdout); + Jim_fprintf( interp,interp->cookie_stdout, JIM_NL); } } } out: return 0; } + +/* ----------------------------------------------------------------------------- + * Jim's idea of STDIO.. + * ---------------------------------------------------------------------------*/ + +int +Jim_fprintf( Jim_Interp *interp, void *cookie, const char *fmt, ... ) +{ + int r; + + va_list ap; + va_start(ap,fmt); + r = Jim_vfprintf( interp, cookie, fmt,ap ); + va_end(ap); + return r; +} + + +int +Jim_vfprintf( Jim_Interp *interp, void *cookie, const char *fmt, va_list ap ) +{ + if( (interp == NULL) || (interp->cb_vfprintf == NULL) ){ + errno = ENOTSUP; + return -1; + } + return (*(interp->cb_vfprintf))( cookie, fmt, ap ); +} + +size_t +Jim_fwrite( Jim_Interp *interp, const void *ptr, size_t size, size_t n, void *cookie ) +{ + if( (interp == NULL) || (interp->cb_fwrite == NULL) ){ + errno = ENOTSUP; + return 0; + } + return (*(interp->cb_fwrite))( ptr, size, n, cookie); +} + +size_t +Jim_fread( Jim_Interp *interp, void *ptr, size_t size, size_t n, void *cookie ) +{ + if( (interp == NULL) || (interp->cb_fread == NULL) ){ + errno = ENOTSUP; + return 0; + } + return (*(interp->cb_fread))( ptr, size, n, cookie); +} + +int +Jim_fflush( Jim_Interp *interp, void *cookie ) +{ + if( (interp == NULL) || (interp->cb_fflush == NULL) ){ + /* pretend all is well */ + return 0; + } + return (*(interp->cb_fflush))( cookie ); +} + +char * +Jim_fgets( Jim_Interp *interp, char *s, int size, void *cookie ) +{ + if( (interp == NULL) || (interp->cb_fgets == NULL) ){ + errno = ENOTSUP; + return NULL; + } + return (*(interp->cb_fgets))( s, size, cookie ); +} + + + + + + +/* + * Local Variables: ** + * tab-width: 4 ** + * c-basic-offset: 4 ** + * End: ** + */ + diff --git a/src/jim.h b/src/jim.h index cbd0d8fc..b08c11f2 100644 --- a/src/jim.h +++ b/src/jim.h @@ -497,9 +497,14 @@ typedef struct Jim_Interp { struct Jim_HashTable assocData; /* per-interp storage for use by packages */ Jim_PrngState *prngState; /* per interpreter Random Number Gen. state. */ struct Jim_HashTable packages; /* Provided packages hash table */ - FILE *stdin_; /* input file pointer, 'stdin' by default */ - FILE *stdout_; /* output file pointer, 'stdout' by default */ - FILE *stderr_; /* errors file pointer, 'stderr' by default */ + void *cookie_stdin; /* input file pointer, 'stdin' by default */ + void *cookie_stdout; /* output file pointer, 'stdout' by default */ + void *cookie_stderr; /* errors file pointer, 'stderr' by default */ + size_t (*cb_fwrite )( const void *ptr, size_t size, size_t n, void *cookie ); + size_t (*cb_fread )( void *ptr, size_t size, size_t n, void *cookie ); + int (*cb_vfprintf)( void *cookie, const char *fmt, va_list ap ); + int (*cb_fflush )( void *cookie ); + char *(*cb_fgets )( char *s, int size, void *cookie ); } Jim_Interp; /* Currently provided as macro that performs the increment. @@ -662,9 +667,9 @@ JIM_STATIC int JIM_API(Jim_GetFinalizer) (Jim_Interp *interp, Jim_Obj *objPtr, J JIM_STATIC Jim_Interp * JIM_API(Jim_CreateInterp) (void); JIM_STATIC void JIM_API(Jim_FreeInterp) (Jim_Interp *i); JIM_STATIC int JIM_API(Jim_GetExitCode) (Jim_Interp *interp); -JIM_STATIC FILE * JIM_API(Jim_SetStdin) (Jim_Interp *interp, FILE *fp); -JIM_STATIC FILE * JIM_API(Jim_SetStdout) (Jim_Interp *interp, FILE *fp); -JIM_STATIC FILE * JIM_API(Jim_SetStderr) (Jim_Interp *interp, FILE *fp); +JIM_STATIC void * JIM_API(Jim_SetStdin) (Jim_Interp *interp, void *fp); +JIM_STATIC void * JIM_API(Jim_SetStdout) (Jim_Interp *interp, void *fp); +JIM_STATIC void * JIM_API(Jim_SetStderr) (Jim_Interp *interp, void *fp); /* commands */ JIM_STATIC void JIM_API(Jim_RegisterCoreCommands) (Jim_Interp *interp); @@ -815,6 +820,15 @@ JIM_STATIC int JIM_API(Jim_InteractivePrompt) (Jim_Interp *interp); /* Misc */ JIM_STATIC void JIM_API(Jim_Panic) (Jim_Interp *interp, const char *fmt, ...); +/* Jim's STDIO */ +JIM_STATIC int JIM_API( Jim_fprintf )( Jim_Interp *interp, void *cookie, const char *fmt, ... ); +JIM_STATIC int JIM_API( Jim_vfprintf )( Jim_Interp *interp, void *cookie, const char *fmt, va_list ap ); +JIM_STATIC size_t JIM_API( Jim_fwrite )( Jim_Interp *interp, const void *ptr, size_t size, size_t nmeb, void *cookie ); +JIM_STATIC size_t JIM_API( Jim_fread )( Jim_Interp *interp, void *ptr, size_t size, size_t nmeb, void *cookie ); +JIM_STATIC int JIM_API( Jim_fflush )( Jim_Interp *interp, void *cookie ); +JIM_STATIC char * JIM_API( Jim_fgets )( Jim_Interp *interp, char *s, int size, void *cookie ); + + #undef JIM_STATIC #undef JIM_API @@ -942,6 +956,13 @@ static void Jim_InitExtension(Jim_Interp *interp) JIM_GET_API(StackPop); JIM_GET_API(StackPeek); JIM_GET_API(FreeStackElements); + JIM_GET_API(fprintf ); + JIM_GET_API(vfprintf ); + JIM_GET_API(fwrite ); + JIM_GET_API(fread ); + JIM_GET_API(fflush ); + JIM_GET_API(fgets ); + } #endif /* defined JIM_EXTENSION || defined JIM_EMBEDDED */ @@ -962,3 +983,11 @@ static void Jim_InitEmbedded(void) { #endif #endif /* __JIM__H */ + +/* + * Local Variables: ** + * tab-width: 4 ** + * c-basic-offset: 4 ** + * End: ** + */ + diff --git a/src/openocd.c b/src/openocd.c index 99ee6527..b6b217df 100644 --- a/src/openocd.c +++ b/src/openocd.c @@ -165,6 +165,191 @@ void unlockBigLock() Jim_Interp *interp; command_context_t *active_cmd_ctx; +static int +new_int_array_element( Jim_Interp * interp, + const char *varname, + int idx, + u32 val ) +{ + char *namebuf; + Jim_Obj *nameObjPtr, *valObjPtr; + int result; + + namebuf = alloca( strlen(varname) + 30 ); + sprintf( namebuf, "%s(%d)", varname, idx ); + + + nameObjPtr = Jim_NewStringObj(interp, namebuf, -1); + valObjPtr = Jim_NewIntObj(interp, val ); + Jim_IncrRefCount(nameObjPtr); + Jim_IncrRefCount(valObjPtr); + result = Jim_SetVariable(interp, nameObjPtr, valObjPtr); + Jim_DecrRefCount(interp, nameObjPtr); + Jim_DecrRefCount(interp, valObjPtr); + // printf( "%s = 0%08x\n", namebuf, val ); + return result; +} + +static int +Jim_Command_mem2array( Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + target_t *target; + long l; + u32 width; + u32 endian; + u32 len; + u32 addr; + u32 count; + u32 v; + const char *varname; + u8 buffer[4096]; + int i,n,e,retval; + + + /* argv[1] = name of array to receive the data + * argv[2] = desired width + * argv[3] = memory address + * argv[4] = length in bytes to read + */ + if( argc != 5 ){ + Jim_WrongNumArgs( interp, 1, argv, "varname width addr nelems" ); + return JIM_ERR; + } + varname = Jim_GetString( argv[1], &len ); + /* given "foo" get space for worse case "foo(%d)" .. add 20 */ + + + e = Jim_GetLong( interp, argv[2], &l ); + width = l; + if( e != JIM_OK ){ + return e; + } + + e = Jim_GetLong( interp, argv[3], &l ); + addr = l; + if( e != JIM_OK ){ + return e; + } + e = Jim_GetLong( interp, argv[4], &l ); + len = l; + if( e != JIM_OK ){ + return e; + } + switch(width){ + case 8: + width = 1; + break; + case 16: + width = 2; + break; + case 32: + width = 4; + break; + default: + Jim_SetResult(interp, + Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings( interp, Jim_GetResult(interp), + "Invalid width param, must be 8/16/32", NULL ); + return JIM_ERR; + } + if( len == 0 ){ + Jim_SetResult(interp, + Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings( interp, Jim_GetResult(interp), + "mem2array: zero width read?", NULL ); + return JIM_ERR; + } + if( (addr + (len * width)) < addr ){ + Jim_SetResult(interp, + Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings( interp, Jim_GetResult(interp), + "mem2array: addr + len - wraps to zero?", NULL ); + return JIM_ERR; + } + /* absurd transfer size? */ + if( len > 65536 ){ + Jim_SetResult(interp, + Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings( interp, Jim_GetResult(interp), + "mem2array: absurd > 64K item request", NULL ); + return JIM_ERR; + } + + if( (width == 1) || + ((width == 2) && ((addr & 1) == 0)) || + ((width == 4) && ((addr & 3) == 0)) ){ + /* all is well */ + } else { + char buf[100]; + Jim_SetResult(interp, + Jim_NewEmptyStringObj(interp)); + sprintf( buf, + "mem2array address: 0x%08x is not aligned for %d byte reads", + addr, width ); + + Jim_AppendStrings( interp, Jim_GetResult(interp), + buf , NULL ); + return JIM_ERR; + } + + target = get_current_target( active_cmd_ctx ); + + /* Transfer loop */ + + /* index counter */ + n = 0; + /* assume ok */ + e = JIM_OK; + while( len ){ + + /* Slurp... in buffer size chunks */ + + count = len; /* in objects.. */ + if( count > (sizeof(buffer)/width)){ + count = (sizeof(buffer)/width); + } + + retval = target->type->read_memory( target, + addr, + width, + count, + buffer ); + + if( retval != ERROR_OK ){ + /* BOO !*/ + LOG_ERROR("mem2array: Read @ 0x%08x, w=%d, cnt=%d, failed", + addr, width, count ); + Jim_SetResult(interp, + Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings( interp, Jim_GetResult(interp), + "mem2array: cannot read memory", NULL ); + e = JIM_ERR; + len = 0; + } else { + v = 0; /* shut up gcc */ + for( i = 0 ; i < count ; i++, n++ ){ + switch(width){ + case 4: + v = target_buffer_get_u32( target, &buffer[i*width] ); + break; + case 2: + v = target_buffer_get_u16( target, &buffer[i*width] ); + break; + case 1: + v = buffer[i] & 0x0ff; + break; + } + new_int_array_element( interp, varname, n, v ); + } + len -= count; + } + } + Jim_SetResult(interp, + Jim_NewEmptyStringObj(interp)); + + return JIM_OK; +} + static void tcl_output(void *privData, const char *file, int line, const char *function, const char *string) { @@ -323,6 +508,97 @@ Jim_Command_echo(Jim_Interp *interp, return JIM_OK; } +static size_t +openocd_jim_fwrite( const void *_ptr, size_t size, size_t n, void *cookie ) +{ + size_t nbytes; + const char *ptr; + + /* make it a char easier to read code */ + ptr = _ptr; + + nbytes = size * n; + if( nbytes == 0 ){ + return 0; + } + + if( !active_cmd_ctx ){ + /* FIXME: Where should this go? */ + return n; + } + + + /* do we have to chunk it? */ + if( ptr[ nbytes ] == 0 ){ + /* no it is a C style string */ + command_output_text( active_cmd_ctx, ptr ); + return; + } + /* GRR we must chunk - not null terminated */ + while( nbytes ){ + char chunk[128+1]; + int x; + + x = nbytes; + if( x > 128 ){ + x = 128; + } + /* copy it */ + memcpy( chunk, ptr, x ); + /* terminate it */ + chunk[n] = 0; + /* output it */ + command_output_text( active_cmd_ctx, chunk ); + ptr += x; + nbytes -= x; + } + + return n; +} + +static size_t +openocd_jim_fread(void *ptr, size_t size, size_t n, void *cookie ) +{ + /* TCL wants to read... tell him no */ + return 0; +} + + +static int +openocd_jim_vfprintf( void *cookie, const char *fmt, va_list ap ) +{ + char *cp; + int n; + + n = -1; + if( active_cmd_ctx ){ + cp = alloc_vprintf( fmt, ap ); + if( cp ){ + command_output_text( active_cmd_ctx, cp ); + n = strlen(cp); + free(cp); + } + } + return n; +} + +static int +openocd_jim_fflush( void *cookie ) +{ + /* nothing to flush */ + return 0; +} + +static char * +openocd_jim_fgets( char *s, int size, void *cookie ) +{ + /* not supported */ + errno = ENOTSUP; + return NULL; +} + + + void initJim(void) { Jim_InitEmbedded(); @@ -335,6 +611,17 @@ void initJim(void) Jim_CreateCommand(interp, "openocd_throw", Jim_Command_openocd_throw, NULL, NULL); Jim_CreateCommand(interp, "find", Jim_Command_find, NULL, NULL); Jim_CreateCommand(interp, "echo", Jim_Command_echo, NULL, NULL); + Jim_CreateCommand(interp, "mem2array", Jim_Command_mem2array, NULL, NULL ); + + /* Set Jim's STDIO */ + interp->cookie_stdin = NULL; + interp->cookie_stdout = NULL; + interp->cookie_stderr = NULL; + interp->cb_fwrite = openocd_jim_fwrite; + interp->cb_fread = openocd_jim_fread ; + interp->cb_vfprintf = openocd_jim_vfprintf; + interp->cb_fflush = openocd_jim_fflush; + interp->cb_fgets = openocd_jim_fgets; } int main(int argc, char *argv[]) @@ -422,3 +709,11 @@ int main(int argc, char *argv[]) return EXIT_SUCCESS; } + +/* + * Local Variables: ** + * tab-width: 4 ** + * c-basic-offset: 4 ** + * End: ** + */ + diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index c2c18baf..3ea1dde0 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -568,7 +568,7 @@ int gdb_output_con(connection_t *connection, const char* line) return ERROR_OK; } -int gdb_output(struct command_context_s *context, char* line) +int gdb_output(struct command_context_s *context, const char* line) { /* this will be dumped to the log and also sent as an O packet if possible */ LOG_USER_N("%s", line); @@ -577,24 +577,10 @@ int gdb_output(struct command_context_s *context, char* line) int gdb_program_handler(struct target_s *target, enum target_event event, void *priv) { - FILE *script; struct command_context_s *cmd_ctx = priv; - if (target->gdb_program_script) - { - script = open_file_from_path(target->gdb_program_script, "r"); - if (!script) - { - LOG_ERROR("couldn't open script file %s", target->gdb_program_script); - return ERROR_OK; - } - - LOG_INFO("executing gdb_program script '%s'", target->gdb_program_script); - command_run_file(cmd_ctx, script, COMMAND_EXEC); - fclose(script); - - jtag_execute_queue(); - } + target_invoke_script(cmd_ctx, target, "gdb_program"); + jtag_execute_queue(); return ERROR_OK; } diff --git a/src/server/telnet_server.c b/src/server/telnet_server.c index fc3f2116..fc39c729 100644 --- a/src/server/telnet_server.c +++ b/src/server/telnet_server.c @@ -92,7 +92,7 @@ int telnet_outputline(connection_t *connection, const char *line) telnet_write(connection, line, len); if (line_end) { - telnet_write(connection, "\r\n\0", 3); + telnet_write(connection, "\r\n", 2); line += len+1; } else @@ -104,7 +104,7 @@ int telnet_outputline(connection_t *connection, const char *line) return ERROR_OK; } -int telnet_output(struct command_context_s *cmd_ctx, char* line) +int telnet_output(struct command_context_s *cmd_ctx, const char* line) { connection_t *connection = cmd_ctx->output_handler_priv; @@ -181,7 +181,7 @@ int telnet_new_connection(connection_t *connection) if (telnet_service->banner) { telnet_write(connection, telnet_service->banner, strlen(telnet_service->banner)); - telnet_write(connection, "\r\n\0", 3); + telnet_write(connection, "\r\n", 2); } telnet_prompt(connection); @@ -658,3 +658,11 @@ int handle_exit_command(struct command_context_s *cmd_ctx, char *cmd, char **arg { return ERROR_COMMAND_CLOSE_CONNECTION; } + +/* + * Local Variables: ** + * tab-width: 4 ** + * c-basic-offset: 4 ** + * End: ** + */ + diff --git a/src/target/target.c b/src/target/target.c index 0237fd66..86b42db2 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -215,23 +215,6 @@ target_t* get_current_target(command_context_t *cmd_ctx) return target; } -static void execute_script(struct command_context_s *cmd_ctx, char *reset_script) -{ - if (reset_script==NULL) - return; - FILE *script; - script = open_file_from_path(reset_script, "r"); - if (!script) - { - LOG_ERROR("couldn't open script file %s", reset_script); - return; - } - - LOG_INFO("executing script '%s'", reset_script); - command_run_file(cmd_ctx, script, COMMAND_EXEC); - fclose(script); -} - /* Process target initialization, when target entered debug out of reset * the handler is unregistered at the end of this function, so it's only called once */ @@ -243,7 +226,7 @@ int target_init_handler(struct target_s *target, enum target_event event, void * { target_unregister_event_callback(target_init_handler, priv); - execute_script(cmd_ctx, target->reset_script); + target_invoke_script(cmd_ctx, target, "reset"); jtag_execute_queue(); } @@ -305,7 +288,7 @@ int target_process_reset(struct command_context_s *cmd_ctx) target = targets; while (target) { - execute_script(cmd_ctx, target->pre_reset_script); + target_invoke_script(cmd_ctx, target, "pre_reset"); target = target->next; } @@ -950,7 +933,8 @@ int target_register_commands(struct command_context_s *cmd_ctx) { register_command(cmd_ctx, NULL, "target", handle_target_command, COMMAND_CONFIG, "target [reset_init default - DEPRECATED] [cpu type specifc args]"); register_command(cmd_ctx, NULL, "targets", handle_targets_command, COMMAND_EXEC, NULL); - register_command(cmd_ctx, NULL, "target_script", handle_target_script_command, COMMAND_CONFIG, NULL); + register_command(cmd_ctx, NULL, "target_script", handle_target_script_command, COMMAND_CONFIG, + "target_script "); register_command(cmd_ctx, NULL, "run_and_halt_time", handle_run_and_halt_time_command, COMMAND_CONFIG, " "); register_command(cmd_ctx, NULL, "working_area", handle_working_area_command, COMMAND_ANY, "working_area
<'backup'|'nobackup'> [virtual address]"); register_command(cmd_ctx, NULL, "virt2phys", handle_virt2phys_command, COMMAND_ANY, "virt2phys "); @@ -1437,12 +1421,6 @@ int handle_target_command(struct command_context_s *cmd_ctx, char *cmd, char **a } (*last_target_p)->run_and_halt_time = 1000; /* default 1s */ - (*last_target_p)->reset_script = NULL; - (*last_target_p)->pre_reset_script = NULL; - (*last_target_p)->post_halt_script = NULL; - (*last_target_p)->pre_resume_script = NULL; - (*last_target_p)->gdb_program_script = NULL; - (*last_target_p)->working_area = 0x0; (*last_target_p)->working_area_size = 0x0; (*last_target_p)->working_areas = NULL; @@ -1487,7 +1465,14 @@ int handle_target_command(struct command_context_s *cmd_ctx, char *cmd, char **a return ERROR_OK; } -/* usage: target_script */ +int target_invoke_script(struct command_context_s *cmd_ctx, target_t *target, char *name) +{ + return command_run_linef(cmd_ctx, " if {[catch {info body target_%s_%d} t]==0} {target_%s_%d}", + name, get_num_by_target(target), + name, get_num_by_target(target)); +} + + int handle_target_script_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) { target_t *target = NULL; @@ -1505,41 +1490,14 @@ int handle_target_script_command(struct command_context_s *cmd_ctx, char *cmd, c return ERROR_COMMAND_SYNTAX_ERROR; } - if ((strcmp(args[1], "reset") == 0)||(strcmp(args[1], "post_reset") == 0)) - { - if (target->reset_script) - free(target->reset_script); - target->reset_script = strdup(args[2]); - } - else if (strcmp(args[1], "pre_reset") == 0) - { - if (target->pre_reset_script) - free(target->pre_reset_script); - target->pre_reset_script = strdup(args[2]); - } - else if (strcmp(args[1], "post_halt") == 0) - { - if (target->post_halt_script) - free(target->post_halt_script); - target->post_halt_script = strdup(args[2]); - } - else if (strcmp(args[1], "pre_resume") == 0) - { - if (target->pre_resume_script) - free(target->pre_resume_script); - target->pre_resume_script = strdup(args[2]); - } - else if (strcmp(args[1], "gdb_program_config") == 0) - { - if (target->gdb_program_script) - free(target->gdb_program_script); - target->gdb_program_script = strdup(args[2]); - } - else - { - LOG_ERROR("unknown event type: '%s", args[1]); - return ERROR_COMMAND_SYNTAX_ERROR; - } + /* Define a tcl procedure which we'll invoke upon some event */ + command_run_linef(cmd_ctx, + "proc target_%s_%d {} {" + "openocd {script %s}" + "}", + args[1], + get_num_by_target(target), + args[2]); return ERROR_OK; } @@ -2021,14 +1979,11 @@ int handle_md_command(struct command_context_s *cmd_ctx, char *cmd, char **args, output_len = 0; } } - } else - { - LOG_ERROR("Failure examining memory"); } free(buffer); - return ERROR_OK; + return retval; } int handle_mw_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) diff --git a/src/target/target.h b/src/target/target.h index c12dff5e..05ea584f 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -200,11 +200,6 @@ typedef struct target_s target_type_t *type; /* target type definition (name, access functions) */ enum target_reset_mode reset_mode; /* what to do after a reset */ int run_and_halt_time; /* how long the target should run after a run_and_halt reset */ - char *pre_reset_script; /* script file to initialize the target before a reset */ - char *reset_script; /* script file to initialize the target after a reset */ - char *post_halt_script; /* script file to execute after the target halted */ - char *pre_resume_script; /* script file to execute before the target resumed */ - char *gdb_program_script; /* script file to execute before programming vis gdb */ u32 working_area; /* working area (initialized RAM). Evaluated upon first allocation from virtual/physical address. */ @@ -325,6 +320,9 @@ int target_write_u8(struct target_s *target, u32 address, u8 value); /* Issues USER() statements with target state information */ int target_arch_state(struct target_s *target); +int target_invoke_script(struct command_context_s *cmd_ctx, target_t *target, char *name); + + #define ERROR_TARGET_INVALID (-300) #define ERROR_TARGET_INIT_FAILED (-301) #define ERROR_TARGET_TIMEOUT (-302) diff --git a/src/tcl/README_ABOUT_TCL.txt b/src/tcl/README_ABOUT_TCL.txt new file mode 100644 index 00000000..47131fef --- /dev/null +++ b/src/tcl/README_ABOUT_TCL.txt @@ -0,0 +1,430 @@ +**************************************** +**************************************** + +This is a short introduction to 'un-scare' you about the language +known as TCL. It is structured as a guided tour through the files +written by me [Duane Ellis] - in early July 2008 for OpenOCD. + +Which uses the "JIM" embedded Tcl clone-ish language. + +Thing described here are *totally* TCL generic... not Jim specific. + +The goal of this document is to encourage you to add your own set of +chips to the TCL package - and most importantly you should know where +you should put them - so they end up in an orginized way. + +--Duane Ellis. + duane@duaneellis.com + +**************************************** +**************************************** + +Adding "chip" support - Duane Ellis July 5 - 2008. + +The concept is this: + In your "openocd.cfg" file add something like this: + + source [find tcl/chip/VENDOR/FAMILY/NAME.tcl] + + For example... + source [find tcl/chip/atmel/at91/at91sam7x256.tcl] + + You'll notice that it makes use of: + + tcl/cpu/arm/.tcl. + + Yes, that is where you should put "core" specific things. + Be carefull and learn the difference: + + THE "CORE" - is not the entire chip! + +Definition: + That "file" listed above is called a "CHIP FILE". + + It may be standalone, or may need to "source" other "helper" files. + + The reference [7/5/2008] is the at91sam7x256.tcl file. + +**************************************** +**************************************** +=== TCL TOUR === +Open: at91sam7x256.tcl +=== TCL TOUR === + +A walk through --- For those who are new to TCL. + +Examine the file: at91sam7x256.tcl + +It starts with: + source [find path/filename.tcl] + +In TCL - this is very important. + + Rule #1 Everything is a string. + Rule #2 If you think other wise See #1. +Reminds you of: + Rule #1: The wife is correct. + Rule #2: If you think otherwise, See #1 + +Any text contained inside of [square-brackets] +is just like `back-ticks` in BASH. + +Hence, the [find FILENAME] executes the command find with a single +parameter the filename. + +======================================== + +Next you see a series of: + +set NAME VALUE + +It is mostly "obious" what is going on. + +Execption: The arrays. + + You would *THINK* Tcl supports arrays. + In fact, multi-dim arrays. That is false. + + For the index for"FLASH(0,CHIPSELECT)" is actually the string + "0,CHIPSELECT". This is problematic. In the normal world, you think + of array indexes as integers. + + For example these are different: + + set foo(0x0c) 123 + set foo(12) 444 + + Why? Because 0x0c {lowercase} is a string. + Don't forget UPPER CASE. + + You must be careful - always... always... use simple decimal + numbers. When in doubt use 'expr' the evaluator. These are all the + same. + + set x 0x0c + set foo([expr $x]) "twelve" + + set x 12 + set foo([expr $x]) "twelve" + + set x "2 * 6" + set foo([expr $x]) "twelve" + +************************************************** +*************************************************** +=== TCL TOUR === +Open the file: "bitsbytes.tcl" + +There is some tricky things going on. +=============== + +First, there is a "for" loop - at level 0 +{level 0 means: out side of a proc/function} + +This means it is evaluated when the file is parsed. + +== SIDEBAR: About The FOR command == +In TCL, "FOR" is a funny thing, it is not what you think it is. + +Syntatically - FOR is a just a command, it is not language +construct like for(;;) in C... + +The "for" command takes 4 parameters. + (1) The "initial command" to execute. + (2) the test "expression" + (3) the "next command" + (4) the "body command" of the FOR loop. + +Notice I used the words "command" and "expresion" above. + +The FOR command: +1) executes the "initial command" +2) evaluates the expression if 0 it stops. +3) executes the "body command" +4) executes the "next command" +5) Goto Step 2. + +As show, each of these items are in {curly-braces}. This means they +are passed as they are - KEY-POINT: un evaluated to the FOR +command. Think of it like escaping the backticks in Bash so that the +"under-lying" command can evaluate the contents. In this case, the FOR +COMMAND. + +== END: SIDEBAR: About The FOR command == + +You'll see two lines: + +LINE1: + set vn [format "BIT%d" $x] + +Format is like "sprintf". Because of the [brackets], it becomes what +you think. But here's how: + +First - the line is parsed - for {braces}. In this case, there are +none. The, the parser looks for [brackets] and finds them. The, +parser then evaluates the contents of the [brackets], and replaces +them. It is alot this bash statement. + + EXPORT vn=`date` + +LINE 2 & 3 + set $vn [expr (1024 * $x)] + global $vn + +In line 1, we dynamically created a variable name. Here, we are +assigning it a value. Lastly Line 3 we force the variable to be +global, not "local" the the "for command body" + +=============== +The PROCS + +proc create_mask { MSB LSB } { + ... body .... +} + +Like "for" - PROC is really just a command that takes 3 parameters. +The (1) NAME of the function, a (2) LIST of parameters, and a (3) BODY + +Again, this is at "level 0" so it is a global function. (Yes, TCL +supports local functions, you put them inside of a function} + +You'll see in some cases, I nest [brackets] alot and in others I'm +lazy or wanted it to be more clear... it is a matter of choice. +=============== + + +************************************************** +*************************************************** +=== TCL TOUR === +Open the file: "memory.tcl" +=============== + +Here is where I setup some 'memory definitions' that various targets can use. + +For example - there is an "unknown" memory region. + +All memory regions must have 2 things: + + (1) N_ + (2) NAME( array ) + And the array must have some specific names: + ( , THING ) + Where: THING is one of: + CHIPSELECT + BASE + LEN + HUMAN + TYPE + RWX - the access ablity. + WIDTH - the accessable width. + + ie: Some regions of memory are not 'word' + accessable. + +The function "address_info" - given an address should +tell you about the address. + + [as of this writing: 7/5/2008 I have done + only a little bit with this -Duane] + +=== +MAJOR FUNCTION: +== + +proc memread32 { ADDR } +proc memread16 { ADDR } +proc memread8 { ADDR } + +All read memory - and return the contents. + +[ fixme: 7/5/2008 - I need to create "memwrite" functions] + +************************************************** +*************************************************** +=== TCL TOUR === +Open the file: "mmr_helpers.tcl" +=============== + +This file is used to display and work with "memory mapped registers" + +For example - 'show_mmr32_reg' is given the NAME of the register to +display. The assumption is - the NAME is a global variable holding the +address of that MMR. + +The code does some tricks. The [set [set NAME]] is the TCL way +of doing double variable interpolation - like makefiles... + +In a makefile or shell script you may have seen this: + + FOO_linux = "Penguins rule" + FOO_winXP = "Broken Glass" + FOO_mac = "I like cat names" + + # Pick one + BUILD = linux + #BUILD = winXP + #BUILD = mac + FOO = ${FOO_${BUILD}} + +The "double [set] square bracket" thing is the TCL way, nothing more. + +---- + +The IF statement - and "CATCH" . + +Notice this IF COMMAND - (not statement) is like this: +[7/5/2008 it is this way] + + if ![catch { command } msg ] { + ...something... + } else { + error [format string...] + } + +The "IF" command expects either 2 params, or 4 params. + + === Sidebar: About "commands" === + + Take a look at the internals of "jim.c" + Look for the function: Jim_IfCoreCommand() + And all those other "CoreCommands" + + You'll notice - they all have "argc" and "argv" + + Yea, the entire thing is done that way. + + IF is a command. SO is "FOR" and "WHILE" and "DO" and the + others. That is why I keep using the prhase it is a "command" + + === END: Sidebar: About "commands" === + +Paramter 1 to the IF command is expected to be an expression. + +As such, I do not need to wrap it in {braces}. + +In this case, the "expression" is the resul of the "CATCH" command. + +CATCH - is an error catcher. + +You give CATCH 1 or 2 parameters. + The first 1st parameter is the "code to execute" + The 2nd (optional) is where to put the error message. + + CATCH returns 0 on success, 1 for failure. + The "![catch command]" is self explaintory. + + +The 3rd parameter to IF must be exacty "else" or "elseif" [I lied +above, the IF command can take many parameters they just have to +be joined by exactly the words "else" or "elseif". + +The 4th parameter contains: + + "error [format STRING....]" + +This lets me modify the previous lower level error by tacking more +text onto the end of it. In this case, i want to add the MMR register +name to make my error message look better. + +--------- +Back to something inside show_mmr32_reg{}. + +You'll see something 'set fn show_${NAME}_helper' Here I am +constructing a 'function name' Then - I look it up to see if it +exists. {the function: "proc_exists" does this} + +And - if it does - I call the function. + +In "C" it is alot like using: 'sprintf()' to construct a function name +string, then using "dlopen()" and "dlsym()" to look it up - and get a +function pointer - and calling the function pointer. + +In this case - I execute a dynamic command. You can do some cool +tricks with interpretors. + +---------- + +Function: show_mmr32_bits() + +In this case, we use the special TCL command "upvar" which tcl's way +of passing things by reference. In this case, we want to reach up into +the callers lexical scope and find the array named "NAMES" + +The rest of the function is pretty straight forward. + +First - we figure out the longest name. +Then print 4 rows of 8bits - with names. + + +************************************************** +*************************************************** +=== TCL TOUR === +Open the file: "chips/atmel/at91/usarts.tcl" +=============== + +First - about the AT91SAM series - all of the usarts +are basically identical... + +Second - there can be many of them. + +In this case - I do some more TCL tricks to dynamically +create functions out of thin air. + +Some assumptions: + +The "CHIP" file has defined some variables in a proper form. + +ie: AT91C_BASE_US0 - for usart0, + AT91C_BASE_US1 - for usart1 + ... And so on ... + +Near the end of the file - look for a large "foreach" loop that +looks like this: + + foreach WHO { US0 US1 US2 US3 US4 .... } { + + } + +In this case, I'm trying to figure out what USARTs exist. + +Step 1 - is to determine if the NAME has been defined. +ie: Does AT91C_BASE_USx - where X is some number exist? + +The "info exists VARNAME" tells you if the variable exists. Then - +inside the IF statement... There is another loop. This loop is the +name of various "sub-registers" within the USART. + +Some more trick are played with the [set VAR] backtick evaluation stuff. +And we create two variables + +We calculate and create the global variable name for every subregister in the USART. +And - declare that variable as GLOBAL so the world can find it. + +Then - we dynamically create a function - based on the register name. + +Look carefully at how that is done. You'll notice the FUNCTION BODY is +a string - not something in {braces}. Why? This is because we need TCL +to evaluate the contents of that string "*NOW*" - when $vn exists not +later, when the function "show_FOO" is invoked. + +Lastly - we build a "str" of commands - and create a single function - +with the generated list of commands for the entire USART. + +With that little bit of code - I now have a bunch of functions like: + + show_US0, show_US1, show_US2, .... etc ... + + And show_US0_MR, show_US0_IMR ... etc... + +And - I have this for every USART... without having to create tons of +boiler plate yucky code. + +**************************************** +**************************************** +END of the Tcl Intro and Walk Through +**************************************** +**************************************** + +FUTURE PLANS + + Some "GPIO" functions... diff --git a/src/tcl/bitsbytes.tcl b/src/tcl/bitsbytes.tcl new file mode 100644 index 00000000..b1771b4b --- /dev/null +++ b/src/tcl/bitsbytes.tcl @@ -0,0 +1,63 @@ +#---------------------------------------- +# Purpose - Create some $BIT variables +# Create $K and $M variables +# and some bit field extraction variables. +# Creat helper variables ... +# BIT0.. BIT31 + +for { set x 0 } { $x < 32 } { set x [expr $x + 1]} { + set vn [format "BIT%d" $x] + set $vn [expr (1 << $x)] + global $vn + +} + +# Create K bytes values +# __1K ... to __2048K +for { set x 1 } { $x < 2048 } { set x [expr $x * 2]} { + set vn [format "__%dK" $x] + set $vn [expr (1024 * $x)] + global $vn +} + +# Create M bytes values +# __1M ... to __2048K +for { set x 1 } { $x < 2048 } { set x [expr $x * 2]} { + set vn [format "__%dM" $x] + set $vn [expr (1024 * 1024 * $x)] + global $vn +} + +proc create_mask { MSB LSB } { + return [expr (((1 << ($MSB - $LSB + 1))-1) << $LSB)] + +} + +# Cut Bits $MSB to $LSB out of this value. +# Example: % format "0x%08x" [extract_bitfield 0x12345678 27 16] +# Result: 0x02340000 + +proc extract_bitfield { VALUE MSB LSB } { + return [expr [create_mask $MSB $LSB] & $VALUE] +} + + +# Cut bits $MSB to $LSB out of this value +# and shift (normalize) them down to bit 0. +# +# Example: % format "0x%08x" [normalize_bitfield 0x12345678 27 16] +# Result: 0x00000234 +# +proc normalize_bitfield { VALUE MSB LSB } { + return [expr [extract_bitfield $VALUE $MSB $LSB ] >> $LSB] +} + +proc show_normalize_bitfield { VALUE MSB LSB } { + set m [create_mask $MSB $LSB] + set mr [expr $VALUE & $m] + set sr [expr $mr >> $LSB] + puts [format "((0x%08x & 0x%08x) -> 0x%08x) >> %2d => (0x%x) %5d " $VALUE $m $mr $LSB $sr $sr] + return $sr +} + + diff --git a/src/tcl/chip/atmel/at91/aic.tcl b/src/tcl/chip/atmel/at91/aic.tcl new file mode 100644 index 00000000..245224a5 --- /dev/null +++ b/src/tcl/chip/atmel/at91/aic.tcl @@ -0,0 +1,101 @@ +set AIC_SMR [expr $AT91C_BASE_AIC + 0x00000000 ] +global AIC_SMR +set AIC_SVR [expr $AT91C_BASE_AIC + 0x00000080 ] +global AIC_SVR +set AIC_IVR [expr $AT91C_BASE_AIC + 0x00000100 ] +global AIC_IVR +set AIC_FVR [expr $AT91C_BASE_AIC + 0x00000104 ] +global AIC_FVR +set AIC_ISR [expr $AT91C_BASE_AIC + 0x00000108 ] +global AIC_ISR +set AIC_IPR [expr $AT91C_BASE_AIC + 0x0000010C ] +global AIC_IPR +set AIC_IMR [expr $AT91C_BASE_AIC + 0x00000110 ] +global AIC_IMR +set AIC_CISR [expr $AT91C_BASE_AIC + 0x00000114 ] +global AIC_CISR +set AIC_IECR [expr $AT91C_BASE_AIC + 0x00000120 ] +global AIC_IECR +set AIC_IDCR [expr $AT91C_BASE_AIC + 0x00000124 ] +global AIC_IDCR +set AIC_ICCR [expr $AT91C_BASE_AIC + 0x00000128 ] +global AIC_ICCR +set AIC_ISCR [expr $AT91C_BASE_AIC + 0x0000012C ] +global AIC_ISCR +set AIC_EOICR [expr $AT91C_BASE_AIC + 0x00000130 ] +global AIC_EOICR +set AIC_SPU [expr $AT91C_BASE_AIC + 0x00000134 ] +global AIC_SPU +set AIC_DCR [expr $AT91C_BASE_AIC + 0x00000138 ] +global AIC_DCR +set AIC_FFER [expr $AT91C_BASE_AIC + 0x00000140 ] +global AIC_FFER +set AIC_FFDR [expr $AT91C_BASE_AIC + 0x00000144 ] +global AIC_FFDR +set AIC_FFSR [expr $AT91C_BASE_AIC + 0x00000148 ] +global AIC_FFSR + + +proc aic_enable_disable_list { VAL ENAME DNAME } { + global AT91C_ID + + show_mmr32_bits AT91C_ID $VAL + +} + +proc show_AIC_IPR_helper { NAME ADDR VAL } { + aic_enable_disable_list $VAL "IRQ PENDING" "irq not-pending" +} + +proc show_AIC_IMR_helper { NAME ADDR VAL } { + aic_enable_disable_list $VAL "IRQ ENABLED" "irq disabled" +} + + +proc show_AIC { } { + global AIC_SMR + if [catch { mem2array aaa 32 $AIC_SMR [expr 32 * 4] } msg ] { + error [format "%s (%s)" $msg AIC_SMR] + } + puts "AIC_SMR: Mode & Type" + global AT91C_ID + for { set x 0 } { $x < 32 } { } { + puts -nonewline " " + puts -nonewline [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) $aaa($x)] + incr x + puts -nonewline [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) $aaa($x)] + incr x + puts -nonewline [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) $aaa($x)] + incr x + puts [format "%2d: %5s 0x%08x" $x $AT91C_ID($x) $aaa($x)] + incr x + } + global AIC_SVR + if [catch { mem2array aaa 32 $AIC_SVR [expr 32 * 4] } msg ] { + error [format "%s (%s)" $msg AIC_SVR] + } + puts "AIC_SVR: Vectors" + for { set x 0 } { $x < 32 } { } { + puts -nonewline " " + puts -nonewline [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) $aaa($x)] + incr x + puts -nonewline [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) $aaa($x)] + incr x + puts -nonewline [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) $aaa($x)] + incr x + puts [format "%2d: %5s 0x%08x" $x $AT91C_ID($x) $aaa($x)] + incr x + } + + foreach REG { + AIC_IVR AIC_FVR AIC_ISR + AIC_IPR AIC_IMR AIC_CISR AIC_IECR AIC_IDCR + AIC_ICCR AIC_ISCR AIC_EOICR AIC_SPU AIC_DCR + AIC_FFER AIC_FFDR AIC_FFSR } { + if [catch { show_mmr32_reg $REG } msg ] { + error $msg + break + } + } +} + diff --git a/src/tcl/chip/atmel/at91/at91sam7x128.tcl b/src/tcl/chip/atmel/at91/at91sam7x128.tcl new file mode 100644 index 00000000..1cf7c1cf --- /dev/null +++ b/src/tcl/chip/atmel/at91/at91sam7x128.tcl @@ -0,0 +1,128 @@ +source [find tcl/bitsbytes.tcl] +source [find tcl/cpu/arm/arm7tdmi.tcl] +source [find tcl/memory.tcl] +source [find tcl/mmr_helpers.tcl] + +set CHIP_MAKER atmel +set CHIP_FAMILY at91sam7 +set CHIP_NAME at91sam7x128 +# how many flash regions. +set N_FLASH 1 +set FLASH(0,CHIPSELECT) -1 +set FLASH(0,BASE) 0x00100000 +set FLASH(0,LEN) $__128K +set FLASH(0,HUMAN) "internal flash" +set FLASH(0,TYPE) "flash" +set FLASH(0,RWX) $RWX_R_X +set FLASH(0,ACCESS_WIDTH) $ACCESS_WIDTH_ANY +# how many ram regions. +set N_RAM 1 +set RAM(0,CHIPSELECT) -1 +set RAM(0,BASE) 0x00200000 +set RAM(0,LEN) $__32K +set RAM(0,HUMAN) "internal ram" +set RAM(0,TYPE) "ram" +set RAM(0,RWX) $RWX_RWX +set RAM(0,ACCESS_WIDTH) $ACCESS_WIDTH_ANY + +# I AM LAZY... I create 1 region for all MMRs. +set N_MMREGS 1 +set MMREGS(0,CHIPSELECT) -1 +set MMREGS(0,BASE) 0xfff00000 +set MMREGS(0,LEN) 0x000fffff +set MMREGS(0,HUMAN) "mm-regs" +set MMREGS(0,TYPE) "mmr" +set MMREGS(0,RWX) $RWX_RW +set MMREGS(0,ACCESS_WIDTH) $ACCESS_WIDTH_ANY + +# no external memory +set N_XMEM 0 + + + + +set AT91C_BASE_SYS 0xFFFFF000 +set AT91C_BASE_AIC 0xFFFFF000 +set AT91C_BASE_PDC_DBGU 0xFFFFF300 +set AT91C_BASE_DBGU 0xFFFFF200 +set AT91C_BASE_PIOA 0xFFFFF400 +set AT91C_BASE_PIOB 0xFFFFF600 +set AT91C_BASE_CKGR 0xFFFFFC20 +set AT91C_BASE_PMC 0xFFFFFC00 +set AT91C_BASE_RSTC 0xFFFFFD00 +set AT91C_BASE_RTTC 0xFFFFFD20 +set AT91C_BASE_PITC 0xFFFFFD30 +set AT91C_BASE_WDTC 0xFFFFFD40 +set AT91C_BASE_VREG 0xFFFFFD60 +set AT91C_BASE_MC 0xFFFFFF00 +set AT91C_BASE_PDC_SPI1 0xFFFE4100 +set AT91C_BASE_SPI1 0xFFFE4000 +set AT91C_BASE_PDC_SPI0 0xFFFE0100 +set AT91C_BASE_SPI0 0xFFFE0000 +set AT91C_BASE_PDC_US1 0xFFFC4100 +set AT91C_BASE_US1 0xFFFC4000 +set AT91C_BASE_PDC_US0 0xFFFC0100 +set AT91C_BASE_US0 0xFFFC0000 +set AT91C_BASE_PDC_SSC 0xFFFD4100 +set AT91C_BASE_SSC 0xFFFD4000 +set AT91C_BASE_TWI 0xFFFB8000 +set AT91C_BASE_PWMC_CH3 0xFFFCC260 +set AT91C_BASE_PWMC_CH2 0xFFFCC240 +set AT91C_BASE_PWMC_CH1 0xFFFCC220 +set AT91C_BASE_PWMC_CH0 0xFFFCC200 +set AT91C_BASE_PWMC 0xFFFCC000 +set AT91C_BASE_UDP 0xFFFB0000 +set AT91C_BASE_TC0 0xFFFA0000 +set AT91C_BASE_TC1 0xFFFA0040 +set AT91C_BASE_TC2 0xFFFA0080 +set AT91C_BASE_TCB 0xFFFA0000 +set AT91C_BASE_CAN_MB0 0xFFFD0200 +set AT91C_BASE_CAN_MB1 0xFFFD0220 +set AT91C_BASE_CAN_MB2 0xFFFD0240 +set AT91C_BASE_CAN_MB3 0xFFFD0260 +set AT91C_BASE_CAN_MB4 0xFFFD0280 +set AT91C_BASE_CAN_MB5 0xFFFD02A0 +set AT91C_BASE_CAN_MB6 0xFFFD02C0 +set AT91C_BASE_CAN_MB7 0xFFFD02E0 +set AT91C_BASE_CAN 0xFFFD0000 +set AT91C_BASE_EMAC 0xFFFDC000 +set AT91C_BASE_PDC_ADC 0xFFFD8100 +set AT91C_BASE_ADC 0xFFFD8000 + +set AT91C_ID(0) FIQ +set AT91C_ID(1) SYS +set AT91C_ID(2) PIOA +set AT91C_ID(3) PIOB +set AT91C_ID(4) SPI0 +set AT91C_ID(5) SPI1 +set AT91C_ID(6) US0 +set AT91C_ID(7) US1 +set AT91C_ID(8) SSC +set AT91C_ID(9) TWI +set AT91C_ID(10) PWMC +set AT91C_ID(11) UDP +set AT91C_ID(12) TC0 +set AT91C_ID(13) TC1 +set AT91C_ID(14) TC2 +set AT91C_ID(15) CAN +set AT91C_ID(16) EMAC +set AT91C_ID(17) ADC +set AT91C_ID(18) "" +set AT91C_ID(19) "" +set AT91C_ID(20) "" +set AT91C_ID(21) "" +set AT91C_ID(22) "" +set AT91C_ID(23) "" +set AT91C_ID(24) "" +set AT91C_ID(25) "" +set AT91C_ID(26) "" +set AT91C_ID(27) "" +set AT91C_ID(28) "" +set AT91C_ID(29) "" +set AT91C_ID(30) IRQ0 +set AT91C_ID(31) IRQ1 + +source [find tcl/chip/atmel/at91/aic.tcl] +source [find tcl/chip/atmel/at91/usarts.tcl] +source [find tcl/chip/atmel/at91/pmc.tcl] +source [find tcl/chip/atmel/at91/rtt.tcl] diff --git a/src/tcl/chip/atmel/at91/at91sam7x256.tcl b/src/tcl/chip/atmel/at91/at91sam7x256.tcl new file mode 100644 index 00000000..1cba4859 --- /dev/null +++ b/src/tcl/chip/atmel/at91/at91sam7x256.tcl @@ -0,0 +1,126 @@ +source [find tcl/bitsbytes.tcl] +source [find tcl/cpu/arm/arm7tdmi.tcl] +source [find tcl/memory.tcl] +source [find tcl/mmr_helpers.tcl] + +set CHIP_MAKER atmel +set CHIP_FAMILY at91sam7 +set CHIP_NAME at91sam7x256 +# how many flash regions. +set N_FLASH 1 +set FLASH(0,CHIPSELECT) -1 +set FLASH(0,BASE) 0x00100000 +set FLASH(0,LEN) $__256K +set FLASH(0,HUMAN) "internal flash" +set FLASH(0,TYPE) "flash" +set FLASH(0,RWX) $RWX_R_X +set FLASH(0,ACCESS_WIDTH) $ACCESS_WIDTH_ANY +# how many ram regions. +set N_RAM 1 +set RAM(0,CHIPSELECT) -1 +set RAM(0,BASE) 0x00200000 +set RAM(0,LEN) $__64K +set RAM(0,HUMAN) "internal ram" +set RAM(0,TYPE) "ram" +set RAM(0,RWX) $RWX_RWX +set RAM(0,ACCESS_WIDTH) $ACCESS_WIDTH_ANY + +# I AM LAZY... I create 1 region for all MMRs. +set N_MMREGS 1 +set MMREGS(0,CHIPSELECT) -1 +set MMREGS(0,BASE) 0xfff00000 +set MMREGS(0,LEN) 0x000fffff +set MMREGS(0,HUMAN) "mm-regs" +set MMREGS(0,TYPE) "mmr" +set MMREGS(0,RWX) $RWX_RW +set MMREGS(0,ACCESS_WIDTH) $ACCESS_WIDTH_ANY + +# no external memory +set N_XMEM 0 + +set AT91C_BASE_SYS 0xFFFFF000 +set AT91C_BASE_AIC 0xFFFFF000 +set AT91C_BASE_PDC_DBGU 0xFFFFF300 +set AT91C_BASE_DBGU 0xFFFFF200 +set AT91C_BASE_PIOA 0xFFFFF400 +set AT91C_BASE_PIOB 0xFFFFF600 +set AT91C_BASE_CKGR 0xFFFFFC20 +set AT91C_BASE_PMC 0xFFFFFC00 +set AT91C_BASE_RSTC 0xFFFFFD00 +set AT91C_BASE_RTTC 0xFFFFFD20 +set AT91C_BASE_PITC 0xFFFFFD30 +set AT91C_BASE_WDTC 0xFFFFFD40 +set AT91C_BASE_VREG 0xFFFFFD60 +set AT91C_BASE_MC 0xFFFFFF00 +set AT91C_BASE_PDC_SPI1 0xFFFE4100 +set AT91C_BASE_SPI1 0xFFFE4000 +set AT91C_BASE_PDC_SPI0 0xFFFE0100 +set AT91C_BASE_SPI0 0xFFFE0000 +set AT91C_BASE_PDC_US1 0xFFFC4100 +set AT91C_BASE_US1 0xFFFC4000 +set AT91C_BASE_PDC_US0 0xFFFC0100 +set AT91C_BASE_US0 0xFFFC0000 +set AT91C_BASE_PDC_SSC 0xFFFD4100 +set AT91C_BASE_SSC 0xFFFD4000 +set AT91C_BASE_TWI 0xFFFB8000 +set AT91C_BASE_PWMC_CH3 0xFFFCC260 +set AT91C_BASE_PWMC_CH2 0xFFFCC240 +set AT91C_BASE_PWMC_CH1 0xFFFCC220 +set AT91C_BASE_PWMC_CH0 0xFFFCC200 +set AT91C_BASE_PWMC 0xFFFCC000 +set AT91C_BASE_UDP 0xFFFB0000 +set AT91C_BASE_TC0 0xFFFA0000 +set AT91C_BASE_TC1 0xFFFA0040 +set AT91C_BASE_TC2 0xFFFA0080 +set AT91C_BASE_TCB 0xFFFA0000 +set AT91C_BASE_CAN_MB0 0xFFFD0200 +set AT91C_BASE_CAN_MB1 0xFFFD0220 +set AT91C_BASE_CAN_MB2 0xFFFD0240 +set AT91C_BASE_CAN_MB3 0xFFFD0260 +set AT91C_BASE_CAN_MB4 0xFFFD0280 +set AT91C_BASE_CAN_MB5 0xFFFD02A0 +set AT91C_BASE_CAN_MB6 0xFFFD02C0 +set AT91C_BASE_CAN_MB7 0xFFFD02E0 +set AT91C_BASE_CAN 0xFFFD0000 +set AT91C_BASE_EMAC 0xFFFDC000 +set AT91C_BASE_PDC_ADC 0xFFFD8100 +set AT91C_BASE_ADC 0xFFFD8000 + +set AT91C_ID(0) "FIQ" +set AT91C_ID(1) "SYS" +set AT91C_ID(2) "PIOA" +set AT91C_ID(3) "PIOB" +set AT91C_ID(4) "SPI0" +set AT91C_ID(5) "SPI1" +set AT91C_ID(6) "US0" +set AT91C_ID(7) "US1" +set AT91C_ID(8) "SSC" +set AT91C_ID(9) "TWI" +set AT91C_ID(10) "PWMC" +set AT91C_ID(11) "UDP" +set AT91C_ID(12) "TC0" +set AT91C_ID(13) "TC1" +set AT91C_ID(14) "TC2" +set AT91C_ID(15) "CAN" +set AT91C_ID(16) "EMAC" +set AT91C_ID(17) "ADC" +set AT91C_ID(18) "" +set AT91C_ID(19) "" +set AT91C_ID(20) "" +set AT91C_ID(21) "" +set AT91C_ID(22) "" +set AT91C_ID(23) "" +set AT91C_ID(24) "" +set AT91C_ID(25) "" +set AT91C_ID(26) "" +set AT91C_ID(27) "" +set AT91C_ID(28) "" +set AT91C_ID(29) "" +set AT91C_ID(30) "IRQ0" +set AT91C_ID(31) "IRQ1" + + +source [find tcl/chip/atmel/at91/aic.tcl] +source [find tcl/chip/atmel/at91/usarts.tcl] +source [find tcl/chip/atmel/at91/pmc.tcl] +source [find tcl/chip/atmel/at91/rtt.tcl] diff --git a/src/tcl/chip/atmel/at91/pmc.tcl b/src/tcl/chip/atmel/at91/pmc.tcl new file mode 100644 index 00000000..584acb80 --- /dev/null +++ b/src/tcl/chip/atmel/at91/pmc.tcl @@ -0,0 +1,17 @@ + +if [info exists AT91C_MAINOSC_FREQ] { + # user set this... let it be. +} { + # 18.432mhz is a common thing... + set AT91C_MAINOSC_FREQ 18432000 +} +global AT91C_MAINOSC_FREQ + +if [info exists AT91C_SLOWOSC_FREQ] { + # user set this... let it be. +} { + # 32khz is the norm + set AT91C_SLOWOSC_FREQ 32768 +} +global AT91C_SLOWOSC_FREQ + diff --git a/src/tcl/chip/atmel/at91/rtt.tcl b/src/tcl/chip/atmel/at91/rtt.tcl new file mode 100644 index 00000000..56abb29e --- /dev/null +++ b/src/tcl/chip/atmel/at91/rtt.tcl @@ -0,0 +1,54 @@ + +set RTTC_RTMR [expr $AT91C_BASE_RTTC + 0x00] +set RTTC_RTAR [expr $AT91C_BASE_RTTC + 0x04] +set RTTC_RTVR [expr $AT91C_BASE_RTTC + 0x08] +set RTTC_RTSR [expr $AT91C_BASE_RTTC + 0x0c] +global RTTC_RTMR +global RTTC_RTAR +global RTTC_RTVR +global RTTC_RTSR + +proc show_RTTC_RTMR_helper { NAME ADDR VAL } { + set rtpres [expr $VAL & 0x0ffff] + global BIT16 BIT17 + if { $rtpres == 0 } { + set rtpres 65536; + } + global AT91C_SLOWOSC_FREQ + set f [expr double($AT91C_SLOWOSC_FREQ) / double($rtpres)] + puts [format "\tPrescale value: 0x%04x (%5d) => %f Hz" $rtpres $rtpres $f] + if { $VAL & $BIT16 } { + puts "\tBit16 -> Alarm IRQ Enabled" + } else { + puts "\tBit16 -> Alarm IRQ Disabled" + } + if { $VAL & $BIT17 } { + puts "\tBit17 -> RTC Inc IRQ Enabled" + } else { + puts "\tBit17 -> RTC Inc IRQ Disabled" + } + # Bit 18 is write only. +} + +proc show_RTTC_RTSR_helper { NAME ADDR VAL } { + global BIT0 BIT1 + if { $VAL & $BIT0 } { + puts "\tBit0 -> ALARM PENDING" + } else { + puts "\tBit0 -> alarm not pending" + } + if { $VAL & $BIT1 } { + puts "\tBit0 -> RTINC PENDING" + } else { + puts "\tBit0 -> rtinc not pending" + } +} + +proc show_RTTC { } { + + show_mmr32_reg RTTC_RTMR + show_mmr32_reg RTTC_RTAR + show_mmr32_reg RTTC_RTVR + show_mmr32_reg RTTC_RTSR +} + diff --git a/src/tcl/chip/atmel/at91/usarts.tcl b/src/tcl/chip/atmel/at91/usarts.tcl new file mode 100644 index 00000000..19f4ed4c --- /dev/null +++ b/src/tcl/chip/atmel/at91/usarts.tcl @@ -0,0 +1,135 @@ +# the DBGU and USARTs are 'almost' indentical' +set DBGU_CR [expr $AT91C_BASE_DBGU + 0x00000000] +set DBGU_MR [expr $AT91C_BASE_DBGU + 0x00000004] +set DBGU_IER [expr $AT91C_BASE_DBGU + 0x00000008] +set DBGU_IDR [expr $AT91C_BASE_DBGU + 0x0000000C] +set DBGU_IMR [expr $AT91C_BASE_DBGU + 0x00000010] +set DBGU_CSR [expr $AT91C_BASE_DBGU + 0x00000014] +set DBGU_RHR [expr $AT91C_BASE_DBGU + 0x00000018] +set DBGU_THR [expr $AT91C_BASE_DBGU + 0x0000001C] +set DBGU_BRGR [expr $AT91C_BASE_DBGU + 0x00000020] +# no RTOR +# no TTGR +# no FIDI +# no NER +set DBGU_CIDR [expr $AT91C_BASE_DBGU + 0x00000040] +set DBGU_EXID [expr $AT91C_BASE_DBGU + 0x00000044] +set DBGU_FNTR [expr $AT91C_BASE_DBGU + 0x00000048] + + +set USx_CR 0x00000000 +set USx_MR 0x00000004 +set USx_IER 0x00000008 +set USx_IDR 0x0000000C +set USx_IMR 0x00000010 +set USx_CSR 0x00000014 +set USx_RHR 0x00000018 +set USx_THR 0x0000001C +set USx_BRGR 0x00000020 +set USx_RTOR 0x00000024 +set USx_TTGR 0x00000028 +set USx_FIDI 0x00000040 +set USx_NER 0x00000044 +set USx_IF 0x0000004C + +# Create all the uarts that exist.. +# we blow up if there are >9 + + +proc show_mmr_USx_MR_helper { NAME ADDR VAL } { + # First - just print it + + set x [show_normalize_bitfield $VAL 3 0] + if { $x == 0 } { + puts "\tNormal operation" + } else { + puts [format "\tNon Normal operation mode: 0x%02x" $x] + } + + set x [show_normalize_bitfield $VAL 11 9] + set s "unknown" + switch -exact $x { + 0 { set s "Even" } + 1 { set s "Odd" } + 2 { set s "Force=0" } + 3 { set s "Force=1" } + * { + set $x [expr $x & 6] + switch -exact $x { + 4 { set s "None" } + 6 { set s "Multidrop Mode" } + } + } + } + puts [format "\tParity: %s " $s] + + set x [expr 5 + [show_normalize_bitfield $VAL 7 6]] + puts [format "\tDatabits: %d" $x] + + set x [show_normalize_bitfield $VAL 13 12] + switch -exact $x { + 0 { puts "\tStop bits: 1" } + 1 { puts "\tStop bits: 1.5" } + 2 { puts "\tStop bits: 2" } + 3 { puts "\tStop bits: Illegal/Reserved" } + } +} + +# For every possbile usart... +foreach WHO { US0 US1 US2 US3 US4 US5 US6 US7 US8 US9 } { + set n AT91C_BASE_[set WHO] + set str "" + + # Only if it exists on the chip + if [ info exists $n ] { + # Hence: $n - is like AT91C_BASE_USx + # For every sub-register + foreach REG {CR MR IER IDR IMR CSR RHR THR BRGR RTOR TTGR FIDI NER IF} { + # vn = variable name + set vn [set WHO]_[set REG] + # vn = USx_IER + # vv = variable value + set vv [expr $$n + [set USx_[set REG]]] + # And VV is the address in memory of that register + + + # make that VN a GLOBAL so others can find it + global $vn + set $vn $vv + + # Create a command for this specific register. + proc show_$vn { } "show_mmr32_reg $vn" + + # Add this command to the Device(as a whole) command + set str "$str\nshow_$vn" + } + # Now - create the DEVICE(as a whole) command + set fn show_$WHO + proc $fn { } $str + } +} + +# The Debug Uart is special.. +set str "" + + +# For every sub-register +foreach REG {DBGU_CR DBGU_MR DBGU_IER DBGU_IDR DBGU_IMR + DBGU_CSR DBGU_RHR DBGU_THR DBGU_BRGR DBGU_CIDR DBGU_EXID DBGU_FNTR} { + + # Create a command for this specific register. + proc show_$REG { } "show_mmr32_reg $REG" + + # Add this command to the Device(as a whole) command + set str "$str\nshow_$REG" +} + +# Now - create the DEVICE(as a whole) command +proc show_DBGU { } $str + +unset str + +proc show_DBGU_MR_helper { NAME ADDR VAL } { show_mmr_USx_MR_helper $NAME $ADDR $VAL } + + + diff --git a/src/tcl/cpu/arm/arm7tdmi.tcl b/src/tcl/cpu/arm/arm7tdmi.tcl new file mode 100644 index 00000000..37db2661 --- /dev/null +++ b/src/tcl/cpu/arm/arm7tdmi.tcl @@ -0,0 +1,6 @@ +set CPU_TYPE arm +set CPU_NAME arm7tdmi +set CPU_ARCH armv4t +set CPU_MAX_ADDRESS 0xFFFFFFFF +set CPU_NBITS 32 + diff --git a/src/tcl/cpu/arm/arm920.tcl b/src/tcl/cpu/arm/arm920.tcl new file mode 100644 index 00000000..f19b20b3 --- /dev/null +++ b/src/tcl/cpu/arm/arm920.tcl @@ -0,0 +1,6 @@ +set CPU_TYPE arm +set CPU_NAME arm920 +set CPU_ARCH armv4t +set CPU_MAX_ADDRESS 0xFFFFFFFF +set CPU_NBITS 32 + diff --git a/src/tcl/cpu/arm/arm946.tcl b/src/tcl/cpu/arm/arm946.tcl new file mode 100644 index 00000000..52041017 --- /dev/null +++ b/src/tcl/cpu/arm/arm946.tcl @@ -0,0 +1,6 @@ +set CPU_TYPE arm +set CPU_NAME arm946 +set CPU_ARCH armv5te +set CPU_MAX_ADDRESS 0xFFFFFFFF +set CPU_NBITS 32 + diff --git a/src/tcl/cpu/arm/arm966.tcl b/src/tcl/cpu/arm/arm966.tcl new file mode 100644 index 00000000..83ce0f67 --- /dev/null +++ b/src/tcl/cpu/arm/arm966.tcl @@ -0,0 +1,6 @@ +set CPU_TYPE arm +set CPU_NAME arm966 +set CPU_ARCH armv5te +set CPU_MAX_ADDRESS 0xFFFFFFFF +set CPU_NBITS 32 + diff --git a/src/tcl/memory.tcl b/src/tcl/memory.tcl new file mode 100644 index 00000000..1f90ef2c --- /dev/null +++ b/src/tcl/memory.tcl @@ -0,0 +1,108 @@ +# MEMORY +# +# All Memory regions have two components. +# (1) A count of regions, in the form N_NAME +# (2) An array within info about each region. +# +# The ARRAY +# +# ( RegionNumber , ATTRIBUTE ) +# +# Where is one of: +# +# N_FLASH & FLASH (internal memory) +# N_RAM & RAM (internal memory) +# N_MMREGS & MMREGS (for memory mapped registers) +# N_XMEM & XMEM (off chip memory, ie: flash on cs0, sdram on cs2) +# or N_UNKNOWN & UNKNOWN for things that do not exist. +# +# We have 1 unknown region. +set N_UNKNOWN 1 +# All MEMORY regions must have these attributes +# CS - chip select (if internal, use -1) +set UNKNOWN(0,CHIPSELECT) -1 +# BASE - base address in memory +set UNKNOWN(0,BASE) 0 +# LEN - length in bytes +set UNKNOWN(0,LEN) $CPU_MAX_ADDRESS +# HUMAN - human name of the region +set UNKNOWN(0,HUMAN) "unknown" +# TYPE - one of: +# flash, ram, mmr, unknown +# For harvard arch: +# iflash, dflash, iram, dram +set UNKNOWN(0,TYPE) "unknown" +# RWX - access ablity +# unix style chmod bits +# 0 - no access +# 1 - execute +# 2 - write +# 4 - read +# hence: 7 - readwrite execute +set RWX_NO_ACCESS 0 +set RWX_X_ONLY $BIT0 +set RWX_W_ONLY $BIT1 +set RWX_R_ONLY $BIT2 +set RWX_RW [expr $RWX_R_ONLY + $RWX_W_ONLY] +set RWX_R_X [expr $RWX_R_ONLY + $RWX_X_ONLY] +set RWX_RWX [expr $RWX_R_ONLY + $RWX_W_ONLY + $RWX_X_ONLY] +set UNKNOWN(0,RWX) $RWX_NO_ACCESS + +# WIDTH - access width +# 8,16,32 [0 means ANY] +set ACCESS_WIDTH_NONE 0 +set ACCESS_WIDTH_8 $BIT0 +set ACCESS_WIDTH_16 $BIT1 +set ACCESS_WIDTH_32 $BIT2 +set ACCESS_WIDTH_ANY [expr $ACCESS_WIDTH_8 + $ACCESS_WIDTH_16 + $ACCESS_WIDTH_32] +set UNKNOWN(0,ACCESS_WIDTH) $ACCESS_WIDTH_NONE + +proc iswithin { ADDRESS BASE LEN } { + return [expr ((($ADDRESS - $BASE) > 0) && (($ADDRESS - $BASE + $LEN) > 0))] +} + +proc address_info { ADDRESS } { + + foreach WHERE { FLASH RAM MMREGS XMEM UNKNOWN } { + if { info exists $WHERE } { + set lmt [set N_[set WHERE]] + for { set region 0 } { $region < $lmt } { incr region } { + if { iswithin $ADDRESS $WHERE($region,BASE) $WHERE($region,LEN) } { + return "$WHERE $region"; + } + } + } + } + + # Return the 'unknown' + return "UNKNOWN 0" +} + +proc memread32 {ADDR } { + set foo(0) 0 + if ![ catch { mem2array foo 32 $ADDR 1 } msg ] { + return $foo(0) + } else { + error "memead32: $msg" + } +} + +proc memread16 {ADDR } { + set foo(0) 0 + if ![ catch { mem2array foo 16 $ADDR 1 } msg ] { + return $foo(0) + } else { + error "memead16: $msg" + } +} + +proc memread82 {ADDR } { + set foo(0) 0 + if ![ catch { mem2array foo 8 $ADDR 1 } msg ] { + return $foo(0) + } else { + error "memead8: $msg" + } +} + + diff --git a/src/tcl/mmr_helpers.tcl b/src/tcl/mmr_helpers.tcl new file mode 100644 index 00000000..5dac48a8 --- /dev/null +++ b/src/tcl/mmr_helpers.tcl @@ -0,0 +1,59 @@ + +proc proc_exists { NAME } { + set n [info commands $NAME] + set l [string length $n] + return [expr $l != 0] +} + +# Give: REGISTER name - must be a global variable. +proc show_mmr32_reg { NAME } { + + global $NAME + # we want $($NAME) + set a [set [set NAME]] + + if ![catch { set v [memread32 $a] } msg ] { + puts [format "%10s: (0x%08x): 0x%08x" $NAME $a $v] + + # Was a helper defined? + set fn show_${NAME}_helper + if [ proc_exists $fn ] { + # Then call it + $fn $NAME $a $v + } + return $v; + } else { + error [format "%s (%s)" $msg $NAME ] + } +} + + +# Give: NAMES - an array of names accessable +# in the callers symbol-scope. +# VAL - the bits to display. + +proc show_mmr32_bits { NAMES VAL } { + + upvar $NAMES MYNAMES + + set w 0 + foreach {IDX N} $MYNAMES { + set l [string length $N] + if { $l > $w } { set w $l } + } + + for { set x 24 } { $x >= 0 } { incr x -8 } { + puts -nonewline " " + for { set y 7 } { $y >= 0 } { incr y -1 } { + set s $MYNAMES([expr $x + $y]) + puts -nonewline [format "%2d: %-*s | " [expr $x + $y] $w $s ] + } + puts "" + + puts -nonewline " " + for { set y 7 } { $y >= 0 } { incr y -1 } { + puts -nonewline [format " %d%*s | " [expr !!($VAL & (1 << ($x + $y)))] [expr $w -1] ""] + } + puts "" + } +} diff --git a/src/tcl/readable.tcl b/src/tcl/readable.tcl new file mode 100644 index 00000000..39f43caa --- /dev/null +++ b/src/tcl/readable.tcl @@ -0,0 +1,25 @@ +proc iswithin { ADDRESS BASE LEN } { + return [expr ((($ADDRESS - $BASE) > 0) && (($ADDRESS - $BASE + $LEN) > 0))] +} + +proc memorytype { ADDRESS } { + for { set chip 0 } { $chip < $N_CHIP } { incr chip } { + if { iswithin $ADDRESS $FLASH($chip,BASE) $FLASH($chip,LEN) } { + return "flash" + } + } + + for { set chip 0 } { $chip < $N_RAM } { incr chip } { + if { iswithin $ADDRESS $RAM($chip,BASE) $RAM($chip,LEN) } { + return "ram" + } + } +} + +# default to 32bit reads. +proc isreadable { ADDRESS } { + return isreadable32 $ADDRESS +} + +proc isreadable32 { ADDRESS } { + \ No newline at end of file