// (c)1998, Carl Burch. This may not be redistributed
import java.util.Hashtable;
import java.util.Vector;
import java.util.StringTokenizer;

import DLX;

class ParseProblem extends Exception {
	public String str;
	public ParseProblem(String s) { super(s); str  = s; }
}

class OpType {
	public String name;
	public int op;
	public int func;
	public int args;
	public OpType(String s, int opcode, int funccode, int argtype) {
		name = s; op = opcode; func = funccode; args = argtype;
	}
}

class DirectiveData {
	public int op = DLXProg.DIR_ALIGN;
	public int alignment = 1;
	public byte[] data = null;
}

class LineData {
	public int line_num = 0; // where the line occurs in the program
	public int byte_pos = 0; // where the line belongs in memory
	public int value = 0; // the binary code to use
	public String ref = null; // the label of a reference on the line
	public int ref_bits = 0; // number of bits of reference to include
}

public class DLXProg {

	// comments begin with this character and extend to the end of line
	public static final char COMMENT_CHAR = ';';

	//
	// argument types
	//   A = address: offs(reg)
	//   I = immediate: number
	//   L = long immediate (as in J instructions)
	//   R = special register
	//
	public static final int ARGTYPE_R = 0;
	public static final int ARGTYPE_I = 1;
	public static final int ARGTYPE_J = 2;
	public static final int ARGTYPE_A = 3;

	//
	// instruction types
	//
	//
	public static final int INSTR_RA  = 0;
	public static final int INSTR_AR  = 1;
	public static final int INSTR_RRR = 2;
	public static final int INSTR_RRI = 3;
	public static final int INSTR_RI  = 4;
	public static final int INSTR_I   = 5;
	public static final int INSTR_J   = 6;
	public static final int INSTR_RR  = 7;
	public static final int INSTR_R   = 8;

	private static final int SOP_NONE = 0;

