Skip to content

Instantly share code, notes, and snippets.

@cmgauger
Last active April 23, 2022 01:02
Show Gist options
  • Save cmgauger/b99d234c40bc775c9fec03fc7addf4b1 to your computer and use it in GitHub Desktop.
Save cmgauger/b99d234c40bc775c9fec03fc7addf4b1 to your computer and use it in GitHub Desktop.
PDP-11 C program to initialize/format IBM 3740 formatte diskettes using RT-11
/* DX3741.C
*
* Copyright (c) 2022 Christian Gauger-Cosgrove
*
* 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.
*
*
*
* The following program is a PDP-11 C (as evidenced by the "rtsys.h" header for
* RT-11 system library function calls) program to initialize a standard RX01
* diskette in drive unit 0 to the format of a diskette produced by the IBM 3740
* Data Entry System. Although presently it is in ASCII, not EBCDIC.
*
* The diskette format is explained in IBM publications GA21-9151-0 "IBM 3740
* Data Entry System, IBM 3741 Data Station, & IBM 3742 Dual Data Station
* Systems Reference Manual", and GA21-9182-3 "The IBM Diskette General
* Information Manual".
*
*
* To compile the program using PDP-11 C, using either the XM or ZM monitor:
* .cc dx3741
* .link dx3741,ceisrt
* To run the program you must use the virtual run command VRUN, e.g.:
* .v dx3741
*
*
* !!!!! ONCE AGAIN NOTE THE CURRENT PROGRAM PRODUCES *ASCII* DISKETTES, NOT
* EBCDIC DISKETTES !!!!!
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include <rtsys.h>
/* Define function codes for SPFUN
* Taken from AA-PE7VA-TC "RT-11 Device Handler Manual", chapter 2 section 3
*/
#define ARD 0377
#define AWR 0376
#define WDD 0375
/* I am lazy; and this is not multi-threaded so: global I/O channel number */
short channel;
/* Unbiased bounded rand; this is assuredly overkill */
unsigned short brand(unsigned short range) {
unsigned short x, r;
do {
x = rand();
r = x % range;
} while (x - r > (-range));
return r;
}
/* Open the I/O channel
*
* Refer to AA-PD6LA-TC "RT-11 System Macro Library Manual" for details on the
* .CSTAT and .LOOKUP system calls
*/
int chanOpen(void) {
short csb[6];
short dblk[4] = {__RAD50("DX0"), 0, 0, 0};
/* Generate a random channel number between 000 and 016
* Check if it is "free" to use using .CSTAT
*/
do {
while((channel = brand(020)) == 017);
/* Call .CSTAT, while() check the "free" bit in the channel
* status word of the returned channel status information block
*/
CSTAT(&channel, csb, NULL);
} while((csb[0] & 0x8000) != 0);
/* Open the channel for raw access to DX0: */
LOOKUP(&channel, dblk, NULL);
return 0;
}
/* Close the I/O channel
* Refer to AA-PD6LA-TC "RT-11 System Macro Library Manual" for details on the
* .CLOSEC system call
*/
int chanClose(void) {
short e;
CLOSEC(&channel, &e);
return 0;
}
/* Write the 128-byte buffer to specified sector of the diskette
* Refer both to AA-PD6LA-TC and AA-PE7VA-TC for documentation on .SPFUN and how
* to call the DX handler
*/
int wrBuffer(int track, int sector, char bufr[128]) {
short b[65], c, s, t;
int i;
/* Convert the 128-byte buffer of characters into the correct 65-word
* buffer for the SP.AWR function of .SPFUN
*
* Word 0 should (read: must) be 0; words 1-65 are the 128-bytes of data
* to be written.
*/
for (i = 0; i < 65; ++i) {
b[i] = 0;
if (i > 0) {
b[i] = bufr[(i - 1) * 2 + 1] << 8 | bufr[(i - 1) * 2];
}
}
/* Setup the function code, track, and sector words */
c = AWR;
t = track;
s = sector;
/* Call .SPFUN */
SPFN(&c, &channel, &t, b, &s);
return 0;
}
/* Read specified sector of the diskette to a 128-byte buffer
* Refer both to AA-PD6LA-TC and AA-PE7VA-TC for documentation on .SPFUN and how
* to call the DX handler
*/
int rdBuffer(int track, int sector, char bufr[128]) {
short b[65], c, s, t;
int i;
/* Setup the function code, track, and sector words */
c = ARD;
t = track;
s = sector;
/* Call .SPFUN */
SPFN(&c, &channel, &t, b, &s);
/* Conver the 65-word buffer of the SP.ARD function of .SPFUN into the
* 128-byte data buffer
*
* Word 0 indicates of if the block was written with the deleted data
* mark; however we are ignoring that. Words 1-65 contain the 128-bytes
* of data contained in the sector.
*/
for (i = 0; i < 65; ++i) {
if (i > 0) {
bufr[(i - 1) * 2 + 1] = (b[i] & 0xFF00) >> 8;
bufr[(i - 1) * 2] = b[i] & 0x00FF;
}
}
return 0;
}
/* If you don't know what main() is what are you doing in a C program? */
int main(void) {
int trk, sec;
char buf[128];
/* Behold: the lazy way of seeding rand() */
srand(time(NULL) ^ 0xCAFE);
/* Open an I/O channel */
chanOpen();
/* Format the diskette
* The diskette format is 77 tracks of 26 sectors; tracks numbered 0-76,
* sectors numberer 1-26
*/
for (trk = 0; trk < 77; ++trk) {
for (sec = 1; sec <= 26; ++sec) {
/* Tell the user what sector we're initializing */
printf("Track %2d, Sector %2d\n", trk, sec);
fflush(stdout);
/* Zeroize the sector buffer */
memset(buf, 0, 128 * sizeof(char));
/* Select track specific format */
if (trk == 0) {
/* All sectors have 80 blanks (i.s. spaces), and
* remainder is zeroes
*/
memset(buf, ' ', 80 * sizeof(char));
/* Index cylinder; check sector */
switch (sec) {
case 1:
case 2:
case 3:
case 4:
case 6:
/* Sectors 1 through 4, and 6 are
* reserved; sectors 1 and 2 are
* IPL/IMPL data (that we don't use) and
* sector 3 is the system scratch sector
*/
break;
case 5:
/* Sector 4 is the ERMAP sector; we're
* presuming the diskette is defect
* free (which is true of various
* emulators like SIMH)
*/
buf[0] = 'E';
buf[1] = 'R';
buf[2] = 'M';
buf[3] = 'A';
buf[4] = 'P';
break;
case 7:
/* Sector 7 is the volume label; we're
* using the "newly formatted" diskette
* label from GA21-9182-3
*/
buf[ 0] = 'V';
buf[ 1] = 'O';
buf[ 2] = 'L';
buf[ 3] = '1';
buf[ 4] = 'I';
buf[ 5] = 'B';
buf[ 6] = 'M';
buf[ 7] = 'I';
buf[ 8] = 'R';
buf[ 9] = 'D';
buf[72] = 'P';
buf[76] = '0';
buf[77] = '1';
buf[79] = 'W';
break;
case 8:
/* Sector 8 is the first data set label,
* refer to GA21-9182-3
*/
buf[ 0] = 'H';
buf[ 1] = 'D';
buf[ 2] = 'R';
buf[ 3] = '1';
buf[ 5] = 'D';
buf[ 6] = 'A';
buf[ 7] = 'T';
buf[ 8] = 'A';
buf[24] = '0';
buf[25] = '8';
buf[26] = '0';
buf[28] = '0';
buf[29] = '1';
buf[30] = '0';
buf[31] = '0';
buf[32] = '1';
buf[34] = '7';
buf[35] = '3';
buf[36] = '0';
buf[37] = '2';
buf[38] = '6';
buf[74] = '0';
buf[75] = '1';
buf[76] = '0';
buf[77] = '0';
buf[78] = '1';
break;
default:
/* Sectors 9 through 26 are the rest of
* the data set labels; again refer to
* GA21-9182-3 for the format
*/
sprintf(buf, "DDR1 DATA%02d", sec);
buf[11] = ' ';
buf[24] = '0';
buf[25] = '8';
buf[26] = '0';
buf[28] = '7';
buf[29] = '4';
buf[30] = '0';
buf[31] = '0';
buf[32] = '1';
buf[34] = '7';
buf[35] = '3';
buf[36] = '0';
buf[37] = '2';
buf[38] = '6';
buf[74] = '7';
buf[75] = '4';
buf[76] = '0';
buf[77] = '0';
buf[78] = '1';
break;
}
} else if ((trk >= 1) && (trk <= 74)) {
/* Data cylinders; all sectors have 80 blanks
* (i.s. spaces), and remainder is zeroes */
memset(buf, ' ', 80 * sizeof(char));
} else {
/* All zeroes */
if ((trk == 76) && (sec == 26)) {
/* !TESTING ONLY!
*
* Make a "note" on the last sector
*
*/
sprintf(buf, "RT11ZM Diskette initia"
"lized on RT-11 v5.7; in ASCII not "
"EBCDIC.");
}
}
/* Write the buffer to the diskette */
wrBuffer(trk, sec, buf);
}
}
/* We're done so clean up and close the channel */
chanClose();
exit(1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment