/* Simple program to display a binary card-image file in ASCII.
 * We assume the deck was written with one card per 16-bit word, left-justified,
 * and written in PC little-endian order
 *
 * (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
 */

#include <stdio.h>
#include <stdlib.h>
#include "util_io.h"

#define TRUE  1
#define FALSE 0
typedef int BOOL;

int  hollerith_to_ascii (unsigned short h);
void bail (char *msg);
void format_coldstart (unsigned short *buf);

int main (int argc, char **argv)
{
	FILE *fd;
	char *fname = NULL, line[82], *arg;
	BOOL coldstart = FALSE;
	unsigned short buf[80];
	int i, lastnb;
	static char usestr[] =
		"Usage: viewdeck [-c] deckfile\n"
		"\n"
		"-c: convert cold start card to 16-bit format as a C array initializer\n";

	for (i = 1; i < argc; i++) {				// process command line arguments
		arg = argv[i];

		if (*arg == '-') {
			arg++;
			while (*arg) {
				switch (*arg++) {
					case 'c':
						coldstart = TRUE;
						break;
					default:
						bail(usestr);
				}
			}
		}
		else if (fname == NULL)					// first non-switch arg is file name
			fname = arg;
		else
			bail(usestr);						// there can be only one name
	}

	if (fname == NULL)							// there must be a name
		bail(usestr);

	if ((fd = fopen(fname, "rb")) == NULL) {
		perror(fname);
		return 1;
	}

	while (fxread(buf, sizeof(short), 80, fd) == 80) {
		if (coldstart) {
			format_coldstart(buf);
			break;
		}

		lastnb = -1;
		for (i = 0; i < 80; i++) {
			line[i] = hollerith_to_ascii(buf[i]);
			if (line[i] > ' ')
				lastnb = i;
		}
		line[++lastnb] = '\n';
		line[++lastnb] = '\0';
		fputs(line, stdout);
	}

	if (coldstart) {
		if (fxread(buf, sizeof(short), 1, fd) == 1)
			bail("Coldstart deck has more than one card");
	}

	fclose(fd);

	return 0;
}

void format_coldstart (unsigned short *buf)
{
	int i, nout = 0;
	unsigned short word;

	for (i = 0; i < 80; i++) {
		word = buf[i];							// expand 12-bit card data to 16-bit instruction
		word = (word & 0xF800) | ((word & 0x0400) ? 0x00C0 : 0x0000) | ((word & 0x03F0) >> 4);

		if (nout >= 8) {
			fputs(",\n", stdout);
			nout = 0;
		}
		else if (i > 0)
			fputs(", ", stdout);

		printf("0x%04x", word);
		nout++;
	}

	putchar('\n');
}

typedef struct {
	unsigned short hollerith;
	char	ascii;
} CPCODE;

static CPCODE cardcode_029[] =
{
	0x0000,		' ',
	0x8000, 	'&',	 		// + in 026 Fortran
	0x4000,		'-',
	0x2000,		'0',
	0x1000,		'1',
	0x0800,		'2',
	0x0400,		'3',
	0x0200,		'4',
	0x0100,		'5',
	0x0080,		'6',
	0x0040,		'7',
	0x0020,		'8',
	0x0010,		'9',
	0x9000, 	'A',
	0x8800,		'B',
	0x8400,		'C',
	0x8200,		'D',
	0x8100,		'E',
	0x8080,		'F',
	0x8040,		'G',
	0x8020,		'H',
	0x8010,		'I',
	0x5000, 	'J',
	0x4800,		'K',
	0x4400,		'L',
	0x4200,		'M',
	0x4100,		'N',
	0x4080,		'O',
	0x4040,		'P',
	0x4020,		'Q',
	0x4010,		'R',
	0x3000, 	'/',
	0x2800,		'S',
	0x2400,		'T',
	0x2200,		'U',
	0x2100,		'V',
	0x2080,		'W',
	0x2040,		'X',
	0x2020,		'Y',
	0x2010,		'Z',
	0x0820,		':',
	0x0420,		'#',		// = in 026 Fortran
	0x0220,		'@',		// ' in 026 Fortran
	0x0120,		'\'',
	0x00A0,		'=',
	0x0060,		'"',
	0x8820,		'\xA2',		// cent, in MS-DOS encoding
	0x8420,		'.',
	0x8220,		'<',		// ) in 026 Fortran
	0x8120,		'(',
	0x80A0,		'+',
	0x8060,		'|',
	0x4820,		'!',
	0x4420,		'$',
	0x4220,		'*',
	0x4120,		')',
	0x40A0,		';',
	0x4060,		'\xAC',		// not, in MS-DOS encoding
	0x2420,		',',
	0x2220,		'%',		// ( in 026 Fortran
	0x2120,		'_',
	0x20A0,		'>',
	0xB000,		'a',
	0xA800,		'b',
	0xA400,		'c',
	0xA200,		'd',
	0xA100,		'e',
	0xA080,		'f',
	0xA040,		'g',
	0xA020,		'h',
	0xA010,		'i',
	0xD000,		'j',
	0xC800,		'k',
	0xC400,		'l',
	0xC200,		'm',
	0xC100,		'n',
	0xC080,		'o',
	0xC040,		'p',
	0xC020,		'q',
	0xC010,		'r',
	0x6800,		's',
	0x6400,		't',
	0x6200,		'u',
	0x6100,		'v',
	0x6080,		'w',
	0x6040,		'x',
	0x6020,		'y',
	0x6010,		'z',				// these odd punch codes are used by APL:
	0x1010,		'\001',				// no corresponding ASCII	using ^A
	0x0810,		'\002',				// SYN						using ^B
	0x0410,		'\003',				// no corresponding ASCII	using ^C
	0x0210,		'\004',				// PUNCH ON					using ^D
	0x0110,		'\005',				// READER STOP				using ^E
	0x0090,		'\006',				// UPPER CASE				using ^F
	0x0050,		'\013',				// EOT						using ^K
	0x0030,		'\016',				// no corresponding ASCII	using ^N
	0x1030,		'\017',				// no corresponding ASCII	using ^O
	0x0830,		'\020',				// no corresponding ASCII	using ^P
};

int hollerith_to_ascii (unsigned short h)
{
	int i;

 	h &= 0xFFF0;

	for (i = 0; i < sizeof(cardcode_029) / sizeof(CPCODE); i++)
		if (cardcode_029[i].hollerith == h)
			return cardcode_029[i].ascii;

	return '?';
}

void bail (char *msg)
{
	fprintf(stderr, "%s\n", msg);
	exit(1);
}