X-Git-Url: http://git.subgeniuskitty.com/pdp11-bin2load/.git/blobdiff_plain/f2b550c07b95260bf480d4722424c158f9fc68de..HEAD:/bin2load.c diff --git a/bin2load.c b/bin2load.c index 5764c9f..9786ecd 100644 --- a/bin2load.c +++ b/bin2load.c @@ -10,29 +10,28 @@ #include #include -#define VERSION 1 +#define VERSION 2 void -print_usage( char ** argv ) +print_usage(char ** argv) { printf( "bin2load v%d (www.subgeniuskitty.com)\n" - "Usage: %s i -o [-a
]\n" - " -i Binary file to be written to tape.\n" - " For example, binary executable from 'pdp11-aout-objdump'.\n" - " -o New paper tape image for use with SIMH.\n" - " -a
Address on PDP-11 at which to load paper tape contents.\n" + "Usage: %s -i -o [-a
]\n" + " -i Raw binary file to be written to tape.\n" + " For example, output from 'pdp11-aout-objdump' (see README.md).\n" + " -o Output file created by bin2load containing tape image for use with SIMH.\n" + " -a
(optional) Address on PDP-11 at which to load tape contents.\n" , VERSION, argv[0] ); } - int -main( int argc, char ** argv) +main(int argc, char ** argv) { int c; FILE * src = NULL; FILE * dst = NULL; - uint16_t address = 01000; /* Default address to load tape contents. */ + uint16_t address = 01000; /* Default address to load tape contents in RAM. */ while ((c = getopt(argc, argv, "i:o:a:h")) != -1) { switch (c) { @@ -62,45 +61,117 @@ main( int argc, char ** argv) print_usage(argv); exit(EXIT_FAILURE); } + printf("Paper tape will load at address 0%o.\n", address); /* - * Paper tape format(bytes, not words): - * 01 - * 00 - * Low byte of packet length (binary length + 6 for header) - * High byte of packet length - * Low byte of address to load binary data at - * High byte of address - * DataBegin - * | - * | - * DataEnd - * Checksum + * SIMH Binary Loader Format + * + * Loader format consists of blocks, optionally preceded, separated, and + * followed by zeroes. Each block consists of the following entries. Note + * that all entries are one byte. + * + * 0001 + * 0000 + * Low byte of block length (data byte count + 6 for header, excludes checksum) + * High byte of block length + * Low byte of load address + * High byte of load address + * Data byte 0 + * ... + * Data byte N + * Checksum * - * I am unsure of the checksum format and need to check the SIMH source. - * In the meantime a zero checksum still functions, albeit with a warning. + * The 8-bit checksum for a block is the twos-complement of the lower eight + * sum bits for all six header bytes and all data bytes. + * + * If the block length is exactly six bytes (i.e. only header, no data), + * then the block marks the end-of-tape. The checksum should be zero. If + * the load address of this final block is not 000001, then it is used as + * the starting PC. */ - uint16_t size = 6; + uint32_t checksum = 0; + uint32_t size = 6; + uint8_t data; - uint16_t header[] = {1,0,01000}; - fwrite(header,6,1,dst); + /* Write header for data block. */ + for (int i = 0; i < size; i++) { + switch (i) { + case 0: data = 0001; break; + case 1: data = 0000; break; + case 2: data = 0000; break; /* Size will be populated later */ + case 3: data = 0000; break; /* Size will be populated later */ + case 4: data = address & 0xff; break; + case 5: data = (address >> 8) & 0xff; break; + } + if (!fwrite(&data, 1, 1, dst)) { + fprintf(stderr, "ERROR: Failed to write block header.\n"); + exit(EXIT_FAILURE); + } + checksum += data; + } - uint8_t byte; - int read; - do { - read = fread(&byte,1,1,src); - if(read == 1) fwrite(&byte,1,1,dst); - size += read; - } while (read == 1); + /* Write contents of data block. */ + while (1) { + if (fread(&data, 1, 1, src)) { + if (!fwrite(&data, 1, 1, dst)) { + fprintf(stderr, "ERROR: Failed to write block data.\n"); + exit(EXIT_FAILURE); + } + size++; + checksum += data; + } else { + break; + } + } + fclose(src); - uint16_t footer[] = {0,1,6,01000,0}; - fwrite(footer,10,1,dst); + /* Now that block size is known, update block header. */ + if (fseek(dst, 2, SEEK_SET)) { + fprintf(stderr, "ERROR: Failed seek back to header of data block.\n"); + exit(EXIT_FAILURE); + } + for (int i = 0; i < 2; i++) { + switch (i) { + case 0: data = size & 0xff; break; + case 1: data = (size >> 8) & 0xff; break; + } + if (!fwrite(&data, 1, 1, dst)) { + fprintf(stderr, "ERROR: Failed to write block size into header.\n"); + exit(EXIT_FAILURE); + } + checksum += data; // Header is included in checksum. + } + if (fseek(dst, 0, SEEK_END)) { + fprintf(stderr, "ERROR: Failed seek to end of data block.\n"); + exit(EXIT_FAILURE); + } - fseek(dst,2,SEEK_SET); - fwrite(&size,2,1,dst); + /* Write checksum for data block. */ + checksum = (~checksum) + 1; + data = checksum & 0xff; + if (!fwrite(&data, 1, 1, dst)) { + fprintf(stderr, "ERROR: Failed to write checksum.\n"); + exit(EXIT_FAILURE); + } - fclose(src); + /* Write empty block to indicate end-of-tape. */ + for (int i = 0; i < 8; i++) { + switch (i) { + case 0: data = 0001; break; + case 1: data = 0000; break; + case 2: data = 0006; break; + case 3: data = 0000; break; + case 4: data = address & 0xff; break; + case 5: data = (address >> 8) & 0xff; break; + case 6: data = 0000; break; + case 7: data = 0000; break; + } + if (!fwrite(&data, 1, 1, dst)) { + fprintf(stderr, "ERROR: Failed to write end-of-tape block.\n"); + exit(EXIT_FAILURE); + } + } fclose(dst); }