	private static OpType[] instr_ops = {
		new OpType("J",      DLX.OP_J,     SOP_NONE, INSTR_J),
		new OpType("JAL",    DLX.OP_JAL,   SOP_NONE, INSTR_J),
		new OpType("BEQZ",   DLX.OP_BEQZ,  SOP_NONE, INSTR_RI),
		new OpType("BNEZ",   DLX.OP_BNEZ,  SOP_NONE, INSTR_RI),
		new OpType("BFPT",   DLX.OP_BFPT,  SOP_NONE, INSTR_RI),
		new OpType("BFPF",   DLX.OP_BFPF,  SOP_NONE, INSTR_RI),
		new OpType("ADDI",   DLX.OP_ADDI,  SOP_NONE, INSTR_RRI),
		new OpType("ADDUI",  DLX.OP_ADDUI, SOP_NONE, INSTR_RRI),
		new OpType("SUBI",   DLX.OP_SUBI,  SOP_NONE, INSTR_RRI),
		new OpType("SUBUI",  DLX.OP_SUBUI, SOP_NONE, INSTR_RRI),
		new OpType("ANDI",   DLX.OP_ANDI,  SOP_NONE, INSTR_RRI),
		new OpType("ORI",    DLX.OP_ORI,   SOP_NONE, INSTR_RRI),
		new OpType("XORI",   DLX.OP_XORI,  SOP_NONE, INSTR_RRI),
		new OpType("LHI",    DLX.OP_LHI,   SOP_NONE, INSTR_RI),
		new OpType("RFE",    DLX.OP_RFE,   SOP_NONE, INSTR_J), // ??
		new OpType("TRAP",   DLX.OP_TRAP,  SOP_NONE, INSTR_J),
		new OpType("JR",     DLX.OP_JR,    SOP_NONE, INSTR_R),
		new OpType("JALR",   DLX.OP_JALR,  SOP_NONE, INSTR_R),
		new OpType("SLLI",   DLX.OP_SLLI,  SOP_NONE, INSTR_RRI),
		new OpType("SRLI",   DLX.OP_SRLI,  SOP_NONE, INSTR_RRI),
		new OpType("SRAI",   DLX.OP_SRAI,  SOP_NONE, INSTR_RRI),
		new OpType("SEQI",   DLX.OP_SEQI,  SOP_NONE, INSTR_RRI),
		new OpType("SNEI",   DLX.OP_SNEI,  SOP_NONE, INSTR_RRI),
		new OpType("SLTI",   DLX.OP_SLTI,  SOP_NONE, INSTR_RRI),
		new OpType("SGTI",   DLX.OP_SGTI,  SOP_NONE, INSTR_RRI),
		new OpType("SLEI",   DLX.OP_SLEI,  SOP_NONE, INSTR_RRI),
		new OpType("SGEI",   DLX.OP_SGEI,  SOP_NONE, INSTR_RRI),
		new OpType("LB",     DLX.OP_LB,    SOP_NONE, INSTR_RA),
		new OpType("LH",     DLX.OP_LH,    SOP_NONE, INSTR_RA),
		new OpType("LW",     DLX.OP_LW,    SOP_NONE, INSTR_RA),
		new OpType("LBU",    DLX.OP_LBU,   SOP_NONE, INSTR_RA),
		new OpType("LHU",    DLX.OP_LHU,   SOP_NONE, INSTR_RA),
//		new OpType("LF",     DLX.OP_LF,    SOP_NONE, INSTR_RA),
//		new OpType("LD",     DLX.OP_LD,    SOP_NONE, INSTR_RA),
		new OpType("SB",     DLX.OP_SB,    SOP_NONE, INSTR_AR),
		new OpType("SH",     DLX.OP_SH,    SOP_NONE, INSTR_AR),
		new OpType("SW",     DLX.OP_SW,    SOP_NONE, INSTR_AR),
//		new OpType("SF",     DLX.OP_SF,    SOP_NONE, INSTR_AR),
//		new OpType("SD",     DLX.OP_SD,    SOP_NONE, INSTR_AR),
		new OpType("SLL",    DLX.OP_SPECIAL, DLX.SOP_SLL,    INSTR_RRR),
		new OpType("SRL",    DLX.OP_SPECIAL, DLX.SOP_SRL,    INSTR_RRR),
		new OpType("SRA",    DLX.OP_SPECIAL, DLX.SOP_SRA,    INSTR_RRR),
		new OpType("TRAP",   DLX.OP_SPECIAL, DLX.SOP_TRAP,   INSTR_R),
		new OpType("ADD",    DLX.OP_SPECIAL, DLX.SOP_ADD,    INSTR_RRR),
		new OpType("ADDU",   DLX.OP_SPECIAL, DLX.SOP_ADDU,   INSTR_RRR),
		new OpType("SUB",    DLX.OP_SPECIAL, DLX.SOP_SUB,    INSTR_RRR),
		new OpType("SUBU",   DLX.OP_SPECIAL, DLX.SOP_SUBU,   INSTR_RRR),
		new OpType("AND",    DLX.OP_SPECIAL, DLX.SOP_AND,    INSTR_RRR),
		new OpType("OR",     DLX.OP_SPECIAL, DLX.SOP_OR,     INSTR_RRR),
		new OpType("XOR",    DLX.OP_SPECIAL, DLX.SOP_XOR,    INSTR_RRR),
		new OpType("SEQ",    DLX.OP_SPECIAL, DLX.SOP_SEQ,    INSTR_RRR),
		new OpType("SNE",    DLX.OP_SPECIAL, DLX.SOP_SNE,    INSTR_RRR),
		new OpType("SLT",    DLX.OP_SPECIAL, DLX.SOP_SLT,    INSTR_RRR),
		new OpType("SGT",    DLX.OP_SPECIAL, DLX.SOP_SGT,    INSTR_RRR),
		new OpType("SLE",    DLX.OP_SPECIAL, DLX.SOP_SLE,    INSTR_RRR),
		new OpType("SGE",    DLX.OP_SPECIAL, DLX.SOP_SGE,    INSTR_RRR),
//		new OpType("MOVI2S", DLX.OP_SPECIAL, DLX.SOP_MOVI2S, INSTR_),
//		new OpType("MOVS2I", DLX.OP_SPECIAL, DLX.SOP_MOVS2I, INSTR_),
//		new OpType("MOVF",   DLX.OP_SPECIAL, DLX.SOP_MOVF,   INSTR_),
//		new OpType("MOVD",   DLX.OP_SPECIAL, DLX.SOP_MOVD,   INSTR_),
//		new OpType("MOVFP2I",DLX.OP_SPECIAL, DLX.SOP_MOVFP2I,INSTR_),
//		new OpType("MOVI2FP",DLX.OP_SPECIAL, DLX.SOP_MOVI2FP,INSTR_),
		new OpType("MULT",   DLX.OP_SPECIAL, DLX.SOP_MULT, INSTR_RRR),
		new OpType("DIV",    DLX.OP_SPECIAL, DLX.SOP_DIV, INSTR_RRR),
		new OpType("MOD",    DLX.OP_SPECIAL, DLX.SOP_MOD, INSTR_RRR)
	};

