From 99a25fcd160666b00996bd26f7ef5128522e7fa1 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Sun, 11 Jun 2023 14:28:12 +0100 Subject: [PATCH 1/7] Rename function segment_normalise() --- src/fileio.c | 5 ++--- src/libavrdude.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/fileio.c b/src/fileio.c index 480b63bc..c6eea07b 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -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 Date: Sun, 11 Jun 2023 15:54:53 +0100 Subject: [PATCH 2/7] Suppress comma at begin of memory segment, not begin of memory --- src/fileio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fileio.c b/src/fileio.c index c6eea07b..4553d03b 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 From 48f1fc75e9deb4ec50b867ef54fcad2c3d14adf0 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Sun, 11 Jun 2023 18:15:36 +0100 Subject: [PATCH 3/7] Provide terminal save command Examples with filename - (stdout): $ avrdude -qqt -p m328p avrdude> save eeprom -:i :2000000054686973206973206120746573740048656C6C6F2C20776F726C64210A0054689C :200020006520717569636B2062726F776E20666F78206A756D7073206F76657220746865DD :20004000206C617A7920646F6700FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7C :20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE7FEE7FEE7FEE7FEE7FE1D :20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80 :2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60 :2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40 :2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20 :20010000280992D30B0000F4FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF62 :20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF :20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF :20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F :20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F :2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F :2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F :2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F :20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE :20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE :20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE :20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E :20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E :2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E :2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E :2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E :20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD :20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD :20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD :20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D :20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D :2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D :2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D :2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D :00000001FF avrdude> save eeprom 15 5 22 5 -:I :05000F0048656C6C6FF8 // 0000f> Hello :020000040000FA :05001600776F726C64BD // 00016> world :00000001FF avrdude> save eeprom 15 5 22 5 -:h 0x48,0x65,0x6c,0x6c,0x6f 0x77,0x6f,0x72,0x6c,0x64 --- src/term.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/src/term.c b/src/term.c index 1a8383b4..0c7e5c89 100644 --- a/src/term.c +++ b/src/term.c @@ -67,6 +67,7 @@ struct command { static int cmd_dump (const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]); static int cmd_write (const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]); +static int cmd_save (const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]); static int cmd_flush (const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]); static int cmd_abort (const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]); static int cmd_erase (const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]); @@ -93,6 +94,7 @@ struct command cmd[] = { { "dump", cmd_dump, _fo(read_byte_cached), "display a memory section as hex dump" }, { "read", cmd_dump, _fo(read_byte_cached), "alias for dump" }, { "write", cmd_write, _fo(write_byte_cached), "write data to memory; flash and EEPROM are cached" }, + { "save", cmd_save, _fo(write_byte_cached), "save memory data to file" }, { "flush", cmd_flush, _fo(flush_cache), "synchronise flash and EEPROM cache with the device" }, { "abort", cmd_abort, _fo(reset_cache), "abort flash and EEPROM writes, ie, reset the r/w cache" }, { "erase", cmd_erase, _fo(chip_erase_cached), "perform a chip or memory erase" }, @@ -634,6 +636,110 @@ static int cmd_write(const PROGRAMMER *pgm, const AVRPART *p, int argc, char *ar return 0; } +static int cmd_save(const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]) { + if(argc < 3 || (argc > 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"); + + // 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) { From bea3d099f85bc74ebe26ff1ac6fa565da3c8e8fd Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Mon, 12 Jun 2023 18:21:13 +0100 Subject: [PATCH 4/7] Add terminal save documentation --- src/avrdude.1 | 15 ++++++++++++++- src/doc/avrdude.texi | 17 ++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/avrdude.1 b/src/avrdude.1 index 9594a07e..b8ca651f 100644 --- a/src/avrdude.1 +++ b/src/avrdude.1 @@ -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..1f6b70b0 100644 --- a/src/doc/avrdude.texi +++ b/src/doc/avrdude.texi @@ -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}. @@ -2058,7 +2058,7 @@ Read all bytes from the specified memory, and display them (deprecated: use @cod @item read Can be used as an alias for dump. -@item write @var{memtype} @var{addr} @var{data[,]} @{@var{data[,]}@} +@item write @var{memtype} @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 @@ -2182,12 +2182,23 @@ and paste of lists. 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{memtype} @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. From 51275c36ef4ae9d209259cf2ad1c09dbadad5360 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Mon, 12 Jun 2023 18:23:52 +0100 Subject: [PATCH 5/7] Rename memtype to memory in documentation --- src/avrdude.1 | 8 ++++---- src/doc/avrdude.texi | 26 +++++++++++++------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/avrdude.1 b/src/avrdude.1 index b8ca651f..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 diff --git a/src/doc/avrdude.texi b/src/doc/avrdude.texi index 1f6b70b0..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. @@ -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,11 +2178,11 @@ 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 @@ -2880,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 From 3d71c4c66b749310eab2cae54c665806897e4681 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Mon, 12 Jun 2023 18:28:05 +0100 Subject: [PATCH 6/7] Fix size setting on file read --- src/fileio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fileio.c b/src/fileio.c index 4553d03b..d3b2f4a2 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -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}; From b298d9e10311b3deef0234c79d316dea8223a41c Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Tue, 13 Jun 2023 12:13:16 +0100 Subject: [PATCH 7/7] Return when save detects missing length for a memory segement --- src/term.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/term.c b/src/term.c index 0c7e5c89..2a2b0016 100644 --- a/src/term.c +++ b/src/term.c @@ -651,8 +651,10 @@ static int cmd_save(const PROGRAMMER *pgm, const AVRPART *p, int argc, char *arg return -1; } - if(argc > 3 && !(argc&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;