/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:afpacode.c 12.0$ */
/* $ACIS:afpacode.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ibm/RCS/afpacode.c,v $ */

#ifndef lint
static char *rcsid = "$Header:afpacode.c 12.0$";
#endif

/*
 * ucode - we take a representation (in ascii) of the afpa microcode,
 * and turn it into binary.
 *
 * Lines in the input file look like:
'39800240010000A0'XC,
 *
 * There are (nominally) 4096 such lines on the new cards (Pass 'A' cards
 * had only 2048).
 */

#include <stdio.h>
#include <ctype.h>

#include <machine/float.h>
#include <machine/fpa.h>

	/* To take a ucode location number, and turn it into its byte offset */
#define	ucodeTObyte(x)	((x)*(2*sizeof (long)))
#define	byteTOucode(x)	((x)/ucodeTObyte(1))
#define	ucodeTOword(x)	((x)*2)
#define	wordTOucode(x)	((x)/ucodeTOword(1))

#define BZERO(x)	bzero((char *)&x, sizeof x);
#define	UCODE_SIZE_NOMINAL	4096

    /* Variables having to do with the actual microcode and control store */

long
    intarray[ucodeTOword(UCODE_SIZE_NOMINAL)],	/* Properly aligned array */
    ucode_array[ucodeTOword(UCODE_SIZE_NOMINAL)],	/* microcode */
    chkarray[ucodeTOword(UCODE_SIZE_NOMINAL)];	/* For checking */

int
			/* Size of control store (in 64-bit words) */
	control_store_size = UCODE_SIZE_NOMINAL,
	ucode_size = 0;			/* Number of words of ucode read */

long
	*registersets[NUM_FPA_SETS] = {0};

int
				/*
				 * Number of register sets reserved
				 * by the current level of microcode
				 */
	reserved = 0;

/*
 * Variables having to do with line numbering, column numbering.
 *
 * Plus, some misc.
 */

int
	line = 1,
	column = 1,
	debug = 0;
static char *afpa_state[] = {
    "No AFPA on system.\n",
    "AFPA online, but no hardware present!",
    "AFPA waiting for register sets to be loaded (and then brought online).",
    "AFPA online.",
    "AFPA control store enabled for loading, but no hardware present!",
    "AFPA control store enabled for loading, but AFPA is already online to system\n\t(AND, the AFPA hardware is not present)!!",
    "AFPA awaiting microcode load.",
    "AFPA control store enabled for loading, but AFPA is already online to system!",
    "System indicates E_AFPA, but no hardware present!\n",
    "E_AFPA online, but no hardware present!",
    "E_AFPA waiting for register sets to be loaded (and then brought online).",
    "E_AFPA online.",
    "E_AFPA control store enabled for loading, but no hardware present!",
    "E_AFPA control store enabled for loading, but E_AFPA is already online to system\n\t(AND, the E_AFPA hardware is not present)!!",
    "E_AFPA awaiting microcode load.",
    "E_AFPA control store enabled for loading, but E_AFPA is already online to system!"
};


/*
 * The following has to do with the tokenization scheme we use.
 * Things defined on this page are for general use by the higher level code.
 */

typedef enum {
	TOK_EOF,

	TOK_UNKNOWN,

	TOK_SINGLE_QUOTE,
	TOK_COMMA,
	TOK_LBRACKET,
	TOK_RBRACKET,
	TOK_LBRACE,
	TOK_RBRACE,
	TOK_SEMICOLON,
	TOK_EQUAL,

	TOK_NUMERIC,
	TOK_64_BIT,

	TOK_XC,
	TOK_XB,
	TOK_RESERVED,
	TOK_MICROCODE,
	TOK_REGISTERSET
} token_t;

token_t
	token;			/* What we cracked into */

char
	token_literal[200];	/* The character string we cracked */

struct {
    long
	high,
	low;
} token_64_bit_value;		/* If a 64-bit (ucode) value, its value */

long
	token_numeric_value,	/* If a numeric, what was it */
	token_16_numeric_value;	/* If thought of as a hex number, what is it? */

/*
 * More tokenization variables.  In theory, these are only
 * for use within the tokenization routines.
 */

FILE
	*TokenFile;

char
	TokenBuffer[200],
	*TokenPtr = TokenBuffer+ 2*(sizeof TokenBuffer);	/* Refresh */

int
	TokenRecycle = 0;		/* Re-use last token cracked */

		/*
		 * Bump the token scan pointer, taking care of column numbers
		 * and line numbers.
		 */
#define	BumpTokenPtr()	    {	if (*TokenPtr == '\n') { \
				    line++; \
				    column = 1; \
				} else { \
				    column++; \
				} \
				TokenPtr++; \
			    }

		/* To make sure we have something to scan */
#define	IsItFull() \
		    if ((*TokenPtr == 0) || \
			    (TokenPtr >= TokenBuffer+sizeof TokenBuffer)) { \
			FillIt(); \
		    }


/*
 * Turn a token type into an ascii valued string.
 */

char *
tokTOstring(token)
token_t	token;
{
    switch (token) {
    case TOK_EOF:
	return "end of file";
    case TOK_UNKNOWN:
	return "unknown input";
    case TOK_SINGLE_QUOTE:
	return "'";
    case TOK_64_BIT:
	return "64-bit (microcode word) value";
    case TOK_XC:
	return "XC";
    case TOK_XB:
	return "XB";
    case TOK_COMMA:
	return ",";
    case TOK_LBRACKET:
	return "[";
    case TOK_RBRACKET:
	return "]";
    case TOK_LBRACE:
	return "{";
    case TOK_RBRACE:
	return "}";
    case TOK_NUMERIC:
	return "numeric value";
    case TOK_SEMICOLON:
	return ";";
    case TOK_EQUAL:
	return "=";
    case TOK_RESERVED:
	return "'reserved' instruction";
    case TOK_MICROCODE:
	return "'microcode' instruction";
    case TOK_REGISTERSET:
	return "'registerset' instruction";
    default:
	return "(PROGRAM ERROR:  Unknown token type)";
    }
}
/*
 * Print out basic information about the error location.
 */

void
ErrorHeader()
{
    int colno = column-strlen(token_literal);
    char *exact;

    if (colno > 0) {
	exact = "at";
    } else {
	colno = column;
	exact = "near";
    }

    fflush(stdout);
    fprintf(stderr, "Error %s line %d, column %d:  ", exact, line, colno);
}


/*
 * Make sure the input area is full.
 */

void
FillIt()
{
    if (fgets(TokenBuffer, sizeof TokenBuffer, TokenFile) == NULL) {
	if (ferror(TokenFile)) {
	    perror("fgets");
	    exit(5);
	} else {
	    BZERO(TokenBuffer);
	}
    }
    TokenPtr = TokenBuffer;
}

/*
 * We want to skip white space in tokenization.  Do that.
 */

