/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.Allegrex.compiler;
import static jpcsp.Allegrex.Common._ra;
import static jpcsp.Allegrex.Common._zr;
import static jpcsp.Allegrex.Common.Instruction.FLAG_WRITES_RD;
import static jpcsp.Allegrex.Common.Instruction.FLAG_WRITES_RT;
import jpcsp.Emulator;
import jpcsp.Allegrex.Instructions;
import jpcsp.Allegrex.Common.Instruction;
import jpcsp.Allegrex.compiler.nativeCode.NativeCodeInstruction;
import jpcsp.Allegrex.compiler.nativeCode.NativeCodeSequence;
import org.apache.log4j.Logger;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* @author gid15
*
*/
public class CodeInstruction {
private static Logger log = Compiler.log;
private static final boolean interpretAllVfpuInstructions = false;
protected int address;
private int opcode;
private Instruction insn;
private boolean isBranchTarget;
private int branchingTo;
private boolean isBranching;
private Label label;
private boolean isDelaySlot;
protected CodeInstruction() {
}
public CodeInstruction(int address, int opcode, Instruction insn, boolean isBranchTarget, boolean isBranching, int branchingTo) {
this.address = address;
this.opcode = opcode;
this.insn = insn;
this.isBranchTarget = isBranchTarget;
this.isBranching = isBranching;
this.branchingTo = branchingTo;
}
public CodeInstruction(CodeInstruction codeInstruction) {
this.address = codeInstruction.address;
this.opcode = codeInstruction.opcode;
this.insn = codeInstruction.insn;
this.isBranchTarget = codeInstruction.isBranchTarget;
this.branchingTo = codeInstruction.branchingTo;
this.isBranching = codeInstruction.isBranching;
this.label = codeInstruction.label;
}
public int getAddress() {
return address;
}
public int getEndAddress() {
return address;
}
public int getLength() {
return 1;
}
public void setAddress(int address) {
this.address = address;
}
public int getOpcode() {
return opcode;
}
public void setOpcode(int opcode) {
this.opcode = opcode;
}
public Instruction getInsn() {
return insn;
}
public void setInsn(Instruction insn) {
this.insn = insn;
}
public boolean isBranchTarget() {
return isBranchTarget;
}
public void setBranchTarget(boolean isBranchTarget) {
this.isBranchTarget = isBranchTarget;
}
public boolean isBranching() {
return isBranching;
}
public void setBranching(boolean isBranching) {
this.isBranching = isBranching;
}
public int getBranchingTo() {
return branchingTo;
}
public void setBranchingTo(int branchingTo) {
this.branchingTo = branchingTo;
}
public boolean isDelaySlot() {
return isDelaySlot;
}
public void setIsDelaySlot(boolean isDelaySlot) {
this.isDelaySlot = isDelaySlot;
}
public Label getLabel(boolean isBranchTarget) {
if (label == null) {
label = new Label();
if (isBranchTarget) {
setBranchTarget(true);
}
}
return label;
}
public Label getLabel() {
return getLabel(true);
}
public boolean hasLabel() {
return label != null;
}
public void forceNewLabel() {
label = new Label();
}
protected void setLabel(Label label) {
this.label = label;
}
protected void startCompile(CompilerContext context, MethodVisitor mv) {
if (log.isDebugEnabled()) {
log.debug("CodeInstruction.compile " + toString());
}
context.setCodeInstruction(this);
context.beforeInstruction(this);
if (hasLabel()) {
mv.visitLabel(getLabel());
}
context.startInstruction(this);
}
public void compile(CompilerContext context, MethodVisitor mv) {
startCompile(context, mv);
if (isBranching()) {
compileBranch(context, mv);
} else if (insn == Instructions.JR) {
compileJr(context, mv);
} else if (insn == Instructions.JALR) {
compileJalr(context, mv);
} else if (interpretAllVfpuInstructions && insn.category().startsWith("VFPU")) {
context.visitIntepreterCall(opcode, insn);
} else {
insn.compile(context, getOpcode());
}
context.endInstruction();
}
private void compileJr(CompilerContext context, MethodVisitor mv) {
// Retrieve the call address from the Rs register before executing
// the delay slot instruction, as it might theoretically modify the
// content of the Rs register.
context.loadRs();
compileDelaySlot(context, mv);
context.visitJump();
}
private void compileJalr(CompilerContext context, MethodVisitor mv) {
// Retrieve the call address from the Rs register before executing
// the delay slot instruction, as it might theoretically modify the
// content of the Rs register.
context.loadRs();
compileDelaySlot(context, mv);
context.visitCall(getAddress() + 8, context.getRdRegisterIndex());
}
private void compileBranch(CompilerContext context, MethodVisitor mv) {
int branchingOpcode = getBranchingOpcode(context, mv);
if (branchingOpcode != Opcodes.NOP) {
CodeInstruction branchingToCodeInstruction = context.getCodeBlock().getCodeInstruction(getBranchingTo());
// Fallback when branching to the 2nd instruction of a native code sequence whose
// 1st instruction is the delay slot instruction.
// In such a case, assume a branch to the native code sequence.
if (branchingToCodeInstruction == null) {
CodeInstruction nativeCodeInstruction = context.getCodeInstruction(getBranchingTo() - 4);
if (nativeCodeInstruction != null && nativeCodeInstruction instanceof NativeCodeInstruction) {
NativeCodeSequence nativeCodeSequence = ((NativeCodeInstruction) nativeCodeInstruction).getNativeCodeSequence();
if (getDelaySlotCodeInstruction(context).getOpcode() == nativeCodeSequence.getFirstOpcode()) {
if (log.isDebugEnabled()) {
log.debug(String.format("0x%08X: branching to the 2nd instruction of a native code sequence, assuming the 1st instruction", getAddress()));
}
branchingToCodeInstruction = nativeCodeInstruction;
}
}
}
if (branchingToCodeInstruction != null) {
// Some applications do have branches to delay slot instructions
// (probably from programmers that didn't know/care about delay slots).
//
// Handle a branch to a NOP in a delay slot: just skip the NOP and assume the branch
// is to the instruction following the NOP.
// E.g.:
// 0x00000000 b 0x00000014 -> branching to a NOP in a delay slot, assume a branch to 0x00000018
// 0x00000004 nop
// ...
// 0x00000010 b 0x00000020
// 0x00000014 nop
// 0x00000018 something
//
if (branchingToCodeInstruction.getInsn() == Instructions.NOP) {
CodeInstruction beforeBranchingToCodeInstruction = context.getCodeBlock().getCodeInstruction(getBranchingTo() - 4);
if (beforeBranchingToCodeInstruction != null && beforeBranchingToCodeInstruction.hasFlags(Instruction.FLAG_HAS_DELAY_SLOT)) {
if (log.isDebugEnabled()) {
log.debug(String.format("0x%08X: branching to a NOP in a delay slot, correcting to the next instruction", getAddress()));
}
branchingToCodeInstruction = context.getCodeBlock().getCodeInstruction(getBranchingTo() + 4);
}
}
context.visitJump(branchingOpcode, branchingToCodeInstruction);
} else {
context.visitJump(branchingOpcode, getBranchingTo());
}
}
}
private CodeInstruction getAfterDelaySlotCodeInstruction(CompilerContext context) {
return context.getCodeBlock().getCodeInstruction(getAddress() + 8);
}
private CodeInstruction getDelaySlotCodeInstruction(CompilerContext context) {
return context.getCodeBlock().getCodeInstruction(getAddress() + 4);
}
private void compileDelaySlot(CompilerContext context, MethodVisitor mv) {
CodeInstruction delaySlotCodeInstruction = getDelaySlotCodeInstruction(context);
compileDelaySlot(context, mv, delaySlotCodeInstruction);
}
private void compileDelaySlot(CompilerContext context, MethodVisitor mv, CodeInstruction delaySlotCodeInstruction) {
if (delaySlotCodeInstruction == null) {
log.error(String.format("Cannot find delay slot instruction at 0x%08X", getAddress() + 4));
return;
}
if (delaySlotCodeInstruction.hasFlags(Instruction.FLAG_HAS_DELAY_SLOT)) {
// Issue a warning when compiling an instruction having a delay slot inside a delay slot.
// See http://code.google.com/p/pcsx2/source/detail?r=5541
String lineSeparator = System.getProperty("line.separator");
log.warn(String.format("Instruction in a delay slot having a delay slot:%s%s%s%s", lineSeparator, this, lineSeparator, delaySlotCodeInstruction));
}
delaySlotCodeInstruction.setIsDelaySlot(true);
Label delaySlotLabel = null;
if (delaySlotCodeInstruction.hasLabel()) {
delaySlotLabel = delaySlotCodeInstruction.getLabel();
delaySlotCodeInstruction.forceNewLabel();
}
delaySlotCodeInstruction.compile(context, mv);
if (delaySlotLabel != null) {
delaySlotCodeInstruction.setLabel(delaySlotLabel);
} else if (delaySlotCodeInstruction.hasLabel()) {
delaySlotCodeInstruction.forceNewLabel();
}
context.setCodeInstruction(this);
context.skipInstructions(1, false);
}
private int getBranchingOpcodeBranch0(CompilerContext context, MethodVisitor mv) {
compileDelaySlot(context, mv);
if (getBranchingTo() == getAddress()) {
context.visitLogInfo(mv, String.format("Pausing emulator - jump to self (death loop) at 0x%08X", getAddress()));
context.visitPauseEmuWithStatus(mv, Emulator.EMU_STATUS_JUMPSELF);
}
return Opcodes.GOTO;
}
/**
* Check if the delay slot instruction is modifying the given register.
*
* @param context the current compiler context
* @param registerIndex the register to be checked
* @return true if the delay slot instruction is modifying the register
* false otherwise.
*/
private boolean isDelaySlotWritingRegister(CompilerContext context, int registerIndex) {
CodeInstruction delaySlotCodeInstruction = getDelaySlotCodeInstruction(context);
if (delaySlotCodeInstruction == null) {
return false;
}
return delaySlotCodeInstruction.isWritingRegister(registerIndex);
}
private int getBranchingOpcodeCall0(CompilerContext context, MethodVisitor mv) {
context.prepareCall(getBranchingTo(), getAddress() + 8, _ra);
compileDelaySlot(context, mv);
context.visitCall(getBranchingTo(), getAddress() + 8, _ra, isDelaySlotWritingRegister(context, _ra), false);
return Opcodes.NOP;
}
private int getBranchingOpcodeBranch1(CompilerContext context, MethodVisitor mv, int branchingOpcode, int notBranchingOpcode) {
// Retrieve the call address from the Rs register before executing
// the delay slot instruction, as it might theoretically modify the
// content of the Rs register.
context.loadRs();
compileDelaySlot(context, mv);
return branchingOpcode;
}
private int getBranchingOpcodeBranch1L(CompilerContext context, MethodVisitor mv, int branchingOpcode, int notBranchingOpcode) {
context.loadRs();
CodeInstruction afterDelaySlotCodeInstruction = getAfterDelaySlotCodeInstruction(context);
context.visitJump(notBranchingOpcode, afterDelaySlotCodeInstruction);
compileDelaySlot(context, mv);
return Opcodes.GOTO;
}
private int getBranchingOpcodeCall1(CompilerContext context, MethodVisitor mv, int branchingOpcode, int notBranchingOpcode) {
context.prepareCall(getBranchingTo(), getAddress() + 8, _ra);
context.loadRs();
compileDelaySlot(context, mv);
CodeInstruction afterDelaySlotCodeInstruction = getAfterDelaySlotCodeInstruction(context);
context.visitJump(notBranchingOpcode, afterDelaySlotCodeInstruction);
context.visitCall(getBranchingTo(), getAddress() + 8, _ra, isDelaySlotWritingRegister(context, _ra), true);
return Opcodes.NOP;
}
private int getBranchingOpcodeCall1L(CompilerContext context, MethodVisitor mv, int branchingOpcode, int notBranchingOpcode) {
context.prepareCall(getBranchingTo(), getAddress() + 8, _ra);
context.loadRs();
CodeInstruction afterDelaySlotCodeInstruction = getAfterDelaySlotCodeInstruction(context);
context.visitJump(notBranchingOpcode, afterDelaySlotCodeInstruction);
compileDelaySlot(context, mv);
context.visitCall(getBranchingTo(), getAddress() + 8, _ra, isDelaySlotWritingRegister(context, _ra), true);
return Opcodes.NOP;
}
private int loadRegistersForBranchingOpcodeBranch2(CompilerContext context, MethodVisitor mv, int branchingOpcode) {
boolean loadRs = true;
boolean loadRt = true;
if (context.getRsRegisterIndex() == context.getRtRegisterIndex()) {
if (branchingOpcode == Opcodes.IF_ICMPEQ) {
loadRs = false;
loadRt = false;
branchingOpcode = Opcodes.GOTO;
// The ASM library has problems with small frames having no
// stack (NullPointerException). Generate a dummy stack requirement:
// ILOAD 0
// POP
context.loadImm(0);
context.getMethodVisitor().visitInsn(Opcodes.POP);
} else if (branchingOpcode == Opcodes.IF_ICMPNE) {
loadRs = false;
loadRt = false;
branchingOpcode = Opcodes.NOP;
}
} else if (context.isRsRegister0()) {
if (branchingOpcode == Opcodes.IF_ICMPEQ) {
loadRs = false;
branchingOpcode = Opcodes.IFEQ;
} else if (branchingOpcode == Opcodes.IF_ICMPNE) {
loadRs = false;
branchingOpcode = Opcodes.IFNE;
}
} else if (context.isRtRegister0()) {
if (branchingOpcode == Opcodes.IF_ICMPEQ) {
loadRt = false;
branchingOpcode = Opcodes.IFEQ;
} else if (branchingOpcode == Opcodes.IF_ICMPNE) {
loadRt = false;
branchingOpcode = Opcodes.IFNE;
}
}
if (loadRs) {
context.loadRs();
}
if (loadRt) {
context.loadRt();
}
return branchingOpcode;
}
private int getBranchingOpcodeBranch2(CompilerContext context, MethodVisitor mv, int branchingOpcode, int notBranchingOpcode) {
// Retrieve the registers for the branching opcode before executing
// the delay slot instruction, as it might theoretically modify the
// content of these registers.
branchingOpcode = loadRegistersForBranchingOpcodeBranch2(context, mv, branchingOpcode);
CodeInstruction delaySlotCodeInstruction = getDelaySlotCodeInstruction(context);
if (delaySlotCodeInstruction != null && delaySlotCodeInstruction.hasFlags(Instruction.FLAG_HAS_DELAY_SLOT)) {
// We are compiling a sequence where the delay instruction has itself a delay slot:
// beq $reg1, $reg2, label
// jr $ra
// nop
// Handle the sequence by inserting one nop between the instructions:
// bne $reg1, $reg2, label
// nop
// jr $ra
// nop
String lineSeparator = System.getProperty("line.separator");
log.warn(String.format("Instruction in a delay slot having a delay slot:%s%s%s%s", lineSeparator, this, lineSeparator, delaySlotCodeInstruction));
} else {
compileDelaySlot(context, mv, delaySlotCodeInstruction);
}
if (branchingOpcode == Opcodes.GOTO && getBranchingTo() == getAddress()) {
context.visitLogInfo(mv, String.format("Pausing emulator - branch to self (death loop) at 0x%08X", getAddress()));
context.visitPauseEmuWithStatus(mv, Emulator.EMU_STATUS_JUMPSELF);
}
return branchingOpcode;
}
private int getBranchingOpcodeBranch2L(CompilerContext context, MethodVisitor mv, int branchingOpcode, int notBranchingOpcode) {
// Retrieve the registers for the branching opcode before executing
// the delay slot instruction, as it might theoretically modify the
// content of these registers.
notBranchingOpcode = loadRegistersForBranchingOpcodeBranch2(context, mv, notBranchingOpcode);
if (notBranchingOpcode != Opcodes.NOP) {
CodeInstruction afterDelaySlotCodeInstruction = getAfterDelaySlotCodeInstruction(context);
context.visitJump(notBranchingOpcode, afterDelaySlotCodeInstruction);
}
compileDelaySlot(context, mv);
return Opcodes.GOTO;
}
private int getBranchingOpcodeBC1(CompilerContext context, MethodVisitor mv, int branchingOpcode, int notBranchingOpcode) {
context.loadFcr31c();
compileDelaySlot(context, mv);
return branchingOpcode;
}
private int getBranchingOpcodeBC1L(CompilerContext context, MethodVisitor mv, int branchingOpcode, int notBranchingOpcode) {
context.loadFcr31c();
CodeInstruction afterDelaySlotCodeInstruction = getAfterDelaySlotCodeInstruction(context);
context.visitJump(notBranchingOpcode, afterDelaySlotCodeInstruction);
compileDelaySlot(context, mv);
return Opcodes.GOTO;
}
private int getBranchingOpcodeBV(CompilerContext context, MethodVisitor mv, int branchingOpcode, int notBranchingOpcode) {
context.loadVcrCc();
CodeInstruction delaySlotCodeInstruction = getDelaySlotCodeInstruction(context);
if (delaySlotCodeInstruction != null && delaySlotCodeInstruction.insn == insn) {
// We are compiling a sequence where the delay instruction is again a BV
// instruction:
// bvt 0, label
// bvt 1, label
// bvt 2, label
// bvt 3, label
// nop
// Handle the sequence by inserting nop's between the BV instructions:
// bvt 0, label
// nop
// bvt 1, label
// nop
// bvt 2, label
// nop
// bvt 3, label
// nop
} else {
compileDelaySlot(context, mv, delaySlotCodeInstruction);
}
return branchingOpcode;
}
private int getBranchingOpcodeBVL(CompilerContext context, MethodVisitor mv, int branchingOpcode, int notBranchingOpcode) {
context.loadVcrCc();
CodeInstruction afterDelaySlotCodeInstruction = getAfterDelaySlotCodeInstruction(context);
context.visitJump(notBranchingOpcode, afterDelaySlotCodeInstruction);
compileDelaySlot(context, mv);
return Opcodes.GOTO;
}
private int getBranchingOpcode(CompilerContext context, MethodVisitor mv) {
int branchingOpcode = Opcodes.NOP;
if (insn == Instructions.BEQ) {
branchingOpcode = getBranchingOpcodeBranch2(context, mv, Opcodes.IF_ICMPEQ, Opcodes.IF_ICMPNE);
} else if (insn == Instructions.BEQL) {
branchingOpcode = getBranchingOpcodeBranch2L(context, mv, Opcodes.IF_ICMPEQ, Opcodes.IF_ICMPNE);
} else if (insn == Instructions.BNE) {
branchingOpcode = getBranchingOpcodeBranch2(context, mv, Opcodes.IF_ICMPNE, Opcodes.IF_ICMPEQ);
} else if (insn == Instructions.BNEL) {
branchingOpcode = getBranchingOpcodeBranch2L(context, mv, Opcodes.IF_ICMPNE, Opcodes.IF_ICMPEQ);
} else if (insn == Instructions.BGEZ) {
branchingOpcode = getBranchingOpcodeBranch1(context, mv, Opcodes.IFGE, Opcodes.IFLT);
} else if (insn == Instructions.BGEZL) {
branchingOpcode = getBranchingOpcodeBranch1L(context, mv, Opcodes.IFGE, Opcodes.IFLT);
} else if (insn == Instructions.BGTZ) {
branchingOpcode = getBranchingOpcodeBranch1(context, mv, Opcodes.IFGT, Opcodes.IFLE);
} else if (insn == Instructions.BGTZL) {
branchingOpcode = getBranchingOpcodeBranch1L(context, mv, Opcodes.IFGT, Opcodes.IFLE);
} else if (insn == Instructions.BLEZ) {
branchingOpcode = getBranchingOpcodeBranch1(context, mv, Opcodes.IFLE, Opcodes.IFGT);
} else if (insn == Instructions.BLEZL) {
branchingOpcode = getBranchingOpcodeBranch1L(context, mv, Opcodes.IFLE, Opcodes.IFGT);
} else if (insn == Instructions.BLTZ) {
branchingOpcode = getBranchingOpcodeBranch1(context, mv, Opcodes.IFLT, Opcodes.IFGE);
} else if (insn == Instructions.BLTZL) {
branchingOpcode = getBranchingOpcodeBranch1L(context, mv, Opcodes.IFLT, Opcodes.IFGE);
} else if (insn == Instructions.J) {
branchingOpcode = getBranchingOpcodeBranch0(context, mv);
} else if (insn == Instructions.JAL) {
branchingOpcode = getBranchingOpcodeCall0(context, mv);
} else if (insn == Instructions.BLTZAL) {
branchingOpcode = getBranchingOpcodeCall1(context, mv, Opcodes.IFLT, Opcodes.IFGE);
} else if (insn == Instructions.BLTZALL) {
branchingOpcode = getBranchingOpcodeCall1L(context, mv, Opcodes.IFLT, Opcodes.IFGE);
} else if (insn == Instructions.BGEZAL) {
branchingOpcode = getBranchingOpcodeCall1(context, mv, Opcodes.IFGE, Opcodes.IFLT);
} else if (insn == Instructions.BGEZALL) {
branchingOpcode = getBranchingOpcodeCall1L(context, mv, Opcodes.IFGE, Opcodes.IFLT);
} else if (insn == Instructions.BC1F) {
branchingOpcode = getBranchingOpcodeBC1(context, mv, Opcodes.IFEQ, Opcodes.IFNE);
} else if (insn == Instructions.BC1FL) {
branchingOpcode = getBranchingOpcodeBC1L(context, mv, Opcodes.IFEQ, Opcodes.IFNE);
} else if (insn == Instructions.BC1T) {
branchingOpcode = getBranchingOpcodeBC1(context, mv, Opcodes.IFNE, Opcodes.IFEQ);
} else if (insn == Instructions.BC1TL) {
branchingOpcode = getBranchingOpcodeBC1L(context, mv, Opcodes.IFNE, Opcodes.IFEQ);
} else if (insn == Instructions.BVF) {
branchingOpcode = getBranchingOpcodeBV(context, mv, Opcodes.IFEQ, Opcodes.IFNE);
} else if (insn == Instructions.BVT) {
branchingOpcode = getBranchingOpcodeBV(context, mv, Opcodes.IFNE, Opcodes.IFEQ);
} else if (insn == Instructions.BVFL) {
branchingOpcode = getBranchingOpcodeBVL(context, mv, Opcodes.IFEQ, Opcodes.IFNE);
} else if (insn == Instructions.BVTL) {
branchingOpcode = getBranchingOpcodeBVL(context, mv, Opcodes.IFNE, Opcodes.IFEQ);
} else {
log.error("CodeInstruction.getBranchingOpcode: unknown instruction " + insn.disasm(getAddress(), getOpcode()));
}
return branchingOpcode;
}
public boolean hasFlags(int flags) {
return getInsn().hasFlags(flags);
}
public int getSaValue() {
return (opcode >> 6) & 0x1F;
}
public int getRsRegisterIndex() {
return (opcode >> 21) & 0x1F;
}
public int getRtRegisterIndex() {
return (opcode >> 16) & 0x1F;
}
public int getRdRegisterIndex() {
return (opcode >> 11) & 0x1F;
}
public int getFdRegisterIndex() {
return (opcode >> 6) & 0x1F;
}
public int getFsRegisterIndex() {
return (opcode >> 11) & 0x1F;
}
public int getFtRegisterIndex() {
return (opcode >> 16) & 0x1F;
}
public int getCrValue() {
return (opcode >> 11) & 0x1F;
}
public int getVdRegisterIndex() {
return (opcode >> 0) & 0x7F;
}
public int getVsRegisterIndex() {
return (opcode >> 8) & 0x7F;
}
public int getVtRegisterIndex() {
return (opcode >> 16) & 0x7F;
}
public int getVsize() {
int one = (opcode >> 7) & 1;
int two = (opcode >> 15) & 1;
return 1 + one + (two << 1);
}
public int getImm14(boolean signedImm) {
int imm14 = opcode & 0xFFFC;
if (signedImm) {
imm14 = (int)(short) imm14;
}
return imm14;
}
public int getImm16(boolean signedImm) {
int imm16 = opcode & 0xFFFF;
if (signedImm) {
imm16 = (int)(short) imm16;
}
return imm16;
}
public int getImm7() {
return opcode & 0x7F;
}
public int getImm5() {
return (opcode >> 16) & 0x1F;
}
public int getImm4() {
return opcode & 0xF;
}
public int getImm3() {
return (opcode >> 16) & 0x7;
}
public boolean isWritingRegister(int registerIndex) {
// Register $zr is never written
if (registerIndex == _zr) {
return false;
}
if (hasFlags(FLAG_WRITES_RT)) {
if (getRtRegisterIndex() == registerIndex) {
return true;
}
}
if (hasFlags(FLAG_WRITES_RD)) {
if (getRdRegisterIndex() == registerIndex) {
return true;
}
}
return false;
}
public String disasm(int address, int opcode) {
if (getInsn() == null) {
return toString();
}
return getInsn().disasm(address, opcode);
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
String branchingFlag;
if (isBranching()) {
if (hasFlags(Instruction.FLAG_STARTS_NEW_BLOCK)) {
branchingFlag = "<"; // branching "out" of current block
} else if (getBranchingTo() <= getAddress()) {
branchingFlag = "^"; // branching "up"
} else {
branchingFlag = "v"; // branching "down"
}
} else {
branchingFlag = " "; // no branching
}
result.append(branchingFlag);
result.append(isBranchTarget() ? ">" : " ");
result.append(" 0x");
result.append(Integer.toHexString(getAddress()).toUpperCase());
result.append(" - ");
result.append(disasm(getAddress(), getOpcode()));
return result.toString();
}
}