Improved description of tape format in source code comment.
[pdp11-bin2load] / bin2load.c
/*
* © 2020 Aaron Taylor <ataylor at subgeniuskitty dot com>
* See LICENSE.txt file for copyright and license details.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#define VERSION 1
void
print_usage( char ** argv )
{
printf( "bin2load v%d (www.subgeniuskitty.com)\n"
"Usage: %s i <file> -o <file> [-a <address>]\n"
" -i <file> Binary file to be written to tape.\n"
" For example, binary executable from 'pdp11-aout-objdump'.\n"
" -o <file> New paper tape image for use with SIMH.\n"
" -a <address> Address on PDP-11 at which to load paper tape contents.\n"
, VERSION, argv[0]
);
}
int
main( int argc, char ** argv)
{
int c;
FILE * src = NULL;
FILE * dst = NULL;
uint16_t address = 01000; /* Default address to load tape contents. */
while ((c = getopt(argc, argv, "i:o:a:h")) != -1) {
switch (c) {
case 'i':
if ((src = fopen(optarg, "r")) == NULL ) {
fprintf(stderr, "ERROR: %s: %s\n", optarg, strerror(errno));
}
break;
case 'o':
if ((dst = fopen(optarg, "w+")) == NULL ) {
fprintf(stderr, "ERROR: %s: %s\n", optarg, strerror(errno));
}
break;
case 'a':
address = (uint16_t) strtol(optarg, NULL, 0);
break;
case 'h':
print_usage(argv);
exit(EXIT_FAILURE);
break;
default:
break;
}
}
if (src == NULL || dst == NULL) {
print_usage(argv);
exit(EXIT_FAILURE);
}
printf("Paper tape will load at address 0%o.\n", address);
/*
* 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
*
* 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;
uint16_t header[] = {1,0,01000};
fwrite(header,6,1,dst);
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);
uint16_t footer[] = {0,1,6,01000,0};
fwrite(footer,10,1,dst);
fseek(dst,2,SEEK_SET);
fwrite(&size,2,1,dst);
fclose(src);
fclose(dst);
}