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