/* | |
* (C) Copyright 2002, 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. | |
* | |
* This is not a supported product, but I welcome bug reports and fixes. | |
* Mail to sim@ibm1130.org | |
*/ | |
// DISKVIEW - lists contents of an 1130 system disk image file. Not finished yet. | |
// needs LET/SLET listing routine. | |
// | |
// usage: | |
// diskview -v diskfile | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdarg.h> | |
#include "util_io.h" | |
#define BETWEEN(v,a,b) (((v) >= (a)) && ((v) <= (b))) | |
#define MIN(a,b) (((a) <= (b)) ? (a) : (b)) | |
#define MAX(a,b) (((a) >= (b)) ? (a) : (b)) | |
#ifndef TRUE | |
# define TRUE 1 | |
# define FALSE 0 | |
# define BOOL int | |
#endif | |
#define NOT_DEF 0x0658 // defective cylinder table entry means no defect | |
#define DSK_NUMWD 321 /* words/sector */ | |
#define DSK_NUMCY 203 /* cylinders/drive */ | |
#define DSK_SECCYL 8 /* sectors per cylinder */ | |
#define SECLEN 320 /* data words per sector */ | |
#define SLETLEN ((3*SECLEN)/4) /* length of slet in records */ | |
typedef unsigned short WORD; | |
FILE *fp; | |
WORD buf[DSK_NUMWD]; | |
WORD dcom[DSK_NUMWD]; | |
#pragma pack(2) | |
struct tag_slet { | |
WORD phid; | |
WORD addr; | |
WORD nwords; | |
WORD sector; | |
} slet[SLETLEN]; | |
#pragma pack() | |
WORD dcyl[3]; | |
BOOL verbose = FALSE; | |
void checksectors (void); | |
void dump_id (void); | |
void dump_dcom (void); | |
void dump_resmon (void); | |
void dump_slet (void); | |
void dump_hdng (void); | |
void dump_scra (void); | |
void dump_let (void); | |
void dump_flet (void); | |
void dump_cib (void); | |
void getsector (int sec, WORD *sbuf); | |
void getdcyl (void); | |
char *lowcase (char *str); | |
void bail(char *fmt, ...); | |
char *trim (char *s); | |
int main (int argc, char **argv) | |
{ | |
char *fname = NULL, *arg; | |
static char usestr[] = "Usage: diskview [-v] filename"; | |
int i; | |
util_io_init(); | |
for (i = 1; i < argc;) { | |
arg = argv[i++]; | |
if (*arg == '-') { | |
arg++; | |
lowcase(arg); | |
while (*arg) { | |
switch (*arg++) { | |
case 'v': | |
verbose = TRUE; | |
break; | |
default: | |
bail(usestr); | |
} | |
} | |
} | |
else if (fname == NULL) | |
fname = arg; | |
else | |
bail(usestr); | |
} | |
if (fname == NULL) | |
bail(usestr); | |
if ((fp = fopen(fname, "rb")) == NULL) { | |
perror(fname); | |
return 2; | |
} | |
printf("%s:\n", fname); | |
checksectors(); | |
getdcyl(); | |
dump_id(); // ID & coldstart | |
dump_dcom(); // DCOM | |
dump_resmon(); // resident image | |
dump_slet(); // SLET | |
dump_hdng(); // heading sector | |
dump_scra(); | |
dump_flet(); | |
dump_cib(); | |
dump_let(); | |
fclose(fp); | |
return 0; | |
} | |
// checksectors - verify that all sectors are properly numbered | |
void checksectors () | |
{ | |
WORD sec = 0; | |
fseek(fp, 0, SEEK_SET); | |
for (sec = 0; sec < DSK_NUMCY*DSK_SECCYL; sec++) { | |
if (fxread(buf, sizeof(WORD), DSK_NUMWD, fp) != DSK_NUMWD) | |
bail("File read error or not a disk image file"); | |
if (buf[0] != sec) | |
bail("Sector /%x is misnumbered, run checkdisk [-f]", sec); | |
} | |
} | |
// get defective cylinder list | |
void getdcyl (void) | |
{ | |
fseek(fp, sizeof(WORD), SEEK_SET); // skip sector count | |
if (fxread(dcyl, sizeof(WORD), 3, fp) != 3) | |
bail("Unable to read defective cylinder table"); | |
} | |
// getsector - read specified absolute sector | |
void getsector (int sec, WORD *sbuf) | |
{ | |
int i, cyl, ssec; | |
sec &= 0x7FF; // mask of drive bits, if any | |
cyl = sec / DSK_SECCYL; // get cylinder | |
ssec = sec & ~(DSK_SECCYL-1); // mask to get starting sector of cylinder | |
for (i = 0; i < 3; i++) { // map through defective cylinder table | |
if (dcyl[i] == ssec) { | |
sec &= (DSK_SECCYL-1); // mask to get base sector | |
cyl = DSK_NUMCY-3+i; // replacements are last three on disk | |
sec += cyl*DSK_SECCYL; // add new cylinder offset | |
break; | |
} | |
} | |
// read the sector | |
if (fseek(fp, (sec*DSK_NUMWD+1)*sizeof(WORD), SEEK_SET) != 0) | |
bail("File seek failed"); | |
if (fxread(sbuf, sizeof(WORD), DSK_NUMWD, fp) != DSK_NUMWD) | |
bail("File read error or not a disk image file"); | |
} | |
void dump (int nwords) | |
{ | |
int i, nline = 0; | |
for (i = 0; i < nwords; i++) { | |
if (nline == 16) { | |
putchar('\n'); | |
nline = 0; | |
} | |
printf("%04x", buf[i]); | |
nline++; | |
} | |
putchar('\n'); | |
} | |
void showmajor (char *label) | |
{ | |
int i; | |
printf("\n--- %s ", label); | |
for (i = strlen(label); i < 40; i++) | |
putchar('-'); | |
putchar('\n'); | |
putchar('\n'); | |
} | |
void name (char *label) | |
{ | |
printf("%-32.32s ", label); | |
} | |
void pbf (char *label, WORD *buf, int nwords) | |
{ | |
int i, nout; | |
name(label); | |
for (i = nout = 0; i < nwords; i++, nout++) { | |
if (nout == 8) { | |
putchar('\n'); | |
name(""); | |
nout = 0; | |
} | |
printf(" %04x", buf[i]); | |
} | |
putchar('\n'); | |
} | |
void prt (char *label, char *fmt, ...) | |
{ | |
va_list args; | |
name(label); | |
putchar(' '); | |
va_start(args, fmt); | |
vprintf(fmt, args); | |
va_end(args); | |
putchar('\n'); | |
} | |
void dump_id (void) | |
{ | |
showmajor("Sector 0 - ID & coldstart"); | |
getsector(0, buf); | |
pbf("DCYL def cyl table", buf+ 0, 3); | |
pbf("CIDN cart id", buf+ 3, 1); | |
pbf(" copy code", buf+ 4, 1); | |
pbf("DTYP disk type", buf+ 7, 1); | |
pbf(" diskz copy", buf+ 30, 8); | |
pbf(" cold start pgm",buf+270, 8); | |
} | |
// EQUIVALENCES FOR DCOM PARAMETERS | |
#define NAME 4 // NAME OF PROGRAM/CORE LOAD | |
#define DBCT 6 // BLOCK CT OF PROGRAM/CORE LOAD | |
#define FCNT 7 // FILES SWITCH | |
#define SYSC 8 // SYSTEM/NON-SYSTEM CARTRIDGE INDR | |
#define JBSW 9 // JOBT SWITCH | |
#define CBSW 10 // CLB-RETURN SWITCH | |
#define LCNT 11 // NO. OF LOCALS | |
#define MPSW 12 // CORE MAP SWITCH | |
#define MDF1 13 // NO. DUP CTRL RECORDS (MODIF) | |
#define MDF2 14 // ADDR OF MODIF BUFFER | |
#define NCNT 15 // NO. OF NOCALS | |
#define ENTY 16 // RLTV ENTRY ADDR OF PROGRAM | |
#define RP67 17 // 1442-5 SWITCH | |
#define TODR 18 // OBJECT WORK STORAGE DRIVE CODE | |
#define FHOL 20 // ADDR LARGEST HOLE IN FIXED AREA | |
#define FSZE 21 // BLK CNT LARGEST HOLE IN FXA | |
#define UHOL 22 // ADDR LAST HOLE IN USER AREA 2-10 | |
#define USZE 23 // BLK CNT LAST HOLE IN UA 2-10 | |
#define DCSW 24 // DUP CALL SWITCH | |
#define PIOD 25 // PRINCIPAL I/O DEVICE INDICATOR | |
#define PPTR 26 // PRINCIPAL PRINT DEVICE INDICATOR | |
#define CIAD 27 // RLTV ADDR IN @STRT OF CIL ADDR | |
#define ACIN 28 // AVAILABLE CARTRIDGE INDICATOR | |
#define GRPH 29 // 2250 INDICATOR 2G2 | |
#define GCNT 30 // NO. G2250 RECORDS 2G2 | |
#define LOSW 31 // LOCAL-CALLS-LOCAL SWITCH 2-2 | |
#define X3SW 32 // SPECIAL ILS SWITCH 2-2 | |
#define ECNT 33 // NO. OF *EQUAT RCDS 2-4 | |
#define ANDU 35 // 1+BLK ADDR END OF UA (ADJUSTED) | |
#define BNDU 40 // 1+BLK ADDR END OF UA (BASE) | |
#define FPAD 45 // FILE PROTECT ADDR | |
#define PCID 50 // CARTRIDGE ID, PHYSICAL DRIVE | |
#define CIDN 55 // CARTRIDGE ID, LOGICAL DRIVE | |
#define CIBA 60 // SCTR ADDR OF CIB | |
#define SCRA 65 // SCTR ADDR OF SCRA | |
#define FMAT 70 // FORMAT OF PROG IN WORKING STG | |
#define FLET 75 // SCTR ADDR 1ST SCTR OF FLET | |
#define ULET 80 // SCTR ADDR 1ST SCTR OF LET | |
#define WSCT 85 // BLK CNT OF PROG IN WORKING STG | |
#define CSHN 90 // NO. SCTRS IN CUSHION AREA | |
struct tag_dcominfo { | |
char *nm; | |
int offset; | |
char *descr; | |
} dcominfo[] = { | |
"NAME", 4, "NAME OF PROGRAM/CORE LOAD", | |
"DBCT", 6, "BLOCK CT OF PROGRAM/CORE LOAD", | |
"FCNT", 7, "FILES SWITCH", | |
"SYSC", 8, "SYSTEM/NON-SYSTEM CARTRIDGE INDR", | |
"JBSW", 9, "JOBT SWITCH", | |
"CBSW", 10, "CLB-RETURN SWITCH", | |
"LCNT", 11, "NO. OF LOCALS", | |
"MPSW", 12, "CORE MAP SWITCH", | |
"MDF1", 13, "NO. DUP CTRL RECORDS (MODIF)", | |
"MDF2", 14, "ADDR OF MODIF BUFFER", | |
"NCNT", 15, "NO. OF NOCALS", | |
"ENTY", 16, "RLTV ENTRY ADDR OF PROGRAM", | |
"RP67", 17, "1442-5 SWITCH", | |
"TODR", 18, "OBJECT WORK STORAGE DRIVE CODE", | |
"FHOL", 20, "ADDR LARGEST HOLE IN FIXED AREA", | |
"FSZE", 21, "BLK CNT LARGEST HOLE IN FXA", | |
"UHOL", 22, "ADDR LAST HOLE IN USER AREA", | |
"USZE", 23, "BLK CNT LAST HOLE IN UA", | |
"DCSW", 24, "DUP CALL SWITCH", | |
"PIOD", 25, "PRINCIPAL I/O DEVICE INDICATOR", | |
"PPTR", 26, "PRINCIPAL PRINT DEVICE INDICATOR", | |
"CIAD", 27, "RLTV ADDR IN @STRT OF CIL ADDR", | |
"ACIN", 28, "AVAILABLE CARTRIDGE INDICATOR", | |
"GRPH", 29, "2250 INDICATOR", | |
"GCNT", 30, "NO. G2250 RECORDS", | |
"LOSW", 31, "LOCAL-CALLS-LOCAL SWITCH", | |
"X3SW", 32, "SPECIAL ILS SWITCH", | |
"ECNT", 33, "NO. OF *EQUAT RCDS", | |
"ANDU", 35, "1+BLK ADDR END OF UA (ADJUSTED)", | |
"BNDU", 40, "1+BLK ADDR END OF UA (BASE)", | |
"FPAD", 45, "FILE PROTECT ADDR", | |
"PCID", 50, "CARTRIDGE ID, PHYSICAL DRIVE", | |
"CIDN", 55, "CARTRIDGE ID, LOGICAL DRIVE", | |
"CIBA", 60, "SCTR ADDR OF CIB", | |
"SCRA", 65, "SCTR ADDR OF SCRA", | |
"FMAT", 70, "FORMAT OF PROG IN WORKING STG", | |
"FLET", 75, "SCTR ADDR 1ST SCTR OF FLET", | |
"ULET", 80, "SCTR ADDR 1ST SCTR OF LET", | |
"WSCT", 85, "BLK CNT OF PROG IN WORKING STG", | |
"CSHN", 90, "NO. SCTRS IN CUSHION AREA", | |
NULL | |
}; | |
void dump_dcom (void) | |
{ | |
struct tag_dcominfo *d; | |
char txt[50]; | |
showmajor("Sector 1 - DCOM"); | |
getsector(1, dcom); | |
for (d = dcominfo; d->nm != NULL; d++) { | |
sprintf(txt, "%-4.4s %s", d->nm, d->descr); | |
pbf(txt, dcom+d->offset, 1); | |
} | |
} | |
void dump_resmon (void) | |
{ | |
showmajor("Sector 2 - Resident Image"); | |
getsector(2, buf); | |
dump(verbose ? SECLEN : 32); | |
} | |
struct { | |
int pfrom, pto; | |
int printed; | |
char *name; | |
} sletinfo[] = { | |
0x01, 0x12, FALSE, "DUP", | |
0x1F, 0x39, FALSE, "Fortran", | |
0x51, 0x5C, FALSE, "Cobol", | |
0x6E, 0x74, FALSE, "Supervisor", | |
0x78, 0x84, FALSE, "Core Load Builder", | |
0x8C, 0x8C, FALSE, "Sys 1403 prt", | |
0x8D, 0x8D, FALSE, "Sys 1132 prt", | |
0x8E, 0x8E, FALSE, "Sys console prt", | |
0x8F, 0x8F, FALSE, "Sys 2501 rdr", | |
0x90, 0x90, FALSE, "Sys 1442 rdr/pun", | |
0x91, 0x91, FALSE, "Sys 1134 paper tape", | |
0x92, 0x92, FALSE, "Sys kbd", | |
0x93, 0x93, FALSE, "Sys 2501/1442 conv", | |
0x94, 0x94, FALSE, "Sys 1134 conv", | |
0x95, 0x95, FALSE, "Sys kbd conv", | |
0x96, 0x96, FALSE, "Sys diskz", | |
0x97, 0x97, FALSE, "Sys disk1", | |
0x98, 0x98, FALSE, "Sys diskn", | |
0x99, 0x99, FALSE, "(primary print)", | |
0x9A, 0x9A, FALSE, "(primary input)", | |
0x9B, 0x9B, FALSE, "(primary input excl kbd)", | |
0x9C, 0x9C, FALSE, "(primary sys conv)", | |
0x9D, 0x9D, FALSE, "(primary conv excl kbd)", | |
0xA0, 0xA1, FALSE, "Core Image Loader", | |
0xB0, 0xCC, FALSE, "RPG", | |
0xCD, 0xCE, FALSE, "Dup Part 2", | |
0xCF, 0xF6, FALSE, "Macro Assembler", | |
0 | |
}; | |
void dump_slet (void) | |
{ | |
int i, j, iphase, nsecs, sec, max_sec = 0; | |
char sstr[16], *smark; | |
showmajor("Sectors 3-5 - SLET"); | |
for (i = 0; i < 3; i++) { | |
getsector(3+i, buf); | |
memmove(((WORD *) slet)+SECLEN*i, buf, SECLEN*sizeof(WORD)); | |
} | |
printf("# PHID Addr Len Sector Secs\n"); | |
printf("------------------------------------------\n"); | |
for (i = 0; i < SLETLEN; i++) { | |
if (slet[i].phid == 0) | |
break; | |
sec = slet[i].sector; | |
iphase = (int) (signed short) slet[i].phid; | |
nsecs = (slet[i].nwords + SECLEN-1)/SECLEN; | |
if (sec & 0xF800) { | |
smark = "*"; | |
sec &= 0x7FF; | |
} | |
else | |
smark = " "; | |
for (j = 0; sletinfo[j].pfrom != 0; j++) | |
if (sletinfo[j].pfrom <= iphase && sletinfo[j].pto >= iphase) | |
break; | |
sprintf(sstr, "(%d.%d)", sec / DSK_SECCYL, slet[i].sector % DSK_SECCYL); | |
printf("%3d %04x %4d %04x %04x %04x %s %-7s %3x", | |
i, slet[i].phid, iphase, slet[i].addr, slet[i].nwords, slet[i].sector, smark, sstr, nsecs); | |
if (iphase < 0) | |
iphase = -iphase; | |
if (sletinfo[j].pfrom == 0) | |
printf(" ???"); | |
else if (! sletinfo[j].printed) { | |
printf(" %s", sletinfo[j].name); | |
sletinfo[j].printed = TRUE; | |
} | |
for (j = 0; j < i; j++) { | |
if (sec == (slet[j].sector & 0x7FF)) { | |
printf(" (same as %04x)", slet[j].phid); | |
break; | |
} | |
} | |
max_sec = MAX(max_sec, sec+nsecs-1); // find last sector used | |
putchar('\n'); | |
if (i >= 15 && ! verbose) { | |
printf("...\n"); | |
break; | |
} | |
} | |
} | |
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, | |
0x40,0x5a,0x7f,0x7b,0x5b,0x6c,0x50,0x7d, 0x4d,0x5d,0x5c,0x4e,0x6b,0x60,0x4b,0x61, | |
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7, 0xf8,0xf9,0x7a,0x5e,0x4c,0x7e,0x6e,0x6f, | |
0x7c,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, 0xc8,0xc9,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6, | |
0xd7,0xd8,0xd9,0xe2,0xe3,0xe4,0xe5,0xe6, 0xe7,0xe8,0xe9,0xba,0xe0,0xbb,0xb0,0x6d, | |
0x79,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x91,0x92,0x93,0x94,0x95,0x96, | |
0x97,0x98,0x99,0xa2,0xa3,0xa4,0xa5,0xa6, 0xa7,0xa8,0xa9,0xc0,0x4f,0xd0,0xa1,0x07, | |
}; | |
int ebcdic_to_ascii (int ch) | |
{ | |
int j; | |
for (j = 32; j < 128; j++) | |
if (ascii_to_ebcdic_table[j] == ch) | |
return j; | |
return '?'; | |
} | |
#define HDR_LEN 120 | |
void dump_hdng(void) | |
{ | |
int i; | |
char str[HDR_LEN+1], *p = str; | |
showmajor("Sector 7 - Heading"); | |
getsector(7, buf); | |
for (i = 0; i < (HDR_LEN/2); i++) { | |
*p++ = ebcdic_to_ascii((buf[i] >> 8) & 0xFF); | |
*p++ = ebcdic_to_ascii( buf[i] & 0xFF); | |
} | |
*p = '\0'; | |
trim(str); | |
printf("%s\n", str); | |
} | |
BOOL mget (int offset, char *name) | |
{ | |
char title[80]; | |
if (dcom[offset] == 0) | |
return FALSE; | |
getsector(dcom[offset], buf); | |
sprintf(title, "Sector %x - %s", dcom[offset], name); | |
showmajor(title); | |
return TRUE; | |
} | |
void dump_scra (void) | |
{ | |
if (! mget(SCRA, "SCRA")) | |
return; | |
dump(verbose ? SECLEN : 32); | |
} | |
void dump_let (void) | |
{ | |
if (! mget(ULET, "LET")) | |
return; | |
} | |
void dump_flet (void) | |
{ | |
if (! mget(FLET, "FLET")) | |
return; | |
} | |
void dump_cib (void) | |
{ | |
if (! mget(CIBA, "CIB")) | |
return; | |
dump(verbose ? SECLEN : 32); | |
} | |
#define LFHD 5 // WORD COUNT OF LET/FLET HEADER PMN09970 | |
#define LFEN 3 // NO OF WDS PER LET/FLET ENTRY PMN09980 | |
#define SCTN 0 // RLTY ADDR OF LET/FLET SCTR NO. PMN09990 | |
#define UAFX 1 // RLTV ADDR OF SCTR ADDR OF UA/FXA PMN10000 | |
#define WDSA 3 // RLTV ADDR OF WDS AVAIL IN SCTR PMN10010 | |
#define NEXT 4 // RLTV ADDR OF ADDR NEXT SCTR PMN10020 | |
#define LFNM 0 // RLTV ADDR OF LET/FLET ENTRY NAME PMN10030 | |
#define BLCT 2 // RLTV ADDR OF LET/FLET ENTRY DBCT PMN10040 | |
void bail (char *fmt, ...) | |
{ | |
va_list args; | |
va_start(args, fmt); | |
fprintf(stderr, fmt, args); | |
va_end(args); | |
putchar('\n'); | |
exit(1); | |
} | |
// --------------------------------------------------------------------------------- | |
// trim - remove trailing whitespace from string s | |
// --------------------------------------------------------------------------------- | |
char *trim (char *s) | |
{ | |
char *os = s, *nb; | |
for (nb = s-1; *s; s++) | |
if (*s > ' ') | |
nb = s; | |
nb[1] = '\0'; | |
return os; | |
} | |
/* ------------------------------------------------------------------------ | |
* lowcase - force a string to lowercase (ASCII) | |
* ------------------------------------------------------------------------ */ | |
char *lowcase (char *str) | |
{ | |
char *s; | |
for (s = str; *s; s++) { | |
if (*s >= 'A' && *s <= 'Z') | |
*s += 32; | |
} | |
return str; | |
} | |