SkipWs()
{
    while (!feof(TokenFile)) {
	IsItFull();		/* Make sure we have something */
	if (*TokenPtr == '#') {		/* Comment */
	    while ((*TokenPtr != '\n') && !feof(TokenFile)) {
		BumpTokenPtr();
		IsItFull();
	    }
	}
	if (!isspace(*TokenPtr)) {
	    return;
	}
	BumpTokenPtr();
    }
}



/*
 * We are pointing at something which is NOT white space;
 *	pull it into token_literal.
 *
 * Alphanumeric strings go in as a whole.
 * Special characters go in one at a time.
 */

void
GetSomething()
{
    char *ptr;

    IsItFull();		/* Make sure we have something */
    if (isalnum(*TokenPtr)) {
	for (ptr = token_literal; (!feof(TokenFile)) && isalnum(*TokenPtr); ) {
	    *ptr++ = *TokenPtr;
	    BumpTokenPtr();
	    IsItFull();		/* Make sure we have something */
	}
	*ptr = 0;		/* Terminate string */
    } else {
	token_literal[0] = *TokenPtr;
	BumpTokenPtr();
	token_literal[1] = 0;
    }
}

/*
 * This should be a numeric token.  Figure out which flavor (if any).
 *
 * Numeric values come in various flavors, since we
 * get our input data from various flavors of people
 * (including hardware people).
 *
 * At this level, we really can't tell for sure whether
 * we have decimal or hexadecimal data (though we can
 * guess).
 *
 * So, what we do is to keep both a value, and a "value if
 * it were base 16" lying around.
 *
 * In addition, microcode words are 64-bits, so we need to
 * keep in mind that we might be dealing with a 64 bit
 * quantity.  The assumption, however, is that the 64-bit
 * values take up 16 character positions in the input file.
 */
 
void
CrackNumeric()
{
    int decimal = 0, hex = 0, ucodeHigh = 0, ucodeLow = 0;
    int radix = 10;		/* Default radix */
    int explicit_radix = 0;
    char *began;		/* Where actual number began */
    static char validate[] = "0123456789abcdef";
    int c;
    int cant_be_decimal;
    char *ptr;
    char *index();

    cant_be_decimal = 0;
    ptr = token_literal;
    if (*ptr == '0') {
	explicit_radix = 1;
	cant_be_decimal = 1;
	ptr++;
	switch (*ptr) {
	case 'x':
	case 'X':
	    radix = 16;
	    ptr++;
	    break;
	case 'o':
	case 'O':
	    radix = 8;
	    ptr++;
	    break;
	case 't':
	case 'T':
	    radix = 10;
	    cant_be_decimal = 0;		/* It is!  It is! */
	    ptr++;
	    break;
	default:
	    radix = 8;
	    explicit_radix = 0;			/* Not necessarily */
	    break;
	}
    }
    began = ptr;
    while (*ptr) {
	c = *ptr;
	if (explicit_radix) {
	    if (index(validate, c) >= validate+radix) {
		char *english;

		switch (radix) {
		case 10:
		    english = "decimal";
		    break;
		case 8:
		    english = "octal";
		    break;
		case 16:
		    english = "hexadecimal";
		    break;
		default:
		    english = "(PROGRAM ERROR: Unknown radix)";
		    break;
		}

		ErrorHeader();
		fprintf(stderr, "Invalid %s value '%c'.\n", english, *ptr);
		exit(1);
	    }
	}
	if (isupper(c)) {
	    c = tolower(c);		/* For validation */
	}
	if (isxdigit(c)) {
	    if (isdigit(c)) {
		c -= '0';
		decimal = (decimal*radix)+c;
	    } else {
		cant_be_decimal = 1;
		c -= 'a';
		c += 10;
	    }
	    hex = (hex*16)+c;
	    ucodeHigh = (ucodeHigh<<4)+((ucodeLow>>28)&0xf);
	    ucodeLow = (ucodeLow*16)+c;
	} else {
	    token = TOK_UNKNOWN;
	    return;
	}
	ptr++;
    }
    if ((ptr-began) > 16) {
	ErrorHeader();
	fprintf(stderr, "Numeric quantity has too many digits.\n");
	fprintf(stderr,
		"\t(16 maximum for microcode words; 8 otherwise)\n");
    } else if (ptr-began > 8) {
	token = TOK_64_BIT;
	token_64_bit_value.high = ucodeHigh;
	token_64_bit_value.low = ucodeLow;
    } else {
	token = TOK_NUMERIC;
	token_16_numeric_value = hex;
	if (cant_be_decimal) {
	    token_numeric_value = hex;
	} else {
	    token_numeric_value = decimal;
	}
    }
}

/*
 * CrackToken()
 *
 * Look at next token in input, and decide what to do.  The algorithm is:
 *
 *	skip ws
 *	what do we have ? SPECIAL, ALPHANUM
 *	fill up token_literal until no longer in same
 *	figure out what is in token_literal
 *	if is numeric (or 64-bit), generate a numeric result also
 *	return
 */


void
CrackToken()
{
    if (TokenRecycle) {
	TokenRecycle = 0;
	return;
    }
    SkipWs();			/* Skip any whitespace */
    if (feof(TokenFile)) {
	token = TOK_EOF;
    } else {
	GetSomething();
	if (!isalnum(token_literal[0])) {	/* special character */
	    switch (token_literal[0]) {
	    case '\'':
		token = TOK_SINGLE_QUOTE;
		break;
	    case ',':
		token = TOK_COMMA;
		break;
	    case '[':
		token = TOK_LBRACKET;
		break;
	    case ']':
		token = TOK_RBRACKET;
		break;
	    case '{':
		token = TOK_LBRACE;
		break;
	    case '}':
		token = TOK_RBRACE;
		break;
	    case ';':
		token = TOK_SEMICOLON;
		break;
	    case '=':
		token = TOK_EQUAL;
		break;
	    default:
		token = TOK_UNKNOWN;
		break;
	    }
	} else if ((token_literal[0] == 'r') &&
			(strcmp(token_literal, "reserved") == 0)) {
	    token = TOK_RESERVED;
	} else if ((token_literal[0] == 'r') &&
			(strcmp(token_literal, "registerset") == 0)) {
	    token = TOK_REGISTERSET;
	} else if ((token_literal[0] == 'm') &&
			(strcmp(token_literal, "microcode") == 0)) {
	    token = TOK_MICROCODE;
	} else if (((token_literal[0] == 'X')
				&& (strcmp(token_literal, "XB") == 0))
		|| ((token_literal[0] == 'x')
				&& (strcmp(token_literal, "xb") == 0))) {
	    token = TOK_XB;
	} else if (((token_literal[0] == 'X')
				&& (strcmp(token_literal, "XC") == 0))
		|| ((token_literal[0] == 'x')
				&& (strcmp(token_literal, "xc") == 0))) {
	    token = TOK_XC;
	} else {
	    CrackNumeric();
	}
    }
    if (debug) {
	printf("token is %s (*%s*).\n", tokTOstring(token), token_literal);
    }
}

/*
 * Peek at the next token.  If it is the desired token, consume it,
 * and return true.  Else, return false (and leave token for next
 * caller).
 */

