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