aboutsummaryrefslogtreecommitdiff
path: root/fenders.c
diff options
context:
space:
mode:
Diffstat (limited to 'fenders.c')
-rw-r--r--fenders.c418
1 files changed, 418 insertions, 0 deletions
diff --git a/fenders.c b/fenders.c
new file mode 100644
index 0000000..55ad709
--- /dev/null
+++ b/fenders.c
@@ -0,0 +1,418 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "fenders_bin.h"
+#include "fenders_offsets.h"
+#include "fendersdbl_bin.h"
+#include "fendersdbl_offsets.h"
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+#ifndef VERSION
+#define VERSION "???"
+#endif
+
+#define SELF "fenders"
+#define BANNER SELF " v" VERSION " by B. Watson (WTFPL)\n"
+#define DEFAULT_TITLE "atari arcade"
+#define OPTIONS "hrscit:v"
+
+char *usage =
+ BANNER
+ "Install Fenders 3-sector loader in boot sectors of an ATR image\n"
+ "Usage: " SELF " -[hrcsiv] [-t title] infile.atr [outfile.atr]\n"
+ " -h Print this help message\n"
+ " -r DON'T reboot (coldstart) the Atari if Reset is pressed\n"
+ " -c Rotate colors during load\n"
+ " -s Screen off after load\n"
+ " -i In-place update (original renamed to end in ~)\n"
+ " -v Set inverse video bit in title (blue/red text)\n"
+ " -t title Set title (up to 20 chars, default: '" DEFAULT_TITLE "')\n";
+
+typedef enum { SD, DD } density;
+
+void set_title(char *title, density dens, int inverse) {
+ int i;
+ int offset;
+ unsigned char *bin;
+ int len = strlen(title);
+
+ if(dens == SD) {
+ offset = OFFSET_TITLE;
+ bin = fenders_bin;
+ } else {
+ offset = OFFSET_TITLE_DD;
+ bin = fendersdbl_bin;
+ }
+
+ /* zero out the title area first (zeroes are Atari spaces BTW) */
+ memset(&bin[offset], 0, 20);
+
+ if(len > 20) {
+ len = 20;
+ fprintf(stderr, SELF ": Truncating title to 20 characters\n");
+ } else if(!len) {
+ return;
+ }
+
+ /* convert ASCII to Atari screen codes (not the same as ATASCII)
+ charset in GR.2 is space plus:
+ !"#$%'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
+
+ Punctuation comes out orange (blue with -v)
+ Digits and uppercase letters are orange/blue
+ Lowercase letters are green/red
+ ` = green/red @
+ { = green/red [
+ | = green/red ^
+ } = green/red ]
+ ~ = green/red ^
+ */
+ for(i=0; i<len; i++) {
+ char c = title[i];
+
+ if(c >= 32 && c <= 95)
+ c -= 32;
+
+ if(inverse)
+ c |= 128;
+
+ title[i] = c;
+ }
+
+ /* Center title in 20-byte title area */
+ memcpy(&bin[offset + 10 - len / 2], title, len);
+
+ /*
+ fprintf(stderr, SELF ": Set title to \"");
+ for(i=0; i<20; i++) {
+ char c = bin[offset + i];
+ if(!c) c = ' ';
+ if(!isprint(c)) c = '.';
+ putc(c, stderr);
+ }
+
+ putc('"', stderr);
+ putc('\n', stderr);
+ */
+}
+
+/* for set_coldstart(), set_rot_color(), set_screen_off(),
+ see fenders.dasm (and fendersdbl.dasm) to understand what's going on.
+ Search for the OFFSET_* strings. */
+
+void set_coldstart(density dens) {
+ if(dens == SD) {
+ fenders_bin[OFFSET_COLDST_1] = 1; /* operand for LDY # (replaces 0) */
+ fenders_bin[OFFSET_COLDST_2] = 0xea; /* NOP (replaces INY) */
+ } else {
+ fendersdbl_bin[OFFSET_COLDST_1_DD] = 1;
+ fendersdbl_bin[OFFSET_COLDST_2_DD] = 0xea;
+ }
+}
+
+void set_rot_color(density dens) {
+ if(dens == SD)
+ fenders_bin[OFFSET_ROTCOLOR] = 0x8d; /* STA abs (replace LDA abs) */
+ else
+ fendersdbl_bin[OFFSET_ROTCOLOR_DD] = 0x8d;
+}
+
+void set_screen_off(density dens) {
+ if(dens == SD)
+ fenders_bin[OFFSET_SCREENOFF] = 0x8d; /* STA abs (replace LDA abs) */
+ else
+ fendersdbl_bin[OFFSET_SCREENOFF_DD] = 0x8d;
+}
+
+int main(int argc, char **argv) {
+ int coldstart = 1, rot_color = 0, screen_off = 0;
+ int c, res, size, inverse = 0;
+ char title[21];
+ int in_place = 0;
+ char rename_to[4096];
+ unsigned char buf[384], *bin;
+ char *infile = "-", *outfile = "-";
+ FILE *in = stdin, *out = stdout;
+ density dens;
+
+ /* initialize title (may be changed by -t option) */
+ strcpy(title, DEFAULT_TITLE);
+
+ /* parse options */
+ while( (c = getopt(argc, argv, OPTIONS)) != -1) {
+ switch(c) {
+ case 'h':
+ printf(usage);
+ exit(0);
+ break;
+
+ case 'r':
+ coldstart = 0;
+ break;
+
+ case 'c':
+ rot_color = 1;
+ break;
+
+ case 's':
+ screen_off = 1;
+ break;
+
+ case 't':
+ strcpy(title, optarg);
+ break;
+
+ case 'i':
+ in_place = 1;
+ break;
+
+ case 'v':
+ inverse = 1;
+ break;
+
+ default:
+ fprintf(stderr, usage);
+ exit(1);
+ break;
+ }
+ }
+
+ /* get input filename if present */
+ if(optind < argc)
+ infile = argv[optind++];
+
+ /* get output filename if present */
+ if(!in_place && optind < argc)
+ outfile = argv[optind++];
+
+ if(optind < argc) {
+ fprintf(stderr, SELF
+ ": Ignoring extra junk '%s ...' on command line.\n",
+ argv[optind]);
+ }
+
+ if(in_place) {
+ /* rename infile to infile~, set outfile to old infile */
+ int len = strlen(infile);
+
+ if(strcmp(infile, "-") == 0) {
+ fprintf(stderr, SELF
+ ": Can't use in-place mode with standard input.\n");
+ exit(1);
+ }
+
+ strcpy(rename_to, infile);
+ rename_to[len] = '~';
+ rename_to[len + 1] = '\0';
+
+ fprintf(stderr, SELF ": Backing up %s to %s\n", infile, rename_to);
+ if(link(infile, rename_to)) {
+ perror("link()");
+ exit(1);
+ }
+
+ if(unlink(infile)) {
+ perror("unlink()");
+ exit(1);
+ }
+
+ outfile = infile;
+ infile = rename_to;
+ }
+
+ /* open input and output files, if not stdin/stdout */
+ if(strcmp(infile, "-") != 0) {
+ in = fopen(infile, "rb");
+ if(!in) {
+ perror(infile);
+ exit(1);
+ }
+ }
+
+ /* read ATR header */
+ res = fread(buf, 1, 16, in);
+ if(res < 16) {
+ perror(infile);
+ exit(1);
+ }
+
+ /* make sure it's an ATR image */
+ if( !(buf[0] == 0x96 && buf[1] == 0x02) ) {
+ fprintf(stderr, SELF
+ ": %s not an ATR file (no NICKATARI signature)!\n"
+ "If this is an XFD file, try xfd2atr\n",
+ infile);
+ exit(2);
+ }
+
+ /* get sector size. The single- and double-density versions of
+ the loader are totally different, so pick the one we need. */
+ if( (buf[4] == 0x80 && buf[5] == 0x00) ) {
+ dens = SD;
+ bin = fenders_bin;
+ } else if( (buf[4] == 0x00 && buf[5] == 0x01) ) {
+ dens = DD;
+ bin = fendersdbl_bin;
+ } else {
+ fprintf(stderr, SELF ": ATR image must have 128 or 256 byte sectors\n");
+ exit(2);
+ }
+
+ /* modify the loader according to the user's options */
+ set_title(title, dens, inverse);
+ if(coldstart) set_coldstart(dens);
+ if(rot_color) set_rot_color(dens);
+ if(screen_off) set_screen_off(dens);
+
+ /* size of ATR image in bytes (minus the header). We don't support
+ DD images less than 720 sectors. */
+ size = (buf[2] + (buf[3] << 8) + (buf[6] << 16)) * 16;
+ if(dens == SD && size < (128 * 369)) {
+ fprintf(stderr, SELF
+ ": ATR is single density < 369 sectors, not supported\n"
+ "Use atrsize to grow the image.\n");
+ exit(2);
+ } else if(dens == SD && size > (128 * 720)) {
+ fprintf(stderr, SELF
+ ": ATR is single density > 720 sectors; "
+ "some files may not appear in menu.\n");
+ } else if(dens == DD && size < (128 * 3 + 256 * 717)) {
+ fprintf(stderr, SELF
+ ": ATR is double density < 720 sectors, not supported\n"
+ "Use atrsize to grow the image.\n");
+ exit(2);
+ } else if(dens == DD && size > (128 * 3 + 256 * 717)) {
+ /* 20071005 bkw: whoops, we were truncating large images to 180K.
+ The bootloader doesn't work with MyDOS >180K formats anyway, so
+ don't try.
+ fprintf(stderr, SELF
+ ": ATR file is double density > 720 sectors; some files may not"
+ "appear in the menu or load correctly\n");
+ */
+
+ /* Abort instead */
+ fprintf(stderr, SELF
+ ": ATR is double density > 720 sectors, not supported "
+ "by bootloader (try MyPicoDOS)\n");
+ exit(2);
+ }
+
+ /* Input looks OK, open the output... */
+ if(strcmp(outfile, "-") != 0) {
+ out = fopen(outfile, "wb");
+ if(!out) {
+ fclose(in);
+ perror(outfile);
+ exit(1);
+ }
+ } else if(isatty(fileno(stdout))) {
+ /* don't scare the n00bs! */
+ fprintf(stderr,
+ SELF ": Standard output is a terminal, not writing binary data.\n"
+ "Either redirect to a file or set the output filename.\n");
+ exit(1);
+ }
+
+ /* write ATR header */
+ res = fwrite(buf, 1, 16, out);
+ if(res < 16) {
+ perror(outfile);
+ exit(1);
+ }
+
+ /* read (and ignore) first 3 sectors. A DD image still uses SD
+ sectors for the first 3 (boot) sectors on the disk. */
+ res = fread(buf, 1, 384, in);
+ if(res < 384) {
+ perror(infile);
+ exit(1);
+ }
+
+ /* Write the loader. For SD disks, this is the whole thing.
+ For DD disks, this is the first 384 bytes (3 boot sectors),
+ and we'll have to write the rest of the object code to sector 720 */
+ res = fwrite(bin, 1, 384, out);
+ if(res < 384) {
+ perror(outfile);
+ exit(1);
+ }
+
+ if(dens == SD) {
+ /* single density can just use a simple copy loop */
+ while( (c = getc(in)) != EOF )
+ putc(c, out);
+ } else {
+ /* double density: copy sectors 4-719 as-is... */
+ for(c=4; c<720; c++) {
+ if(fread(buf, 1, 256, in) < 256) {
+ if(feof(in)) {
+ fprintf(stderr, SELF
+ ": got premature EOF (bad/truncated ATR image).\n");
+ } else {
+ perror(infile);
+ }
+ exit(1);
+ }
+
+ if(fwrite(buf, 1, 256, out) < 256) {
+ perror(outfile);
+ exit(1);
+ }
+ }
+
+ /* TODO: check the VTOC and/or look for non-zero data in sector
+ 720, warn the user if the sector was in use. */
+
+ /* TODO: fix bootloader to work with MyDOS-style sector link bytes.
+ Also, store last part of bootloader somewhere not used by MyDOS,
+ maybe sector 369 (last directory sector, unused on disks with
+ less than 56 files... and 56 files is way too many to fit on
+ screen in GR.1). This should happen on both single and double
+ density images. */
+
+ /* TODO: examine directory sectors, look for:
+ - DOS 2.5 extended files. Either warn about them, or clear the
+ extended flag (which causes the bootloader to load them just fine).
+ - MyDOS subdirectories. It's probably best to abort in that case.
+ - Non-DOS-compatible disk formats (boot disks, SpartaDOS, Atari
+ DOS 3 or 4).
+ */
+
+ /* TODO: add option to delete DOS.SYS and DUP.SYS (or more likely,
+ delete them by default, and add option to allow user to keep them).
+ */
+
+ /* TODO: option that creates a new, blank image, with bootloader
+ already on it? There's already "atrsize -b" for creating a blank
+ image... */
+
+ /* write the rest of the loader code code to sector 720.
+ The code in the boot sectors will load sector 720. */
+ if(fwrite(bin+384, 1, 256, out) < 256) {
+ perror(outfile);
+ exit(1);
+ }
+ }
+
+ /* set return value: 0 for success, 1 for failure */
+ c = 0;
+
+ if(ferror(in)) {
+ perror(infile);
+ c = 1;
+ }
+
+ if(ferror(out)) {
+ perror(outfile);
+ c = 1;
+ }
+
+ /* ...and I'm spent! */
+ return c;
+}