int
PeekFor(couldbe)
token_t	couldbe;		/* What we might see there */
{
    CrackToken();
    if (couldbe != token) {
	TokenRecycle = 1;
	return 0;
    } else {
	return 1;
    }
}


/*
 * The next token HAS to be "shouldbe".  If it isn't, complain and exit.
 */

void
CheckFor(shouldbe)
token_t	shouldbe;
{
    CrackToken();
    if (shouldbe != token) {
	ErrorHeader();
	fprintf(stderr, "Expected \"%s\", got \"%s\" (\"%s\").\n",
		tokTOstring(shouldbe), tokTOstring(token), token_literal);
	exit(1);
    }
}


/*
 * CrackUcode -
 *
 *	We break down the input file into binary.
 */

void
CrackUcode(verbose, dontstart)
int	verbose,
	dontstart;
{
    long *data = ucode_array;

    if (verbose) {
	printf("Converting input microcode to binary.\n");
    }
    CheckFor(TOK_EQUAL);
    CheckFor(TOK_LBRACE);
    do {
	CheckFor(TOK_SINGLE_QUOTE);
	if (data > &ucode_array[ucodeTOword(control_store_size-1)]) {
	    ErrorHeader();
	    fprintf(stderr, "Too much data in the input file.\n");
	    fprintf(stderr,
		"\tThe afpa needs at most %d 64-bit words,\n",
						    control_store_size);
	    fprintf(stderr,
		"\tbut the input file contained more than %d 64-bit words.\n",
						    control_store_size);
	    exit(1);
	}

	CheckFor(TOK_64_BIT);
	data[0] = token_64_bit_value.high;
	data[1] = token_64_bit_value.low;
	data += 2;

	CheckFor(TOK_SINGLE_QUOTE);
	CheckFor(TOK_XC);
    } while (PeekFor(TOK_COMMA) && !PeekFor(TOK_RBRACE));
    /*
     * Sigh.  Maybe really need a token stack (or positive
     * acknowledgements).
     *
     * Anyway, at this point, we want to use "token", but if
     * TokenRecycle is true, "token" isn't "correct".  So,...
     */
    if (TokenRecycle) {
	CrackToken();		/* (really, just turning off TokenRecycle */
    }
    if (token != TOK_RBRACE) {
	CheckFor(TOK_RBRACE);
    }
    CheckFor(TOK_SEMICOLON);

    if (!dontstart) {
	if (data != &ucode_array[ucodeTOword(control_store_size)]) {
	    ErrorHeader();
	    fflush(stdout);
	    fprintf(stderr, "Not enough microcode in file.\n");
	    fprintf(stderr,
		"\tThe afpa needs exactly %d 64-bit words (%d bytes),\n",
			control_store_size, ucodeTObyte(control_store_size));
	    fprintf(stderr,
		    "\tbut the input file contained only %d 64-bit words.\n",
			    byteTOucode(data-ucode_array));
	    exit(1);
	}
    }
    if (verbose) {
	printf("Input data converted to binary.\n");
    }
    ucode_size = wordTOucode(data-ucode_array);	/* 64-bit entities */
    return;
}


CrackRegisterset()
{
    int i;
    int regset;
    long *ptr;
    static int registerline[NUM_FPA_SETS] = { 0 };

    CheckFor(TOK_LBRACKET);
    CheckFor(TOK_NUMERIC);
    regset = token_numeric_value;
    if ((regset < 0) || (regset >= NUM_FPA_SETS)) {
	ErrorHeader();
	fprintf(stderr,
	    "'registerset[%d]' invalid.  Register set numbers\n", regset);
	fprintf(stderr, "\tmust be in the range [0..%d].\n",
							NUM_FPA_SETS-1);
	exit(1);
    } else if (registersets[regset] != 0) {
	ErrorHeader();
	fprintf(stderr,
	    "'registerset[%d]' defined earlier (line %d).\n",
	    regset, registerline[regset]);
	exit(1);
    }
    registerline[regset] = line;
    ptr = (long *) malloc(NUM_AFPA_FLOATS*sizeof (int));
    if (ptr == 0) {
	ErrorHeader();
	fprintf(stderr,
	    "Unable to acquire 'malloc'-memory for another register set.\n");
	exit(1);
    }
    CheckFor(TOK_RBRACKET);
    CheckFor(TOK_EQUAL);
    CheckFor(TOK_LBRACE);
    registersets[regset] = ptr;
    for (i = 0; i < NUM_AFPA_FLOATS; i++) {
	CheckFor(TOK_SINGLE_QUOTE);
	CheckFor(TOK_NUMERIC);
	*ptr++ = token_16_numeric_value;
	CheckFor(TOK_SINGLE_QUOTE);
	CheckFor(TOK_XB);
	if (i != (NUM_AFPA_FLOATS-1)) {
	    CheckFor(TOK_COMMA);
	}
    }

    /*
     * At this point, could have trailing ",", or just rbrace.
     */

    if (PeekFor(TOK_COMMA)) {
	if (PeekFor(TOK_NUMERIC)) {
	    ErrorHeader();
	    fprintf(stderr,
		    "\n\tMore than %d values specified for register set %d.\n",
		    NUM_AFPA_FLOATS, regset);
	    exit(1);
	}
    }
    CheckFor(TOK_RBRACE);
    CheckFor(TOK_SEMICOLON);
}


/*
 * CrackInput
 *
 * Read and understand the input data file.
 *
 * The file is in the form:
 *	reserved = NN;
 *	registerset[MM] = { (64 64-bit values) };
 *	ucode = { (4096 (nominally) 64-bit values) };
 *
 * Note:  All 64-bit input is in the form *'ABCDEFGHABCDEFGH'XC*, where
 *		[A-H] are hex digits, and the *'s are not really part
 *		of the input.
 *
 */

void
CrackInput(infile, verbose, dontstart)
FILE	*infile;		/* Input (control) file */
int	verbose,
	dontstart;
{
    int gotRegisterset = 0;
    int gotReserved = 0;
    int gotUcode = 0;

    TokenFile = infile;

    while (CrackToken(), token != TOK_EOF) {
	switch (token) {
	case TOK_RESERVED:
	    CheckFor(TOK_EQUAL);
	    CheckFor(TOK_NUMERIC);
	    gotReserved = 1;
	    reserved = token_numeric_value;
	    CheckFor(TOK_SEMICOLON);
	    break;
	case TOK_MICROCODE:
	    CrackUcode(verbose, dontstart);
	    gotUcode = 1;
	    break;
	case TOK_REGISTERSET:
	    CrackRegisterset();
	    gotRegisterset = 1;
	    break;
	default:
	    ErrorHeader();
	    fprintf(stderr,
		    "\n\tLooking for an instruction, found %s (*%s*).\n",
		    tokTOstring(token), token_literal);
	    exit(1);
	    break;
	}
    }
    if (gotReserved == 0) {
	fprintf(stderr, "No 'reserved' value specified.\n");
    }
    if (gotUcode == 0) {
	fprintf(stderr, "No 'microcode' data specified.\n");
    }
    if (gotRegisterset == 0) {
	fprintf(stderr, "WARNING:  No initial register values specified.\n");
    }
    if ((gotReserved == 0) || (gotUcode == 0)) {
	exit(1);
    }
}


