229 lines
8.5 KiB
Java
229 lines
8.5 KiB
Java
package vue;
|
|
|
|
// Instruction state and decoder
|
|
public class Instruction {
|
|
|
|
// Instruction fields
|
|
public int bits; // Binary encoding
|
|
public int cond; // Condition for Bcond
|
|
public int disp; // Displacement for jumps and branches
|
|
public int format; // Binary format
|
|
public int id; // Library-specific identifier
|
|
public int imm; // Immediate operand
|
|
public int opcode; // Instruction opcode
|
|
public int reg1; // Source/right register
|
|
public int reg2; // Destination/left register
|
|
public int size; // Number of bytes in encoding
|
|
public int subopcode; // Instruction subopcode
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Constants //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Intermediate instruction IDs
|
|
private static final int BITSTRING = -2;
|
|
private static final int FLOATENDO = -3;
|
|
|
|
// Opcode lookup table
|
|
private static final byte[] LOOKUP_OPCODE = {
|
|
Vue.MOV_REG, 1, Vue.ADD_REG, 1, Vue.SUB , 1, Vue.CMP_REG, 1,
|
|
Vue.SHL_REG, 1, Vue.SHR_REG, 1, Vue.JMP , 1, Vue.SAR_REG, 1,
|
|
Vue.MUL , 1, Vue.DIV , 1, Vue.MULU , 1, Vue.DIVU , 1,
|
|
Vue.OR , 1, Vue.AND , 1, Vue.XOR , 1, Vue.NOT , 1,
|
|
Vue.MOV_IMM,-2, Vue.ADD_IMM,-2, Vue.SETF , 2, Vue.CMP_IMM,-2,
|
|
Vue.SHL_IMM, 2, Vue.SHR_IMM, 2, Vue.CLI , 2, Vue.SAR_IMM, 2,
|
|
Vue.TRAP , 2, Vue.RETI , 2, Vue.HALT , 2, Vue.ILLEGAL, 0,
|
|
Vue.LDSR , 2, Vue.STSR , 2, Vue.SEI , 2, BITSTRING , 2,
|
|
Vue.BCOND , 3, Vue.BCOND , 3, Vue.BCOND , 3, Vue.BCOND , 3,
|
|
Vue.BCOND , 3, Vue.BCOND , 3, Vue.BCOND , 3, Vue.BCOND , 3,
|
|
Vue.MOVEA ,-5, Vue.ADDI ,-5, Vue.JR , 4, Vue.JAL , 4,
|
|
Vue.ORI , 5, Vue.ANDI , 5, Vue.XORI , 5, Vue.MOVHI , 5,
|
|
Vue.LD_B , 6, Vue.LD_H , 6, Vue.ILLEGAL, 0, Vue.LD_W , 6,
|
|
Vue.ST_B , 6, Vue.ST_H , 6, Vue.ILLEGAL, 0, Vue.ST_W , 6,
|
|
Vue.IN_B , 6, Vue.IN_H , 6, Vue.CAXI , 6, Vue.IN_W , 6,
|
|
Vue.OUT_B , 6, Vue.OUT_H , 6, FLOATENDO , 7, Vue.OUT_W , 6
|
|
};
|
|
|
|
// Bit string lookup table
|
|
private static final byte[] LOOKUP_BITSTRING = {
|
|
Vue.SCH0BSU, Vue.SCH0BSD, Vue.SCH1BSU, Vue.SCH1BSD,
|
|
Vue.ILLEGAL, Vue.ILLEGAL, Vue.ILLEGAL, Vue.ILLEGAL,
|
|
Vue.ORBSU , Vue.ANDBSU , Vue.XORBSU , Vue.MOVBSU ,
|
|
Vue.ORNBSU , Vue.ANDNBSU, Vue.XORNBSU, Vue.XORNBSU
|
|
};
|
|
|
|
// Floating-point and Nintendo lookup table
|
|
private static final byte[] LOOKUP_FLOATENDO = {
|
|
Vue.CMPF_S , Vue.ILLEGAL, Vue.CVT_WS , Vue.CVT_SW ,
|
|
Vue.ADDF_S , Vue.SUBF_S , Vue.MULF_S , Vue.DIVF_S ,
|
|
Vue.XB , Vue.XH , Vue.REV , Vue.TRNC_SW,
|
|
Vue.MPYHW , Vue.ILLEGAL, Vue.ILLEGAL, Vue.ILLEGAL
|
|
};
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Static Methods //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Determine the size of an instruction given its opcode
|
|
public static int size(int opcode) {
|
|
return Math.abs(LOOKUP_OPCODE[opcode << 1 | 1]) < 4 ? 2 : 4;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Constructors //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Default constructor
|
|
public Instruction() { }
|
|
|
|
// Decoding constructor
|
|
public Instruction(int bits) {
|
|
decode(bits);
|
|
}
|
|
|
|
// Cloning constructor
|
|
Instruction(Instruction o) {
|
|
this();
|
|
bits = o.bits;
|
|
cond = o.cond;
|
|
disp = o.disp;
|
|
format = o.format;
|
|
id = o.id;
|
|
imm = o.imm;
|
|
opcode = o.opcode;
|
|
reg1 = o.reg1;
|
|
reg2 = o.reg2;
|
|
size = o.size;
|
|
subopcode = o.subopcode;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Public Methods //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Produce an access descriptor from the current instruction state
|
|
public Access access(Vue vue) {
|
|
boolean read = false;
|
|
var ret = new Access();
|
|
ret.address = vue.getRegister(reg1, false) + disp;
|
|
ret.fetch = vue.getFetch();
|
|
ret.type = Vue.S32;
|
|
|
|
// Configure descriptor by ID
|
|
switch (id) {
|
|
case Vue.CAXI : read = true; break;
|
|
case Vue.IN_B : ret.type = Vue.U8 ; read = true; break;
|
|
case Vue.IN_H : ret.type = Vue.U16; read = true; break;
|
|
case Vue.IN_W : read = true; break;
|
|
case Vue.LD_B : ret.type = Vue.S8 ; read = true; break;
|
|
case Vue.LD_H : ret.type = Vue.S16; read = true; break;
|
|
case Vue.LD_W : read = true; break;
|
|
case Vue.OUT_B: ret.type = Vue.U8 ; break;
|
|
case Vue.OUT_H: ret.type = Vue.U16; break;
|
|
case Vue.OUT_W: break;
|
|
case Vue.ST_B : ret.type = Vue.S8 ; break;
|
|
case Vue.ST_H : ret.type = Vue.S16; break;
|
|
case Vue.ST_W : break;
|
|
// TODO: Bit strings
|
|
default: return null;
|
|
}
|
|
|
|
// Read the value currently on the bus
|
|
if (read)
|
|
ret.value = vue.read(ret.address, ret.type);
|
|
|
|
// Write a register to the bus
|
|
else if (id != Vue.CAXI)
|
|
vue.getRegister(reg2, false);
|
|
|
|
// CAXI processing
|
|
else {
|
|
int value = vue.read(ret.address, Vue.S32);
|
|
int compare = vue.getRegister(reg2, false);
|
|
int exchange = vue.getRegister( 30, false);
|
|
ret.value = value == compare ? value : exchange;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Decode an instruction from a byte array
|
|
public Instruction decode(byte[] data, int offset) {
|
|
return decode(
|
|
(data[offset + 0] & 0xFF) |
|
|
(data[offset + 1] & 0xFF) << 8 |
|
|
(data[offset + 2] & 0xFF) << 16 |
|
|
(data[offset + 3] & 0xFF) << 24
|
|
);
|
|
}
|
|
|
|
// Decode an instruction from its binary encoding (swapped halfwords)
|
|
public Instruction decode(int bits) {
|
|
byte extend; // Sign-extend the immediate operand
|
|
int x; // Working variable
|
|
|
|
// Configure instance fields
|
|
this.bits = bits = bits << 16 | bits >>> 16;
|
|
opcode = bits >> 26 & 63;
|
|
x = opcode << 1;
|
|
id = LOOKUP_OPCODE[x ];
|
|
extend = LOOKUP_OPCODE[x + 1];
|
|
format = extend < 0 ? -extend : extend;
|
|
size = format < 4 ? 2 : 4;
|
|
|
|
// Decode by format
|
|
switch (format) {
|
|
case 1:
|
|
reg2 = bits >> 21 & 31;
|
|
reg1 = bits >> 16 & 31;
|
|
break;
|
|
case 2:
|
|
reg2 = bits >> 21 & 31;
|
|
imm = extend < 0 ? bits << 11 >> 27 : bits >> 16 & 31;
|
|
if (id == BITSTRING) {
|
|
id = imm >= 16 ? Vue.ILLEGAL : LOOKUP_BITSTRING[imm];
|
|
subopcode = imm;
|
|
}
|
|
if (id == Vue.SETF)
|
|
cond = imm & 15;
|
|
break;
|
|
case 3:
|
|
opcode = 0x20;
|
|
cond = bits >> 25 & 15;
|
|
disp = bits << 7 >> 23;
|
|
break;
|
|
case 4:
|
|
disp = bits << 6 >> 6;
|
|
break;
|
|
case 5:
|
|
reg2 = bits >> 21 & 31;
|
|
reg1 = bits >> 16 & 31;
|
|
imm = extend < 0 ? bits << 16 >> 16 : bits & 0xFFFF;
|
|
break;
|
|
case 6:
|
|
reg2 = bits >> 21 & 31;
|
|
reg1 = bits >> 16 & 31;
|
|
disp = bits << 16 >> 16;
|
|
break;
|
|
case 7:
|
|
reg2 = bits >> 21 & 31;
|
|
reg1 = bits >> 16 & 31;
|
|
subopcode = bits >> 10 & 63;
|
|
id = subopcode >= 16 ? Vue.ILLEGAL :
|
|
LOOKUP_FLOATENDO[subopcode];
|
|
break;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
}
|