	// directive types
	protected static final int DIR_ALIGN = 0;
	protected static final int DIR_DATA = 1;

	protected static int skipSpaces(String s, int begin) {
		for(int i = begin; i < s.length(); i++) {
			if(!Character.isWhitespace(s.charAt(i))) return i;
		}
		return s.length();
	}
	protected static int skipDigits(String s, int begin, int base) {
		for(int i = begin; i < s.length(); i++) {
			if(Character.digit(s.charAt(i), base) == -1) return i;
		}
		return s.length();
	}
	protected static int skipToSpace(String s, int begin) {
		for(int i = begin; i < s.length(); i++) {
			if(Character.isWhitespace(s.charAt(i))) return i;
		}
		return s.length();
	}
	protected static int skipToChar(String s, int begin, char dest) {
		for(int i = begin; i < s.length(); i++) {
			if(s.charAt(i) == dest) return i;
		}
		return s.length();
	}

	protected static String intToStr(int val, int length) {
		String init = Integer.toString(val);
		if(init.length() >= length) return init;

		StringBuffer ret = new StringBuffer();
		for(int i = init.length(); i < length; i++) {
			ret.append(' ');
		}
		ret.append(init);
		return new String(ret);
	}

	private static int align(int what, int alignment) {
		int mask = (1 << alignment) - 1;
		return ((what & mask) == 0)
			? what
			: ((what & ~mask) + (1 << alignment));
	}

	public static void load(DLX mach, String text) throws ParseException {
		ParseException errs = new ParseException();
		int cur_byte = 0;
		Hashtable labels = new Hashtable();
		Vector refs = new Vector();

		// parse each line
		int text_len = text.length(); // constant length of text
		int line_num = 0; // number of line (for ParseExceptions)
		int begin_pos = 0; // position of beginning of line
		for(int pos = 0; pos <= text_len; pos++) {
			// if this character terminates a label, add the label
			if(pos < text_len && text.charAt(pos) == ':') {
				String label_name = text.substring(begin_pos, pos).trim().toUpperCase();
				labels.put(label_name, new Integer(cur_byte));
				begin_pos = pos + 1;
			}

			// if this terminates the line, add the line
			if(pos == text_len || text.charAt(pos) == COMMENT_CHAR || text.charAt(pos) == '\n') {
				String line = text.substring(begin_pos, pos).toUpperCase().trim();
				if(line.length() == 0) continue;
				try {
					if(line.charAt(0) == '.') {
						// handle assembler directives
						DirectiveData dir = parseDirective(line);
						switch(dir.op) {
						case DIR_ALIGN:
							cur_byte = align(cur_byte, dir.alignment);
							break;
						case DIR_DATA:
							cur_byte = align(cur_byte, dir.alignment);
							mach.setMemBytes(cur_byte, dir.data);
							cur_byte += dir.data.length;
							break;
						}
					} else {
						// handle assembly code
						cur_byte = align(cur_byte, 2);
						LineData line_data = parseLine(line);
						if(line_data != null) {
							line_data.byte_pos = cur_byte;
							line_data.line_num = line_num;
							mach.setMemWord(cur_byte, line_data.value);
							cur_byte += 4;

							// remember any references
							if(line_data.ref != null) {
								refs.addElement(line_data);
							}
						}
					}
				} catch(ParseProblem e) {
					errs.add(line_num, e.getMessage());
				} catch(RunException e) {
					errs.add(line_num, e.getMessage());
					break;
				}

				// swallow any comment
				while(pos < text_len && text.charAt(pos) != '\n') ++pos;
				begin_pos = pos + 1;
				++line_num;
			}
		}

		// handle references to labels
		for(int i = 0; i < refs.size(); i++) {
			LineData data = (LineData) refs.elementAt(i);
			if(data.ref == null) continue;

			// get the value and translate
			Integer pos = (Integer) labels.get(data.ref);
			if(pos == null) {
				errs.add(data.line_num, "label " + data.ref
					+ " is not defined");
				continue;
			}
			int val = pos.intValue() - (data.byte_pos + 4); // 4 because relative to next PC

			// convert value to positive number
			if(val > (1 << (data.ref_bits - 1)) - 1) {
				errs.add(data.line_num, "label " + data.ref
					+ " is too far forward to fit in instruction");
				continue;
			}
			if(val < -(1 << (data.ref_bits - 1))) {
				errs.add(data.line_num, "label " + data.ref
					+ " is too far back to fit in instruction");
				continue;
			}
			if(val < 0) val += (1 << data.ref_bits);

			// OR in the value
			data.value |= val & ((1 << data.ref_bits) - 1);
			try {
				mach.setMemWord(data.byte_pos, data.value);
			} catch(RunException e) {
				errs.add(data.line_num, e.getMessage());
				continue;
			}
		}

		if(errs.size() > 0) {
			throw errs;
		}
	}

