/* * mk86p, make an 86p (TI-86 program variable) from a file * Copyright 2003, 2004 Chris Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #define LO(n) ((n)&0xFF) #define HI(n) (((n)>>8)&0xFF) #define STRINGIZE(x) STRINGIZE2(x) #define STRINGIZE2(x) #x char PROGRAMNAME[] = "mk86p"; char PROGRAMVERS[] = "0.9"; int usage() { printf("Usage: %s [options] [infile [outfile]]\n" "\n" "Options:\n" " -b Program type is BASIC\n" " -c comment Set the comment\n" " -h Display this help and exit\n" " -k Program is tokenized (BASIC) or compiled (ASM)\n" " -l Edit-lock the program (BASIC only)\n" " -n varname Set the on-calc variable name\n" " -v Display this program's version and exit\n" "Note: untokenized, uncompiled ASM is the same as untokenized BASIC\n" "\n" "If infile or outfile is missing or is -, then standard in or out is used\n", PROGRAMNAME); return 0; } int version() { printf("%s version %s "PROGRAMDATE"\n" "Copyright 2003, 2004 Chris Williams \n" "This program is free software; you may redistribute it under the terms of\n" "the GNU General Public License. This program has absolutely no warranty.\n", PROGRAMNAME, PROGRAMVERS); return 0; } /* FIXME: make this function more accurate at defining valid variable names */ int isvalidvarname(char *v) { char *accept = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789"; if (v[0] == '\0' || strlen(v) > 8 || strspn(v, accept) < strlen(v)) return 0; return 1; } int bytesum(int n) { return LO(n) + HI(n); } /* put a 16-bit little-endian word to stream */ int fputw(int w, FILE *stream) { if (fputc(LO(w), stream) == EOF || fputc(HI(w), stream) == EOF) return EOF; return w; } /* put a null-padded field to stream */ int fputfield(char *s, int size, FILE *stream) { int len; int i; len = s ? (strlen(s) >= size ? size : strlen(s)) : 0; fwrite(s, 1, len, stream); for (i = 0; i < size - len; ++i) fputc(0x00, stream); return size; } int fieldsum(unsigned char *s, int size) { int len; unsigned char *e; int sum = 0; len = s ? (strlen(s) >= size ? size : strlen(s)) : 0; for (e = s + len; s < e; ++s) sum += *s; return sum; } int memsum(unsigned char *s, int size) { unsigned char *e; int sum = 0; for (e = s + size; s < e; ++s) sum += *s; return sum; } int main(int argc, char *argv[]) { FILE *in = NULL, *out = NULL; char mem[0x10000]; int datasize; char *varname = NULL, *comment = NULL; char *infile = NULL, *outfile = NULL; enum { ASM, BASIC } type = ASM; int locked = 0; int tokenized = 0; int arg = 1; /* if (argc < 3) { usage(); return -1; } */ while (arg < argc) { if (argv[arg][0] == '-') { /* beginning of a flag */ switch (argv[arg][1]) { case 'b': type = BASIC; break; case 'c': if (arg < argc-1) { if (!comment) comment = argv[++arg]; else { fprintf(stderr, "%s: comment is already set\n", PROGRAMNAME); return -1; } } else { usage(); return -1; } break; case 'h': usage(); return 0; case 'k': tokenized = 1; break; case 'l': locked = 1; break; case 'n': if (arg < argc-1) { if (!varname) varname = argv[++arg]; else { fprintf(stderr, "%s: varname is already set\n", PROGRAMNAME); return -1; } } else { usage(); return -1; } break; case 'v': version(); return 0; case '\0': if (!infile) infile = argv[arg]; else if (!outfile) outfile = argv[arg]; else { fprintf(stderr, "%s: must be at most two -'s\n", PROGRAMNAME); return -1; } break; default: fprintf(stderr, "%s: invalid option %s\n", PROGRAMNAME, argv[arg]); return -1; } } else if (argv[arg][0] != '\0') { if (!infile) infile = argv[arg]; else if (!outfile) outfile = argv[arg]; else { fprintf(stderr, "%s: invalid option %s\n", PROGRAMNAME, argv[arg]); return -1; } } ++arg; } #ifdef DEBUG fputs("done with options\n", stderr); #endif if (!varname) { if (!infile || infile[0] == '-') { fprintf(stderr, "%s: must specify variable name (try %s -h)\n", PROGRAMNAME, PROGRAMNAME); return -1; } else if (isvalidvarname(infile)) varname = infile; else { fprintf(stderr, "%s: please specify a variable name (-v option)\n", PROGRAMNAME); return -1; } } if (!isvalidvarname(varname)) { fprintf(stderr, "%s: variable name \"%s\" is invalid\n", PROGRAMNAME, varname); return -1; } if (!infile || infile[0] == '-') { if ((in = fdopen(STDIN_FILENO, "rb")) == NULL) { fprintf(stderr, "%s: could not open stdin for read\n", PROGRAMNAME); return -1; } } else if ((in = fopen(infile, "rb")) == NULL) { fprintf(stderr, "%s: could not open %s for read\n", PROGRAMNAME, infile); return -1; } #ifdef DEBUG fputs("opened infile\n", stderr); #endif datasize = fread(mem, 1, 0x10000, in); if (!feof(in)) { fprintf(stderr, "%s: error reading from %s\n", PROGRAMNAME, infile); fclose(in); return -1; } fclose(in); if (!outfile || outfile[0] == '-') { if ((out = fdopen(STDOUT_FILENO, "wb")) == NULL) { fprintf(stderr, "%s: could not open stdout for write\n", PROGRAMNAME); return -1; } } else if ((out = fopen(outfile, "wb")) == NULL) { fprintf(stderr, "%s: could not open %s for write\n", PROGRAMNAME, infile); return -1; } #ifdef DEBUG fputs("opened outfile\n", stderr); #endif { int checksum = 0; int varnamelen = strlen(varname) > 8 ? 8 : strlen(varname); int varsize = datasize + 2; if (type == ASM) varsize += 2; else { if (locked) varsize += 2; else if (!tokenized) ++varsize; } fputfield("**TI86**\x1A\x0A\x00", 0x0B, out); fputfield(comment, 0x2A, out); fputw(varsize+16, out); fputw(0x000C, out); /* beginning of var header */ checksum += 0x0C; #ifdef DEBUG fprintf(stderr, "checksum = %04X\n", checksum); #endif fputw(varsize, out); checksum += bytesum(varsize); #ifdef DEBUG fprintf(stderr, "checksum = %04X\n", checksum); #endif fputc(0x12, out); fputc(varnamelen, out); checksum += 0x12 + varnamelen; #ifdef DEBUG fprintf(stderr, "checksum = %04X\n", checksum); #endif fputfield(varname, 8, out); checksum += fieldsum(varname, 8); #ifdef DEBUG fprintf(stderr, "checksum = %04X\n", checksum); #endif fputw(varsize, out); checksum += bytesum(varsize); #ifdef DEBUG fprintf(stderr, "checksum = %04X\n", checksum); #endif fputw(varsize-2, out); checksum += bytesum(varsize-2); #ifdef DEBUG fprintf(stderr, "checksum = %04X\n", checksum); #endif if (type == ASM) { int token; if (tokenized) token = 0x288E; else token = 0x278E; fputw(token, out); checksum += bytesum(token); } else { if (locked) { int token; if (tokenized) token = 0x298E; else token = 0x0000; fputw(token, out); checksum += bytesum(token); } else if (!tokenized) fputc(0x00, out); } #ifdef DEBUG fprintf(stderr, "checksum = %04X\n", checksum); #endif fwrite(mem, 1, datasize, out); checksum += memsum(mem, datasize); #ifdef DEBUG fprintf(stderr, "sum of mem = %04X\n", memsum(mem, datasize)); fprintf(stderr, "checksum = %04X\n", checksum); #endif fputw(checksum&0xFFFF, out); } fclose(out); return 0; }