module instr;
/* Reference on the DLX instruction set---mostly a subset
of MIPS with slightly different semantics---can be
found at
http://www.csee.umbc.edu/courses/undergraduate/411/spring96/dlx.html
*/
import std.string;
import std.stdio;
import cpu;
/* the full list of DLX opcodes; the only one missing is
the arithmetic opcode, which is always 0x00. */
enum Opcode {
addi = 0x08,
andi = 0x0c,
beqz = 0x04,
bnez = 0x05,
j = 0x02,
jal = 0x03,
jalr = 0x13,
jr = 0x12,
lhi = 0x0f,
lw = 0x23,
ori = 0x0d,
seqi = 0x18,
slei = 0x1c,
slli = 0x14,
slti = 0x1a,
snei = 0x19,
srai = 0x17,
srli = 0x16,
subi = 0x0a,
sw = 0x2b,
xori = 0x0e,
halt = 0x30, /* These two are not part of the instruction */
pln = 0x31, /* set but were added to make debugging easier. */
}
/* These are the arithmetic 'opcodes', which are found to
the far right in an arithmetic instruction. */
enum Func {
add = 0x20,
and = 0x24,
or = 0x25,
seq = 0x28,
sle = 0x2c,
sll = 0x04,
slt = 0x2a,
sne = 0x29,
sra = 0x07,
srl = 0x06,
sub = 0x22,
xor = 0x26,
nul = 0x00,
}
/* The masks to extract the individual portions of
the bytecode instructions. */
enum Masks {
opcode = 0b11111100000000000000000000000000,
rs = 0b00000011111000000000000000000000,
rt = 0b00000000000111110000000000000000,
rd = 0b00000000000000001111100000000000,
shamt = 0b00000000000000000000011111000000,
funct = 0b00000000000000000000000000111111,
immed = 0b00000000000000001111111111111111,
addr = 0b00000011111111111111111111111111,
}
/* A set of related functions to extract and shift
* the relevant pieces of an instruction. */
ubyte extract_opcode(uint instr) {
return (Masks.opcode & instr) >> 26;
}
ubyte extract_rs(uint instr) {
return (Masks.rs & instr) >> 21;
}
ubyte extract_rt(uint instr) {
return (Masks.rt & instr) >> 16;
}
ubyte extract_rd(uint instr) {
return (Masks.rd & instr) >> 11;
}
ubyte extract_shamt(uint instr) {
return (Masks.shamt & instr) >> 6;
}
ubyte extract_funct(uint instr) {
return (Masks.funct & instr);
}
ushort extract_immed(uint instr) {
return (Masks.immed & instr);
}
uint extract_addr(uint instr) {
return (Masks.addr & instr);
}
/* Ensure the sign still exists.*/
short extend(ushort x) {
return cast(short)x;
}
int addr_extend(uint address) {
uint na = address & 0x1fffffff;
na |= (address & 0x20000000) << 6;
return cast(int)na;
}
/* The structs representing the parts of instructions;
one for each of the three types. */
struct R_Type {
ubyte opcode, rs, rt, rd, shamt, funct;
};
struct I_Type {
ubyte opcode, rs, rt;
ushort immediate;
};
struct J_Type {
ubyte opcode;
uint address;
};
/* An Instr consists of a single flag to tell the type of the
* instruction, and a payload that is a union whose initialized
* member corresponds to the type. Each member of the payload
* is a struct mirroring the constituents of that MIPS
* instruction type. */
struct Instr {
static r_string = "R(op:0x%x, rs:0x%x, rt:0x%x, rd:0x%x, shamt:0x%x, funct:0x%x)";
static i_string = "I(op:0x%x, rs:0x%x, rt:0x%x, immediate:0x%x)";
static j_string = "J(op:0x%x, addr:0x%x)";
enum Type : short { R, I, J };
Type type;
union {
R_Type r_instr;
I_Type i_instr;
J_Type j_instr;
};
/* Unless specified otherwise, an instruction takes 2 cycles to
complete. */
uint time = 2u;
static Instr decode(uint raw_instruction) {
ubyte op = extract_opcode(raw_instruction);
Instr result;
writefln("opcode: %02x\n", op);
if (op == 0x00) {
result.type = Type.R;
with (result.r_instr) {
opcode = op;
rs = extract_rs(raw_instruction);
rt = extract_rt(raw_instruction);
rd = extract_rd(raw_instruction);
shamt = extract_shamt(raw_instruction);
funct = extract_funct(raw_instruction);
}
} else if (op == Opcode.j || op == Opcode.jal) {
result.type = Type.J;
with (result.j_instr) {
opcode = op;
address = extract_addr(raw_instruction);
}
} else {
result.type = Type.I;
with (result.i_instr) {
opcode = op;
rs = extract_rs(raw_instruction);
rt = extract_rt(raw_instruction);
immediate = extract_immed(raw_instruction);
}
}
return result;
}
/* Perform the action indicated by this instruction. Has
access to the CPU (which also contains memory to be
used. */
uint compute(CPU cpu) {
writefln("Executing %s\n", this.toString());
switch (type) {
case Type.R:
with(r_instr) {
uint vs = cpu.registers[rs];
uint vt = cpu.registers[rt];
switch (funct) {
case Func.add:
cpu.registers[rd] = vs + vt;
break;
case Func.and:
cpu.registers[rd] = vs & vt;
break;
case Func.or:
cpu.registers[rd] = vs | vt;
break;
case Func.seq:
cpu.registers[rd] = (vs == vt ? 1u : 0u);
break;
case Func.sle:
cpu.registers[rd] = (vs <= vt ? 1u : 0u);
break;
case Func.sll:
cpu.registers[rd] = vs << (vt % 8);
break;
case Func.slt:
cpu.registers[rd] = (vs < vt ? 1u : 0u);
break;
case Func.sne:
cpu.registers[rd] = (vs != vt ? 1u : 0u);
break;
case Func.sra:
cpu.registers[rd] = vs >> (vt % 8);
break;
case Func.srl:
cpu.registers[rd] = vs >> (vt % 8);
break;
case Func.sub:
cpu.registers[rd] = vs - vt;
break;
case Func.xor:
cpu.registers[rd] = rs ^ rt;
break;
default:
throw new Exception("Unknown funct: %d".format(funct));
}
return cpu.registers[rd];
}
break;
case Type.J:
switch (j_instr.opcode) {
case Opcode.j:
cpu.pc += addr_extend(j_instr.address) - 1;
break;
case Opcode.jal:
cpu.registers[31] = cpu.pc + 4;
cpu.pc += addr_extend(j_instr.address) - 1;
break;
default:
throw new Exception("Unknown opcode for J-type: %d".format(j_instr.opcode));
}
break;
case Type.I:
with (i_instr) {
uint vs = cpu.registers[rs];
uint eimm = extend(immediate);
switch (opcode) {
case Opcode.addi:
cpu.registers[rt] = vs + eimm;
break;
case Opcode.andi:
cpu.registers[rt] = vs & eimm;
break;
case Opcode.beqz:
cpu.pc += (vs == 0 ? eimm : 0);
break;
case Opcode.bnez:
cpu.pc += (vs != 0 ? eimm : 0);
break;
case Opcode.jalr:
cpu.registers[31] = cpu.pc + 4;
cpu.pc = vs - 1;
break;
case Opcode.jr:
cpu.pc = vs - 1;
break;
case Opcode.lhi:
cpu.registers[rt] = immediate << 16;
break;
case Opcode.lw:
cpu.registers[rt] = cpu.memory[vs + eimm];
break;
case Opcode.ori:
cpu.registers[rt] = vs | eimm;
break;
case Opcode.seqi:
cpu.registers[rt] = (vs == eimm ? 1 : 0);
break;
case Opcode.slei:
cpu.registers[rt] = (vs <= eimm ? 1 : 0);
break;
case Opcode.slli:
cpu.registers[rt] = vs << (immediate % 8);
break;
case Opcode.slti:
cpu.registers[rt] = (vs < eimm ? 1 : 0);
break;
case Opcode.snei:
cpu.registers[rt] = (vs != eimm ? 1 : 0);
break;
case Opcode.srai:
cpu.registers[rt] = vs >> (immediate % 8);
break;
case Opcode.srli:
cpu.registers[rt] = vs >> (immediate % 8);
break;
case Opcode.subi:
cpu.registers[rt] = vs - eimm;
break;
case Opcode.sw:
cpu.memory[vs + eimm] = cpu.registers[rt];
break;
case Opcode.xori:
cpu.registers[rt] = vs ^ immediate;
break;
case Opcode.pln:
cpu.print_line(vs);
break;
case Opcode.halt:
cpu.halt();
break;
default:
throw new Exception("Unknown opcode in %s".format(this.toString()));
}
return cpu.registers[rt];
}
break;
default:
throw new Exception("Unknown type: %d".format(type));
break;
}
return 0u;
}
string toString() {
switch(type) {
case Type.R:
with(r_instr) {
return r_string.format(opcode, rs, rt, rd, shamt, funct);
}
case Type.I:
with (i_instr) {
return i_string.format(opcode, rs, rt, immediate);
}
case Type.J:
with (j_instr) {
return j_string.format(opcode, address);
}
default:
return "UNKNOWN";
}
}
}