void
Check(shouldbe, is, length, offset, verbose, interactive, source)
long	*shouldbe,		/* What should be there */
	*is;			/* What IS there */
int	length,			/* Length (in 64-bit words) */
	offset,			/* Where (in 64-bit words) to start check at */
	verbose,		/* Talkative index */
	interactive;		/* Are we running in interactive mode */
char	*source;		/* English name of source */
{
#define	HIGH_MBZ_BIT	11
#define	LOW_MBZ_BIT_A	40
#define	LOW_MBZ_BIT_B	63
#define	MBZ_HIGH   (1L<<(31-HIGH_MBZ_BIT))	/* Must be zero in high half */
#define	MBZ_LOW    ((1L<<(63-LOW_MBZ_BIT_A))|(1L<<(63-LOW_MBZ_BIT_B))) /* low */
    if (bcmp((char *) &shouldbe[ucodeTOword(offset)],
			(char *) &is[ucodeTOword(offset)],
			ucodeTObyte(length)) != 0) {
	long
	    highSBO = 0,	/* inclusive or 0's which s/b 1's */
	    lowSBO = 0,
	    highSBZ = 0,	/* inclusive or 1's which s/b 0's */
	    lowSBZ = 0,
	    firstUNEQUAL = -1,	/* where is first unequal word */
	    lastUNEQUAL = 0;	/* where is last unequal word */
	int
	    numFOUND = 0,	/* number of unequal ucode words found */
	    numXTRA = 0,	/* after numFOUND, some more weird ones */
	    numTOlookFOR = (verbose)? 20:5,
	    i;

	fflush(stdout);
	fprintf(stderr,
	    "Data written not equal to data read back from %s.\n", source);
	fprintf(stderr, "location\twritten\t\t\tread back\n");
	for (i = ucodeTOword(offset); i < ucodeTOword(length+offset); i += 2) {
	    if ((shouldbe[i] != is[i]) || (shouldbe[i+1] != is[i+1])) {

		highSBZ |= ((~shouldbe[i]) & is[i]);
		lowSBZ |= ((~shouldbe[i+1]) & is[i+1]);
		highSBO |= (shouldbe[i] & ~is[i]);
		lowSBO |= (shouldbe[i+1] & ~is[i+1]);

		if (firstUNEQUAL == -1) {
		    firstUNEQUAL = wordTOucode(i);
		}
		lastUNEQUAL = wordTOucode(i);
		if (numFOUND++ < numTOlookFOR) {
		    fprintf(stderr, "0x%08x\t0x%08x%08x\t0x%08x%08x.\n",
			wordTOucode(i), shouldbe[i], shouldbe[i+1],
			is[i], is[i+1]);
				/* Check for MBZ bits on in read back */
		} else if (((is[i]&MBZ_HIGH) || (is[i+1]&MBZ_LOW)) ||
			((shouldbe[i]&MBZ_HIGH) || (shouldbe[i+1]&MBZ_LOW))) {
		    if (numXTRA++ < numTOlookFOR) {
			fprintf(stderr, "0x%08x\t0x%08x%08x\t0x%08x%08x.\n",
			    wordTOucode(i), shouldbe[i], shouldbe[i+1],
			    is[i], is[i+1]);
		    }
		}
	    }
	}
	if (numFOUND > numTOlookFOR) {
	    fprintf(stderr, "+ %d more\n", 1+numFOUND-numTOlookFOR);
	}
	fprintf(stderr, "First bad word at location\n\t0x%08x.\n",
								firstUNEQUAL);
	fprintf(stderr, "Last bad word at location\n\t0x%08x.\n",
								lastUNEQUAL);
	fprintf(stderr,
	    "The inclusive or of the 'stuck at one' bits is\n\t0x%08x%08x.\n",
	    highSBZ, lowSBZ);
	fprintf(stderr,
	    "The inclusive or of the 'stuck at zero' bits is\n\t0x%08x%08x.\n",
	    highSBO, lowSBO);
	fprintf(stderr, "The inclusive or of the bad bits is\n\t0x%08x%08x.\n",
		    highSBO|highSBZ, lowSBO|lowSBZ);

	/* Explain a mysterious bit pattern */
	if (((highSBO|highSBZ) == (~MBZ_HIGH)) &&
					    ((lowSBO|lowSBZ) == (~MBZ_LOW))) {
	    fprintf(stderr,
			"\t(Bits %d, %d, and %d are 'must be zero' bits.)\n",
				HIGH_MBZ_BIT, LOW_MBZ_BIT_A, LOW_MBZ_BIT_B);
	}

	if (!interactive) {
	    exit(99);
	}
    } else {
	if (verbose) {
	    printf(
		"Data written checks with data read back from %s.\n", source);
	}
    }
}

void
DoPrint(printfile, length, offset)
FILE	*printfile;
int
	length,
	offset;
{
    int i;

    for (i = ucodeTOword(offset); i < ucodeTOword(length+offset); i += 2) {
	fprintf(printfile, "'%08X%08X'XC,	%08x\n",
		    chkarray[i], chkarray[i+1], wordTOucode(i));
    }
}


void
DoRead(r_afpa, length, offset, verbose)
FILE	*r_afpa;
int
	length,
	offset,
	verbose;
{
    int r_afpa_fd;


    /*
     * Now, read the data which was written.
     *
     * We need to do Unix I/O (versus stdio) because the stdio
     * library spends its time trying to mess us up when
     * we are reading (by trying to read on disk block aligned
     * boundaries).
     *
     */

    r_afpa_fd = fileno(r_afpa);


    if (lseek(r_afpa_fd, ucodeTObyte(offset), 0) == -1) {
	perror("lseek");
	exit(2);
    }
    if (read(r_afpa_fd, (char *)&chkarray[ucodeTOword(offset)],
			ucodeTObyte(length)) != ucodeTObyte(length)) {
	perror("read");
	exit(2);
    }
    if (verbose) {
	printf("Control store read back in.\n");
    }
}



void
DoWrite(w_afpa, length, offset, verbose)
FILE	*w_afpa;
int
	length,
	offset,
	verbose;
{
    int w_afpa_fd;

    w_afpa_fd = fileno(w_afpa);


    if (lseek(w_afpa_fd, ucodeTObyte(offset), 0) == -1) {
	perror("lseek");
	exit(2);
    }
    if (write(w_afpa_fd, (char *)&intarray[ucodeTOword(offset)],
			ucodeTObyte(length)) != ucodeTObyte(length)) {
	perror("write");
	exit(2);
    }
    if (verbose) {
	printf("Control store written.\n");
    }
}

void
PutBinary(w_afpa, r_afpa, length, offset, verbose)
FILE	*w_afpa,		/* Where data is written */
	*r_afpa;			/* To check data */