	public static DirectiveData parseDirective(String line)
			throws ParseProblem {
		DirectiveData ret = new DirectiveData();

		int pos = skipToSpace(line, 1);
		String op_name = line.substring(1, pos);
		if(op_name.equals("ALIGN")) {
			ret.op = DIR_ALIGN;
			try {
				ret.alignment = parseInt(line.substring(pos).trim());
			} catch(ParseProblem e) {
				throw new ParseProblem("unrecognized characters "
					+ "in alignment argument");
			}
		} else if(op_name.equals("WORD")) {
			ret.op = DIR_DATA;
			ret.alignment = 2;

			StringTokenizer args = new StringTokenizer(line.substring(pos), ",");
			ret.data = new byte[4 * args.countTokens()];
			for(int i = 0; args.hasMoreTokens(); i += 4) {
				try {
					int num = parseInt(args.nextToken().trim());
					ret.data[i + 0] = DLX.unsignedToByte((num >> 24) & 0xFF);
					ret.data[i + 1] = DLX.unsignedToByte((num >> 16) & 0xFF);
					ret.data[i + 2] = DLX.unsignedToByte((num >>  8) & 0xFF);
					ret.data[i + 3] = DLX.unsignedToByte((num      ) & 0xFF);
				} catch(ParseProblem e) {
					throw new ParseProblem("unrecognized characters "
						+ "in argument "
						+ Integer.toString(i / 4));
				}
			}
		}
		return ret;
	}

