mirror of
https://github.com/avrdudes/avrdude.git
synced 2026-09-21 16:46:30 +03:00
Merge pull request #1396 from stefanrueger/save
Provide terminal save command
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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 = <num> ; # autobaud detection byte, default 0x30
|
||||
|
||||
memory <memtype>
|
||||
memory <memory>
|
||||
paged = <yes/no> ; # yes/no (flash only, do not use for EEPROM)
|
||||
offset = <num> ; # memory offset
|
||||
size = <num> ; # bytes
|
||||
|
||||
15
src/fileio.c
15
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<n; i++)
|
||||
if(segmemt_normalise(mem, seglist+i) < 0)
|
||||
if(segment_normalise(mem, seglist+i) < 0)
|
||||
return -1;
|
||||
|
||||
using_stdio = 0;
|
||||
@@ -1639,4 +1639,3 @@ int fileio_segments(int oprwv, const char *filename, FILEFMT format,
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -1036,7 +1036,7 @@ int fileio_fmt_autodetect(const char *fname);
|
||||
int fileio(int oprwv, const char *filename, FILEFMT format,
|
||||
const AVRPART *p, const char *memtype, int size);
|
||||
|
||||
int segmemt_normalise(const AVRMEM *mem, Segment_t *segp);
|
||||
int segment_normalise(const AVRMEM *mem, Segment_t *segp);
|
||||
|
||||
int fileio_segments(int oprwv, const char *filename, FILEFMT format,
|
||||
const AVRPART *p, const AVRMEM *mem, int n, const Segment_t *seglist);
|
||||
|
||||
108
src/term.c
108
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,112 @@ 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 <mem> {<addr> <len>} <file>[:<format>]\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 <addr> <len> 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; i<n; i++) { // Ensure addr and lengths are non-negative
|
||||
if(segment_normalise(mem, seglist+i) < 0)
|
||||
goto done;
|
||||
nbytes += seglist[i].len;
|
||||
}
|
||||
|
||||
if(nbytes <= 0 && !str_eq(filename, "-"))
|
||||
pmsg_warning("(save) no file written owing to empty memory segment%s\n",
|
||||
str_plural(n));
|
||||
|
||||
if(nbytes <= 0) {
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Read memory from device/cache
|
||||
report_progress(0, 1, "Reading");
|
||||
for(int i = 0; i < n; i++) {
|
||||
for(int j = seglist[i].addr; j < seglist[i].addr + seglist[i].len; j++) {
|
||||
int rc = pgm->read_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) {
|
||||
|
||||
Reference in New Issue
Block a user