int	length,			/* Length of data (in 64-bit words) */
	offset,			/* Where to do writing at */
	verbose;		/* Talkative index */
{
    bcopy((char *)ucode_array, (char *)intarray, sizeof intarray);

    DoWrite(w_afpa, length, offset, verbose);
    DoRead(r_afpa, length, offset, verbose);
    /*
     * Now, check with intarray
     */
    Check(intarray, chkarray, length, offset, verbose, 0, "control store");
}

void
DoRegisterSets(verbose,readregsetsFlag,basefilename)
int	verbose, readregsetsFlag;
char	*basefilename;
{
    FILE *regfile;
    int i, j;
    static long zeroes[NUM_AFPA_FLOATS] = 0;
    long readback[NUM_AFPA_FLOATS];
    long *regs;
    char source[30], regfilenames[255];
    struct floatstate state, mask;
    int	id;
    int	load_order[32] = {	 0, 1, 2, 3, 4, 5, 6, 7,
   				 8, 9,10,11,12,13,14,15,
				16,17,18,19,20,21,22,23,
				25,26,31,28,29,27,30,24 };

    /*
     * Note that the following code may NOT be single stepped
     * with dbx.  The reason is as follows:
     *		- we are acquiring, via a special path,
     *			a resgister set.
     *		- if we single step, the debugger will
     *			cause the register set to be
     *			saved.
     *		- when we next attempt to access the
     *			register set, the fpa interrupt
     *			routine in the kernel will note
     *			that we do not have a register
     *			set, that we want one, AND that
     *			there is no floating point
     *			hardware in a usable state.  So,
     *			the fpa interrupt code will send
     *			us a segmentation fault.
     */

    for (id = 0; id < NUM_FPA_SETS; id++) {
	i = load_order[id];
	BZERO(mask);
	BZERO(state);
	j = sizeof state;
	mask.fpa_registerset = -1;
	state.fpa_registerset = i;
	if (setfloatstate(&mask, &state, &j) == -1) {
	    fprintf(stderr, "(acquiring register set %d) ", i);
	    perror("setfloatstate");
	    exit(4);
	}
	if (state.fpa_registerset != i) {
	    fprintf(stderr, "Unable to acquire register set %d.\n", i);
	    exit(4);
	}
	if ((regs = registersets[i]) == 0) {
	    regs = zeroes;
	}
	if (readregsetsFlag) {
	    sprintf(regfilenames,"%s.before.%.2d",basefilename,i);
	    regfile = fopen(regfilenames,"w");
	    fprintf(regfile, "registerset[%d] = {\n", i);
	    for (j = 0; j < NUM_AFPA_FLOATS; j++) {
	   	readback[j] = afpa_read_register(j);
		fprintf(regfile, "'%.8X'XB,\n", readback[j]);
	    }
	    fprintf(regfile, "};\n");
	    fclose(regfile);
	}
	for (j = 0; j < NUM_AFPA_FLOATS; j++) {
	    afpa_write_register(j, regs[j]);
	}
	if (readregsetsFlag) {
	    sprintf(regfilenames,"%s.after.%.2d",basefilename,i);
	    regfile = fopen(regfilenames,"w");
	    fprintf(regfile, "registerset[%d] = {\n", i);
	}
	for (j = 0; j < NUM_AFPA_FLOATS; j++) {
	    readback[j] = afpa_read_register(j);
	    if (readregsetsFlag)
	        fprintf(regfile, "'%.8X'XB,\n", readback[j]);
	}
	if (readregsetsFlag) {
	    fprintf(regfile, "};\n");
	    fclose(regfile);
	}
	sprintf(source, "register set %d", i);
	Check(regs, readback, byteTOucode(sizeof readback),
							0, verbose, 0, source);
    }
}

void
DoPrintreg(basefilename)
char	*basefilename;
{
    FILE *regfile;
    int i, j;
    long readback;
    char regfilenames[255];
    struct floatstate state, mask;

    /*
     * Note that the following code may NOT be single stepped
     * with dbx.  The reason is as follows:
     *		- we are acquiring, via a special path,
     *			a resgister set.
     *		- if we single step, the debugger will
     *			cause the register set to be
     *			saved.
     *		- when we next attempt to access the
     *			register set, the fpa interrupt
     *			routine in the kernel will note
     *			that we do not have a register
     *			set, that we want one, AND that
     *			there is no floating point
     *			hardware in a usable state.  So,
     *			the fpa interrupt code will send
     *			us a segmentation fault.
     */

    for (i = 0; i < NUM_FPA_SETS; i++) {
	BZERO(mask);
	BZERO(state);
	j = sizeof state;
	mask.fpa_registerset = -1;
	state.fpa_registerset = i;
	if (setfloatstate(&mask, &state, &j) == -1) {
	    fprintf(stderr, "(acquiring register set %d) ", i);
	    perror("setfloatstate");
	    exit(4);
	}
	if (state.fpa_registerset != i) {
	    fprintf(stderr, "Unable to acquire register set %d.\n", i);
	    exit(4);
	}
	sprintf(regfilenames,"%s.online.%.2d",basefilename,i);
	regfile = fopen(regfilenames,"w");
	fprintf(regfile, "registerset[%d] = {\n", i);
	for (j = 0; j < NUM_AFPA_FLOATS; j++) {
	    readback = afpa_read_register(j);
	    fprintf(regfile, "'%.8X'XB,\n", readback);
	}
	fprintf(regfile, "};\n");
	fclose(regfile);
    }
}

void
DoPattern(which)
char *which;
{
    enum { ONES, ZEROES, _55, AA, ADDRESS, RANDOM } type;
    int i;
    long high, low;
    long random();
    long *pHigh, *pLow;

    if (strcmp(which, "ones") == 0) {
	type = ONES;
    } else if ((strcmp(which, "zeroes") == 0) ||
		(strcmp(which, "zeros") == 0)) {
	type = ZEROES;
    } else if (strcmp(which, "55") == 0) {
	type = _55;
    } else if (strcmp(which, "aa") == 0) {
	type = AA;
    } else if (strcmp(which, "address") == 0) {
	type = ADDRESS;
    } else if (strcmp(which, "random") == 0) {
	type = RANDOM;
    } else if (strcmp(which, "ucode") == 0) {
	bcopy((char *)ucode_array, (char *)intarray, sizeof intarray);
	return;
    } else {
	if (strcmp(which, "?") != 0) {
	    fprintf(stderr, "*%s* - illegal pattern\n", which);
	}
	printf("Legal patterns are: 'ucode', 'ones', 'zeroes',\n");
	printf("\t'55', 'aa', 'address', or 'random'.\n");
	return;
    }

    pHigh = &intarray[0];
    pLow = &intarray[1];
    for (i = 0; i < UCODE_SIZE_NOMINAL; i++) {
	switch (type) {
	case ONES:
	    high = low = 0xffffffff;
	    break;
	case ZEROES:
	    high = low = 0x0;
	    break;
	case _55:
	    high = low = 0x55555555;
	    break;
	case AA:
	    high = low = 0xaaaaaaaa;
	    break;
	case ADDRESS:
	    high = i;
	    low = ~i;
	    break;
	case RANDOM:
	    high = random();
	    low = random();
	}
	*pHigh = high;
	*pLow = low;
	pHigh += 2;
	pLow += 2;
    }
}

