Skip to content

Instantly share code, notes, and snippets.

@racingmars
Created February 25, 2024 03:37
Show Gist options
  • Save racingmars/9f56df4d128099d88abfc6a6e98d19bc to your computer and use it in GitHub Desktop.
Save racingmars/9f56df4d128099d88abfc6a6e98d19bc to your computer and use it in GitHub Desktop.
A utility to copy routine exports from a text file to a DSM-11 compatible simh tape
/*
* crtdsmtap - Create a DSM-11 compatible routine tape file for use with simh.
*
* This utility reads a single saved M[UMPS] language routine save file and
* converts it into a simh tap file that can be read with the ^%RR utility in
* DSM-11 on a simh PDP-11 (or DSM for OpenVMS on a simh VAX).
*
* The routine file must already be in a format that is supported by DSM-11.
* %^RS files from current versions of Cache/IRIS don't always work...I have
* found that importing the routines into RSM first, then using ^%RS from RSM
* tends to clean these up into a format that DSM-11 understands.
*
* NOTE: DSM uses CRLF line endings, but it seems to tolerate bare linefeeds
* when I export routines from RSM on Linux. If your routine save file was
* created on, e.g., a Unix-like system, and you end up having trouble with
* DSM reading it, you can try converting it first with a utility such as
* unix2dos.
*
* Also keep in mind that DSM-11 doesn't support newer M language features,
* including things like DO with arguments. So a lot of modern code will just
* produce an error while reading the routines from tape under DSM-11.
*
* Matthew R. Wilson <mwilson@mattwilson.org>
* February, 2024
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
/* The first data record in the simh tape when exporting from DSM on OpenVMS
* appears to always be the same, this data record with 14 bytes of content.
* No idea what this is, there's no tape mark after it so I don't think it's a
* tape label, it just seems to be a magic number DSM-11 expects, perhaps? */
const unsigned char header[] = {
0x0e, 0x00, 0x00, 0x00, 0x95, 0x54, 0xf8, 0x66, 0x4f, 0xc0, 0x01, 0x01,
0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00
};
/* The simh tape format uses a little-endian 4-byte int to represent the size
* of a data record. We will use 1024-byte blocks, and each block will be a
* data record in the tap file, so we'll just keep that value handy here. */
const unsigned char recordsize[] = { 0x00, 0x04, 0x00, 0x00 };
/* Tape will be written with 1024-byte blocks. */
#define BLKSZ 1024
int main(int argc, char **argv)
{
int i; /* loop counter */
int b; /* character buffer */
int done; /* EOF indicator */
FILE *in, *out;
/* Check for valid input arguments */
if (argc != 3) {
fprintf(stderr, "\nUsage:\n");
fprintf(stderr, " crtdsmtap <input file> <output file>\n\n");
return 1;
}
in = fopen(argv[1], "r");
if (in == NULL) {
fprintf(stderr, "open '%s': %s\n", argv[1], strerror(errno));
return 1;
}
out = fopen(argv[2], "w");
if (out == NULL) {
fprintf(stderr, "open '%s': %s\n", argv[2], strerror(errno));
return 1;
}
/* Write the tape (DSM format?) header */
for (i=0; i < sizeof(header); i++) {
if (EOF == putc(header[i], out)) {
fprintf(stderr, "write error: %s\n", strerror(errno));
return 1;
}
}
/* Now we'll read 1,024 bytes of the input at a time and then write them
* back out to data records in the tap file. The last block, if less than
* 1,204 bytes, needs to be padded out with nulls. A tap data record has
* the data size (little-endian 4-byte int), the data, then the data size
* again. */
done = 0;
while (!done) {
/* Write the record size at the beginning of the record. */
for (i=0; i < sizeof(recordsize); i++) {
if (EOF == putc(recordsize[i], out)) {
fprintf(stderr, "write error: %s\n", strerror(errno));
return 1;
}
}
/* Write the data. */
for (i=0; i<BLKSZ; i++) {
if (!done) {
if ((b = getc(in)) != EOF) {
if (EOF == putc(b, out)) {
/* Uh oh, write error! */
fprintf(stderr, "write error: %s\n", strerror(errno));
return 1;
}
} else {
/* Was EOF a real EOF, or a different error? */
if (errno) {
/* There was an error. */
fprintf(stderr, "read error: %s\n", strerror(errno));
return 1;
} else {
/* Real EOF. We'll start padding the output. */
if (EOF == putc(0, out)) {
fprintf(stderr, "write error: %s\n",
strerror(errno));
return 1;
}
done = 1;
}
}
} else {
/* Pad final output block */
if (EOF == putc(0, out)) {
fprintf(stderr, "write error: %s\n", strerror(errno));
return 1;
}
}
}
/* Write the record size at the end of the record. */
for (i=0; i < sizeof(recordsize); i++) {
if (EOF == putc(recordsize[i], out)) {
fprintf(stderr, "write error: %s\n", strerror(errno));
return 1;
}
}
}
/* Finish the tape with two tape marks. Each tape mark is 4 null bytes. */
for (i=0; i<8; i++) {
if (EOF == putc(0, out)) {
fprintf(stderr, "write error: %s\n", strerror(errno));
return 1;
}
}
/* We don't care about errors closing the input file, but we should alert
* the user about errors closing the output file. */
fclose(in);
if (fclose(out)) {
fprintf(stderr, "close '%s': %s\n", argv[2], strerror(errno));
return 1;
}
return 0;
}
/*
* MIT License
*
* Copyright (c) 2024 Matthew R. Wilson <mwilson@mattwilson.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment