#include <string.h>
#include <stdlib.h>

/**
 *  OType is an enumerated type for classifying the different ways of
 *  coding operands for a machine instruction.
 */
typedef enum OType {
    reg_mem,	// one register operand and one memory operand
    reg_imm,	// one register operand and one immediate operand
    mem,	// one memory operand
    none	// no operands
} OType;

/**
 *  InstructionData is a struct type for information about a machine
 *  instruction and its coding.
 */
typedef struct InstructionData {
    char * mnemonic;	// mnemonic for the instruction
    OType operandType;	// operand coding type for the instruction
    long basecode;	// code for the instruction without operands
} InstructionData;

/**
 *  instructions is an array with an InstructionData struct for each
 *  machine instruction.
 */
InstructionData instructions[] = {
    //  mnemonic	operandType	basecode
    //	--------	-----------	--------
    {	"ADD",		reg_mem,	0x02000000	},
    {	"BR",		mem,		0x10000000	},
    {	"BRNEG",	mem,		0x05000000	},
    {	"BRNONZ",	mem,		0x09000000	},
    {	"BRPOS",	mem,		0x01000000	},
    {	"BRZERO",	mem,		0x04000000	},
    {	"DIVIDE",	reg_mem,	0x0A000000	},
    {	"HALT",		none,		0x00000000	},
    {	"LADDR",	reg_mem,	0x08000000	},
    {	"LOAD",		reg_mem,	0x03000000	},
    {	"LI",		reg_imm,	0x0B000000	},
    {	"MULT",		reg_mem,	0x0E000000	},
    {	"SUB",		reg_mem,	0x06000000	},
    {	"ST",		reg_mem,	0x07000000	}
};

/**
 *  errorData is instruction data for an invalid instruction mnemonic.  The
 *  basecode field of errorData is -1.
 */
const InstructionData errorData = 
    //  mnemonic	operandType	basecode
    //	--------	-----------	--------
    {	"UNKNOWN",	none,		0xFFFFFFFF	};

/**
 *  INSTRUCTION_COUNT is the number of entries in the instructions array.
 */
const int INSTRUCTION_COUNT = sizeof(instructions)/sizeof(InstructionData);

/**
 *  compareMnemonic(searchKey, entry) returns a negative integer if
 *  searchKey is less than the mnemonic in entry, 0 if searchKey is equal
 *  to the mnemonic in entry, and a positive integer if searchKey is
 *  greater than the mnemonic in entry.  One string is less than another
 *  if the first preceeds the second in dictionary order.
 */
static int compareMnemonic(const void * searchKey, const void * entry) {
    return strcmp((char *)searchKey, ((InstructionData *)entry)->mnemonic);
}  // static int compareMnemonic(const void *, const void *)

/**
 *  getInstructionData(mne) returns the data for the instruction whose
 *  mnemonic is mne.  If there is no instruction with mnemonic mne then
 *  getInstructionData(mne) returns data with basecode -1.
 */
InstructionData getInstructionData(char * mne) {
    InstructionData * dataPtr;
    dataPtr = (InstructionData *)bsearch(mne, instructions,
	    INSTRUCTION_COUNT, sizeof(InstructionData), compareMnemonic);
    if (dataPtr == NULL) {
	return errorData;
    }
    return *dataPtr;
}  // InstructionData getInstructionData(char *)

/**
 *  reg_imm_code(bc, reg, imm) returns the machine coding for an
 *  instruction whose operandType is reg_imm, whose basecode is bc,
 *  whose register operand is reg, and whose immediate operand is imm.
 *  The register is coded into the four bits starting at bit 20.  The
 *  immediate operand is code into the 16 bits starting at bit 0.
 *  These fields are not checked for operands with too many bits.
 */
int reg_imm_code(int bc, int reg, int imm) {
    return bc | (reg<<20) | imm;
}  // int registerFor(long)

/**
 *  instructionCode() returns the machine code for the current instruction.
 */
int instructionCode() {
    char * mne;
    InstructionData iData;
    int reg;
    int imm;
    mne = getMnemonic();
    iData = getInstructionData(mne);
    if (iData.basecode == -1) {
	// Code for unknown mnemonic error.
	// ...
    }
    switch(iData.operandType) {
    case reg_mem:
	// Code for register operand and memory operand case.
	// ...
    case mem:
	// Code for memory operand case.
	// ...
    case none:
	// Code for no operand case.
	// ...
    case reg_imm:
	// Code for register operand and memory operand case.
	// This code will be more complex with error handling.
	reg = getOperand();	// get first operand from scanner
	imm = getOperand();	// get second operand from scanner
	return reg_imm_code(iData.basecode, reg, imm);
    }
}  // int instructionCode()