long
ExtractLongValue(string, perrorint, interactive)
char *string;
int	*perrorint;
int	interactive;		/* if interactive, don't print errors */
{
    long longval;

    if ((string[0] == '0') && (string[1] != '\0')) {
	char *radix, *format;
	int had_0_type_err = 0;

	switch (string[1]) {
	case 'x':
	case 'X':
	    radix = "hexadecimal";
	    format = "%lx";
	    break;
	case 't':
	case 'T':
	    radix = "decimal";
	    format = "%ld";
	    break;
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	    radix = "octal";
	    format = "%lo";
	    break;
	default:
	    had_0_type_err = 1;
	    (*perrorint)++;
	    if (!interactive) {
		fflush(stdout);
		fprintf(stderr, "*%s* - illegal value.\n", string);
	    }
	    break;
	}
	if (!had_0_type_err) {
	    if (sscanf(string+2, format, &longval) != 1) {
		if (!interactive) {
		    fflush(stdout);
		    fprintf(stderr, "*%s* - not a legal %s value.\n",
			    string, radix);
		}
		(*perrorint)++;
	    }
	}
    } else {
	if (sscanf(string, "%ld", &longval) != 1) {
	    if (!interactive) {
		fflush(stdout);
		fprintf(stderr, "*%s* - not a legal decimal value.\n",
			string);
	    }
	    (*perrorint)++;
	}
    }
    return longval;
}

/*
 * Scan the standard input, separating the input line into tokens.
 *
 * We return the number of arguments parsed, and the a arguments.
 *
 * If we get EOF, we return EOF.
 *
 */

int
GetTokens(maxargs, arglist, arglength, prompt)
int	maxargs;		/* Maximum number of arguments allowed */
char	*arglist[];		/* Where arguments go */
int	arglength;		/* Length of each argument */
char	*prompt;
{
    int c;
    int argc;
    char **whereargs;
    char *argptr;
    enum { BEGINNING, INWS, INWORD, DONE } state = BEGINNING;

    while (state != DONE) {
	if (state == BEGINNING) {
	    printf(prompt);
	    fflush(stdout);
	    whereargs = arglist-1;		/* incremented before used */
	    argc = 0;
	}
	c = getchar();
	switch (c) {
	case EOF:
	case '\n':
	case ' ':
	case '\t':
	    if (state == INWORD) {
		*argptr = 0;			/* Terminate token */
	    }
	    if ((c == EOF) && (state == BEGINNING)) {
		return EOF;
	    } else if ((c == '\n') || (c == EOF)) {
		state = DONE;
	    } else {
		state = INWS;
	    }
	    break;
	default:
	    if (state != INWORD) {
		state = INWORD;
		argc++;
		whereargs++;
		argptr = *whereargs;
		if (argc > maxargs) {
		    fprintf(stderr,
			    "Too many arguments in command, %d maximum.\n",
				    maxargs);
		    printf(prompt);
		    state = BEGINNING;
		}
	    }
	    if ((argptr-*whereargs) > arglength) {
		fprintf(stderr, "Argument *%s...* too long.\n", *whereargs);
		state = BEGINNING;
	    } else {
		*argptr++ = c;
	    }
	    break;
	}
    }
    return argc;
}
void
DoInteractive(w_afpa, r_afpa, length, offset, verbose)
FILE	*w_afpa,
	*r_afpa;
int
	length,
	offset,
	verbose;
{
#define	ARG_SIZE	30		/* Size of arguments */
    int argc, lastargc = 1;
    char command[ARG_SIZE], argument[ARG_SIZE];
    static char lastcommand[ARG_SIZE] = "?", lastargument[ARG_SIZE] = "";
    char *arglist[2];
    long argument_value;
    int argument_value_bad;

    arglist[0] = command;
    arglist[1] = argument;
    BZERO(command);
    BZERO(argument);

    while (1) {
	command[sizeof command-1] = 0xff;
	argument[sizeof argument-1] = 0xff;
	argc = GetTokens(2, arglist, ARG_SIZE, "> ");
	if (argc == 0) {
	    bcopy(lastcommand, command, ARG_SIZE);
	    bcopy(lastargument, argument, ARG_SIZE);
	    argc = lastargc;
	} else if (argc == EOF) {
	    printf("quit\n");
	    exit(0);
	}
	argument_value_bad = 0;
	argument_value = ExtractLongValue(argument, &argument_value_bad, 1);
	if (strcmp(command, "pattern") == 0) {
	    DoPattern(argument);
	} else if (strcmp(command, "write") == 0) {
	    if (argc == 1) {
		argument_value = 1;
		argument_value_bad = 0;
	    }
	    if (argument_value_bad) {
		fflush(stdout);
		fprintf(stderr, "Argument for 'write' should be numeric\n");
	    } else while (argument_value-- > 0) {
		DoWrite(w_afpa, length, offset, verbose);
	    }
	} else if (strcmp(command, "read") == 0) {
	    if (argc == 1) {
		argument_value = 1;
		argument_value_bad = 0;
	    }
	    if (argument_value_bad) {
		fflush(stdout);
		fprintf(stderr, "Argument for 'read' should be numeric\n");
	    } else while (argument_value-- > 0) {
		DoRead(r_afpa, length, offset, verbose);
	    }
	} else if (strcmp(command, "length") == 0) {
	    if (argc == 2) {
		if (argument_value_bad || (argument_value <= 0) ||
				(argument_value > control_store_size)) {
		    fprintf(stderr, "Argument for 'length' should be a ");
		    fprintf(stderr,
			"positive number less than %d.\n", control_store_size);
		} else {
		    length = argument_value;
		}
	    }
	    printf("length is %d (0x%x)\n", length, length);
	    if ((offset+length) > control_store_size) {
		offset = control_store_size-length;
		printf("(offset reduced to %d (0x%x))\n", offset, offset);
	    }
	} else if (strcmp(command, "offset") == 0) {
	    if (argc == 2) {
		if (argument_value_bad || (argument_value < 0) ||
				(argument_value >= control_store_size)) {
		    fprintf(stderr,
			"Argument for 'offset' should be a positive number");
		    fprintf(stderr, " less than %d\n", control_store_size);
		} else {
		    offset = argument_value;
		}
	    }
	    printf("offset is %d (0x%x)\n", offset, offset);
	    if ((offset+length) > control_store_size) {
		length = control_store_size-offset;
		printf("(length reduced to %d (0x%x))\n", length, length);
	    }
	} else if (strcmp(command, "check") == 0) {
	    if (argc != 1) {
		fprintf(stderr, "No arguments allowed on 'check' command.\n");
	    } else {
		Check(intarray, chkarray, length, offset, verbose,
							1, "control store");
	    }
	} else if (strcmp(command, "wrc") == 0) {
	    if (argc == 1) {
		argument_value = 1;
		argument_value_bad = 0;
	    }
	    if (argument_value_bad) {
		fflush(stdout);
		fprintf(stderr, "Argument for 'wrc' should be numeric\n");
	    } else while (argument_value-- > 0) {
		DoWrite(w_afpa, length, offset, verbose);
		DoRead(r_afpa, length, offset, verbose);
		Check(intarray, chkarray, length, offset, verbose,
							1, "control store");
	    }
	} else if ((strcmp(command, "help") == 0)
				|| (strcmp(command, "?") == 0)) {
	    printf("Commands are: 'pattern', 'write', 'length', 'offset',\n");
	    printf("\t'read', 'check', 'wrc' (writes, reads, checks),\n");
	    printf("\t'print', 'help' (aka '?'), and 'quit'.\n");
	} else if (strcmp(command, "print") == 0) {
	    static FILE *printfile = NULL;

	    if (argc == 2) {
		if ((printfile = fopen(argument, "w")) == NULL) {
		    fprintf(stderr, "Unable to open output file *%s*.\n",
				argument);
		    continue;
		} else if (printfile == NULL ) {
		    printfile = stdout;
		} /* else, use last file */
	    }
	    DoPrint(printfile, length, offset);
	} else if (strcmp(command, "printreg") == 0) {
	    DoPrintreg(argument);
	} else if (strcmp(command, "quit") == 0) {
	    exit(0);
	} else {
	    fprintf(stderr, "Unknown command *%s*.\n", command);
	    continue;		/* Skip this command */
	}

	/* Keep last command around */
	bcopy(command, lastcommand, ARG_SIZE);
	bcopy(argument, lastargument, ARG_SIZE);
	lastargc = argc;
    }
}