	// it is important that the line already be converted to upper-case
	public static LineData parseLine(String line) throws ParseProblem {
		LineData ret = new LineData();
		int pos = 0;

		// if line empty, return null
		pos = skipSpaces(line, pos);
		if(pos == line.length()) return null;

		// should handle label here

		// get the name of operation
		int op_begin = pos;
		pos = skipToSpace(line, pos);
		String op_name = line.substring(op_begin, pos);

		// get the type of instruction
		OpType instr = null;
		for(int i = 0; i < instr_ops.length; i++) {
			if(op_name.equalsIgnoreCase(instr_ops[i].name)) {
				instr = instr_ops[i];
				break;
			}
		}
		if(instr == null) {
			throw new ParseProblem("unrecognized operation: " + op_name);
		}
		ret.value = (instr.op << 26) | instr.func;

		// parse the arguments
		StringTokenizer args = new StringTokenizer(line.substring(pos), ",");
		int which_arg = 1;
		for(; args.hasMoreTokens(); which_arg++) {
			String arg = args.nextToken().trim();
			int arg_pos = 0;
			if(arg.length() == 0) {
				throw new ParseProblem("empty argument "
					+ Integer.toString(which_arg));
			}

			// calculate the argument type and the offset where to
			// place it
			int argt = ARGTYPE_R; // the type of argument expected
			int offs = 0; // how far to shift the argument left
			switch(which_arg) {
			case 1:
				switch(instr.args) {
				case INSTR_RA:  argt = ARGTYPE_R; offs = 16; break;
				case INSTR_AR:  argt = ARGTYPE_A; offs =  0; break;
				case INSTR_RRR: argt = ARGTYPE_R; offs = 11; break;
				case INSTR_RRI: argt = ARGTYPE_R; offs = 16; break;
				case INSTR_RI:  argt = ARGTYPE_R; offs = 16; break;
				case INSTR_I:   argt = ARGTYPE_I; offs =  0; break;
				case INSTR_J:   argt = ARGTYPE_J; offs =  0; break;
				case INSTR_RR:  argt = ARGTYPE_R; offs = 11; break;
				case INSTR_R:   argt = ARGTYPE_R; offs = 21; break;
				default:
					throw new ParseProblem("unrecognized argument "
						+ "list type " + Integer.toString(instr.args));
				}
				break;
			case 2:
				switch(instr.args) {
				case INSTR_RA:  argt = ARGTYPE_A; offs =  0; break;
				case INSTR_AR:  argt = ARGTYPE_R; offs = 16; break;
				case INSTR_RRR: argt = ARGTYPE_R; offs = 21; break;
				case INSTR_RRI: argt = ARGTYPE_R; offs = 21; break;
				case INSTR_RI:  argt = ARGTYPE_I; offs =  0; break;
				case INSTR_RR:  argt = ARGTYPE_R; offs = 11; break;
				default:
					throw new ParseProblem("too many arguments");
				}
				break;
			case 3:
				switch(instr.args) {
				case INSTR_RRR: argt = ARGTYPE_R; offs = 16; break;
				case INSTR_RRI: argt = ARGTYPE_I; offs =  0; break;
				default:
					throw new ParseProblem("too many arguments");
				}
				break;
			default:
				throw new ParseProblem("too many arguments");
			}

			// parse the argument
			int num; // argument data after parsed
			switch(argt) {
			case ARGTYPE_R:
				if(arg.charAt(0) != 'R') {
					throw new ParseProblem("argument "
						+ Integer.toString(which_arg)
						+ " must be a register argument");
				}

				try {
					num = parseInt(arg.substring(skipSpaces(arg, 1)));
				} catch(ParseProblem e) {
					throw new ParseProblem("unrecognized characters "
						+ "in argument "
						+ Integer.toString(which_arg));
				}
				if(num < 0 || num >= DLX.NUM_REGS) {
					throw new ParseProblem("the machine has only "
						+ " registers 0 to "
						+ Integer.toString(DLX.NUM_REGS - 1));
				}

				ret.value |= num << offs;
				break;
			case ARGTYPE_I:
				if(Character.isUpperCase(arg.charAt(0))) {
					ret.ref = arg;
					ret.ref_bits = 16;
				} else {
					try {
						num = parseInt(arg);
					} catch(ParseProblem e) {
						throw new ParseProblem("unrecognized characters "
							+ "in argument "
							+ Integer.toString(which_arg));
					}
					if(num > 0x3FF || num < -0x400) {
						throw new ParseProblem("number is too extreme to "
							+ "fit in instruction");
					}
					ret.value |= (num & 0xFFFF) << offs;
				}
				break;
			case ARGTYPE_J:
				if(Character.isUpperCase(arg.charAt(0))) {
					ret.ref = arg;
					ret.ref_bits = 26;
				} else {
					try {
						num = parseInt(arg);
					} catch(ParseProblem e) {
						throw new ParseProblem("unrecognized characters "
							+ "in argument "
							+ Integer.toString(which_arg));
					}
					if(num > 0x1FFFFFF || num < -0x2000000) {
						throw new ParseProblem("number is too extreme to "
							+ "fit in instruction");
					}
					ret.value |= (num & 0x3FFFFFF) << offs;
				}
				break;
			case ARGTYPE_A:
				// get the offset
				arg_pos = skipToChar(arg, 0, '(');
				if(arg_pos == arg.length()) {
					throw new ParseProblem("Register base omitted "
						+ "in address argument");
				}
				try {
					num = parseInt(arg.substring(0, arg_pos));
				} catch(ParseProblem e) {
					throw new ParseProblem("unrecognized characters "
						+ "in offset of argument "
						+ Integer.toString(which_arg));
				}
				ret.value |= (num & 0xFFFF);

				// get the register base
				arg_pos = skipSpaces(arg, arg_pos + 1);
				if(arg_pos == arg.length() || arg.charAt(arg_pos) != 'R') {
					throw new ParseProblem("register base mangled "
						+ "in address argument");
				}
				int base_begin = arg_pos + 1;
				arg_pos = skipToChar(arg, arg_pos, ')');
				if(arg_pos == arg.length()) {
					throw new ParseProblem("register base mangled "
						+ "in address argument");
				}
				try {
					num = parseInt(arg.substring(base_begin, arg_pos));
				} catch(ParseProblem e) {
					throw new ParseProblem("register base mangled "
						+ "in address argument");
				}
				if(num < 0 || num >= DLX.NUM_REGS) {
					throw new ParseProblem("the machine has only "
						+ " registers 0 to "
						+ Integer.toString(DLX.NUM_REGS - 1));
				}
				arg_pos = skipSpaces(arg, arg_pos + 1);
				if(arg_pos != arg.length()) {
					throw new ParseProblem("extra characters past end "
						+ "of address argument");
				}
				ret.value |= (num << 21);
			}
		}

		int num_expected = 0;
		switch(instr.args) {
		case INSTR_RA:  num_expected = 2; break;
		case INSTR_AR:  num_expected = 2; break;
		case INSTR_RRR: num_expected = 3; break;
		case INSTR_RRI: num_expected = 3; break;
		case INSTR_RI:  num_expected = 2; break;
		case INSTR_I:   num_expected = 1; break;
		case INSTR_J:   num_expected = 1; break;
		case INSTR_RR:  num_expected = 2; break;
		case INSTR_R:   num_expected = 1; break;
		}
		if(which_arg != num_expected + 1) {
			throw new ParseProblem("not enough arguments");
		}

		return ret;
	}

