diff options
author | B. Watson <urchlay@slackware.uk> | 2024-04-24 15:04:50 -0400 |
---|---|---|
committer | B. Watson <urchlay@slackware.uk> | 2024-04-24 15:04:50 -0400 |
commit | 1deab5d25136624f7e9a6d6446f5d218a243f56b (patch) | |
tree | ab232233646e7d7778c43349b0186f09b660d25e | |
parent | 53a0578bd0605cfa39e7d8d126aa78fb216527d3 (diff) | |
download | bw-atari8-tools-1deab5d25136624f7e9a6d6446f5d218a243f56b.tar.gz |
blob2xex: initial implementation, needs testing.
-rw-r--r-- | blob2xex.1 | 17 | ||||
-rw-r--r-- | blob2xex.c | 176 | ||||
-rw-r--r-- | blob2xex.rst | 17 |
3 files changed, 198 insertions, 12 deletions
@@ -36,7 +36,7 @@ blob2xex \- Create Atari 8-bit executables from arbitrary data . .SH SYNOPSIS .sp -blob2xex [\fB\-l\fP \fIloadaddr\fP [\fB\-r\fP \fIrunaddr\fP] [\fB\-i\fP \fIinitaddr\fP] [\fB\-o\fP \fIoffset\fP] [\fB\-s\fP \fIsize\fP] \fIinfile\fP] ... \fIoutfile\fP +blob2xex \fIoutfile\fP [\fB\-r\fP \fIrunaddr\fP] [\fB\-l\fP \fIloadaddr\fP [\fB\-i\fP \fIinitaddr\fP] [\fB\-o\fP \fIoffset\fP] [\fB\-s\fP \fIsize\fP] \fIinfile\fP] ... .SH DESCRIPTION .sp \fBblob2xex\fP creates an Atari 8\-bit binary load (xex) file from one or @@ -47,13 +47,17 @@ address. Optionally, run and init addresses can be included. Also, using \fB\-o\fP and \fB\-s\fP, it\(aqs possible to include only part of the input file. To read from standard input, use \fB\-\fP for the \fIinfile\fP\&. .sp -Only one \fIoutfile\fP is supported. When multiple input files are used, -the resulting .xex file will have multiple segments. Use \fB\-\fP to -write to standard output. +\fIoutfile\fP must be given as the first argument. When multiple +input files are used, the resulting .xex file will have multiple +segments. If \fIoutfile\fP already exists, it will be overwritten. Use +\fB\-\fP to write to standard output. .sp Addresses, offsets, and sizes may be given in decimal or hex. Hex addresses must be prefixed with either \fB$\fP or \fB0x\fP\&. .SH OPTIONS +.sp +A space is required between an option and its argument; use e.g. \fB\-l 0x2000*, +not **\-l0x2000\fP\&. .INDENT 0.0 .TP .B \-l \fIloadaddr\fP @@ -62,7 +66,7 @@ a \fB\-l\fP option. .TP .B \-r \fIrunaddr\fP Optional; set the run address. Since a .xex file can only have one run address, -the last \fB\-r\fP option will be the one used when the file is loaded on the Atari. +the last \fB\-r\fP option will be the one used. .TP .B \-i \fIinitaddr\fP Optional; set an init address, to be executed after the next segment loads. @@ -71,7 +75,8 @@ Optional; set an init address, to be executed after the next segment loads. Optional; skip this many bytes of the next input file. Default is \fB0\fP\&. .TP .B \-s \fIsize\fP -Optional; read this many bytes of the next input file. Default is the entire file. +Optional; read this many bytes of the next input file. Default is the entire file, +or \fB0xffff\fP (\fB65535\fP) if the file is longer than 64KB. .UNINDENT .SH EXAMPLES .sp @@ -4,6 +4,7 @@ #include <errno.h> #include <string.h> +#include "get_address.h" #include "xex.h" #ifndef VERSION @@ -12,5 +13,180 @@ #define SELF "blob2xex" +#define DEFAULT_SIZE 0xffff + +static FILE *outfh = 0; + +/* get_offset() is patterned on get_address(), but isn't limited + to 64K. TODO: it won't handle offsets >2GB. */ +int get_offset(const char *arg) { + unsigned int got; + + if(sscanf(arg, "0x%x", &got) != 1) + if(sscanf(arg, "$%x", &got) != 1) + if(sscanf(arg, "%d", &got) != 1) { + fprintf(stderr, "Invalid offset '%s'\n", arg); + return -1; + } + + return (int)got; +} + +void write_segment( + const char *infile, + const char *outfile, + int loadaddr, + int initaddr, + int offset, + int size) +{ + FILE *infh; + unsigned char buffer[65536], *p = buffer; + xex_segment seg; + int c; + + if(size < 1) { + fprintf(stderr, SELF ": invalid size %d (must be >= 1).\n", size); + exit(1); + } + + if(strcmp(infile, "-") == 0) { + infh = stdout; + } else { + infh = fopen(infile, "rb"); + if(!infh) { + perror(outfile); + exit(1); + } + } + + if(outfh) { + seg.has_ff_header = 0; + } else { + seg.has_ff_header = 1; + if(strcmp(outfile, "-") == 0) { + outfh = stdout; + } else { + outfh = fopen(outfile, "wb"); + if(!outfh) { + perror(outfile); + exit(-1); + } + } + } + + seg.object = buffer; + seg.len = 0; + seg.start_addr = loadaddr; + + /* skip <offset> bytes in input. don't seek, input might be stdin */ + while(offset) { + /* TODO: not very efficient to read 1 byte at a time? */ + c = getc(infh); + if(c < 0) { + fprintf(stderr, SELF ": offset extends past EOF on file %s\n", infile); + exit(1); /* TODO: handle this better? */ + } + offset--; + } + + /* read <size> bytes, or until EOF (which is not an error) */ + while(size) { + c = getc(infh); + if(c < 0) break; + *p++ = c; + seg.len++; + size--; + } + seg.end_addr = seg.start_addr + seg.len - 1; + + xex_fwrite_seg(&seg, outfh); + + if(initaddr >= 0) { + xex_init_seg(&seg, buffer, initaddr); + xex_fwrite_seg(&seg, outfh); + } + + fclose(infh); +} + +void usage() { + printf(SELF ": Usage:\n\t" + SELF " outfile [-r runaddr] [-l loadaddr [-i initaddr] " + "[-o offset] [-s size] infile] ..." + "\nSee man page for details.\n"); +} + int main(int argc, char **argv) { + char *outfile = 0, *infile = 0; + int i, loadaddr = -1, runaddr = -1, initaddr = -1, offset = 0, size = DEFAULT_SIZE, *param = 0; + + xex_verbose = 1; + + if(argc < 5) { + fprintf(stderr, SELF ": not enough arguments.\n"); + usage(); + exit(1); + } + + outfile = argv[1]; + + for(i = 2; i < argc; i++) { + char *arg = argv[i]; + + if(!arg[0]) continue; /* skip empty args */ + + if(param) { /* previous option needs an argument */ + if(param == &offset) { + if( (offset = get_offset(arg) ) < 0 ) + exit(1); + } else { + if( (*param = get_address(SELF, arg) ) < 0 ) + exit(1); + } + param = 0; + } else if(arg[0] == '-') { + infile = 0; + switch(arg[1]) { + case 'l': param = &loadaddr; break; + case 'r': param = &runaddr; break; + case 'i': param = &initaddr; break; + case 'o': param = &offset; break; + case 's': param = &size; break; + default: + fprintf(stderr, SELF ": unknown option '-%c'\n", arg[1]); + usage(); + exit(1); + break; + } + } else { + if(infile) { + fprintf(stderr, SELF ": input filename without -l option: %s\n", arg); + usage(); + exit(1); + } + infile = arg; + write_segment(infile, outfile, loadaddr, initaddr, offset, size); + loadaddr = -1; initaddr = -1; offset = 0; size = DEFAULT_SIZE; + } + } + + if(param || (loadaddr >= 0) || (initaddr >= 0) || + (offset != 0) || (size != DEFAULT_SIZE)) { + fprintf(stderr, SELF ": " + "warning: extra arguments after last input file ignored.\n"); + } + + if(outfh) { + if(runaddr >= 0) { + xex_segment seg; + unsigned char buf[10]; + + xex_run_seg(&seg, buf, runaddr); + xex_fwrite_seg(&seg, outfh); + } + fclose(outfh); + } + + return 0; } diff --git a/blob2xex.rst b/blob2xex.rst index bbf0e0f..cf5bf10 100644 --- a/blob2xex.rst +++ b/blob2xex.rst @@ -14,7 +14,7 @@ Create Atari 8-bit executables from arbitrary data SYNOPSIS ======== -blob2xex [**-l** *loadaddr* [**-r** *runaddr*] [**-i** *initaddr*] [**-o** *offset*] [**-s** *size*] *infile*] ... *outfile* +blob2xex *outfile* [**-r** *runaddr*] [**-l** *loadaddr* [**-i** *initaddr*] [**-o** *offset*] [**-s** *size*] *infile*] ... DESCRIPTION =========== @@ -27,9 +27,10 @@ address. Optionally, run and init addresses can be included. Also, using **-o** and **-s**, it's possible to include only part of the input file. To read from standard input, use **-** for the *infile*. -Only one *outfile* is supported. When multiple input files are used, -the resulting .xex file will have multiple segments. Use **-** to -write to standard output. +*outfile* must be given as the first argument. When multiple +input files are used, the resulting .xex file will have multiple +segments. If *outfile* already exists, it will be overwritten. Use +**-** to write to standard output. Addresses, offsets, and sizes may be given in decimal or hex. Hex addresses must be prefixed with either **$** or **0x**. @@ -37,13 +38,16 @@ addresses must be prefixed with either **$** or **0x**. OPTIONS ======= +A space is required between an option and its argument; use e.g. **-l 0x2000*, +not **-l0x2000**. + -l *loadaddr* Set the load address of the next *infile*. Each *infile* must be preceded by a **-l** option. -r *runaddr* Optional; set the run address. Since a .xex file can only have one run address, - the last **-r** option will be the one used when the file is loaded on the Atari. + the last **-r** option will be the one used. -i *initaddr* Optional; set an init address, to be executed after the next segment loads. @@ -52,7 +56,8 @@ OPTIONS Optional; skip this many bytes of the next input file. Default is **0**. -s *size* - Optional; read this many bytes of the next input file. Default is the entire file. + Optional; read this many bytes of the next input file. Default is the entire file, + or **0xffff** (**65535**) if the file is longer than 64KB. EXAMPLES ======== |