/* // hex2co // // 2009-10-12 Christopher Osburn // turned into a useful program product by Daryl Tester // // // No copyright claimed. Permission granted to reproduce in whole or in // part for any reason whatsoever without interference from the United // States Government. So there. // // Convert RBASIC HEX output to Model 100 .CO format. // // Background: // // typical input from A100 assembler: // // :10E67800CD274E2AB2FB22B4FB22B6FBCDE9F3CD5F // ^^ ^ ^ ^ ^ // || | | | | // || | | | +-- checksum // || | | +-- data, $datalen bytes // || | +-- spacer, always 00 (?) // || +-- beginning address // +-- number of bytes in record // // // desired output: 1 word, little-endian, for start address // 1 word, little-endian, for object size // 1 word, little-endian, for entry address // (always same as start address) // several bytes of binary data, converted from hex records // // WORKING ASSUMPTIONS TO BE ELIMINATED: // // I assume the HEX file is not sparse, with each successive address field // increasing by the previous record's size field. // // I assume the HEX record is well formed, without non-hex digits. // // I ignore the checksum. // // I assume the ENTRY address is the same as the ORG address. RBASIC writes // code with ENTRY at the top. Further, there is no way to encode an alternate // entry in the HEX record. I'd have to add a command line parameter // // I assume the HEX file is never ORG'd to zero. // */ #include #include #include #include #define BUF 512 static char bigbuf[32768]; /* CO buffer, with 3-word header */ char * getrec(FILE *f); /* get next HEX record into static storage and return a pointer */ unsigned long xtoi(char *p, int len); /* convert len characters starting at p to an unsigned long */ int hex2co(FILE *input, FILE *output); /* convert Intel hex format from input into CO format file on output */ void usage(void); /* Clue user with incisive dialogue */ FILE *makeoutput(char *filename); /* Open output file for writing; filename is derived from input file name */ int main(int argc, char *argv[]) { int i, err; FILE *fpin, *fpout; if (argc == 1) { /* No command line arguments supplied */ usage(); exit(0); } else { err = 0; /* Loop through supplied command line arguments */ for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { /* Argument is an option */ if (argv[i][1] == '?' || argv[i][1] == 'h' || argv[i][1] == 'H') { /* User asked for usage, not an error */ usage(); exit(0); } else if (argv[i][1] == 0) { /* Option is "-", run as filter on standard I/O */ if (hex2co(stdin, stdout) != 0) err = 1; } else { /* User provided unknown option, 'tis an error. */ fprintf(stderr, "hex2co: unknown option: %c\n", argv[i][1]); usage(); exit(1); } } else { /* Argument doesn't look like an option, treat it as text file. */ fpin = fopen(argv[i], "r"); if (fpin == NULL) { fprintf(stderr, "hex2co: cannot open file '%s'\n", argv[i]); err = 1; continue; } fpout = makeoutput(argv[i]); if (fpout == NULL) { /* makeoutput will have already complained */ fclose(fpin); err = 1; continue; } if (hex2co(fpin, fpout) != 0) { err = 1; /* FALL THROUGH TO */ } fclose(fpin); fclose(fpout); } } } /* Exit non-zero if any errors occurred. */ exit(err); } int hex2co(FILE *input, FILE *output) { char *rec; /* pointer to HEX record */ unsigned len, addr; /* HEX record size and address */ char *bbptr = bigbuf + 6; /* pointer to DATA section */ unsigned start = 0, size = 0; /* CO Header fields */ int i; /* the universal iterator */ while (rec = getrec(input), rec != NULL) { len = xtoi(rec+1, 2); if (len > 0) { addr = xtoi(rec+3, 4); /* we're not double checking sparseness today */ /* fprintf(stderr, "reading record at %x\n", addr); */ if (start == 0) { start = addr; /* write START and ENTRY words of the header */ bigbuf[0] = start % 256; /* START word */ bigbuf[1] = start / 256; bigbuf[4] = start % 256; /* ENTRY word */ bigbuf[5] = start / 256; } for (i=0; i sizeof(long)) { errno = ERANGE; return -1; } for (i=0; i= sizeof(outfname) - 4) { fprintf(stderr, "hex2co: input file name too long\n"); return NULL; } /* Copy filename, taking note of last '.' to occur. We also note intervening path separators, so we don't process a path like "/foo.boingle/filename" inappropriately. */ tail = NULL; p = outfname; do { *p = *filename; if (*p == '/' || *p == '\\') tail = NULL; else if (*p == '.') tail = p; p++; } while (*filename++); if (tail != NULL) strcpy(tail, ".co"); else strcat(outfname, ".co"); /* Open file for binary output */ fp = fopen(outfname, "wb"); if (fp == NULL) { fprintf(stderr, "hex2co: cannot create output file '%s'\n", outfname); /* FALL THROUGH TO */ } return fp; }