void
usage(argv0)
char *argv0;
{
    fflush(stdout);
    fprintf(stderr, "usage: %s [-ucode filename] [-afpamem filename]\n", argv0);
    fprintf(stderr, "\t[-rc] [-offset value] [-interactive] [-dontstart]\n");
    fprintf(stderr,
	"\t[-verbose] [-dontloaducode] [-noucode] [-query] [-debug]\n");
    exit(1);
}


main(argc, argv)
int	argc;
char	*argv[];
{
    FILE *infile = stdin, *w_afpa = NULL, *r_afpa = NULL, *regfile = NULL;
    int offset = 0;
    char *cmdname = argv[0], *regfilename;
    int command_line_errors = 0;
    int verbose = 0;
    int dontstart = 0;
    int query = 0;
    int interactive = 0;
    int noucode = 0;
    int dontloaducode = 0;
    int dontwriteucode = 0;
    int rc = 0;
    int afpa_type = 0;			/* assume pass_c afpa card */
    struct floatstate state, mask;
    int i;

    /*
     * First, check to see if the afpa is (1) on the system, and (2)
     * waiting to be loaded.
     */
    argv++;
    argc--;

	int consumed;	/* How many arguments consumed */
	int afpamemFlag = 0, ucodeFlag = 0, readregsetsFlag;

    while (argc > 0) {
	if (strcmp(argv[0], "-verbose") == 0) {
	    consumed = 1;
	    verbose++;
	} else if (strcmp(argv[0], "-dontstart") == 0) {
	    consumed = 1;
	    dontstart++;
	} else if (strcmp(argv[0], "-debug") == 0) {
	    consumed = 1;
	    debug++;
	} else if (strcmp(argv[0], "-rc") == 0) {
	    consumed = 1;
	    rc++;
	} else if (strcmp(argv[0], "-dontloaducode") == 0) {
	    consumed = 1;
	    dontloaducode++;
	} else if (strcmp(argv[0], "-dontwriteucode") == 0) {
	    consumed = 1;
	    dontwriteucode++;
	} else if (strcmp(argv[0], "-query") == 0) {
	    consumed = 1;
	    query++;
	} else if (strcmp(argv[0], "-noucode") == 0) {
	    consumed = 1;
	    noucode++;
	} else if (strcmp(argv[0], "-offset") == 0) {
	    consumed = 2;
	    if (argc < consumed) {
		fflush(stdout);
		fprintf(stderr, "Need a value for -offset option.\n");
		command_line_errors++;
	    } else {
		offset = ExtractLongValue(argv[1], &command_line_errors, 0);
	    }
	} else if (strcmp(argv[0], "-interactive") == 0) {
	    consumed = 1;
	    interactive = 1;
	} else if (strcmp(argv[0], "-afpamem") == 0) {
	    consumed = 2;
	    if (argc < consumed) {
		fflush(stdout);
		fprintf(stderr, "Need a filename for -afpamem option.\n");
		command_line_errors++;
	    } else if (afpamemFlag) {
		fflush(stdout);
		fprintf(stderr, "Only one -afpamem option may be specified.\n");
		command_line_errors++;
	    } else {
		afpamemFlag = 1;
		w_afpa = fopen(argv[1], "w");
		if (w_afpa == NULL) {
		    perror("afpamem fopen (write)");
		    exit(2);
		}
		r_afpa = fopen(argv[1], "r");
		if (r_afpa == NULL) {
		    perror("afpamem fopen (read)");
		    exit(2);
		}
	    }
	} else if (strcmp(argv[0], "-ucode") == 0) {
	    consumed = 2;
	    if (argc < consumed) {
		fflush(stdout);
		fprintf(stderr, "Need a filename for -ucode option.\n");
		command_line_errors++;
	    } else if (ucodeFlag) {
		fflush(stdout);
		fprintf(stderr, "Only one -ucode option may be specified.\n");
		command_line_errors++;
	    } else {
		ucodeFlag = 1;
		infile = fopen(argv[1], "r");
		if (infile == NULL) {
		    perror("ucode fopen (read)");
		    exit(2);
		}
	    }
	/* the -readregsets flag is not documented TQH */
	} else if (strcmp(argv[0], "-readregsets") == 0) {
	    consumed = 2;
	    if (argc < consumed) {
		fflush(stdout);
		fprintf(stderr, "Need a filename for -readregsets option.\n");
		command_line_errors++;
	    } else if (readregsetsFlag) {
		fflush(stdout);
		fprintf(stderr, "Only one -readregsets option may be specified.\n");
		command_line_errors++;
	    } else {
		readregsetsFlag = 1;
		regfile = fopen(argv[1], "w");
		if (regfile == NULL) {
		    perror("ucode fopen (write regfile)");
		    exit(2);
		} else if (fclose(regfile)) {
		    perror("ucode fclose (regfile)");
		    exit(2);
		} else
		    regfilename = argv[1];

	    }
	} else {
	    consumed = 1;
	    fflush(stdout);
	    fprintf(stderr, "*%s* unknown %s\n", argv[0],
				(argv[0][0] == '-')? "option" : "argument");
	    command_line_errors++;
	}
	argv += consumed;		/* How many arguments we just used */
	argc -= consumed;
    }

    if (command_line_errors) {
	usage(cmdname);
	/*NOTREACHED*/
    }

    /* Find out if afpa on system */

    BZERO(state);
    i = sizeof state;

    if (getfloatstate(&state, &i) == -1) {
	perror("getfloatstate");
	exit(3);
    }

    if (i != sizeof state) {
	fprintf(stderr,
		"After getfloatstate:  Bad floatstate length %d.\n", i);
	exit(3);
    }
    if (query) {
	int index = 0;

	if (state.hardware_state&FLOAT_AFPA) {
	    index |= 1;
	}
	if (state.hardware_state&FLOAT_AFPA_HARDWARE) {
	    index |= 2;
	}
	if (state.hardware_state&FLOAT_AFPA_CONTROL_STORE_ENABLE) {
	    index |= 4;
	}
	if (state.hardware_state&FLOAT_AFPA_PASS_D) {
	    index |= 8;
	}

	printf("%s\n", afpa_state[index]);
	exit(0);
    }
    if ((state.hardware_state&FLOAT_AFPA_HARDWARE) == 0) {
	if (rc == 0) {
	    fprintf(stderr,
		"The kernel did not detect an AFPA at system boot time;");
	    fprintf(stderr, " no action taken.\n");
	}
	exit(0);		/* XXX - some other number? */
    }
    if (state.hardware_state&FLOAT_AFPA) {
	if (rc == 0) {
	    fprintf(stderr, "The AFPA is already online.\n");
	}
	exit(0);
    }
    if ((state.hardware_state&FLOAT_AFPA_CONTROL_STORE_ENABLE) == 0) {
	fprintf(stderr, "The kernel did not enable access to AFPA ucode;");
	fprintf(stderr, " no action taken.\n");
	exit(4);
    }
    if (state.hardware_state&FLOAT_AFPA_PASS_D) {
	afpa_type = FLOAT_AFPA_PASS_D;
    }

    if (rc && !verbose && !noucode) {
	printf("Loading AFPA microcode...  ");
	fflush(stdout);
	fflush(stderr);
    }

    if (w_afpa == NULL) {		/* Open up default /dev/afpamem */
	r_afpa = fopen("/dev/afpamem", "r");
	if (r_afpa == NULL) {
	    perror("/dev/afpamem fopen (read)");
	    exit(2);
	}
	w_afpa = fopen("/dev/afpamem", "w");
	if (w_afpa == NULL) {
	    perror("/dev/afpamem fopen (write)");
	    exit(2);
	}
    }

    BZERO(mask);

    if (!noucode) {
	CrackInput(infile, verbose, dontstart);
    }
    if (interactive) {
	DoInteractive(w_afpa, r_afpa, ucode_size, offset, verbose);
    } else {
	if (!dontloaducode) {
#ifdef GOGO
	    if ((state.hardware_state&FLOAT_AFPA_CONTROL_STORE_ENABLE) == 0) {
		if (verbose) {
		    printf("Raising FLOAT_AFPA_CONTROL_STORE_ENABLE");
		    printf(" (allows loading of the microcode).\n"
		    );
		}
		BZERO(mask);
		BZERO(state);
		i = sizeof state;
		mask.hardware_state = FLOAT_AFPA_CONTROL_STORE_ENABLE;
		state.hardware_state = FLOAT_AFPA_CONTROL_STORE_ENABLE;
		if (setfloatstate(&mask, &state, &i) == -1) {
		    perror(
			"setfloatstate (set FLOAT_AFPA_CONTROL_STORE_ENABLE)");
		    exit(4);
		}
		if ((state.hardware_state&FLOAT_AFPA_CONTROL_STORE_ENABLE)
									== 0) {
					    /* Kernel wouldn't accept it */
		    fprintf(stderr, "The kernel would not enable access");
		    fprintf(stderr, " to the AFPA microcode.\n");
		    exit(4);
		}
	    }
#endif GOGO
	    /* Actually write out the microcode */
	    if (!dontwriteucode)
	    	PutBinary(w_afpa, r_afpa, ucode_size, offset, verbose);
	    if (dontstart) {
		return;
	    }

	    /*
	     * Next, we drop FLOAT_AFPA_CONTROL_STORE_ENABLE.
	     * This causes the kernel to disable the control
	     * store at the afpa, which has the side-effect
	     * of causing the afpa to start up.
	     */

	    if (verbose) {
		printf(
		  "Dropping FLOAT_AFPA_CONTROL_STORE_ENABLE.\n"
		);
	        }
	    BZERO(mask);
	    BZERO(state);
	    i = sizeof state;
	    mask.hardware_state = FLOAT_AFPA_CONTROL_STORE_ENABLE;
	    state.hardware_state = 0;
	    if (setfloatstate(&mask, &state, &i) == -1) {
		perror("setfloatstate (clear FLOAT_AFPA_CONTROL_STORE_ENABLE)");
		exit(4);
	    }
	    if (state.hardware_state&FLOAT_AFPA_CONTROL_STORE_ENABLE) {
						/* Kernel wouldn't accept it */
		fprintf(stderr, "The kernel would not start the AFPA.\n");
		exit(4);
	    }
	}
	/* Find out if afpa ready for registers */

	BZERO(state);
	i = sizeof state;

	if (getfloatstate(&state, &i) == -1) {
	    perror("getfloatstate");
	    exit(3);
	}

	if (i != sizeof state) {
	    fprintf(stderr,
		    "After getfloatstate:  Bad floatstate length %d.\n", i);
	    exit(3);
	}

	if (state.hardware_state&FLOAT_AFPA_CONTROL_STORE_ENABLE) {
	    fprintf(stderr, "Unable to load the AFPA registers.\n");
	    fprintf(stderr, "(and thus unable to bring the AFPA online)\n");
	    exit(5);
	} else {
#ifdef LOADREGSET
	    /* Next, we put out the values into the register sets */
	    if (verbose) {
		printf("Loading register sets with initial values.\n");
	    }
	    DoRegisterSets(verbose,readregsetsFlag,regfilename);
#endif LOADREGSET

	    /* Next, we assert FLOAT_AFPA */

	    if (verbose) {
		printf("Bringing AFPA online (starts up AFPA).\n");
	    }
	    BZERO(mask);
	    BZERO(state);
	    i = sizeof state;
	    mask.hardware_state = FLOAT_AFPA;
	    state.hardware_state = FLOAT_AFPA;
	    mask.fpa_registerset = -1;
	    state.fpa_registerset = NUM_FPA_SETS-reserved;
	    if (setfloatstate(&mask, &state, &i) == -1) {
		perror("setfloatstate (set FLOAT_AFPA)");
		exit(6);
	    }
	    if ((state.hardware_state&FLOAT_AFPA) == 0) {
		fprintf(stderr,
			"The kernel would not bring the AFPA online.\n");
		exit(6);
	    }
#ifdef LOADREGSET
	    /*
	    DoRegisterSets(verbose,readregsetsFlag,regfilename);
	    if (readregsetsFlag)
		    DoPrintreg(regfilename);
	    */
#endif LOADREGSET
	}
	if (afpa_type == FLOAT_AFPA_PASS_D) {
		printf("Adapter type is E_AFPA.\n");
	} else {
		printf("Adapter type is AFPA.\n");
	}
    }
    return 0;
}