	protected static int parseInt(String s)
			throws ParseProblem {
		int pos = 0;
		boolean negate = false;

		// handle negation
		if(s.charAt(0) == '-') {
			pos += 1;
			negate = true;
			if(!Character.isDigit(s.charAt(pos))) {
				throw new ParseProblem("no digit following negation");
			}
		}

		// this is an integer literal; determine the base
		int base = 10; // 10 by default
		if(s.charAt(pos) == '#') {
			base = 10;
			pos += 1;
		} else if(s.charAt(pos) == '0'
				&& pos < s.length() - 1) {
			if(s.charAt(pos + 1) == 'X') {
				base = 16; // hex (0x7FFF) is hex
				pos += 2;
			} else if(s.charAt(pos + 1) == 'B') {
				base = 2; // binary (0b0110) is binary
				pos += 2;
			} else if(Character.isDigit(s.charAt(pos + 1))) {
				base = 8; // octal (0023 is octal)
				pos += 1;
			}
		}

		int ret = 0;
		for(; pos < s.length(); pos++) {
			int j = Character.digit(s.charAt(pos), base);
			if(j < 0) {
				throw new ParseProblem("unrecognized character in number");
			}
			ret = base * ret + j;
		}
		return negate ? -ret : ret;
	}

	public static String decode(int instr) {
		int opcode = (instr >> 26) & 0x3F;
		if(opcode == DLX.OP_SPECIAL) {
			// R-type instruction
			int func = instr & 0x7FF;
			String rs1 = "r" + Integer.toString((instr >> 21) & 0x1F);
			String rs2 = "r" + Integer.toString((instr >> 16) & 0x1F);
			String rd  = "r" + Integer.toString((instr >> 11) & 0x1F);
			switch(func) {
			case DLX.SOP_SLL: return "SLL " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_SRL: return "SRL " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_SRA: return "SRA " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_TRAP: return "TRAP " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_ADD: return "ADD " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_ADDU: return "ADDU " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_SUB: return "SUB " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_SUBU: return "SUBU " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_AND: return "AND " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_OR: return "OR " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_XOR: return "XOR " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_SEQ: return "SEQ " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_SNE: return "SNE " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_SLT: return "SLT " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_SGT: return "SGT " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_SLE: return "SLE " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_SGE: return "SGE " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_MOVI2S: return "MOVI2S " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_MOVS2I: return "MOVS2I " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_MOVF: return "MOVF " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_MOVD: return "MOVD " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_MOVFP2I: return "MOVFP2I " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_MOVI2FP: return "MOVI2FP " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_MULT: return "MULT " + rd + ", " + rs1 + ", " + rs2;
			case DLX.SOP_DIV: return "DIV " + rd + ", " + rs1 + ", " + rs2;
			}
		} else {
			int offs_val = instr & 0x3FFFFFF;
			if(offs_val > 0x1FFFFFF) offs_val -= 0x4000000;
			int immed_val = instr & 0xFFFF;
			if(immed_val > 0x7FFF) immed_val -= 0x10000;
			String offs = Integer.toString(offs_val);
			String immed = Integer.toString(immed_val);
			String rd  = "r" + Integer.toString((instr >> 16) & 0x1F);
			String rs1 = "r" + Integer.toString((instr >> 21) & 0x1F);
			switch(opcode) {
			case DLX.OP_J: return "J " + offs;
			case DLX.OP_JAL: return "JAL " + offs;
			case DLX.OP_RFE: return "RFE " + offs;
			case DLX.OP_TRAP: return "TRAP " + offs;
			case DLX.OP_JR: return "JR " + rd;
			case DLX.OP_JALR: return "JALR " + rd;
			case DLX.OP_BEQZ: return "BEQZ " + rd + ", #" + immed;
			case DLX.OP_BNEZ: return "BNEZ " + rd + ", #" + immed;
			case DLX.OP_BFPT: return "BFPT " + rd + ", #" + immed;
			case DLX.OP_BFPF: return "BFPF " + rd + ", #" + immed;
			case DLX.OP_LHI: return "LHI " + rd + ", #" + immed;
			case DLX.OP_ADDI: return "ADDI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_ADDUI: return "ADDUI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_SUBI: return "SUBI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_SUBUI: return "SUBUI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_ANDI: return "ANDI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_ORI: return "ORI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_XORI: return "XORI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_SLLI: return "SLLI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_SRLI: return "SRLI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_SRAI: return "SRAI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_SEQI: return "SEQI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_SNEI: return "SNEI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_SLTI: return "SLTI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_SGTI: return "SGTI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_SLEI: return "SLEI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_SGEI: return "SGEI " + rd + ", " + rs1 + ", #" + immed;
			case DLX.OP_LB: return "LB " + rd + ", " + offs + "(" + rs1 + ")";
			case DLX.OP_LH: return "LH " + rd + ", " + offs + "(" + rs1 + ")";
			case DLX.OP_LW: return "LW " + rd + ", " + offs + "(" + rs1 + ")";
			case DLX.OP_LBU: return "LBU " + rd + ", " + offs + "(" + rs1 + ")";
			case DLX.OP_LHU: return "LHU " + rd + ", " + offs + "(" + rs1 + ")";
			case DLX.OP_LF: return "LF " + rd + ", " + offs + "(" + rs1 + ")";
			case DLX.OP_LD: return "LD " + rd + ", " + offs + "(" + rs1 + ")";
			case DLX.OP_SB: return "SB " + offs + "(" + rs1 + "), " + rd;
			case DLX.OP_SH: return "SH " + offs + "(" + rs1 + "), " + rd;
			case DLX.OP_SW: return "SW " + offs + "(" + rs1 + "), " + rd;
			case DLX.OP_SF: return "SF " + offs + "(" + rs1 + "), " + rd;
			case DLX.OP_SD: return "SD " + offs + "(" + rs1 + "), " + rd;
			}
		}
		return "??";
	}
}
