blob: 406f367a9cc9db63f18d5d5f30610ec3d6ee9c30 [file] [log] [blame] [raw]
// -------------------------------------------------------------------------------------------
// DISKLIST - print directory listing of DMS2 disk image
// -------------------------------------------------------------------------------------------
/*
* (C) Copyright 2004, Brian Knittel.
* You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN
* RISK basis, there is no warranty of fitness for any purpose, and the rest of the
* usual yada-yada. Please keep this notice and the copyright in any distributions
* or modifications.
*/
// -------------------------------------------------------------------------------------------
// HISTORY
// -------------------------------------------------------------------------------------------
// 24-Nov-2004 Written. It would be nice to make this distinguish a DMS disk from
// other potential disk formats (e.g. APL\1130).
//
// 03-Dec-2004 Split get_let and print_let so we could get listings for specific files.
// 06-Dec-2004 Added printout of detailed file info (print_xxx_info) and dump, and listing of calls
// 21-Dec-2006 Added file type column in standard output mode
// -------------------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util_io.h"
// -------------------------------------------------------------------------------------------
// DEFINITIONS
// -------------------------------------------------------------------------------------------
typedef int BOOL; // boolean
typedef unsigned short uint16; // unsigned 16-bit integer
typedef short int16; // signed 16-bit integer
#define TRUE 1 // BOOL values
#define FALSE 0
#define ALLOCATE(obj) ((obj *) calloc(1, sizeof(obj))) // macro to allocate an object and return pointer to it
#define SEC_WORDS 320 // useful words per sector
#define SEC_BYTES 640 // bytes per sector
#define PHY_WORDS 321 // physical words per sector
#define SLET_LENGTH 160 // size of SLET (2 sectors of 4 words per entry)
#define SEC_BLOCKS 16 // disk blocks per sector
#define BLK_WORDS 20 // size of a "disk block", a sub-sector
#define BLK_BYTES 40
#define THOUSANDS_SEP ',' // thousands separator (eg. 9,999,999)
// (in Europe, define as '.', or comment out definition)
typedef struct tag_letentry { // linked list node for directory entry
struct tag_letentry *next;
char name[6]; // file name (1-5 chars)
uint16 filetype; // file image type
uint16 dbcount; // length in DMS "disk blocks" (20 words per block)
uint16 dbaddr; // disk block address
struct tag_letentry *master; // master entry, if this is an alternate name entry
BOOL dummy; // TRUE if this is a 1DUMY entry
} LETENTRY;
typedef struct tag_filearg { // node in linked list of filename arguments
struct tag_filearg *next;
char *name;
} FILEARG;
static char *progtype_nm[16] = { // DMS2 program types for DSF format files
"Undefined", "Mainline, absolute", "Mainline, relocatable", "LIBF Subprogram",
"CALL Subprogram", "LIBF Interrupt Service Subroutine (ISS)",
"CALL Interrupt Service Subroutine (ISS)", "Interrupt Level Subroutine (ILS)",
"Undefined", "Undefined", "Undefined", "Undefined",
"Undefined", "Undefined", "Undefined", "Undefined"
};
struct { // DMS2 program subtypes for DSF format files
unsigned progtype;
unsigned subtype;
char *descr;
} subtype_nm[] = {
3, 0, "In-core subprogram",
3, 1, "FORTRAN Disk IO subroutine",
3, 2, "Arithmetic subroutine",
3, 3, "FORTRAN non-disk IO and \"Z\" conversion subroutine",
5, 3, "\"Z\" device suboutine",
5, 0, NULL, // NULL suppresses printing of subtype
4, 0, "In-core subprogram",
4, 8, "Function subprogram",
7, 1, "Dummy ILS02 or ILS04",
};
#define N_SUBTYPE_NMS (sizeof(subtype_nm) / sizeof(subtype_nm[0]))
#pragma pack(1)
typedef struct tag_dsf_program_header { // header block of a DSF format file, disk layout
uint16 zero1;
uint16 checksum;
uint16 type;
uint16 proglen; // effective length, terminal address
uint16 commonlen; // length of common
uint16 hdr_len9; // length of this header - 9
uint16 zero2;
uint16 dblen; // length of program including header in disk blocks (20 wds)
uint16 fortran_info;
union {
struct { // normal programs: entry point information. 1-15. Actual number is hdr_len9/3
uint16 name[2];
uint16 addr;
} entry[15];
struct { // ISS (types 5 and 6)
uint16 name[2];
uint16 addr;
uint16 iss_50; // ISS number + 50
uint16 issnumber; // ISS number
uint16 nlevels; // # of levels required (1 or 2)
uint16 level[2]; // interrupt level associated with interrupt (nlevels entries used)
} iss;
struct { // ILS (type 7)
uint16 name[2];
uint16 addr;
uint16 level; // interrupt level
} ils;
} x;
} DSF_PROGRAM_HEADER;
typedef struct tag_dci_program_header { // header of a DCI (core image) format file, disk layout
uint16 xeqa; // execute address
uint16 cmon; // length of COMMON
uint16 dreq; // disk IO indicator, /FFFF for DISKZ, 0000 for DISK1, 0001 for DISKN
uint16 file; // number of files defined
uint16 hwct; // length of core image header in words
uint16 lsct; // sector count of files in system WS
uint16 ldad; // loading address of core load
uint16 xctl; // exit control address for DISK1/N
uint16 tvwc; // length of transfer vector in words
uint16 wcnt; // length, in words of the core load, core image header and transfer vector
uint16 xr3x; // setting for index register 3 during execution of core load
uint16 itv[6]; // ITV (values of words 8-13 during execution)
uint16 reserved1;
uint16 ibt[8]; // IBT for ILS04. interrupt entry for ISS of 1231 (3 words), 1403, 2501, 1442, keyboard/prt, 1134/1055 respectively
uint16 ovsw; // sector count of LOCALs/SOCALs
uint16 core; // core size of system on which core load was built
uint16 reserved2[2];
uint16 hend; // last word of header
} DCI_PROGRAM_HEADER;
#pragma pack()
// -------------------------------------------------------------------------------------------
// GLOBALS
// -------------------------------------------------------------------------------------------
#define FILETYPE_DSF 0 // DMS2 filetypes. Type 1 is undefined
#define FILETYPE_1 1
#define FILETYPE_DCI 2
#define FILETYPE_DDF 3
uint16 defective[3] = {0xFFFF, 0xFFFF, 0xFFFF}; // defective cylinder table, number is 1st sector number of bad cylinder
FILE *fd; // stream for open disk image file
BOOL verbose = FALSE; // verbose switch
BOOL show_all = FALSE; // switch to display alternate file entries
BOOL dumpslet = FALSE; // dump SLET switch
BOOL do_dump = FALSE;
char *ftname[4] = {"DSF", "???", "DCI", "DDF"}; // DMS2 filetype names
LETENTRY *flet = NULL, *let = NULL; // pointers to contents of FLET and LET
#pragma pack(1) // (don't pad struct elements)
struct { // buffer for one sector
uint16 secno;
uint16 data[SEC_WORDS];
} sector;
struct { // structure of the SLET on disk
uint16 id;
uint16 addr;
uint16 size;
uint16 secno;
} slet[SLET_LENGTH];
struct { // DCOM sector
uint16 _dummy0;
uint16 _dummy1;
uint16 _dummy2;
uint16 _dummy3;
uint16 name[2]; // 4 name of program
uint16 dbct; // 6 disk block count of program
uint16 fcnt; // 7 files indicator
uint16 sysc; // 8 system cartridge indicator switch
uint16 jbsw; // 9 temporary job switch (nonzero = JOB T)
uint16 cbsw; // 10 clb switch (nonzero = storeci)
uint16 lcnt; // 11 local indicator (# of locals)
uint16 mpsw; // 12 core map desired switch
uint16 mdf1; // 13 no. of dup ctrl rcds (modif)
uint16 mdf2; // 14 addr of modif buffer
uint16 ncnt; // 15 nocal indicator
uint16 enty; // 16 rel entry addr of program
uint16 rp67; // 17 1442-5 switch (nonzero = mod 6 or 7)
uint16 todr; // 18 -to- wk stg drive code
uint16 frdr; // 19 -from- wk stg drive code
uint16 fhol; // 20 addr of largest hole in fxa
uint16 fsze; // 21 blk cnt largest hole in fxa
uint16 uhol; // 22 addr of largest hole in ua
uint16 usze; // 23 blk cnt largest hole in ua
uint16 dcsw; // 24 dup call switch
uint16 piod; // 25 principal IO device indicator
uint16 pptr; // 26 print print device indicator
uint16 ciad; // 27 sctr 0 loc of cil sctr addr
uint16 cain; // 28 avail cartridge indicator
uint16 grph; // 29 2250 indicator
uint16 gcnt; // 30 g2260 count
uint16 locw; // 31 local call locals sw
uint16 x3sw; // 32 special ils sw
uint16 ecnt; // 33 equat count
uint16 _dummy34; // 34
uint16 andu[5]; // 35 end of UA address (adj)
uint16 bndu[5]; // 40 end of UA address (base)
uint16 fpad[5]; // 45 file protected address
uint16 pcid[5]; // 50 available cartridge IDs (physical drvs 0..4)
uint16 cidn[5]; // 55 cartridge ID (logical drvs 0..4)
uint16 ciba[5]; // 60 sector address of CIB
uint16 scra[5]; // 65 sector address of SCRA
uint16 fmat[5]; // 70 format of program in WS
uint16 flet[5]; // 75 FLET sector address
uint16 ulet[5]; // 80 LET sector address
uint16 wsct[5]; // 85 BLK count of program in WS
uint16 cshn[5]; // 90 1+sctr addr of end of cusn.
} dcom;
#pragma pack()
BOOL is_system = FALSE; // TRUE if this is a system disk
// NOTE: in DMS, disk blocks are 1/16 of a sector (20 words) -- DMS suballocates sectors for files. Some
// files have to start on a sector boundary, and there are 1DUMY entries for the little lost bits. The last
// 1DUMY entry in the LET is the size of Working Storage.
// -------------------------------------------------------------------------------------------
// PROTOTYPES
// -------------------------------------------------------------------------------------------
void getsec (uint16 secno); // read sector by number
void getdata (void *buf, uint16 dbaddr, uint16 offset, uint16 nwords); // read data from file relative to its disk block address
void bail (char *msg); // print error message and exit
void print_slet (void); // print contents of SLET
void get_let (LETENTRY **listhead, uint16 secno); // read FLET or LET, building linked list
void print_let (char *title, LETENTRY *listhead); // print contents of FLET or LET
void list_named_files (char *name, char *image) ; // print info for specified file(s)
void print_onefile (LETENTRY *entry, BOOL in_flet); // print detailed info about one particular file
int ebcdic_to_ascii (int ch); // convert EBCDIC character to ASCII
void convert_namecode (uint16 *namecode, char *name); // convert DMS name code words into ASCII filename
char *upcase (char *str); // convert string to upper case
void commas (int n, int width); // print number n with commas
char *astring (char *str); // allocate memory for and return copy of string
void print_dsf_info (LETENTRY *entry); // print information about a Disk System Format file
void print_dci_info (LETENTRY *entry); // print information about a Disk Core Image file
void print_ddf_info (LETENTRY *entry); // print information about a Disk Data Format file
char * file_progtype (LETENTRY *entry); // description of module type
// -------------------------------------------------------------------------------------------
// main - main routine
// -------------------------------------------------------------------------------------------
int main (int argc, char **argv)
{
int i;
char *arg, cartid[10];
char *image = NULL;
FILEARG *fileargs = NULL, *filearg, *filearg_tail = NULL;
char *usestr = "Usage: disklist [-sadv] diskfile [filename ...]\n"
"\n"
"Lists contents of fixed and user area directories in IBM 1130 DMS 2\n"
"disk image file \"diskfile\". With the optional filename argument(s)\n"
"(1-5 letters), prints detailed information about the named file(s).\n"
"Wildcard characters ? and * may be specfied in the filename."
"\n"
" -s dump SLET in addition to fixed and user areas\n"
" -a dump additional information including alternate entries and addresses\n"
" For named file(s), prints information about entry points and calls\n"
" -d dumps contents of named file(s) in hex\n"
" -v verbose mode, prints internal information\n";
for (i = 1; i < argc; i++) { // scan command line arguments
arg = argv[i];
if (*arg == '-') { // command line switch
arg++; // skip over the -
while (*arg) { // process all flags
switch (*arg++) {
case 'v':
verbose = TRUE; // -v turns on verbose mode
break;
case 'a':
show_all = TRUE; // -a turns on listing of alternate file entries & pad spaces
break;
case 's': // -s turns on dump slet
dumpslet = TRUE;
break;
case 'd':
do_dump = TRUE;
break;
default:
bail(usestr); // unrecognized flag
}
}
}
else if (image == NULL)
image = arg; // first name is the name of disk image file
else {
filearg = ALLOCATE(FILEARG); // subsequent names are filename arguments,
filearg->name = upcase(astring(arg)); // copy to a FILEARG object (as uppercase)
filearg->next = NULL;
if (fileargs == NULL) // add to end of linked list
fileargs = filearg;
else
filearg_tail->next = filearg;
filearg_tail = filearg;
}
}
if (image == NULL) // filename was not specified
bail(usestr);
if ((fd = fopen(image, "rb")) == NULL) { // open file in binary mode
perror(image); // print reason for open failure and exit
return 1;
}
getsec(0); // get sector 0, which has defective cylinder table
defective[0] = sector.data[0]; // load defective cylinder table
defective[1] = sector.data[1];
defective[2] = sector.data[2];
if (verbose)
printf("Defective cylinder table: %04x %04x %04x\n", defective[0], defective[1], defective[2]);
printf("Filename: %s", image);
sprintf(cartid, "%04x", sector.data[3]); // display cartridge ID in upper case
printf(" Cartridge ID: %s", upcase(cartid));
if (show_all)
printf(" Copy: number %d", sector.data[4]);
printf("\n\n");
getsec(1); // get sector 1, save to DCOM
memcpy(&dcom, sector.data, min(sizeof(dcom), SEC_BYTES));
is_system = dcom.sysc != 0; // is this a system cartridge?
if (dumpslet) { // display SLET
if (is_system) {
getsec(3);
memcpy(slet+0, sector.data, SEC_BYTES);
getsec(4);
memcpy(slet+80, sector.data, SEC_BYTES);
print_slet();
}
else
printf("(Not a system cartridge, no SLET)\n\n");
}
if (dcom.flet[0] != 0) // do we have a FLET?
get_let(&flet, dcom.flet[0]); // read it
get_let(&let, dcom.ulet[0]); // read LET
if (fileargs == NULL) { // if there are no filename arguments
if (flet != NULL) // print FLET and LET
print_let("FIXED AREA", flet);
print_let("USER AREA", let);
}
else { // print information for specified file(s)
for (filearg = fileargs; filearg != NULL; filearg = filearg->next)
list_named_files(filearg->name, image);
}
return 0;
}
// -------------------------------------------------------------------------------------------
// upcase - force a string to uppercase (ASCII)
// -------------------------------------------------------------------------------------------
char *upcase (char *str)
{
char *s;
for (s = str; *s; s++) {
if (*s >= 'a' && *s <= 'z')
*s -= 32;
}
return str;
}
// -------------------------------------------------------------------------------------------
// commas - print n as a decimal number with commas; width is minimum width
// -------------------------------------------------------------------------------------------
void commas (int n, int width)
{
char fmt[20];
#ifdef THOUSANDS_SEP
int nchar;
char tmp[20], *cin, *cout;
sprintf(tmp, "%d", n); // format number n into string
nchar = strlen(tmp); // get length of string
for (cin = tmp, cout = fmt; *cin; ) { // scan through the formatted number
*cout++ = *cin++; // output digit
--nchar; // get number of digits left
if (nchar > 0 && (nchar % 3) == 0)
*cout++ = THOUSANDS_SEP; // if there is a multiple of three digits left, emit a comma
}
*cout = '\0'; // terminate string
#else // THOUSANDS_SEP is undefined, output number w/o commas
sprintf(fmt, "%d", n);
#endif
width -= strlen(fmt); // get width shortage
while (--width >= 0) // output spaces if necessary
putchar(' ');
fputs(fmt, stdout); // print formatted number
}
// -------------------------------------------------------------------------------------------
// bail - print fatal error message and exit
// -------------------------------------------------------------------------------------------
void bail (char *msg)
{
fprintf(stderr, "%s\n", msg);
exit(1);
}
// -------------------------------------------------------------------------------------------
// getsec - read desired sector
// -------------------------------------------------------------------------------------------
void getsec (uint16 secno)
{
int i, phys_sec;
static uint16 cur_sec = 0xFFFF;
if (secno == cur_sec) // see if we already have the sector. Presumes
return; // we haven't modified its contents!
cur_sec = secno; // remember current sector
phys_sec = secno; // physical sector
for (i = 0; i < 3; i++) { // bump cylinder if it's past any in the defective cylinder list
if (secno >= defective[i]) // (use logical secno for comparisons, not physical secno)
phys_sec += 8;
else
break;
}
fseek(fd, phys_sec*PHY_WORDS*2, SEEK_SET); // jump to translated sector
if (fxread(&sector, 2, PHY_WORDS, fd) != PHY_WORDS) // fxread handles flipping data on little-endian machines
bail("error reading disk image");
if (sector.secno != secno) { // verify that 1st word in sector is sector number
fprintf(stderr, "* expected sector number /%04x, got /%04x\n", secno, sector.secno);
bail("disk image is corrupt");
}
}
// -------------------------------------------------------------------------------------------
// getdata -- read data from file relative to its disk block address
// -------------------------------------------------------------------------------------------
void getdata (void *buf, uint16 dbaddr, uint16 offset, uint16 nwords)
{
uint16 secno, nsec, nw;
if (nwords == 0)
return;
secno = dbaddr / SEC_BLOCKS; // desired sector number from dbaddr
dbaddr -= SEC_BLOCKS*secno; // # of blocks offset within that sector
offset += dbaddr*BLK_WORDS; // add offset in words
nsec = offset / SEC_WORDS; // turn offset into integer sectors
secno += nsec; // bump sector number
offset -= nsec*SEC_WORDS; // ultimate offset within sector
for (;;) {
getsec(secno); // read desired sector
nw = min(SEC_WORDS-offset, nwords); // number of words to copy from this sector
memcpy(buf, &sector.data[offset], nw*2); // copy the data
if ((nwords -= nw) <= 0) // decrement remaining word count
break;
secno++; // bump sector
offset = 0; // no offset in subsequent sector(s)
((uint16 *) buf) += nw; // bump buffer pointer
}
}
// -------------------------------------------------------------------------------------------
// print_slet - list the contents of the SLET
// -------------------------------------------------------------------------------------------
struct {
unsigned int id;
char *name;
} slet_phase[] = {
0x01, "@DDUP DUPCO *** DUP", // DMS R1V12 phase ID's and names
0x02, "@DCTL DUP CONTROL - PART 1",
0x03, "@STOR STORE",
0x04, "@FILQ FILE EQUATE",
0x05, "@DUMP DUMP",
0x06, "@DL/F DUMP LET/FLET",
0x07, "@DLTE DELETE",
0x08, "@DFNE DEFINE",
0x09, "@EXIT DEXIT",
0x0A, "@CFCE CARD INTERFACE",
0x0B, "@DU11 KEYBOARD INTERFACE",
0x0C, "@DU12 PAPER TAPE INTERFACE",
0x0D, "@DU13 DUP UPCOR",
0x0E, "@DU14 DUP PRINCIPAL I/O",
0x0F, "@DU15 DUP PRINCIPAL I/O SANS KB",
0x10, "@DU16 DUP PAPER TAPE I/O",
0x11, "@PRCI PRE CORE IMAGE",
0x12, "@DU18 DUP RESERVED",
0x1F, "@FR01 INPUT *** FORTRAN COMPILER",
0x20, "@FR02 CLASSIFIER",
0x21, "@FR03 CHECK ORDER/STMNT NUMBER",
0x22, "@FR04 COMMON/SUBROUTINE OR FUNC",
0x23, "@FR05 DIM/REAL, INTEGER, EXTERNAL",
0x24, "@FR06 REAL CONSTANTS",
0x25, "@FR07 DEFN FILE, CALL LINK/EXIT",
0x26, "@FR08 VARIABLES AND STMNT FUNC",
0x27, "@FR09 DATA STATEMENT",
0x28, "@FR10 FORMAT",
0x29, "@FR11 SUBSCRIPT DECOMPOSITION",
0x2A, "@FR12 ASCAN I",
0x2B, "@FR13 ASCAN II",
0x2C, "@FR14 DO, CONTINUE, ETC",
0x2D, "@FR15 SUBSCRIPT OPTIMIZE",
0x2E, "@FR16 SCAN",
0x2F, "@FR17 EXPANDER I",
0x30, "@FR18 EXPANDER II",
0x31, "@FR19 DATA ALLOCATION",
0x32, "@FR20 COMPILATION ERRORS",
0x33, "@FR21 STATEMENT ALLOCATION",
0x34, "@FR22 LIST STATEMENT ALLOCATION",
0x35, "@FR23 LIST SYMBOLS",
0x36, "@FR24 LIST CONSTANTS",
0x37, "@FR25 OUTPUT I",
0x38, "@FR26 OUTPUT II",
0x39, "@FR27 RECOVERY",
0x3A, "DUMMY DUMMY NAME",
0x3B, "DUMMY DUMMY NAME",
0x3C, "DUMMY DUMMY NAME",
0x51, "@QCTL PROCESS CTL CDS *** COBOL COMPILER ",
0x52, "@QTXT SOURCE TEXT REDUCTION",
0x53, "@QLIT LITERAL ALLOCATION",
0x54, "@QDTA DATA DIVISION PROCESSING",
0x55, "@QPRO PROCEDURE DIV SCAN",
0x56, "@QGEN GENERATE INST STRINGS",
0x57, "@QOBJ PRODUCE DSF-MODULE",
0x58, "@QERR MAP/DIAGNOSTIC OUTPUT",
0x59, "@QEND COMPILE TERMINATION",
0x5A, "@QSER PRODUCE SERVICEABILITY",
0x5B, "@QXR1",
0x5C, "@QXR2",
0x6E, "@SUP1 MONITOR CTRL RCD ANALYZER *** SUPERVISOR",
0x6F, "@SUP2 JOB RECORD PROCESSING",
0x70, "@SUP3 DELETE TEMPOTARY LET",
0x71, "@SUP4 XEQ RECORD PROCESSING",
0x72, "@SUP5 SCR PROCESSING",
0x73, "@SUP6 SYSTEM DUMP PROGRAM",
0x74, "@SUP7 AUXILIARY SUPERVISOR",
0x78, "@CLB1 PHASE 1 *** CORE LOAD BUILDER",
0x79, "@CLB2 PHASE 2",
0x7A, "@CLB3 PHASE 3",
0x7B, "@CLB4 PHASE 4",
0x7C, "@CLB5 PHASE 5",
0x7D, "@CLB6 PHASE 6",
0x7E, "@CLB7 PHASE 7",
0x7F, "@CLB8 PHASE 8",
0x80, "@CLB9 PHASE 9",
0x81, "@CLBA PHASE 10",
0x82, "@CLBB PHASE 11",
0x83, "@CLBC PHASE 12",
0x84, "@CLBD PHASE 13 (GRAPHICS)",
0x8C, "@1403 1403 SUBR *** SYSTEM DEVICE DRIVERS",
0x8D, "@1132 1132 SUBR",
0x8E, "@CPTR CONSOLE PRINTER SUBR",
0x8F, "@2501 2501 SUBR",
0x90, "@1442 1442 SUBR",
0x91, "@1134 1134 SUBR",
0x92, "@KBCP KB/CONSOLE PRINTER SUBR",
0x93, "@CDCV 2501/1442 CONVERSION SUBR",
0x94, "@PTCV 1134 CONVERSION SUBR",
0x95, "@KBCV KB/CP CONVERSION SUBR",
0x96, "@DZID DISKZ",
0x97, "@D1ID DISK1",
0x98, "@DNID DISKN",
0x99, "@PPRT PRINCIPAL PRINT SUBROUTINE",
0x9A, "@PIWK PRINCIPAL INPUT SUBROUTINE",
0x9B, "@PIXK PRINCIPAL INPUT W/O KB",
0x9C, "@PCWK PRINCIPAL CONV W/ KEYBOARD",
0x9D, "@PCXK PRINCIPAL CONV W/O KEYBOARD",
0xA0, "@CIL1 PHASE 1 *** CORE IMAGE LOADER",
0xA1, "@CIL2 PHASE 2",
0xB0, "@RG00 PHASE 0 *** RPG COMPILER",
0xB1, "@RG02 PHASE 2",
0xB2, "@RG04 PHASE 4",
0xB3, "@RG06 PHASE 6",
0xB4, "@RG08 PHASE 8",
0xB5, "@RG10 PHASE 10",
0xB6, "@RG12 PHASE 12",
0xB7, "@RG14 PHASE 14",
0xB8, "@RG16 PHASE 16",
0xB9, "@RG17 PHASE 17",
0xBA, "@RG19 PHASE 19",
0xBB, "@RG20 PHASE 20",
0xBC, "@RG21 PHASE 21",
0xBD, "@RG22 PHASE 22",
0xBE, "@RG24 PHASE 24",
0xBF, "@RG26 PHASE 26",
0xC0, "@RG28 PHASE 28",
0xC1, "@RG32 PHASE 32",
0xC2, "@RG34 PHASE 34",
0xC3, "@RG36 PHASE 36",
0xC4, "@RG38 PHASE 38",
0xC5, "@RG40 PHASE 40",
0xC6, "@RG42 PHASE 42",
0xC7, "@RG44 PHASE 44",
0xC8, "@RG46 PHASE 46",
0xC9, "@RG52 PHASE 52",
0xCA, "@RG54 PHASE 54",
0xCB, "@RG58 PHASE 58",
0xCC, "@RG60 PHASE 60",
0xCD, "@DCL2 *** DUP CONTROL - PART 2",
0xCE, "@DMUP MACRO UPDATE PROGRAM",
0xCF, "@AS00 PHASE 0 *** ASSEMBLER",
0xD0, "@ACNV CARD CONVERSION",
0xD1, "@AS10 PHASE 10",
0xD2, "@AS11 PHASE 11",
0xD3, "@AS12 PHASE 12",
0xD4, "@AERM ERROR MESSAGES",
0xD5, "@AS01 PHASE 1",
0xD6, "@AS1A PHASE 1A",
0xD7, "@ASYM SYSTEM SYMBOL TABLE",
0xD8, "@AS03 PHASE 3",
0xD9, "@AS04 PHASE 4",
0xDA, "@AS02 PHASE 2",
0xDB, "@AS2A PHASE 2A",
0xDC, "@AS09 PHASE 9",
0xDD, "@AS05 PHASE 5",
0xDE, "@AS06 PHASE 6",
0xDF, "@AS07 PHASE 7",
0xE0, "@AS7A PHASE 7A",
0xE1, "@AS08 PHASE 8",
0xE2, "@AS8A PHASE 8A",
0xE3, "@APCV CARD PUNCH CONVERSION",
0xE4, "@AINT INTERMEDIATE DISK OUTPT",
0xE5, "@ASAA PHASE 10A",
0xE6, "@ASGR PHASE 13 GRAPHICS",
0xE7, "@ADIV DIVISION OPERATOR",
0xE8, "@AMCC MACRO CONTROL CARDS III",
0xE9, "@AM01 MACRO PHASE 1",
0xEA, "@AM1A MACRO PHASE 1A",
0xEB, "@AM1B MACRO PHASE 1B",
0xEC, "@AM02 MACRO PHASE 2",
0xED, "@AM2A MACRO PHASE 2A",
0xEE, "@AM2B MACRO PHASE 2B",
0xEF, "@AM03 MACRO PHASE 3",
0xF0, "@AM3A MACRO PHASE 3A",
0xF1, "@AM3B MACRO PHASE 3B",
0xF2, "@AX01 CROSS REF - PART 1",
0xF3, "@AX2A CROSS REF - PART 2A",
0xF4, "@AX2B CROSS REF - PART 2B",
0xF5, "@AX2C CROSS REF - PART 2C",
0xF6, "@AX03 CROSS REF - PART 3",
0x100, "@AS00 *** MSP7 ASSEMBLER",
0x101, "@ACNV",
0x102, "@AS10",
0x103, "@AS11",
0x104, "@AS12",
0x105, "@AERM",
0x106, "@AS01",
0x107, "@AS1A",
0x108, "@ASYM",
0x109, "@AS03",
0x10A, "@AS04",
0x10B, "@AS02",
0x10C, "@AS2A",
0x10D, "@AS09",
0x10E, "@AS05",
0x10F, "@AS06",
0x110, "@AS07",
0x111, "@AS7A",
0x112, "@AS08",
0x113, "@AS8A",
0x114, "@APCV",
0x115, "@AINT",
0x116, "@ASAA",
0x117, "@ASGR",
0x118, "@ADIV",
0x119, "@AMCC",
0x11A, "@AM01",
0x11B, "@AM1A",
0x11C, "@AM1B",
0x11D, "@AM02",
0x11E, "@AM2A",
0x11F, "@AM2B",
0x120, "@AM03",
0x121, "@AM3A",
0x122, "@AM3B",
0x123, "@AX01",
0x124, "@AX2A",
0x125, "@AX2B",
0x126, "@AX2C",
0x127, "@AX03",
0x128, "@ASP7",
0xFFFF, ""
};
#define N_SLET_PHASES (sizeof(slet_phase)/sizeof(slet_phase[0]) - 1)
void print_slet (void)
{
int i, j = 0;
printf("SLET (System Logical Equivalence Table)\n\n"); // dump SLET
printf("ID Addr Size Sect Description\n");
printf("---- ---- ---- ---- -----------------------\n");
for (i = 0; i < SLET_LENGTH; i++) {
if (slet[i].id == 0 && slet[i].secno == 0)
break;
printf("%04x %04x %04x %04x ", slet[i].id, slet[i].addr, slet[i].size, slet[i].secno);
while (j < N_SLET_PHASES && slet_phase[j].id < slet[i].id)
j++; // skip to entry in slet_phase name table
printf("%s\n", (slet_phase[j].id == slet[i].id) ? slet_phase[j].name : "?");
}
putchar('\n');
}
// -------------------------------------------------------------------------------------------
// ebcdic_to_ascii - convert EBCDIC character to ASCII (ignores controls)
// -------------------------------------------------------------------------------------------
int ebcdic_to_ascii (int ch)
{
int j;
static int ascii_to_ebcdic_table[128] = {
//
0x00,0x01,0x02,0x03,0x37,0x2d,0x2e,0x2f, 0x16,0x05,0x25,0x0b,0x0c,0x0d,0x0e,0x0f,
//
0x10,0x11,0x12,0x13,0x3c,0x3d,0x32,0x26, 0x18,0x19,0x3f,0x27,0x1c,0x1d,0x1e,0x1f,
// spac ! " # $ % & ' ( ) * + , - . /
0x40,0x5a,0x7f,0x7b,0x5b,0x6c,0x50,0x7d, 0x4d,0x5d,0x5c,0x4e,0x6b,0x60,0x4b,0x61,
// 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7, 0xf8,0xf9,0x7a,0x5e,0x4c,0x7e,0x6e,0x6f,
// @ A B C D E F G H I J K L M N O
0x7c,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, 0xc8,0xc9,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,
// P Q R S T U V W X Y Z [ \ ] & _
0xd7,0xd8,0xd9,0xe2,0xe3,0xe4,0xe5,0xe6, 0xe7,0xe8,0xe9,0xba,0xe0,0xbb,0xb0,0x6d,
// a b c d e f g h i j k l m n o
0x79,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x91,0x92,0x93,0x94,0x95,0x96,
// p q r s t u v w x y z { | } ~
0x97,0x98,0x99,0xa2,0xa3,0xa4,0xa5,0xa6, 0xa7,0xa8,0xa9,0xc0,0x4f,0xd0,0xa1,0x07,
};
for (j = 32; j < 128; j++) // look it up in table. (Of course if we constructed
if (ascii_to_ebcdic_table[j] == ch) // an ebcdic_to_ascii table we could just get the result directly)
return j;
return '?';
}
// -------------------------------------------------------------------------------------------
// convert_namecode - convert two-word name code into 5 character ASCII name
// -------------------------------------------------------------------------------------------
void convert_namecode (uint16 *namecode, char *name)
{
unsigned long val;
int i, ch;
val = (namecode[0] << 16) | namecode[1]; // reconstruct the 30-bit code
for (i = 0; i < 5; i++) { // scan for 5 letters
ch = ((val >> 24) & 0x3F); // pick up 6 bits at leftmost character position
if (ch == 0)
ch = ' '; // zero is a space
else
ch = ebcdic_to_ascii(ch | 0xC0); // add assumed high bits and convert to ASCII
name[i] = ch; // save it
val <<= 6; // shift next character into position
}
while (--i >= 0) // back up to last nonblank character
if (name[i] != ' ')
break;
name[i+1] = '\0'; // terminate string
}
// -------------------------------------------------------------------------------------------
// get_let - get FLET or LET, build into linked list
// -------------------------------------------------------------------------------------------
void get_let (LETENTRY **listhead, uint16 secno)
{
uint16 seq, sec_addr, avail, chain, namecode[2], dbcount, addr;
int i, nwords, filetype;
char name[6];
LETENTRY *head = NULL, *tail, *entry, *master = NULL;
for (; secno != 0; secno = chain) { // scan through linked sectors
getsec(secno);
seq = sector.data[0]; // relative sector number
sec_addr = sector.data[1]; // sector address of FA or UA
avail = sector.data[3]; // available words in this sector
chain = sector.data[4]; // next sector number, 0 if this is the last
if (seq == 0) // first time through, get starting address of FA or UA
addr = sec_addr*16; // (convert sector to "disk block")
if (verbose)
printf(" (sector %d, addr /%04x, next %04x)\n", seq, secno, chain);
nwords = SEC_WORDS - 5 - avail; // number of words used by F/LET entries in this sector
for (i = 5; nwords >= 3; ) { // scan through entries
filetype = (sector.data[i] >> 14) & 0x03; // get file type: 0=DSF, 2=DCI, 3=data
namecode[0] = sector.data[i] & 0x3FFF; // get name
namecode[1] = sector.data[i+1];
convert_namecode(namecode, name);
dbcount = sector.data[i+2]; // get disk block count
i += 3; // advance index, decrement words-left count
nwords -= 3;
entry = ALLOCATE(LETENTRY);
strcpy(entry->name, name);
entry->next = NULL;
entry->filetype = filetype;
entry->dbaddr = addr;
entry->dbcount = dbcount;
entry->dummy = strcmp(entry->name, "1DUMY") == 0;
if (dbcount == 0)
entry->master = master; // this is an alternate entry for previous master entry
else {
entry->master = NULL; // this is a master entry
master = entry;
}
if (head == NULL)
head = entry; // first entry is head of linked list
else
tail->next = entry; // add to end of linked list
tail = entry;
addr += dbcount; // skip to next file
}
}
*listhead = head;
}
// -------------------------------------------------------------------------------------------
// print_let - list FLET or LET
// -------------------------------------------------------------------------------------------
void print_let (char *title, LETENTRY *entry)
{
int nblocks, nfiles, nalternates, nfree; // used to get total files, blocks in a directory
static char *ftname[4] = {"DSF", "???", "DCI", "DDF"}; // filetype names
nfiles = 0; // reset total counts
nblocks = 0;
nalternates = 0;
nfree = 0;
printf("%s\n\n", title);
printf("Name Type Blocks%s\n", show_all ? " Addr Type" : "");
printf("----- ---- ------%s\n", show_all ? " ---- --------------------------------------" : "");
for (; entry != NULL; entry = entry->next) {
if (entry->dummy) { // this is an unused LET/FLET slot
if (entry->next == NULL) {
nfree = entry->dbcount; // last 1DUMY gives amount of free space
}
else {
nblocks += entry-> dbcount; // in middle, it's a bit of space lost due to sector-padding
if (show_all) { // display padding entries in show_all mode
printf("%-5s %-3s ", "(pad)", "");
commas(entry->dbcount, 8);
printf(" %04x\n", entry->dbaddr);
}
}
}
else if (entry->dbcount > 0) { // if disk block count is nonzero, it's a file
printf("%-5s %-3s", entry->name, ftname[entry->filetype]);
commas(entry->dbcount, 8);
if (show_all)
printf(" %04x %s", entry->dbaddr, file_progtype(entry));
putchar('\n');
nblocks += entry->dbcount; // add to cumulative totals
nfiles++;
}
else { // 0 blocks means it's an alternate entry for previous file
if (show_all)
printf("%-5s\n", entry->name);
nalternates++;
}
}
putchar('\n'); // double space after table
printf("\nTotal: "); // print summary
commas(nfiles, 0);
printf(" file%s", (nfiles == 1) ? "" : "s");
if (show_all) {
printf(", ");
commas(nalternates, 0);
printf(" entr%s", (nalternates == 1) ? "y" : "ies");
}
putchar('\n');
printf("Space Used: ");
commas(nblocks, 0);
printf(" block%s, ", (nblocks == 1) ? "" : "s");
commas(nblocks*BLK_WORDS, 0);
printf(" words\n");
printf("Space Free: ");
commas(nfree, 0);
printf(" block%s, ", (nfree == 1) ? "" : "s");
commas(nfree*BLK_WORDS, 0);
printf(" words\n\n");
}
// -------------------------------------------------------------------------------------------
// astring - allocate memory for an return copy of a string
// -------------------------------------------------------------------------------------------
char *astring (char *str)
{
char *cpy;
cpy = malloc(strlen(str)+1);
strcpy(cpy, str);
return cpy;
}
// -------------------------------------------------------------------------------------------
// matchname - see if filename matches (wildcard) file specification. We assume that both
// name and spec are uppercase.
// -------------------------------------------------------------------------------------------
BOOL matchname (char *name, char *spec)
{
while (*name) { // scan through the filename
if (*name == *spec || *spec == '?') { // if exact match, or single-char ? match,
name++; // so far so good; skip to next character
spec++;
}
else if (*spec == '*') { // we are at a * in the pattern. We need to try all possible
while (*spec == '*') // see if there are any more literal characters
spec++;
if (*spec == '\0') // no more literal pattern characters; this qualifies as a match
return TRUE;
// if we get here, we need to start matching the remaining part of the pattern against
// some reduction of the name; we can skip 0 or more characters looking for a hit.
// For example, if called with matchname("ABCDEF", "AB*F"), when we get here,
// name = "CDEF" and spec = "F". What we need to do is start eating away at name to see
// if it can be made to match the final F.
while (*name) {
if (matchname(name, spec)) // if the remaining part of the pattern matches the name,
return TRUE; // it's a hit
name++; // skip one character in name (the part matched by our *) and try again
}
return FALSE; // we skipped everything and still couldn't match the residual pattern
}
else
return FALSE; // this is a definite mismatch
}
// we've hit the end of the actual filename
while (*spec == '*')
spec++; // skip over any trailing * wildcards
return *spec == '\0'; // match is true if we're now at end of the pattern
}
typedef struct tag_namelist {
struct tag_namelist *next;
char name[6];
} NAMENODE, *NAMELIST;
NAMELIST free_nodes = NULL;
#define INDENT " " // indent for information lines
#define INDENT2 " " // indent for debugging lines
// -------------------------------------------------------------------------------------------
// add_list - add a name to a linked list of names, in alphabetical order
// -------------------------------------------------------------------------------------------
void add_list (char *name, NAMELIST *plisthead)
{
NAMELIST n, prev;
int cmp;
for (n = *plisthead, prev = NULL; n != NULL; prev = n, n = n->next) { // scan list. 'Prev' is trailing pointer
if ((cmp = strcmp(n->name, name)) == 0)
return; // name is already in the list
if (cmp > 0) // new entry goes before this entry
break;
}
if (free_nodes == NULL) // get a NAMELIST node from freelist or
n = ALLOCATE(NAMENODE); // by allocating more memory
else {
n = free_nodes;
free_nodes = n->next;
}
strcpy(n->name, name); // save the name
if (prev == NULL) { // add to head of list
n->next = *plisthead;
*plisthead = n;
}
else { // add to middle of list, after entry 'prev'
n->next = prev->next;
prev->next = n;
}
}
// -------------------------------------------------------------------------------------------
// print_list - print list of names
// -------------------------------------------------------------------------------------------
void print_list (NAMELIST list, char *title)
{
int i;
printf(INDENT "%-14s", title); // print title string
for (i = 0; list != NULL; list = list->next, i++) { // print up to 8 names per line
if (i == 8) {
printf("\n" INDENT "%-14s", ""); // (start a new line)
i = 0;
}
printf("%s%s", (i == 0) ? "" : ", ", list->name); // print names, separated by commas
}
putchar('\n'); // terminate last line
}
// -------------------------------------------------------------------------------------------
// free_list - put list of names into the freelist for possible reuse
// -------------------------------------------------------------------------------------------
void free_list (NAMELIST list)
{
NAMELIST n;
if (free_nodes == NULL) // freelist is empty, this list becomes the freelist
free_nodes = list;
else {
for (n = free_nodes; n->next != NULL; n = n->next) // find last node in freelist
;
n->next = list; // tack this list onto the end
}
}
// -------------------------------------------------------------------------------------------
// init_dsf_stream - like "fopen", prepares to read data out of a DSF file. A DSFSTREAM structure
// hold necessary state information like file offset, current data module and current data block
// -------------------------------------------------------------------------------------------
typedef struct {
uint16 dbaddr; // dbaddr of current file
uint16 offset; // current offset in file
uint16 nwords; // number of words left in current data module
uint16 addr; // load address of next word
uint16 nw; // number of words in current data block (2-9)
uint16 ind; // index of next word to extract from current data block (1-8)
uint16 relflag; // relocation flags; next word's flags are left adjusted
uint16 datablock[9]; // current data block; datablock[ind] is next word
} DSFSTREAM;
void init_dsf_stream (DSFSTREAM *dsf_stream, LETENTRY *entry, DSF_PROGRAM_HEADER *hdr)
{
dsf_stream->dbaddr = entry->dbaddr; // set up dbaddr and offset for data in chosen file
dsf_stream->offset = hdr->hdr_len9 + 9; // point just past the file header (hdr_len9 is the length - 9)
dsf_stream->ind = 999; // set state so we have to read the first data module
dsf_stream->nw = 0;
dsf_stream->nwords = 0;
}
// -------------------------------------------------------------------------------------------
// get_dsf_word - read next data word and associated relocation flag bits from DSF data stream
// -------------------------------------------------------------------------------------------
BOOL get_dsf_word (DSFSTREAM *dsf_stream, uint16 *word, uint16 *addr, uint16 *relflag)
{
uint16 dataheader[2];
int i;
if (dsf_stream->ind >= dsf_stream->nw) { // we've exhausted the current data block, get next one
if (dsf_stream->nwords == 0) { // we've exhausted the current module, get next one
getdata(dataheader, dsf_stream->dbaddr, dsf_stream->offset, 2); // get two-word data block header
dsf_stream->offset += 2;
dsf_stream->addr = dataheader[0]; // save address and module size
dsf_stream->nwords = dataheader[1];
if (dsf_stream->nwords == 0) // end of file
return FALSE;
if (verbose) // in verbose mode, show module header
printf(INDENT2 "%04x %04x %d\n", dsf_stream->addr, dsf_stream->nwords, dsf_stream->nwords-2);
dsf_stream->nwords -= 2; // deduct 2 dataheader words we just read
}
dsf_stream->nw = min(dsf_stream->nwords, 9); // size of next data block
getdata(dsf_stream->datablock, dsf_stream->dbaddr, dsf_stream->offset, dsf_stream->nw);
dsf_stream->offset += dsf_stream->nw; // bump file offset
dsf_stream->nwords -= dsf_stream->nw; // and number of words left in current module
dsf_stream->relflag = dsf_stream->datablock[0]; // get relocation flag word
dsf_stream->ind = 1; // initialize index
if (verbose) { // in verbose mode, show data block including relocation bits
static char flagchar[4] = {'.', 'r', 'L', 'C'}; // (show . r L or C for abs, rel, LIBF or CALL)
char flagstr[10];
for (i = 1; i < dsf_stream->nw; i++) // construct string showing meaning of relocation bits
flagstr[i-1] = flagchar[(dsf_stream->relflag >> (16-2*i)) & 3];
flagstr[i-1] = '\0';
// display address, relocation info, data words
printf(INDENT2 " %04x [%04x %-8s]", dsf_stream->addr, dsf_stream->relflag, flagstr);
for (i = 1; i < dsf_stream->nw; i++)
printf(" %04x", dsf_stream->datablock[i]);
putchar('\n');
}
}
// ready to extract the next word...
*word = dsf_stream->datablock[dsf_stream->ind++]; // give caller the word, and increment index
*relflag = (dsf_stream->relflag >> 14) & 3; // give caller the top two bits of the relocation flag word
dsf_stream->relflag <<= 2; // and slide next two bits into place
*addr = dsf_stream->addr; // give caller the word's address, and increment address
if (*relflag != 2) // unless relflag was 2 (LIBF), which occupies only 1 word
dsf_stream->addr++; // in core. We'll increment addr when we fetch the 2nd name word
}
// -------------------------------------------------------------------------------------------
// print_dsf_info - print information about a Disk System Format file
// -------------------------------------------------------------------------------------------
void print_dsf_info (LETENTRY *entry)
{
DSF_PROGRAM_HEADER hdr;
char name[6], *nm, label[4];
int i, nentries;
unsigned subtype, progtype, int_precis, real_precis, n_defined_files, fortran_indicator;
uint16 namewords[2], word, addr, relflag;
NAMELIST call_list, dsn_list;
DSFSTREAM dsf_stream;
getdata(&hdr, entry->dbaddr, 0, sizeof(DSF_PROGRAM_HEADER)/2); // read file header (assume maximum size)
subtype = (hdr.type >> 12) & 0x0F; // extract file type and subtype
progtype = (hdr.type >> 8) & 0x0F;
int_precis = (hdr.type >> 4) & 0x0F; // get precision specification
real_precis = hdr.type & 0x0F;
fortran_indicator = (hdr.fortran_info >> 8) & 0xFF; // get fortran specifications
n_defined_files = hdr.fortran_info & 0xFF;
if (hdr.zero1 != 0) // this word is supposed to be zero
printf(INDENT "CORRUPT: hdr word 1 should be 0, is %d\n", hdr.zero1);
// if (hdr.zero2 != 0) // so is this word, but it turns out not to be reliably zero
// printf(INDENT "CORRUPT: hdr word 7 should be 0, is %d\n", hdr.zero2);
printf(INDENT "Program type: %d=%s\n", progtype, progtype_nm[progtype]);
if (progtype == 3 || progtype == 4 || progtype == 5 || progtype == 7) {
nm = "Undefined"; // types 3, 4, 5 and 7 should have a subtype
for (i = 0; i < N_SUBTYPE_NMS; i++) {
if (subtype_nm[i].progtype == progtype && subtype_nm[i].subtype == subtype) {
nm = subtype_nm[i].descr;
break;
}
}
if (nm != NULL) // print subtype unless name was defined as NULL
printf(INDENT "Subtype: %d=%s\n", subtype, nm);
}
// print fortran information
printf(INDENT "Precision: Real=%s Integer=%s\n",
(real_precis == 0) ? "Unspecified" : (real_precis == 1) ? "Standard" : (real_precis == 2) ? "Extended" : "invalid",
(int_precis == 0) ? "Unspecified" : (int_precis == 8) ? "Matches Real" : (int_precis == 9) ? "One word" : "invalid");
printf(INDENT "Prog length: %d wd\n", hdr.proglen);
printf(INDENT "COMMON: %d wd\n", hdr.commonlen);
printf(INDENT "Fortran ind: 0x%02x, %d defined file%s\n",
fortran_indicator, n_defined_files, (n_defined_files == 1) ? "" : "s");
switch (progtype) { // print entry information for...
default: // ... mainline or subprogram
if ((hdr.hdr_len9 % 3) != 0) {
printf(INDENT "CORRUPT: header length-9 is %d, should be multiple of 3\n", hdr.hdr_len9);
break;
}
nentries = hdr.hdr_len9 / 3; // get number of entry points (assuming not an ILS or ISS)
if (nentries > 15)
printf(INDENT "CORRUPT: # of entries is %d, max is 15\n", nentries);
for (i = 0; i < nentries; i++) { // list entry point names and addresses
convert_namecode(hdr.x.entry[i].name, name);
sprintf(label, (i < 9) ? "%d: " : "%d:", i+1); // print, e.g. "2: " or "12:"
printf(INDENT "Entry %s %-5s addr /%04x\n", label, name, hdr.x.entry[i].addr);
}
break;
case 5: // ... ISS (device interrupt service routine)
case 6:
if (hdr.hdr_len9 != 7 && hdr.hdr_len9 != 8)
printf(INDENT "CORRUPT: header length-9 is %d, should be 7 or 8\n", hdr.hdr_len9);
convert_namecode(hdr.x.iss.name, name); // has just one entry point name
printf(INDENT "Entry: %-5s addr /%04x\n", name, hdr.x.iss.addr);
printf(INDENT "ISS number: %d\n", hdr.x.iss.issnumber);
if (hdr.x.iss.nlevels != 1 && hdr.x.iss.nlevels != 2) { // should have 1 or 2 associated interrupt levels
printf(INDENT "CORRUPT: # of levels is %d, should be 1 or 2\n", hdr.x.iss.nlevels);
hdr.x.iss.nlevels = 1;
}
for (i = 0; i < hdr.x.iss.nlevels; i++)
printf(INDENT "Int level %d: %d\n", i+1, hdr.x.iss.level[i]);
break;
case 7: // ... ILS (interrupt handler)
if (hdr.hdr_len9 != 4)
printf(INDENT "CORRUPT: header length-9 is %d, should be 4\n", hdr.hdr_len9);
convert_namecode(hdr.x.ils.name, name);
printf(INDENT "Entry: %-5s addr /%04x\n", name, hdr.x.ils.addr);
printf(INDENT "ILS level: %d\n", hdr.x.ils.level);
break;
}
call_list = dsn_list = NULL; // clear list of external references
init_dsf_stream(&dsf_stream, entry, &hdr); // prepare to read data from the file
while (get_dsf_word(&dsf_stream, &word, &addr, &relflag)) {
switch (relflag) {
case 0: // 00 - absolute data
case 1: // 01 - relocatable data
break;
case 2: // 10 - LIBF
namewords[0] = word; // save first name word and get the second
get_dsf_word(&dsf_stream, &namewords[1], &addr, &relflag);
convert_namecode(namewords, name); // convert to ASCII
add_list(name, &call_list); // add name to list of external references
break;
case 3: // 11 - CALL or DSN
namewords[0] = word; // save first name word and get the second
get_dsf_word(&dsf_stream, &namewords[1], &addr, &relflag);
convert_namecode(namewords, name); // convert to ASCII
if (relflag == 0) // 1100 - CALL
add_list(name, &call_list); // add name to list of external references
else if (relflag == 1) // 1101 - DSN
add_list(name, &dsn_list); // add name to list of data source names
else // 1110 or 1111 - invalid
printf(INDENT, "CORRUPT: object data contains invalid relocation bits 111%d\n", relflag & 1);
break;
}
}
if (call_list != NULL) { // print list(s) of external references
print_list(call_list, "Calls:");
free_list(call_list);
}
if (dsn_list != NULL) {
print_list(dsn_list, "DSN's referenced:");
free_list(dsn_list);
}
putchar('\n');
}
// -------------------------------------------------------------------------------------------
// print_dci_info - print information about a Disk Core Image file
// -------------------------------------------------------------------------------------------
void print_dci_info (LETENTRY *entry)
{
DCI_PROGRAM_HEADER hdr;
char *diskprog;
int i;
getdata(&hdr, entry->dbaddr, 0, sizeof(DCI_PROGRAM_HEADER)/2); // read file header
diskprog = (hdr.dreq == 0xFFFF) ? "DISKZ" : (hdr.dreq == 0x0000) ? "DISK1" : (hdr.dreq == 0x0001) ? "DISKN" : "Unknown";
printf(INDENT "Execute addr: /%04x\n", hdr.xeqa); // interpret and print it
printf(INDENT "COMMON: %d wd\n", hdr.cmon);
printf(INDENT "Disk IO: /%04x (%s)\n", hdr.dreq, diskprog);
printf(INDENT "# files defd: %d\n", hdr.file);
printf(INDENT "Hdr length: %d wd\n", hdr.hwct);
printf(INDENT "Sector cnt: %d files in WS\n", hdr.lsct);
printf(INDENT "Load address: /%04x\n", hdr.ldad);
printf(INDENT "Exit addr: /%04x\n", hdr.xctl);
printf(INDENT "TV length: %d wd\n", hdr.tvwc);
printf(INDENT "Load size: %d wd including TV\n", hdr.wcnt-hdr.hwct);
printf(INDENT "XR3: /%04x\n", hdr.xr3x);
#define NO_VECTOR 0x0091 // appears to be DMS default handler for unrecognized interrupt
for (i = 0; i < 6; i++) {
if (hdr.itv[i] != NO_VECTOR)
printf(INDENT "Lvl %d vector: /%04x\n", i, hdr.itv[i]);
}
if (hdr.ibt[0] != NO_VECTOR || hdr.ibt[1] != NO_VECTOR || hdr.ibt[2] != NO_VECTOR)
printf(INDENT "ISS of 1231: /%04x /%04x /%04x\n", hdr.ibt[0], hdr.ibt[1], hdr.ibt[2]);
if (hdr.ibt[3] != NO_VECTOR)
printf(INDENT "ISS of 1403: /%04x\n", hdr.ibt[3]);
if (hdr.ibt[4] != NO_VECTOR)
printf(INDENT "ISS of 2501: /%04x\n", hdr.ibt[4]);
if (hdr.ibt[5] != NO_VECTOR)
printf(INDENT "ISS of 1442: /%04x\n", hdr.ibt[5]);
if (hdr.ibt[6] != NO_VECTOR)
printf(INDENT "ISS of kb/pr: /%04x\n", hdr.ibt[6]);
if (hdr.ibt[7] != NO_VECTOR)
printf(INDENT "ISS of ptr/p: /%04x\n", hdr.ibt[7]);
printf(INDENT "LOCAL/SOCALs: %d sectors\n", hdr.ovsw);
printf(INDENT "Built for: %d wds core\n", hdr.core);
putchar('\n');
}
// -------------------------------------------------------------------------------------------
// print_ddf_info - print information about a Disk Data Format file
// -------------------------------------------------------------------------------------------
void print_ddf_info (LETENTRY *entry)
{
// there's nothing to say, really -- these are user-defined files
}
// -------------------------------------------------------------------------------------------
// dumpfile - print file contents in hex
// -------------------------------------------------------------------------------------------
void dumpfile (LETENTRY *entry)
{
uint16 offset = 0, nw, nwords, buf[8], i;
nwords = entry->dbcount*BLK_WORDS; // number of words to dump
while (nwords > 0) {
printf(" %04x |", offset); // print current offset
nw = min(nwords, 8); // fetch (up to) 8 words of data
getdata(buf, entry->dbaddr, offset, nw);
offset += nw; // bump offset and count
nwords -= nw;
for (i = 0; i < nw; i++) // print values in hex
printf(" %04x", buf[i]);
putchar('\n');
}
putchar('\n');
}
// -------------------------------------------------------------------------------------------
// print_onefile - print detailed info about one particular file
// -------------------------------------------------------------------------------------------
void print_onefile (LETENTRY *entry, BOOL in_flet)
{
static BOOL first = TRUE;
LETENTRY *mst;
if (first) { // print column headings
first = FALSE;
printf("Name Type Blocks Addr Remarks\n");
printf("----- ---- ------ ---- ---------------------------------------------------\n");
}
mst = (entry->master != NULL) ? entry->master : entry; // pointer to main (primary) entry
printf("%-5s %-3s ", entry->name, ftname[mst->filetype]); // print file name and size info
commas(mst->dbcount, 8);
printf(" %04x", mst->dbaddr);
if (entry->master != NULL) // this is an alternate entry since it points to a master
printf(" (alternate entry point in %s)", mst->name);
printf("%s\n", in_flet ? " (in FLET)" : "");
if (do_dump) // if -d specified, dump contents
dumpfile(mst);
if (show_all) { // if -a specified, print detailed info for particular file type
switch (mst->filetype) {
case FILETYPE_DSF: print_dsf_info(mst); break;
case FILETYPE_1: /* unknown format */ break;
case FILETYPE_DCI: print_dci_info(mst); break;
case FILETYPE_DDF: print_ddf_info(mst); break;
default: bail("in print_onefile, can't happen");
}
}
}
// -------------------------------------------------------------------------------------------
// list_named_files - print info for file(s) matching a filename specified on the command line
// -------------------------------------------------------------------------------------------
void list_named_files (char *name, char *image)
{
BOOL has_wild = strchr(name, '?') != NULL || strchr(name, '*') != NULL;
BOOL in_flet, matched;
LETENTRY *entry;
if (flet != NULL) { // start at head of FLET if we have one, otherwise LET
in_flet = TRUE;
entry = flet;
}
else {
in_flet = FALSE;
entry = let;
}
matched = FALSE;
while (entry != NULL) { // scan through flet/let lists
if (! entry->dummy) {
if (matchname(entry->name, name)) { // does this file match the specified name?
print_onefile(entry, in_flet); // print it
matched = TRUE;
if (! has_wild) // if there were no wildcard characters in the name, stop scanning
break;
}
}
// try next file...
if (entry->next == NULL) { // if at end of current list
if (in_flet) {
entry = let; // move from flet to let
in_flet = FALSE;
}
else
break; // done with both lists
}
else
entry = entry->next; // go to next entry in list
}
if (! matched)
printf("%s: no such file in %s", name, image);
}
char * file_progtype (LETENTRY *entry) // description of module type
{
static char buf[100];
DSF_PROGRAM_HEADER hdr;
char *nm;
unsigned subtype, progtype;
int i;
*buf = '\0';
switch (entry->filetype) {
case FILETYPE_DSF:
getdata(&hdr, entry->dbaddr, 0, sizeof(DSF_PROGRAM_HEADER)/2); // read file header (assume maximum size)
subtype = (hdr.type >> 12) & 0x0F; // extract file type and subtype
progtype = (hdr.type >> 8) & 0x0F;
strcpy(buf, progtype_nm[progtype]);
if (progtype == 3 || progtype == 4 || progtype == 5 || progtype == 7) {
nm = NULL;
for (i = 0; i < N_SUBTYPE_NMS; i++) {
if (subtype_nm[i].progtype == progtype && subtype_nm[i].subtype == subtype) {
nm = subtype_nm[i].descr;
break;
}
}
if (nm != NULL) {
strcat(buf, "; ");
strcat(buf, nm);
}
}
break;
case FILETYPE_DCI:
return "Mainline, core image";
case FILETYPE_DDF:
return "Data";
default:
return "unknown";
}
return buf;
}