#include <errno.h>
#include <getopt.h>
-#define VERSION 1
+#define VERSION 2
void
print_usage( char ** argv )
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
*
- * 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.
+ * 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;
+ 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);
}