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  ======== | 
