# Overview #
This is a simple program that converts raw binary data into a paper-tape image
-compatible with SIMH.
+compatible with SIMH's `load` command.
For example, I commonly use it when transfering compiled PDP-11 code into SIMH
with the output of:
pdp11-aout-objcopy --only-section=.text --output-target binary program.out program.bin
-It does not generate correct checksums, but this generates only a warning in
-SIMH, not an error.
-
# Status #
default it installs to `$HOME/bin`.
Once compiled and added to your `$PATH`, simply provide a raw binary blob as
-`input` and receive a SIMH compatible paper tape image at `output`.
+`input` and receive a SIMH compatible paper tape image as `output`.
If provided via the `-a` flag, the paper tape will load at the provided address
in memory. If not provided, this defaults to `01000` (`1000` octal).
#include <errno.h>
#include <getopt.h>
-#define VERSION 1
+#define VERSION 2
void
print_usage( char ** argv )
* 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);
}