| // ------------------------------------------------------------------------------------------- | |
| // 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(§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; | |
| } | |