diff --git a/src/avrdude.1 b/src/avrdude.1 index 9594a07e..db8f4392 100644 --- a/src/avrdude.1 +++ b/src/avrdude.1 @@ -46,7 +46,7 @@ .Op Fl q .Op Fl T Ar cmd .Op Fl t -.Op Fl U Ar memtype:op:filename:filefmt +.Op Fl U Ar memory:op:filename:filefmt .Op Fl v .Op Fl x Ar extended_param .Op Fl V @@ -710,15 +710,15 @@ Tells to enter the interactive terminal shell before up- or downloading files via the -U option or processing other -T terminal commands, if any. See below for a detailed description of the terminal mode. -.It Xo Fl U Ar memtype Ns +.It Xo Fl U Ar memory Ns .Ar \&: Ns Ar op Ns .Ar \&: Ns Ar filename Ns .Op \&: Ns Ar format .Xc Perform a memory operation as indicated. Multiple -U operations are allowed. The -.Ar memtype -field specifies the memory type to operate on. +.Ar memory +field specifies the memory to operate on. The available memory types are device-dependent, the actual configuration can be viewed with the .Cm part @@ -962,7 +962,7 @@ The .Ar addr and .Ar len -parameters of the dump, read, write and erase commands can be +parameters of the dump, read, write, save and erase commands can be negative with the same syntax as substring computations in perl or python. The table below details their meaning with respect to an example memory of size sz=0x800. @@ -1077,6 +1077,19 @@ and, if necessary, pads the remaining space by repeating the last .Ar data item. The fill write command does not write beyond the specified memory area even if more data than needed were given. +.It Ar save memory {addr len} file[:format] +Save one or more memory segments to a file in a format specified by the +:format letter. The default is :r for raw binary. Each memory segment is +described by an address and length pair. In absence of any memory segments +the entire memory is saved to the file. Only Motorola S-Record (:s) and +Intel Hex (:i or :I) formats store address information with the saved +data. +.Nm Avrdude +cannot currently save ELF file formats. All the other file formats lose +the address information and concatenate the chosen memory segments into +the output file. If the file name is - then +.Nm +writes to stdout. .It Ar erase Perform a chip erase and discard all pending writes to EEPROM and flash. Note that EEPROM will be preserved if the EESAVE fuse bit is set. diff --git a/src/doc/avrdude.texi b/src/doc/avrdude.texi index ce6afba0..c5e11f60 100644 --- a/src/doc/avrdude.texi +++ b/src/doc/avrdude.texi @@ -770,11 +770,11 @@ downloading files via the @code{-U} option or processing other @code{-T} terminal commands, if any. See below for a detailed description of the terminal mode. -@item -U @var{memtype}:@var{op}:@var{filename}[:@var{format}] +@item -U @var{memory}:@var{op}:@var{filename}[:@var{format}] Perform a memory operation. Multiple @option{-U} options can be specified in order to operate on multiple memories on the same command-line invocation. The -@var{memtype} field specifies the memory type to operate on. Use +@var{memory} field specifies the memory type to operate on. Use the @option{-v} option on the command line or the @code{part} command from terminal mode to display all the memory types supported by a particular device. @@ -1959,7 +1959,7 @@ abbreviated to the shortest unambiguous form. Terminal mode provides a command history using readline(3), so previously entered command lines can be recalled and edited. -The @var{addr} and @var{len} parameters of the dump, read, write, and erase +The @var{addr} and @var{len} parameters of the dump, read, write, save and erase commands can be negative with the same syntax as substring computations in perl or python. The table below details their meaning with respect to an example memory of size @code{sz=0x800}. @@ -2037,28 +2037,28 @@ The following commands are implemented for all programmers: @table @code -@item dump @var{memtype} @var{addr} @var{len} +@item dump @var{memory} @var{addr} @var{len} Read from the specified memory interval (see above), and display in the usual hexadecimal and ASCII form. -@item dump @var{memtype} @var{addr} +@item dump @var{memory} @var{addr} Read from memory addr as many bytes as the most recent dump memory addr len command with this very memory had specified (default 256 bytes), and display them. -@item dump @var{memtype} +@item dump @var{memory} Continue dumping from the memory and location where the most recent dump command left off; if no previous dump command has addressed a memory an error message will be shown. -@item dump @var{memtype} @var{addr} @dots{} -Start reading from @var{addr}, all the way to the last memory address (deprecated: use @code{dump @var{memtype} @var{addr} -1}). +@item dump @var{memory} @var{addr} @dots{} +Start reading from @var{addr}, all the way to the last memory address (deprecated: use @code{dump @var{memory} @var{addr} -1}). -@item dump @var{memtype} @dots{} -Read all bytes from the specified memory, and display them (deprecated: use @code{dump @var{memtype} 0 -1}). +@item dump @var{memory} @dots{} +Read all bytes from the specified memory, and display them (deprecated: use @code{dump @var{memory} 0 -1}). @item read Can be used as an alias for dump. -@item write @var{memtype} @var{addr} @var{data[,]} @{@var{data[,]}@} +@item write @var{memory} @var{addr} @var{data[,]} @var{@{data[,]@}} Manually program the respective memory cells, starting at address @var{addr}, using the data items provided. The terminal implements reading from and writing to flash and EEPROM type memories normally @@ -2178,16 +2178,27 @@ as it is the same as @code{0xFFFFffffFFFFffffU}. One trailing comma at the end of data items is ignored to facilitate copy and paste of lists. -@item write @var{memtype} @var{addr} @var{data} +@item write @var{memory} @var{addr} @var{data} The start address @code{addr} may be omitted if the size of the memory being written to is 1 byte in size. -@item write @var{memtype} @var{addr} @var{len} @var{data[,]} @{@var{data[,]}@} @dots{} +@item write @var{memory} @var{addr} @var{len} @var{data[,]} @var{@{data[,]@}} @dots{} The ellipsis @dots{} form writes the data to the entire memory intervall addressed by @var{addr len} and, if necessary, pads the remaining space by repeating the last data item. The fill write command does not write beyond the specified memory area even if more data than needed were given. +@item save @var{memory} @var{@{addr len@}} @var{file[:format]} +Save one or more memory segments to a file in a format specified by the +@code{:}format letter. The default is @code{:r} for raw binary. Each +memory segment is described by an address and length pair. In absence of +any memory segments the entire memory is saved to the file. Only Motorola +S-Record (@code{:s}) and Intel Hex (@code{:i} or @code{:I}) formats store +address information with the saved data. Avrdude cannot currently save +ELF file formats. All the other file formats lose the address information +and concatenate the chosen memory segments into the output file. If the +file name is - then avrdude writes to stdout. + @item erase Perform a chip erase and discard all pending writes to EEPROM and flash. Note that EEPROM will be preserved if the EESAVE fuse bit is set. @@ -2869,7 +2880,7 @@ part # parameters for bootloaders autobaud_sync = ; # autobaud detection byte, default 0x30 - memory + memory paged = ; # yes/no (flash only, do not use for EEPROM) offset = ; # memory offset size = ; # bytes diff --git a/src/fileio.c b/src/fileio.c index 480b63bc..d3b2f4a2 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -1203,13 +1203,13 @@ static int b2num(const char *filename, FILE *f, const AVRMEM *mem, const Segment break; } - for (int i = segp->addr; i < segp->addr + segp->len; i++) { + for(int seen = 0, i = segp->addr; i < segp->addr + segp->len; i++) { char cbuf[81]; - if (i > 0) { - if (putc(',', f) == EOF) + if(seen++) + if(putc(',', f) == EOF) goto writeerr; - } + unsigned num = mem->buf[i]; /* * For a base of 8 and a value < 8 to convert, don't write the @@ -1440,7 +1440,7 @@ int fileio(int op, const char *filename, FILEFMT format, return -1; } - if(size < 0 || op == FIO_READ || FIO_READ_FOR_VERIFY) + if(size < 0 || op == FIO_READ || op == FIO_READ_FOR_VERIFY) size = mem->size; const Segment_t seg = {0, size}; @@ -1449,7 +1449,7 @@ int fileio(int op, const char *filename, FILEFMT format, // Normalise segment address and length to be non-negative -int segmemt_normalise(const AVRMEM *mem, Segment_t *segp) { +int segment_normalise(const AVRMEM *mem, Segment_t *segp) { int addr = segp->addr, len = segp->len, maxsize = mem->size; int digits = maxsize > 0x10000? 5: 4; @@ -1493,7 +1493,7 @@ static int fileio_segments_normalise(int oprwv, const char *filename, FILEFMT fo return -1; for(int i=0; i 1 && str_eq(argv[1], "-?"))) { + msg_error( + "Syntax: save { } [:]\n" + "Function: save memory segments to file (default format :r raw binary)\n" + ); + return -1; + } + + AVRMEM *mem, *omem; + if(!(omem = avr_locate_mem(p, argv[1]))) { + pmsg_error("(save) %s memory type not defined for part %s\n", argv[1], p->desc); + return -1; + } + + if(argc > 3 && !(argc&1)) { + pmsg_error("(save) need pairs to describe memory segments\n"); + return -1; + } + + // Last char of filename is format if the penultimate char is a colon + FILEFMT format = FMT_RBIN; + char *fn = argv[argc-1]; + size_t len = strlen(fn); + if(len > 2 && fn[len-2] == ':') { // Assume format specified + format = fileio_format(fn[len-1]); + if(format == FMT_ERROR) { + pmsg_error("(save) invalid file format :%c; known formats are\n", fn[len-1]); + for(int f, c, i=0; i<62; i++) { + c = i<10? '0'+i: (i&1? 'A': 'a') + (i-10)/2; + f = fileio_format(c); + if(f != FMT_ERROR) + msg_error(" :%c %s\n", c, fileio_fmtstr(f)); + } + return -1; + } + len -= 2; + } + char *filename = memcpy(cfg_malloc(__func__, len+1), fn, len); + + mem = avr_dup_mem(omem); + int n = argc > 3? (argc-3)/2: 1; + Segment_t *seglist = cfg_malloc(__func__, n*sizeof*seglist); + + int ret = -1; + + // Build memory segment list + seglist[0].addr = 0; // Defaults to entire memory + seglist[0].len = mem->size; + if(argc > 3) { + for(int cc = 2, i = 0; i < n; i++, cc+=2) { + const char *errstr; + seglist[i].addr = str_int(argv[cc], STR_INT32, &errstr); + if(errstr) { + pmsg_error("(save) address %s: %s\n", argv[cc], errstr); + goto done; + } + seglist[i].len = str_int(argv[cc+1], STR_INT32, &errstr); + if(errstr) { + pmsg_error("(save) length %s: %s\n", argv[cc], errstr); + goto done; + } + } + } + + int nbytes = 0; // Total number of bytes to save + for(int i=0; iread_byte_cached(pgm, p, mem, j, mem->buf+j); + if(rc < 0) { + report_progress(1, -1, NULL); + pmsg_error("(save) error reading %s address 0x%0*x of part %s\n", + mem->desc, j<16? 1: j<256? 2: j<65536? 4: 5, j, p->desc); + return -1; + } + report_progress(j, nbytes, NULL); + } + } + report_progress(1, 1, NULL); + + ret = fileio_segments(FIO_WRITE, filename, format, p, mem, n, seglist); + + done: + avr_free_mem(mem); + free(seglist); + free(filename); + + return ret < 0? ret: 0; +} static int cmd_flush(const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]) { if(argc > 1) {