Created
February 25, 2024 03:37
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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