aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Watson <urchlay@slackware.uk>2024-04-24 15:04:50 -0400
committerB. Watson <urchlay@slackware.uk>2024-04-24 15:04:50 -0400
commit1deab5d25136624f7e9a6d6446f5d218a243f56b (patch)
treeab232233646e7d7778c43349b0186f09b660d25e
parent53a0578bd0605cfa39e7d8d126aa78fb216527d3 (diff)
downloadbw-atari8-tools-1deab5d25136624f7e9a6d6446f5d218a243f56b.tar.gz
blob2xex: initial implementation, needs testing.
-rw-r--r--blob2xex.117
-rw-r--r--blob2xex.c176
-rw-r--r--blob2xex.rst17
3 files changed, 198 insertions, 12 deletions
diff --git a/blob2xex.1 b/blob2xex.1
index 943a1e2..831519f 100644
--- a/blob2xex.1
+++ b/blob2xex.1
@@ -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
diff --git a/blob2xex.c b/blob2xex.c
index e3740f6..fb339e7 100644
--- a/blob2xex.c
+++ b/blob2xex.c
@@ -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
========