// ------------------------------------------------------------------------------------------- // 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 #include #include #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(§or, 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, §or.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; }