}
+void Assembler::seleqz(Register rs, Register rt, Register rd) {
+ DCHECK(IsMipsArchVariant(kMips32r6));
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, SELEQZ_S);
+}
+
+
+void Assembler::seleqz(SecondaryField fmt, FPURegister fd, FPURegister ft,
+ FPURegister fs) {
+ DCHECK(IsMipsArchVariant(kMips32r6));
+ DCHECK((fmt == D) || (fmt == S));
+ GenInstrRegister(COP1, fmt, ft, fs, fd, SELEQZ_C);
+}
+
+
+void Assembler::selnez(Register rs, Register rt, Register rd) {
+ DCHECK(IsMipsArchVariant(kMips32r6));
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, SELNEZ_S);
+}
+
+
+void Assembler::selnez(SecondaryField fmt, FPURegister fd, FPURegister ft,
+ FPURegister fs) {
+ DCHECK(IsMipsArchVariant(kMips32r6));
+ DCHECK((fmt == D) || (fmt == S));
+ GenInstrRegister(COP1, fmt, ft, fs, fd, SELNEZ_C);
+}
+
+
// Bit twiddling.
void Assembler::clz(Register rd, Register rs) {
if (!IsMipsArchVariant(kMips32r6)) {
case MOVZ:
case MOVN:
case MOVCI:
+ case SELEQZ_S:
+ case SELNEZ_S:
return kRegisterType;
default:
return kUnsupported;
case BC1: // Branch on coprocessor condition.
case BC1EQZ:
case BC1NEZ:
+ case SELEQZ_C:
+ case SELNEZ_C:
return kImmediateType;
default:
return kRegisterType;
void Format(Instruction* instr, const char* format);
void Unknown(Instruction* instr);
+
// Each of these functions decodes one particular instruction type.
+ void DecodeTypeRegisterDRsType(Instruction* instr);
+ void DecodeTypeRegisterLRsType(Instruction* instr);
+ void DecodeTypeRegisterSPECIAL(Instruction* instr);
+ void DecodeTypeRegisterSPECIAL2(Instruction* instr);
+ void DecodeTypeRegisterSPECIAL3(Instruction* instr);
void DecodeTypeRegister(Instruction* instr);
void DecodeTypeImmediate(Instruction* instr);
void DecodeTypeJump(Instruction* instr);
}
+void Decoder::DecodeTypeRegisterDRsType(Instruction* instr) {
+ switch (instr->FunctionFieldRaw()) {
+ case ADD_D:
+ Format(instr, "add.d 'fd, 'fs, 'ft");
+ break;
+ case SUB_D:
+ Format(instr, "sub.d 'fd, 'fs, 'ft");
+ break;
+ case MUL_D:
+ Format(instr, "mul.d 'fd, 'fs, 'ft");
+ break;
+ case DIV_D:
+ Format(instr, "div.d 'fd, 'fs, 'ft");
+ break;
+ case ABS_D:
+ Format(instr, "abs.d 'fd, 'fs");
+ break;
+ case MOV_D:
+ Format(instr, "mov.d 'fd, 'fs");
+ break;
+ case NEG_D:
+ Format(instr, "neg.d 'fd, 'fs");
+ break;
+ case SQRT_D:
+ Format(instr, "sqrt.d 'fd, 'fs");
+ break;
+ case CVT_W_D:
+ Format(instr, "cvt.w.d 'fd, 'fs");
+ break;
+ case CVT_L_D:
+ Format(instr, "cvt.l.d 'fd, 'fs");
+ break;
+ case TRUNC_W_D:
+ Format(instr, "trunc.w.d 'fd, 'fs");
+ break;
+ case TRUNC_L_D:
+ Format(instr, "trunc.l.d 'fd, 'fs");
+ break;
+ case ROUND_W_D:
+ Format(instr, "round.w.d 'fd, 'fs");
+ break;
+ case FLOOR_W_D:
+ Format(instr, "floor.w.d 'fd, 'fs");
+ break;
+ case CEIL_W_D:
+ Format(instr, "ceil.w.d 'fd, 'fs");
+ break;
+ case CVT_S_D:
+ Format(instr, "cvt.s.d 'fd, 'fs");
+ break;
+ case C_F_D:
+ Format(instr, "c.f.d 'fs, 'ft, 'Cc");
+ break;
+ case C_UN_D:
+ Format(instr, "c.un.d 'fs, 'ft, 'Cc");
+ break;
+ case C_EQ_D:
+ Format(instr, "c.eq.d 'fs, 'ft, 'Cc");
+ break;
+ case C_UEQ_D:
+ Format(instr, "c.ueq.d 'fs, 'ft, 'Cc");
+ break;
+ case C_OLT_D:
+ Format(instr, "c.olt.d 'fs, 'ft, 'Cc");
+ break;
+ case C_ULT_D:
+ Format(instr, "c.ult.d 'fs, 'ft, 'Cc");
+ break;
+ case C_OLE_D:
+ Format(instr, "c.ole.d 'fs, 'ft, 'Cc");
+ break;
+ case C_ULE_D:
+ Format(instr, "c.ule.d 'fs, 'ft, 'Cc");
+ break;
+ default:
+ Format(instr, "unknown.cop1.d");
+ break;
+ }
+}
+
+
+void Decoder::DecodeTypeRegisterLRsType(Instruction* instr) {
+ switch (instr->FunctionFieldRaw()) {
+ case CVT_D_L:
+ Format(instr, "cvt.d.l 'fd, 'fs");
+ break;
+ case CVT_S_L:
+ Format(instr, "cvt.s.l 'fd, 'fs");
+ break;
+ case CMP_UN:
+ Format(instr, "cmp.un.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_EQ:
+ Format(instr, "cmp.eq.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_UEQ:
+ Format(instr, "cmp.ueq.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_LT:
+ Format(instr, "cmp.lt.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_ULT:
+ Format(instr, "cmp.ult.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_LE:
+ Format(instr, "cmp.le.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_ULE:
+ Format(instr, "cmp.ule.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_OR:
+ Format(instr, "cmp.or.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_UNE:
+ Format(instr, "cmp.une.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_NE:
+ Format(instr, "cmp.ne.d 'fd, 'fs, 'ft");
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Decoder::DecodeTypeRegisterSPECIAL(Instruction* instr) {
+ switch (instr->FunctionFieldRaw()) {
+ case JR:
+ Format(instr, "jr 'rs");
+ break;
+ case JALR:
+ Format(instr, "jalr 'rs");
+ break;
+ case SLL:
+ if (0x0 == static_cast<int>(instr->InstructionBits()))
+ Format(instr, "nop");
+ else
+ Format(instr, "sll 'rd, 'rt, 'sa");
+ break;
+ case SRL:
+ if (instr->RsValue() == 0) {
+ Format(instr, "srl 'rd, 'rt, 'sa");
+ } else {
+ if (IsMipsArchVariant(kMips32r2)) {
+ Format(instr, "rotr 'rd, 'rt, 'sa");
+ } else {
+ Unknown(instr);
+ }
+ }
+ break;
+ case SRA:
+ Format(instr, "sra 'rd, 'rt, 'sa");
+ break;
+ case SLLV:
+ Format(instr, "sllv 'rd, 'rt, 'rs");
+ break;
+ case SRLV:
+ if (instr->SaValue() == 0) {
+ Format(instr, "srlv 'rd, 'rt, 'rs");
+ } else {
+ if (IsMipsArchVariant(kMips32r2)) {
+ Format(instr, "rotrv 'rd, 'rt, 'rs");
+ } else {
+ Unknown(instr);
+ }
+ }
+ break;
+ case SRAV:
+ Format(instr, "srav 'rd, 'rt, 'rs");
+ break;
+ case MFHI:
+ if (instr->Bits(25, 16) == 0) {
+ Format(instr, "mfhi 'rd");
+ } else {
+ if ((instr->FunctionFieldRaw() == CLZ_R6) && (instr->FdValue() == 1)) {
+ Format(instr, "clz 'rd, 'rs");
+ } else if ((instr->FunctionFieldRaw() == CLO_R6) &&
+ (instr->FdValue() == 1)) {
+ Format(instr, "clo 'rd, 'rs");
+ }
+ }
+ break;
+ case MFLO:
+ Format(instr, "mflo 'rd");
+ break;
+ case MULT: // @Mips32r6 == MUL_MUH.
+ if (!IsMipsArchVariant(kMips32r6)) {
+ Format(instr, "mult 'rs, 'rt");
+ } else {
+ if (instr->SaValue() == MUL_OP) {
+ Format(instr, "mul 'rd, 'rs, 'rt");
+ } else {
+ Format(instr, "muh 'rd, 'rs, 'rt");
+ }
+ }
+ break;
+ case MULTU: // @Mips32r6 == MUL_MUH_U.
+ if (!IsMipsArchVariant(kMips32r6)) {
+ Format(instr, "multu 'rs, 'rt");
+ } else {
+ if (instr->SaValue() == MUL_OP) {
+ Format(instr, "mulu 'rd, 'rs, 'rt");
+ } else {
+ Format(instr, "muhu 'rd, 'rs, 'rt");
+ }
+ }
+ break;
+ case DIV: // @Mips32r6 == DIV_MOD.
+ if (!IsMipsArchVariant(kMips32r6)) {
+ Format(instr, "div 'rs, 'rt");
+ } else {
+ if (instr->SaValue() == DIV_OP) {
+ Format(instr, "div 'rd, 'rs, 'rt");
+ } else {
+ Format(instr, "mod 'rd, 'rs, 'rt");
+ }
+ }
+ break;
+ case DIVU: // @Mips32r6 == DIV_MOD_U.
+ if (!IsMipsArchVariant(kMips32r6)) {
+ Format(instr, "divu 'rs, 'rt");
+ } else {
+ if (instr->SaValue() == DIV_OP) {
+ Format(instr, "divu 'rd, 'rs, 'rt");
+ } else {
+ Format(instr, "modu 'rd, 'rs, 'rt");
+ }
+ }
+ break;
+ case ADD:
+ Format(instr, "add 'rd, 'rs, 'rt");
+ break;
+ case ADDU:
+ Format(instr, "addu 'rd, 'rs, 'rt");
+ break;
+ case SUB:
+ Format(instr, "sub 'rd, 'rs, 'rt");
+ break;
+ case SUBU:
+ Format(instr, "subu 'rd, 'rs, 'rt");
+ break;
+ case AND:
+ Format(instr, "and 'rd, 'rs, 'rt");
+ break;
+ case OR:
+ if (0 == instr->RsValue()) {
+ Format(instr, "mov 'rd, 'rt");
+ } else if (0 == instr->RtValue()) {
+ Format(instr, "mov 'rd, 'rs");
+ } else {
+ Format(instr, "or 'rd, 'rs, 'rt");
+ }
+ break;
+ case XOR:
+ Format(instr, "xor 'rd, 'rs, 'rt");
+ break;
+ case NOR:
+ Format(instr, "nor 'rd, 'rs, 'rt");
+ break;
+ case SLT:
+ Format(instr, "slt 'rd, 'rs, 'rt");
+ break;
+ case SLTU:
+ Format(instr, "sltu 'rd, 'rs, 'rt");
+ break;
+ case BREAK:
+ Format(instr, "break, code: 'code");
+ break;
+ case TGE:
+ Format(instr, "tge 'rs, 'rt, code: 'code");
+ break;
+ case TGEU:
+ Format(instr, "tgeu 'rs, 'rt, code: 'code");
+ break;
+ case TLT:
+ Format(instr, "tlt 'rs, 'rt, code: 'code");
+ break;
+ case TLTU:
+ Format(instr, "tltu 'rs, 'rt, code: 'code");
+ break;
+ case TEQ:
+ Format(instr, "teq 'rs, 'rt, code: 'code");
+ break;
+ case TNE:
+ Format(instr, "tne 'rs, 'rt, code: 'code");
+ break;
+ case MOVZ:
+ Format(instr, "movz 'rd, 'rs, 'rt");
+ break;
+ case MOVN:
+ Format(instr, "movn 'rd, 'rs, 'rt");
+ break;
+ case MOVCI:
+ if (instr->Bit(16)) {
+ Format(instr, "movt 'rd, 'rs, 'bc");
+ } else {
+ Format(instr, "movf 'rd, 'rs, 'bc");
+ }
+ break;
+ case SELEQZ_S:
+ Format(instr, "seleqz 'rs, 'rt, 'rd");
+ break;
+ case SELNEZ_S:
+ Format(instr, "selnez 'rs, 'rt, 'rd");
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Decoder::DecodeTypeRegisterSPECIAL2(Instruction* instr) {
+ switch (instr->FunctionFieldRaw()) {
+ case MUL:
+ Format(instr, "mul 'rd, 'rs, 'rt");
+ break;
+ case CLZ:
+ if (!IsMipsArchVariant(kMips32r6)) {
+ Format(instr, "clz 'rd, 'rs");
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Decoder::DecodeTypeRegisterSPECIAL3(Instruction* instr) {
+ switch (instr->FunctionFieldRaw()) {
+ case INS: {
+ if (IsMipsArchVariant(kMips32r2)) {
+ Format(instr, "ins 'rt, 'rs, 'sa, 'ss2");
+ } else {
+ Unknown(instr);
+ }
+ break;
+ }
+ case EXT: {
+ if (IsMipsArchVariant(kMips32r2)) {
+ Format(instr, "ext 'rt, 'rs, 'sa, 'ss1");
+ } else {
+ Unknown(instr);
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+}
+
+
void Decoder::DecodeTypeRegister(Instruction* instr) {
switch (instr->OpcodeFieldRaw()) {
case COP1: // Coprocessor instructions.
Format(instr, "mthc1 'rt, 'fs");
break;
case D:
- switch (instr->FunctionFieldRaw()) {
- case ADD_D:
- Format(instr, "add.d 'fd, 'fs, 'ft");
- break;
- case SUB_D:
- Format(instr, "sub.d 'fd, 'fs, 'ft");
- break;
- case MUL_D:
- Format(instr, "mul.d 'fd, 'fs, 'ft");
- break;
- case DIV_D:
- Format(instr, "div.d 'fd, 'fs, 'ft");
- break;
- case ABS_D:
- Format(instr, "abs.d 'fd, 'fs");
- break;
- case MOV_D:
- Format(instr, "mov.d 'fd, 'fs");
- break;
- case NEG_D:
- Format(instr, "neg.d 'fd, 'fs");
- break;
- case SQRT_D:
- Format(instr, "sqrt.d 'fd, 'fs");
- break;
- case CVT_W_D:
- Format(instr, "cvt.w.d 'fd, 'fs");
- break;
- case CVT_L_D:
- Format(instr, "cvt.l.d 'fd, 'fs");
- break;
- case TRUNC_W_D:
- Format(instr, "trunc.w.d 'fd, 'fs");
- break;
- case TRUNC_L_D:
- Format(instr, "trunc.l.d 'fd, 'fs");
- break;
- case ROUND_W_D:
- Format(instr, "round.w.d 'fd, 'fs");
- break;
- case FLOOR_W_D:
- Format(instr, "floor.w.d 'fd, 'fs");
- break;
- case CEIL_W_D:
- Format(instr, "ceil.w.d 'fd, 'fs");
- break;
- case CVT_S_D:
- Format(instr, "cvt.s.d 'fd, 'fs");
- break;
- case C_F_D:
- Format(instr, "c.f.d 'fs, 'ft, 'Cc");
- break;
- case C_UN_D:
- Format(instr, "c.un.d 'fs, 'ft, 'Cc");
- break;
- case C_EQ_D:
- Format(instr, "c.eq.d 'fs, 'ft, 'Cc");
- break;
- case C_UEQ_D:
- Format(instr, "c.ueq.d 'fs, 'ft, 'Cc");
- break;
- case C_OLT_D:
- Format(instr, "c.olt.d 'fs, 'ft, 'Cc");
- break;
- case C_ULT_D:
- Format(instr, "c.ult.d 'fs, 'ft, 'Cc");
- break;
- case C_OLE_D:
- Format(instr, "c.ole.d 'fs, 'ft, 'Cc");
- break;
- case C_ULE_D:
- Format(instr, "c.ule.d 'fs, 'ft, 'Cc");
- break;
- default:
- Format(instr, "unknown.cop1.d");
- break;
- }
+ DecodeTypeRegisterDRsType(instr);
break;
case S:
switch (instr->FunctionFieldRaw()) {
}
break;
case L:
- switch (instr->FunctionFieldRaw()) {
- case CVT_D_L:
- Format(instr, "cvt.d.l 'fd, 'fs");
- break;
- case CVT_S_L:
- Format(instr, "cvt.s.l 'fd, 'fs");
- break;
- case CMP_UN:
- Format(instr, "cmp.un.d 'fd, 'fs, 'ft");
- break;
- case CMP_EQ:
- Format(instr, "cmp.eq.d 'fd, 'fs, 'ft");
- break;
- case CMP_UEQ:
- Format(instr, "cmp.ueq.d 'fd, 'fs, 'ft");
- break;
- case CMP_LT:
- Format(instr, "cmp.lt.d 'fd, 'fs, 'ft");
- break;
- case CMP_ULT:
- Format(instr, "cmp.ult.d 'fd, 'fs, 'ft");
- break;
- case CMP_LE:
- Format(instr, "cmp.le.d 'fd, 'fs, 'ft");
- break;
- case CMP_ULE:
- Format(instr, "cmp.ule.d 'fd, 'fs, 'ft");
- break;
- case CMP_OR:
- Format(instr, "cmp.or.d 'fd, 'fs, 'ft");
- break;
- case CMP_UNE:
- Format(instr, "cmp.une.d 'fd, 'fs, 'ft");
- break;
- case CMP_NE:
- Format(instr, "cmp.ne.d 'fd, 'fs, 'ft");
- break;
- default:
- UNREACHABLE();
- }
+ DecodeTypeRegisterLRsType(instr);
break;
case PS:
UNIMPLEMENTED_MIPS();
}
break;
case SPECIAL:
- switch (instr->FunctionFieldRaw()) {
- case JR:
- Format(instr, "jr 'rs");
- break;
- case JALR:
- Format(instr, "jalr 'rs");
- break;
- case SLL:
- if ( 0x0 == static_cast<int>(instr->InstructionBits()))
- Format(instr, "nop");
- else
- Format(instr, "sll 'rd, 'rt, 'sa");
- break;
- case SRL:
- if (instr->RsValue() == 0) {
- Format(instr, "srl 'rd, 'rt, 'sa");
- } else {
- if (IsMipsArchVariant(kMips32r2)) {
- Format(instr, "rotr 'rd, 'rt, 'sa");
- } else {
- Unknown(instr);
- }
- }
- break;
- case SRA:
- Format(instr, "sra 'rd, 'rt, 'sa");
- break;
- case SLLV:
- Format(instr, "sllv 'rd, 'rt, 'rs");
- break;
- case SRLV:
- if (instr->SaValue() == 0) {
- Format(instr, "srlv 'rd, 'rt, 'rs");
- } else {
- if (IsMipsArchVariant(kMips32r2)) {
- Format(instr, "rotrv 'rd, 'rt, 'rs");
- } else {
- Unknown(instr);
- }
- }
- break;
- case SRAV:
- Format(instr, "srav 'rd, 'rt, 'rs");
- break;
- case MFHI:
- if (instr->Bits(25, 16) == 0) {
- Format(instr, "mfhi 'rd");
- } else {
- if ((instr->FunctionFieldRaw() == CLZ_R6)
- && (instr->FdValue() == 1)) {
- Format(instr, "clz 'rd, 'rs");
- } else if ((instr->FunctionFieldRaw() == CLO_R6)
- && (instr->FdValue() == 1)) {
- Format(instr, "clo 'rd, 'rs");
- }
- }
- break;
- case MFLO:
- Format(instr, "mflo 'rd");
- break;
- case MULT: // @Mips32r6 == MUL_MUH.
- if (!IsMipsArchVariant(kMips32r6)) {
- Format(instr, "mult 'rs, 'rt");
- } else {
- if (instr->SaValue() == MUL_OP) {
- Format(instr, "mul 'rd, 'rs, 'rt");
- } else {
- Format(instr, "muh 'rd, 'rs, 'rt");
- }
- }
- break;
- case MULTU: // @Mips32r6 == MUL_MUH_U.
- if (!IsMipsArchVariant(kMips32r6)) {
- Format(instr, "multu 'rs, 'rt");
- } else {
- if (instr->SaValue() == MUL_OP) {
- Format(instr, "mulu 'rd, 'rs, 'rt");
- } else {
- Format(instr, "muhu 'rd, 'rs, 'rt");
- }
- }
- break;
- case DIV: // @Mips32r6 == DIV_MOD.
- if (!IsMipsArchVariant(kMips32r6)) {
- Format(instr, "div 'rs, 'rt");
- } else {
- if (instr->SaValue() == DIV_OP) {
- Format(instr, "div 'rd, 'rs, 'rt");
- } else {
- Format(instr, "mod 'rd, 'rs, 'rt");
- }
- }
- break;
- case DIVU: // @Mips32r6 == DIV_MOD_U.
- if (!IsMipsArchVariant(kMips32r6)) {
- Format(instr, "divu 'rs, 'rt");
- } else {
- if (instr->SaValue() == DIV_OP) {
- Format(instr, "divu 'rd, 'rs, 'rt");
- } else {
- Format(instr, "modu 'rd, 'rs, 'rt");
- }
- }
- break;
- case ADD:
- Format(instr, "add 'rd, 'rs, 'rt");
- break;
- case ADDU:
- Format(instr, "addu 'rd, 'rs, 'rt");
- break;
- case SUB:
- Format(instr, "sub 'rd, 'rs, 'rt");
- break;
- case SUBU:
- Format(instr, "subu 'rd, 'rs, 'rt");
- break;
- case AND:
- Format(instr, "and 'rd, 'rs, 'rt");
- break;
- case OR:
- if (0 == instr->RsValue()) {
- Format(instr, "mov 'rd, 'rt");
- } else if (0 == instr->RtValue()) {
- Format(instr, "mov 'rd, 'rs");
- } else {
- Format(instr, "or 'rd, 'rs, 'rt");
- }
- break;
- case XOR:
- Format(instr, "xor 'rd, 'rs, 'rt");
- break;
- case NOR:
- Format(instr, "nor 'rd, 'rs, 'rt");
- break;
- case SLT:
- Format(instr, "slt 'rd, 'rs, 'rt");
- break;
- case SLTU:
- Format(instr, "sltu 'rd, 'rs, 'rt");
- break;
- case BREAK:
- Format(instr, "break, code: 'code");
- break;
- case TGE:
- Format(instr, "tge 'rs, 'rt, code: 'code");
- break;
- case TGEU:
- Format(instr, "tgeu 'rs, 'rt, code: 'code");
- break;
- case TLT:
- Format(instr, "tlt 'rs, 'rt, code: 'code");
- break;
- case TLTU:
- Format(instr, "tltu 'rs, 'rt, code: 'code");
- break;
- case TEQ:
- Format(instr, "teq 'rs, 'rt, code: 'code");
- break;
- case TNE:
- Format(instr, "tne 'rs, 'rt, code: 'code");
- break;
- case MOVZ:
- Format(instr, "movz 'rd, 'rs, 'rt");
- break;
- case MOVN:
- Format(instr, "movn 'rd, 'rs, 'rt");
- break;
- case MOVCI:
- if (instr->Bit(16)) {
- Format(instr, "movt 'rd, 'rs, 'bc");
- } else {
- Format(instr, "movf 'rd, 'rs, 'bc");
- }
- break;
- case SELEQZ_S:
- Format(instr, "seleqz 'rd, 'rs, 'rt");
- break;
- case SELNEZ_S:
- Format(instr, "selnez 'rd, 'rs, 'rt");
- break;
- default:
- UNREACHABLE();
- }
+ DecodeTypeRegisterSPECIAL(instr);
break;
case SPECIAL2:
- switch (instr->FunctionFieldRaw()) {
- case MUL:
- Format(instr, "mul 'rd, 'rs, 'rt");
- break;
- case CLZ:
- if (!IsMipsArchVariant(kMips32r6)) {
- Format(instr, "clz 'rd, 'rs");
- }
- break;
- default:
- UNREACHABLE();
- }
+ DecodeTypeRegisterSPECIAL2(instr);
break;
case SPECIAL3:
- switch (instr->FunctionFieldRaw()) {
- case INS: {
- if (IsMipsArchVariant(kMips32r2)) {
- Format(instr, "ins 'rt, 'rs, 'sa, 'ss2");
- } else {
- Unknown(instr);
- }
- break;
- }
- case EXT: {
- if (IsMipsArchVariant(kMips32r2)) {
- Format(instr, "ext 'rt, 'rs, 'sa, 'ss1");
- } else {
- Unknown(instr);
- }
- break;
- }
- default:
- UNREACHABLE();
- }
+ DecodeTypeRegisterSPECIAL3(instr);
break;
default:
UNREACHABLE();
case DIV:
case DIVU:
// div and divu never raise exceptions.
+ case SELEQZ_S:
+ case SELNEZ_S:
break;
default:
UNREACHABLE();
}
-void Simulator::DecodeTypeRegister(Instruction* instr) {
- // Instruction fields.
- const Opcode op = instr->OpcodeFieldRaw();
- const int32_t rs_reg = instr->RsValue();
- const int32_t rs = get_register(rs_reg);
- const uint32_t rs_u = static_cast<uint32_t>(rs);
- const int32_t rt_reg = instr->RtValue();
- const int32_t rt = get_register(rt_reg);
- const uint32_t rt_u = static_cast<uint32_t>(rt);
- const int32_t rd_reg = instr->RdValue();
+void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
+ const int32_t& fr_reg,
+ const int32_t& fs_reg,
+ const int32_t& ft_reg,
+ const int32_t& fd_reg) {
+ double ft, fs;
+ uint32_t cc, fcsr_cc;
+ int64_t i64;
+ fs = get_fpu_register_double(fs_reg);
+ ft = get_fpu_register_double(ft_reg);
+ int64_t ft_int = static_cast<int64_t>(ft);
+ cc = instr->FCccValue();
+ fcsr_cc = get_fcsr_condition_bit(cc);
+ switch (instr->FunctionFieldRaw()) {
+ case SELEQZ_C:
+ DCHECK(IsMipsArchVariant(kMips32r6));
+ set_fpu_register_double(fd_reg, (ft_int & 0x1) == 0 ? fs : 0.0);
+ break;
+ case SELNEZ_C:
+ DCHECK(IsMipsArchVariant(kMips32r6));
+ set_fpu_register_double(fd_reg, (ft_int & 0x1) != 0 ? fs : 0.0);
+ break;
+ case ADD_D:
+ set_fpu_register_double(fd_reg, fs + ft);
+ break;
+ case SUB_D:
+ set_fpu_register_double(fd_reg, fs - ft);
+ break;
+ case MUL_D:
+ set_fpu_register_double(fd_reg, fs * ft);
+ break;
+ case DIV_D:
+ set_fpu_register_double(fd_reg, fs / ft);
+ break;
+ case ABS_D:
+ set_fpu_register_double(fd_reg, fabs(fs));
+ break;
+ case MOV_D:
+ set_fpu_register_double(fd_reg, fs);
+ break;
+ case NEG_D:
+ set_fpu_register_double(fd_reg, -fs);
+ break;
+ case SQRT_D:
+ set_fpu_register_double(fd_reg, fast_sqrt(fs));
+ break;
+ case C_UN_D:
+ set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
+ break;
+ case C_EQ_D:
+ set_fcsr_bit(fcsr_cc, (fs == ft));
+ break;
+ case C_UEQ_D:
+ set_fcsr_bit(fcsr_cc, (fs == ft) || (std::isnan(fs) || std::isnan(ft)));
+ break;
+ case C_OLT_D:
+ set_fcsr_bit(fcsr_cc, (fs < ft));
+ break;
+ case C_ULT_D:
+ set_fcsr_bit(fcsr_cc, (fs < ft) || (std::isnan(fs) || std::isnan(ft)));
+ break;
+ case C_OLE_D:
+ set_fcsr_bit(fcsr_cc, (fs <= ft));
+ break;
+ case C_ULE_D:
+ set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
+ break;
+ case CVT_W_D: // Convert double to word.
+ // Rounding modes are not yet supported.
+ DCHECK((FCSR_ & 3) == 0);
+ // In rounding mode 0 it should behave like ROUND.
+ case ROUND_W_D: // Round double to word (round half to even).
+ {
+ double rounded = std::floor(fs + 0.5);
+ int32_t result = static_cast<int32_t>(rounded);
+ if ((result & 1) != 0 && result - fs == 0.5) {
+ // If the number is halfway between two integers,
+ // round to the even one.
+ result--;
+ }
+ set_fpu_register_word(fd_reg, result);
+ if (set_fcsr_round_error(fs, rounded)) {
+ set_fpu_register_word(fd_reg, kFPUInvalidResult);
+ }
+ } break;
+ case TRUNC_W_D: // Truncate double to word (round towards 0).
+ {
+ double rounded = trunc(fs);
+ int32_t result = static_cast<int32_t>(rounded);
+ set_fpu_register_word(fd_reg, result);
+ if (set_fcsr_round_error(fs, rounded)) {
+ set_fpu_register_word(fd_reg, kFPUInvalidResult);
+ }
+ } break;
+ case FLOOR_W_D: // Round double to word towards negative infinity.
+ {
+ double rounded = std::floor(fs);
+ int32_t result = static_cast<int32_t>(rounded);
+ set_fpu_register_word(fd_reg, result);
+ if (set_fcsr_round_error(fs, rounded)) {
+ set_fpu_register_word(fd_reg, kFPUInvalidResult);
+ }
+ } break;
+ case CEIL_W_D: // Round double to word towards positive infinity.
+ {
+ double rounded = std::ceil(fs);
+ int32_t result = static_cast<int32_t>(rounded);
+ set_fpu_register_word(fd_reg, result);
+ if (set_fcsr_round_error(fs, rounded)) {
+ set_fpu_register_word(fd_reg, kFPUInvalidResult);
+ }
+ } break;
+ case CVT_S_D: // Convert double to float (single).
+ set_fpu_register_float(fd_reg, static_cast<float>(fs));
+ break;
+ case CVT_L_D: { // Mips32r2: Truncate double to 64-bit long-word.
+ double rounded = trunc(fs);
+ i64 = static_cast<int64_t>(rounded);
+ if (IsFp64Mode()) {
+ set_fpu_register(fd_reg, i64);
+ } else {
+ set_fpu_register_word(fd_reg, i64 & 0xffffffff);
+ set_fpu_register_word(fd_reg + 1, i64 >> 32);
+ }
+ break;
+ }
+ case TRUNC_L_D: { // Mips32r2 instruction.
+ double rounded = trunc(fs);
+ i64 = static_cast<int64_t>(rounded);
+ if (IsFp64Mode()) {
+ set_fpu_register(fd_reg, i64);
+ } else {
+ set_fpu_register_word(fd_reg, i64 & 0xffffffff);
+ set_fpu_register_word(fd_reg + 1, i64 >> 32);
+ }
+ break;
+ }
+ case ROUND_L_D: { // Mips32r2 instruction.
+ double rounded = fs > 0 ? std::floor(fs + 0.5) : std::ceil(fs - 0.5);
+ i64 = static_cast<int64_t>(rounded);
+ if (IsFp64Mode()) {
+ set_fpu_register(fd_reg, i64);
+ } else {
+ set_fpu_register_word(fd_reg, i64 & 0xffffffff);
+ set_fpu_register_word(fd_reg + 1, i64 >> 32);
+ }
+ break;
+ }
+ case FLOOR_L_D: // Mips32r2 instruction.
+ i64 = static_cast<int64_t>(std::floor(fs));
+ if (IsFp64Mode()) {
+ set_fpu_register(fd_reg, i64);
+ } else {
+ set_fpu_register_word(fd_reg, i64 & 0xffffffff);
+ set_fpu_register_word(fd_reg + 1, i64 >> 32);
+ }
+ break;
+ case CEIL_L_D: // Mips32r2 instruction.
+ i64 = static_cast<int64_t>(std::ceil(fs));
+ if (IsFp64Mode()) {
+ set_fpu_register(fd_reg, i64);
+ } else {
+ set_fpu_register_word(fd_reg, i64 & 0xffffffff);
+ set_fpu_register_word(fd_reg + 1, i64 >> 32);
+ }
+ break;
+ case C_F_D:
+ UNIMPLEMENTED_MIPS();
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
- const int32_t fr_reg = instr->FrValue();
- const int32_t fs_reg = instr->FsValue();
- const int32_t ft_reg = instr->FtValue();
- const int32_t fd_reg = instr->FdValue();
- int64_t i64hilo = 0;
- uint64_t u64hilo = 0;
- // ALU output.
- // It should not be used as is. Instructions using it should always
- // initialize it first.
- int32_t alu_out = 0x12345678;
+void Simulator::DecodeTypeRegisterWRsType(Instruction* instr, int32_t& alu_out,
+ const int32_t& fd_reg,
+ const int32_t& fs_reg) {
+ switch (instr->FunctionFieldRaw()) {
+ case CVT_S_W: // Convert word to float (single).
+ alu_out = get_fpu_register_signed_word(fs_reg);
+ set_fpu_register_float(fd_reg, static_cast<float>(alu_out));
+ break;
+ case CVT_D_W: // Convert word to double.
+ alu_out = get_fpu_register_signed_word(fs_reg);
+ set_fpu_register_double(fd_reg, static_cast<double>(alu_out));
+ break;
+ default: // Mips64r6 CMP.S instructions unimplemented.
+ UNREACHABLE();
+ }
+}
- // For break and trap instructions.
- bool do_interrupt = false;
- // For jr and jalr.
- // Get current pc.
- int32_t current_pc = get_pc();
- // Next pc
- int32_t next_pc = 0;
- int32_t return_addr_reg = 31;
-
- // Set up the variables if needed before executing the instruction.
- ConfigureTypeRegister(instr,
- &alu_out,
- &i64hilo,
- &u64hilo,
- &next_pc,
- &return_addr_reg,
- &do_interrupt);
+void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
+ const int32_t& ft_reg,
+ const int32_t& fs_reg,
+ const int32_t& fd_reg) {
+ float f;
+ double ft = get_fpu_register_double(ft_reg);
+ int64_t ft_int = static_cast<int64_t>(ft);
+ switch (instr->FunctionFieldRaw()) {
+ case CVT_D_S:
+ f = get_fpu_register_float(fs_reg);
+ set_fpu_register_double(fd_reg, static_cast<double>(f));
+ break;
+ case SELEQZ_C:
+ DCHECK(IsMipsArchVariant(kMips32r6));
+ set_fpu_register_double(
+ fd_reg, (ft_int & 0x1) == 0 ? get_fpu_register_double(fs_reg) : 0.0);
+ break;
+ case SELNEZ_C:
+ DCHECK(IsMipsArchVariant(kMips32r6));
+ set_fpu_register_double(
+ fd_reg, (ft_int & 0x1) != 0 ? get_fpu_register_double(fs_reg) : 0.0);
+ break;
+ default:
+ // CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S
+ // CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented.
+ UNREACHABLE();
+ }
+}
- // ---------- Raise exceptions triggered.
- SignalExceptions();
- // ---------- Execution.
- switch (op) {
- case COP1:
- switch (instr->RsFieldRaw()) {
- case CFC1:
- set_register(rt_reg, alu_out);
- break;
- case MFC1:
- set_register(rt_reg, alu_out);
- break;
- case MFHC1:
- set_register(rt_reg, alu_out);
- break;
- case CTC1:
- // At the moment only FCSR is supported.
- DCHECK(fs_reg == kFCSRRegister);
- FCSR_ = registers_[rt_reg];
- break;
- case MTC1:
- // Hardware writes upper 32-bits to zero on mtc1.
- set_fpu_register_hi_word(fs_reg, 0);
- set_fpu_register_word(fs_reg, registers_[rt_reg]);
- break;
- case MTHC1:
- set_fpu_register_hi_word(fs_reg, registers_[rt_reg]);
- break;
- case S:
- float f;
- switch (instr->FunctionFieldRaw()) {
- case CVT_D_S:
- f = get_fpu_register_float(fs_reg);
- set_fpu_register_double(fd_reg, static_cast<double>(f));
- break;
- default:
- // CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S
- // CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented.
- UNREACHABLE();
- }
- break;
- case D:
- double ft, fs;
- uint32_t cc, fcsr_cc;
- int64_t i64;
- fs = get_fpu_register_double(fs_reg);
- ft = get_fpu_register_double(ft_reg);
- cc = instr->FCccValue();
- fcsr_cc = get_fcsr_condition_bit(cc);
- switch (instr->FunctionFieldRaw()) {
- case ADD_D:
- set_fpu_register_double(fd_reg, fs + ft);
- break;
- case SUB_D:
- set_fpu_register_double(fd_reg, fs - ft);
- break;
- case MUL_D:
- set_fpu_register_double(fd_reg, fs * ft);
- break;
- case DIV_D:
- set_fpu_register_double(fd_reg, fs / ft);
- break;
- case ABS_D:
- set_fpu_register_double(fd_reg, fabs(fs));
- break;
- case MOV_D:
- set_fpu_register_double(fd_reg, fs);
- break;
- case NEG_D:
- set_fpu_register_double(fd_reg, -fs);
- break;
- case SQRT_D:
- set_fpu_register_double(fd_reg, fast_sqrt(fs));
- break;
- case C_UN_D:
- set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
- break;
- case C_EQ_D:
- set_fcsr_bit(fcsr_cc, (fs == ft));
- break;
- case C_UEQ_D:
- set_fcsr_bit(fcsr_cc,
- (fs == ft) || (std::isnan(fs) || std::isnan(ft)));
- break;
- case C_OLT_D:
- set_fcsr_bit(fcsr_cc, (fs < ft));
- break;
- case C_ULT_D:
- set_fcsr_bit(fcsr_cc,
- (fs < ft) || (std::isnan(fs) || std::isnan(ft)));
- break;
- case C_OLE_D:
- set_fcsr_bit(fcsr_cc, (fs <= ft));
- break;
- case C_ULE_D:
- set_fcsr_bit(fcsr_cc,
- (fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
- break;
- case CVT_W_D: // Convert double to word.
- // Rounding modes are not yet supported.
- DCHECK((FCSR_ & 3) == 0);
- // In rounding mode 0 it should behave like ROUND.
- case ROUND_W_D: // Round double to word (round half to even).
- {
- double rounded = std::floor(fs + 0.5);
- int32_t result = static_cast<int32_t>(rounded);
- if ((result & 1) != 0 && result - fs == 0.5) {
- // If the number is halfway between two integers,
- // round to the even one.
- result--;
- }
- set_fpu_register_word(fd_reg, result);
- if (set_fcsr_round_error(fs, rounded)) {
- set_fpu_register_word(fd_reg, kFPUInvalidResult);
- }
- }
- break;
- case TRUNC_W_D: // Truncate double to word (round towards 0).
- {
- double rounded = trunc(fs);
- int32_t result = static_cast<int32_t>(rounded);
- set_fpu_register_word(fd_reg, result);
- if (set_fcsr_round_error(fs, rounded)) {
- set_fpu_register_word(fd_reg, kFPUInvalidResult);
- }
- }
- break;
- case FLOOR_W_D: // Round double to word towards negative infinity.
- {
- double rounded = std::floor(fs);
- int32_t result = static_cast<int32_t>(rounded);
- set_fpu_register_word(fd_reg, result);
- if (set_fcsr_round_error(fs, rounded)) {
- set_fpu_register_word(fd_reg, kFPUInvalidResult);
- }
- }
- break;
- case CEIL_W_D: // Round double to word towards positive infinity.
- {
- double rounded = std::ceil(fs);
- int32_t result = static_cast<int32_t>(rounded);
- set_fpu_register_word(fd_reg, result);
- if (set_fcsr_round_error(fs, rounded)) {
- set_fpu_register_word(fd_reg, kFPUInvalidResult);
- }
- }
- break;
- case CVT_S_D: // Convert double to float (single).
- set_fpu_register_float(fd_reg, static_cast<float>(fs));
- break;
- case CVT_L_D: { // Mips32r2: Truncate double to 64-bit long-word.
- double rounded = trunc(fs);
- i64 = static_cast<int64_t>(rounded);
- if (IsFp64Mode()) {
- set_fpu_register(fd_reg, i64);
- } else {
- set_fpu_register_word(fd_reg, i64 & 0xffffffff);
- set_fpu_register_word(fd_reg + 1, i64 >> 32);
- }
- break;
- }
- case TRUNC_L_D: { // Mips32r2 instruction.
- double rounded = trunc(fs);
- i64 = static_cast<int64_t>(rounded);
- if (IsFp64Mode()) {
- set_fpu_register(fd_reg, i64);
- } else {
- set_fpu_register_word(fd_reg, i64 & 0xffffffff);
- set_fpu_register_word(fd_reg + 1, i64 >> 32);
- }
- break;
- }
- case ROUND_L_D: { // Mips32r2 instruction.
- double rounded =
- fs > 0 ? std::floor(fs + 0.5) : std::ceil(fs - 0.5);
- i64 = static_cast<int64_t>(rounded);
- if (IsFp64Mode()) {
- set_fpu_register(fd_reg, i64);
- } else {
- set_fpu_register_word(fd_reg, i64 & 0xffffffff);
- set_fpu_register_word(fd_reg + 1, i64 >> 32);
- }
- break;
- }
- case FLOOR_L_D: // Mips32r2 instruction.
- i64 = static_cast<int64_t>(std::floor(fs));
- if (IsFp64Mode()) {
- set_fpu_register(fd_reg, i64);
- } else {
- set_fpu_register_word(fd_reg, i64 & 0xffffffff);
- set_fpu_register_word(fd_reg + 1, i64 >> 32);
- }
- break;
- case CEIL_L_D: // Mips32r2 instruction.
- i64 = static_cast<int64_t>(std::ceil(fs));
- if (IsFp64Mode()) {
- set_fpu_register(fd_reg, i64);
- } else {
- set_fpu_register_word(fd_reg, i64 & 0xffffffff);
- set_fpu_register_word(fd_reg + 1, i64 >> 32);
- }
- break;
- case C_F_D:
- UNIMPLEMENTED_MIPS();
- break;
- default:
- UNREACHABLE();
- }
- break;
- case W:
- switch (instr->FunctionFieldRaw()) {
- case CVT_S_W: // Convert word to float (single).
- alu_out = get_fpu_register_signed_word(fs_reg);
- set_fpu_register_float(fd_reg, static_cast<float>(alu_out));
- break;
- case CVT_D_W: // Convert word to double.
- alu_out = get_fpu_register_signed_word(fs_reg);
- set_fpu_register_double(fd_reg, static_cast<double>(alu_out));
- break;
- default: // Mips64r6 CMP.S instructions unimplemented.
- UNREACHABLE();
- }
- break;
- case L:
- fs = get_fpu_register_double(fs_reg);
- ft = get_fpu_register_double(ft_reg);
- switch (instr->FunctionFieldRaw()) {
- case CVT_D_L: // Mips32r2 instruction.
- // Watch the signs here, we want 2 32-bit vals
- // to make a sign-64.
- if (IsFp64Mode()) {
- i64 = get_fpu_register(fs_reg);
- } else {
- i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg));
- i64 |= static_cast<int64_t>(
- get_fpu_register_word(fs_reg + 1)) << 32;
- }
- set_fpu_register_double(fd_reg, static_cast<double>(i64));
- break;
- case CVT_S_L:
- UNIMPLEMENTED_MIPS();
- break;
- case CMP_AF: // Mips64r6 CMP.D instructions.
- UNIMPLEMENTED_MIPS();
- break;
- case CMP_UN:
- if (std::isnan(fs) || std::isnan(ft)) {
- set_fpu_register(fd_reg, -1);
- } else {
- set_fpu_register(fd_reg, 0);
- }
- break;
- case CMP_EQ:
- if (fs == ft) {
- set_fpu_register(fd_reg, -1);
- } else {
- set_fpu_register(fd_reg, 0);
- }
- break;
- case CMP_UEQ:
- if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) {
- set_fpu_register(fd_reg, -1);
- } else {
- set_fpu_register(fd_reg, 0);
- }
- break;
- case CMP_LT:
- if (fs < ft) {
- set_fpu_register(fd_reg, -1);
- } else {
- set_fpu_register(fd_reg, 0);
- }
- break;
- case CMP_ULT:
- if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) {
- set_fpu_register(fd_reg, -1);
- } else {
- set_fpu_register(fd_reg, 0);
- }
- break;
- case CMP_LE:
- if (fs <= ft) {
- set_fpu_register(fd_reg, -1);
- } else {
- set_fpu_register(fd_reg, 0);
- }
- break;
- case CMP_ULE:
- if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) {
- set_fpu_register(fd_reg, -1);
- } else {
- set_fpu_register(fd_reg, 0);
- }
- break;
- default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED.
- UNREACHABLE();
- }
- break;
- default:
- UNREACHABLE();
+void Simulator::DecodeTypeRegisterLRsType(Instruction* instr,
+ const int32_t& ft_reg,
+ const int32_t& fs_reg,
+ const int32_t& fd_reg) {
+ double fs = get_fpu_register_double(fs_reg);
+ double ft = get_fpu_register_double(ft_reg);
+ switch (instr->FunctionFieldRaw()) {
+ case CVT_D_L: // Mips32r2 instruction.
+ // Watch the signs here, we want 2 32-bit vals
+ // to make a sign-64.
+ int64_t i64;
+ if (IsFp64Mode()) {
+ i64 = get_fpu_register(fs_reg);
+ } else {
+ i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg));
+ i64 |= static_cast<int64_t>(get_fpu_register_word(fs_reg + 1)) << 32;
}
+ set_fpu_register_double(fd_reg, static_cast<double>(i64));
break;
- case COP1X:
- switch (instr->FunctionFieldRaw()) {
- case MADD_D:
- double fr, ft, fs;
- fr = get_fpu_register_double(fr_reg);
- fs = get_fpu_register_double(fs_reg);
- ft = get_fpu_register_double(ft_reg);
- set_fpu_register_double(fd_reg, fs * ft + fr);
- break;
- default:
- UNREACHABLE();
+ case CVT_S_L:
+ UNIMPLEMENTED_MIPS();
+ break;
+ case CMP_AF: // Mips64r6 CMP.D instructions.
+ UNIMPLEMENTED_MIPS();
+ break;
+ case CMP_UN:
+ if (std::isnan(fs) || std::isnan(ft)) {
+ set_fpu_register(fd_reg, -1);
+ } else {
+ set_fpu_register(fd_reg, 0);
}
break;
- case SPECIAL:
- switch (instr->FunctionFieldRaw()) {
+ case CMP_EQ:
+ if (fs == ft) {
+ set_fpu_register(fd_reg, -1);
+ } else {
+ set_fpu_register(fd_reg, 0);
+ }
+ break;
+ case CMP_UEQ:
+ if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) {
+ set_fpu_register(fd_reg, -1);
+ } else {
+ set_fpu_register(fd_reg, 0);
+ }
+ break;
+ case CMP_LT:
+ if (fs < ft) {
+ set_fpu_register(fd_reg, -1);
+ } else {
+ set_fpu_register(fd_reg, 0);
+ }
+ break;
+ case CMP_ULT:
+ if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) {
+ set_fpu_register(fd_reg, -1);
+ } else {
+ set_fpu_register(fd_reg, 0);
+ }
+ break;
+ case CMP_LE:
+ if (fs <= ft) {
+ set_fpu_register(fd_reg, -1);
+ } else {
+ set_fpu_register(fd_reg, 0);
+ }
+ break;
+ case CMP_ULE:
+ if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) {
+ set_fpu_register(fd_reg, -1);
+ } else {
+ set_fpu_register(fd_reg, 0);
+ }
+ break;
+ default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED.
+ UNREACHABLE();
+ }
+}
+
+
+void Simulator::DecodeTypeRegisterCOP1(
+ Instruction* instr, const int32_t& rs_reg, const int32_t& rs,
+ const uint32_t& rs_u, const int32_t& rt_reg, const int32_t& rt,
+ const uint32_t& rt_u, const int32_t& rd_reg, const int32_t& fr_reg,
+ const int32_t& fs_reg, const int32_t& ft_reg, const int32_t& fd_reg,
+ int64_t& i64hilo, uint64_t& u64hilo, int32_t& alu_out, bool& do_interrupt,
+ int32_t& current_pc, int32_t& next_pc, int32_t& return_addr_reg) {
+ switch (instr->RsFieldRaw()) {
+ case CFC1:
+ set_register(rt_reg, alu_out);
+ break;
+ case MFC1:
+ set_register(rt_reg, alu_out);
+ break;
+ case MFHC1:
+ set_register(rt_reg, alu_out);
+ break;
+ case CTC1:
+ // At the moment only FCSR is supported.
+ DCHECK(fs_reg == kFCSRRegister);
+ FCSR_ = registers_[rt_reg];
+ break;
+ case MTC1:
+ // Hardware writes upper 32-bits to zero on mtc1.
+ set_fpu_register_hi_word(fs_reg, 0);
+ set_fpu_register_word(fs_reg, registers_[rt_reg]);
+ break;
+ case MTHC1:
+ set_fpu_register_hi_word(fs_reg, registers_[rt_reg]);
+ break;
+ case S: {
+ DecodeTypeRegisterSRsType(instr, ft_reg, fs_reg, fd_reg);
+ break;
+ }
+ case D:
+ DecodeTypeRegisterDRsType(instr, fr_reg, fs_reg, ft_reg, fd_reg);
+ break;
+ case W:
+ DecodeTypeRegisterWRsType(instr, alu_out, fd_reg, fs_reg);
+ break;
+ case L:
+ DecodeTypeRegisterLRsType(instr, ft_reg, fs_reg, fd_reg);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Simulator::DecodeTypeRegisterCOP1X(Instruction* instr,
+ const int32_t& fr_reg,
+ const int32_t& fs_reg,
+ const int32_t& ft_reg,
+ const int32_t& fd_reg) {
+ switch (instr->FunctionFieldRaw()) {
+ case MADD_D:
+ double fr, ft, fs;
+ fr = get_fpu_register_double(fr_reg);
+ fs = get_fpu_register_double(fs_reg);
+ ft = get_fpu_register_double(ft_reg);
+ set_fpu_register_double(fd_reg, fs * ft + fr);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Simulator::DecodeTypeRegisterSPECIAL(
+ Instruction* instr, const int32_t& rs_reg, const int32_t& rs,
+ const uint32_t& rs_u, const int32_t& rt_reg, const int32_t& rt,
+ const uint32_t& rt_u, const int32_t& rd_reg, const int32_t& fr_reg,
+ const int32_t& fs_reg, const int32_t& ft_reg, const int32_t& fd_reg,
+ int64_t& i64hilo, uint64_t& u64hilo, int32_t& alu_out, bool& do_interrupt,
+ int32_t& current_pc, int32_t& next_pc, int32_t& return_addr_reg) {
+ switch (instr->FunctionFieldRaw()) {
+ case SELEQZ_S:
+ DCHECK(IsMipsArchVariant(kMips32r6));
+ set_register(rd_reg, rt == 0 ? rs : 0);
+ break;
+ case SELNEZ_S:
+ DCHECK(IsMipsArchVariant(kMips32r6));
+ set_register(rd_reg, rt != 0 ? rs : 0);
+ break;
case JR: {
Instruction* branch_delay_instr = reinterpret_cast<Instruction*>(
current_pc+Instruction::kInstrSize);
default: // For other special opcodes we do the default operation.
set_register(rd_reg, alu_out);
}
+}
+
+
+void Simulator::DecodeTypeRegisterSPECIAL2(Instruction* instr,
+ const int32_t& rd_reg,
+ int32_t& alu_out) {
+ switch (instr->FunctionFieldRaw()) {
+ case MUL:
+ set_register(rd_reg, alu_out);
+ // HI and LO are UNPREDICTABLE after the operation.
+ set_register(LO, Unpredictable);
+ set_register(HI, Unpredictable);
+ break;
+ default: // For other special2 opcodes we do the default operation.
+ set_register(rd_reg, alu_out);
+ }
+}
+
+
+void Simulator::DecodeTypeRegisterSPECIAL3(Instruction* instr,
+ const int32_t& rt_reg,
+ int32_t& alu_out) {
+ switch (instr->FunctionFieldRaw()) {
+ case INS:
+ // Ins instr leaves result in Rt, rather than Rd.
+ set_register(rt_reg, alu_out);
+ break;
+ case EXT:
+ // Ext instr leaves result in Rt, rather than Rd.
+ set_register(rt_reg, alu_out);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Simulator::DecodeTypeRegister(Instruction* instr) {
+ // Instruction fields.
+ const Opcode op = instr->OpcodeFieldRaw();
+ const int32_t rs_reg = instr->RsValue();
+ const int32_t rs = get_register(rs_reg);
+ const uint32_t rs_u = static_cast<uint32_t>(rs);
+ const int32_t rt_reg = instr->RtValue();
+ const int32_t rt = get_register(rt_reg);
+ const uint32_t rt_u = static_cast<uint32_t>(rt);
+ const int32_t rd_reg = instr->RdValue();
+
+ const int32_t fr_reg = instr->FrValue();
+ const int32_t fs_reg = instr->FsValue();
+ const int32_t ft_reg = instr->FtValue();
+ const int32_t fd_reg = instr->FdValue();
+ int64_t i64hilo = 0;
+ uint64_t u64hilo = 0;
+
+ // ALU output.
+ // It should not be used as is. Instructions using it should always
+ // initialize it first.
+ int32_t alu_out = 0x12345678;
+
+ // For break and trap instructions.
+ bool do_interrupt = false;
+
+ // For jr and jalr.
+ // Get current pc.
+ int32_t current_pc = get_pc();
+ // Next pc
+ int32_t next_pc = 0;
+ int32_t return_addr_reg = 31;
+
+ // Set up the variables if needed before executing the instruction.
+ ConfigureTypeRegister(instr, &alu_out, &i64hilo, &u64hilo, &next_pc,
+ &return_addr_reg, &do_interrupt);
+
+ // ---------- Raise exceptions triggered.
+ SignalExceptions();
+
+ // ---------- Execution.
+ switch (op) {
+ case COP1:
+ DecodeTypeRegisterCOP1(instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u, rd_reg,
+ fr_reg, fs_reg, ft_reg, fd_reg, i64hilo, u64hilo,
+ alu_out, do_interrupt, current_pc, next_pc,
+ return_addr_reg);
+ break;
+ case COP1X:
+ DecodeTypeRegisterCOP1X(instr, fr_reg, fs_reg, ft_reg, fd_reg);
+ break;
+ case SPECIAL:
+ DecodeTypeRegisterSPECIAL(instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u,
+ rd_reg, fr_reg, fs_reg, ft_reg, fd_reg, i64hilo,
+ u64hilo, alu_out, do_interrupt, current_pc,
+ next_pc, return_addr_reg);
break;
case SPECIAL2:
- switch (instr->FunctionFieldRaw()) {
- case MUL:
- set_register(rd_reg, alu_out);
- // HI and LO are UNPREDICTABLE after the operation.
- set_register(LO, Unpredictable);
- set_register(HI, Unpredictable);
- break;
- default: // For other special2 opcodes we do the default operation.
- set_register(rd_reg, alu_out);
- }
+ DecodeTypeRegisterSPECIAL2(instr, rd_reg, alu_out);
break;
case SPECIAL3:
- switch (instr->FunctionFieldRaw()) {
- case INS:
- // Ins instr leaves result in Rt, rather than Rd.
- set_register(rt_reg, alu_out);
- break;
- case EXT:
- // Ext instr leaves result in Rt, rather than Rd.
- set_register(rt_reg, alu_out);
- break;
- default:
- UNREACHABLE();
- }
+ DecodeTypeRegisterSPECIAL3(instr, rt_reg, alu_out);
break;
// Unimplemented opcodes raised an error in the configuration step before,
// so we can use the default here to set the destination register in common
// Executing is handled based on the instruction type.
void DecodeTypeRegister(Instruction* instr);
+ // Called from DecodeTypeRegisterCOP1
+ void DecodeTypeRegisterDRsType(Instruction* instr, const int32_t& fr_reg,
+ const int32_t& fs_reg, const int32_t& ft_reg,
+ const int32_t& fd_reg);
+ void DecodeTypeRegisterWRsType(Instruction* instr, int32_t& alu_out,
+ const int32_t& fd_reg, const int32_t& fs_reg);
+ void DecodeTypeRegisterSRsType(Instruction* instr, const int32_t& ft_reg,
+ const int32_t& fs_reg, const int32_t& fd_reg);
+ void DecodeTypeRegisterLRsType(Instruction* instr, const int32_t& ft_reg,
+ const int32_t& fs_reg, const int32_t& fd_reg);
+
+ // Functions called from DeocodeTypeRegister
+ void DecodeTypeRegisterCOP1(
+ Instruction* instr, const int32_t& rs_reg, const int32_t& rs,
+ const uint32_t& rs_u, const int32_t& rt_reg, const int32_t& rt,
+ const uint32_t& rt_u, const int32_t& rd_reg, const int32_t& fr_reg,
+ const int32_t& fs_reg, const int32_t& ft_reg, const int32_t& fd_reg,
+ int64_t& i64hilo, uint64_t& u64hilo, int32_t& alu_out, bool& do_interrupt,
+ int32_t& current_pc, int32_t& next_pc, int32_t& return_addr_reg);
+
+
+ void DecodeTypeRegisterCOP1X(Instruction* instr, const int32_t& fr_reg,
+ const int32_t& fs_reg, const int32_t& ft_reg,
+ const int32_t& fd_reg);
+
+
+ void DecodeTypeRegisterSPECIAL(
+ Instruction* instr, const int32_t& rs_reg, const int32_t& rs,
+ const uint32_t& rs_u, const int32_t& rt_reg, const int32_t& rt,
+ const uint32_t& rt_u, const int32_t& rd_reg, const int32_t& fr_reg,
+ const int32_t& fs_reg, const int32_t& ft_reg, const int32_t& fd_reg,
+ int64_t& i64hilo, uint64_t& u64hilo, int32_t& alu_out, bool& do_interrupt,
+ int32_t& current_pc, int32_t& next_pc, int32_t& return_addr_reg);
+
+
+ void DecodeTypeRegisterSPECIAL2(Instruction* instr, const int32_t& rd_reg,
+ int32_t& alu_out);
+
+ void DecodeTypeRegisterSPECIAL3(Instruction* instr, const int32_t& rt_reg,
+ int32_t& alu_out);
+
// Helper function for DecodeTypeRegister.
void ConfigureTypeRegister(Instruction* instr,
int32_t* alu_out,
// FPR.
void Assembler::seleqz(SecondaryField fmt, FPURegister fd,
FPURegister ft, FPURegister fs) {
- DCHECK(kArchVariant == kMips64r6);
- DCHECK(fmt == D);
- DCHECK(fmt == S);
-
- Instr instr = COP1 | fmt << kRsShift | ft.code() << kFtShift |
- fs.code() << kFsShift | fd.code() << kFdShift | SELEQZ_C;
- emit(instr);
+ DCHECK((fmt == D) || (fmt == S));
+ GenInstrRegister(COP1, fmt, ft, fs, fd, SELEQZ_C);
}
void Assembler::selnez(SecondaryField fmt, FPURegister fd,
FPURegister ft, FPURegister fs) {
DCHECK(kArchVariant == kMips64r6);
- DCHECK(fmt == D);
- DCHECK(fmt == S);
-
- Instr instr = COP1 | fmt << kRsShift | ft.code() << kFtShift |
- fs.code() << kFsShift | fd.code() << kFdShift | SELNEZ_C;
- emit(instr);
+ DCHECK((fmt == D) || (fmt == S));
+ GenInstrRegister(COP1, fmt, ft, fs, fd, SELNEZ_C);
}
case MOVZ:
case MOVN:
case MOVCI:
+ case SELEQZ_S:
+ case SELNEZ_S:
return kRegisterType;
default:
return kUnsupported;
int DecodeBreakInstr(Instruction* instr);
// Each of these functions decodes one particular instruction type.
+ void DecodeTypeRegisterDRsType(Instruction* instr);
+ void DecodeTypeRegisterLRsType(Instruction* instr);
+ void DecodeTypeRegisterSPECIAL(Instruction* instr);
+ void DecodeTypeRegisterSPECIAL2(Instruction* instr);
+ void DecodeTypeRegisterSPECIAL3(Instruction* instr);
+ void DecodeTypeRegisterCOP1(Instruction* instr);
+ void DecodeTypeRegisterCOP1X(Instruction* instr);
int DecodeTypeRegister(Instruction* instr);
+
+ void DecodeTypeImmediateCOP1W(Instruction* instr);
+ void DecodeTypeImmediateCOP1L(Instruction* instr);
+ void DecodeTypeImmediateCOP1S(Instruction* instr);
+ void DecodeTypeImmediateCOP1D(Instruction* instr);
+ void DecodeTypeImmediateCOP1(Instruction* instr);
+ void DecodeTypeImmediateREGIMM(Instruction* instr);
void DecodeTypeImmediate(Instruction* instr);
+
void DecodeTypeJump(Instruction* instr);
const disasm::NameConverter& converter_;
}
-int Decoder::DecodeTypeRegister(Instruction* instr) {
- switch (instr->OpcodeFieldRaw()) {
- case COP1: // Coprocessor instructions.
- switch (instr->RsFieldRaw()) {
- case MFC1:
- Format(instr, "mfc1 'rt, 'fs");
- break;
- case DMFC1:
- Format(instr, "dmfc1 'rt, 'fs");
- break;
- case MFHC1:
- Format(instr, "mfhc1 'rt, 'fs");
- break;
- case MTC1:
- Format(instr, "mtc1 'rt, 'fs");
- break;
- case DMTC1:
- Format(instr, "dmtc1 'rt, 'fs");
- break;
- // These are called "fs" too, although they are not FPU registers.
- case CTC1:
- Format(instr, "ctc1 'rt, 'fs");
- break;
- case CFC1:
- Format(instr, "cfc1 'rt, 'fs");
- break;
- case MTHC1:
- Format(instr, "mthc1 'rt, 'fs");
- break;
- case D:
- switch (instr->FunctionFieldRaw()) {
- case ADD_D:
- Format(instr, "add.d 'fd, 'fs, 'ft");
- break;
- case SUB_D:
- Format(instr, "sub.d 'fd, 'fs, 'ft");
- break;
- case MUL_D:
- Format(instr, "mul.d 'fd, 'fs, 'ft");
- break;
- case DIV_D:
- Format(instr, "div.d 'fd, 'fs, 'ft");
- break;
- case ABS_D:
- Format(instr, "abs.d 'fd, 'fs");
- break;
- case MOV_D:
- Format(instr, "mov.d 'fd, 'fs");
- break;
- case NEG_D:
- Format(instr, "neg.d 'fd, 'fs");
- break;
- case SQRT_D:
- Format(instr, "sqrt.d 'fd, 'fs");
- break;
- case CVT_W_D:
- Format(instr, "cvt.w.d 'fd, 'fs");
- break;
- case CVT_L_D:
- Format(instr, "cvt.l.d 'fd, 'fs");
- break;
- case TRUNC_W_D:
- Format(instr, "trunc.w.d 'fd, 'fs");
- break;
- case TRUNC_L_D:
- Format(instr, "trunc.l.d 'fd, 'fs");
- break;
- case ROUND_W_D:
- Format(instr, "round.w.d 'fd, 'fs");
- break;
- case ROUND_L_D:
- Format(instr, "round.l.d 'fd, 'fs");
- break;
- case FLOOR_W_D:
- Format(instr, "floor.w.d 'fd, 'fs");
- break;
- case FLOOR_L_D:
- Format(instr, "floor.l.d 'fd, 'fs");
- break;
- case CEIL_W_D:
- Format(instr, "ceil.w.d 'fd, 'fs");
- break;
- case CEIL_L_D:
- Format(instr, "ceil.l.d 'fd, 'fs");
- break;
- case CVT_S_D:
- Format(instr, "cvt.s.d 'fd, 'fs");
- break;
- case C_F_D:
- Format(instr, "c.f.d 'fs, 'ft, 'Cc");
- break;
- case C_UN_D:
- Format(instr, "c.un.d 'fs, 'ft, 'Cc");
- break;
- case C_EQ_D:
- Format(instr, "c.eq.d 'fs, 'ft, 'Cc");
- break;
- case C_UEQ_D:
- Format(instr, "c.ueq.d 'fs, 'ft, 'Cc");
- break;
- case C_OLT_D:
- Format(instr, "c.olt.d 'fs, 'ft, 'Cc");
- break;
- case C_ULT_D:
- Format(instr, "c.ult.d 'fs, 'ft, 'Cc");
- break;
- case C_OLE_D:
- Format(instr, "c.ole.d 'fs, 'ft, 'Cc");
- break;
- case C_ULE_D:
- Format(instr, "c.ule.d 'fs, 'ft, 'Cc");
- break;
- default:
- Format(instr, "unknown.cop1.d");
- break;
- }
- break;
- case W:
- switch (instr->FunctionFieldRaw()) {
- case CVT_D_W: // Convert word to double.
- Format(instr, "cvt.d.w 'fd, 'fs");
- break;
- default:
- UNREACHABLE();
- }
- break;
- case L:
- switch (instr->FunctionFieldRaw()) {
- case CVT_D_L:
- Format(instr, "cvt.d.l 'fd, 'fs");
- break;
- case CVT_S_L:
- Format(instr, "cvt.s.l 'fd, 'fs");
- break;
- case CMP_UN:
- Format(instr, "cmp.un.d 'fd, 'fs, 'ft");
- break;
- case CMP_EQ:
- Format(instr, "cmp.eq.d 'fd, 'fs, 'ft");
- break;
- case CMP_UEQ:
- Format(instr, "cmp.ueq.d 'fd, 'fs, 'ft");
- break;
- case CMP_LT:
- Format(instr, "cmp.lt.d 'fd, 'fs, 'ft");
- break;
- case CMP_ULT:
- Format(instr, "cmp.ult.d 'fd, 'fs, 'ft");
- break;
- case CMP_LE:
- Format(instr, "cmp.le.d 'fd, 'fs, 'ft");
- break;
- case CMP_ULE:
- Format(instr, "cmp.ule.d 'fd, 'fs, 'ft");
- break;
- case CMP_OR:
- Format(instr, "cmp.or.d 'fd, 'fs, 'ft");
- break;
- case CMP_UNE:
- Format(instr, "cmp.une.d 'fd, 'fs, 'ft");
- break;
- case CMP_NE:
- Format(instr, "cmp.ne.d 'fd, 'fs, 'ft");
- break;
- default:
- UNREACHABLE();
- }
- break;
- default:
- UNREACHABLE();
- }
+void Decoder::DecodeTypeRegisterDRsType(Instruction* instr) {
+ switch (instr->FunctionFieldRaw()) {
+ case SELEQZ_C:
+ Format(instr, "seleqz.D 'ft, 'fs, 'fd");
break;
- case COP1X:
+ case SELNEZ_C:
+ Format(instr, "selnez.D 'ft, 'fs, 'fd");
+ break;
+ case ADD_D:
+ Format(instr, "add.d 'fd, 'fs, 'ft");
+ break;
+ case SUB_D:
+ Format(instr, "sub.d 'fd, 'fs, 'ft");
+ break;
+ case MUL_D:
+ Format(instr, "mul.d 'fd, 'fs, 'ft");
+ break;
+ case DIV_D:
+ Format(instr, "div.d 'fd, 'fs, 'ft");
+ break;
+ case ABS_D:
+ Format(instr, "abs.d 'fd, 'fs");
+ break;
+ case MOV_D:
+ Format(instr, "mov.d 'fd, 'fs");
+ break;
+ case NEG_D:
+ Format(instr, "neg.d 'fd, 'fs");
+ break;
+ case SQRT_D:
+ Format(instr, "sqrt.d 'fd, 'fs");
+ break;
+ case CVT_W_D:
+ Format(instr, "cvt.w.d 'fd, 'fs");
+ break;
+ case CVT_L_D:
+ Format(instr, "cvt.l.d 'fd, 'fs");
+ break;
+ case TRUNC_W_D:
+ Format(instr, "trunc.w.d 'fd, 'fs");
+ break;
+ case TRUNC_L_D:
+ Format(instr, "trunc.l.d 'fd, 'fs");
+ break;
+ case ROUND_W_D:
+ Format(instr, "round.w.d 'fd, 'fs");
+ break;
+ case ROUND_L_D:
+ Format(instr, "round.l.d 'fd, 'fs");
+ break;
+ case FLOOR_W_D:
+ Format(instr, "floor.w.d 'fd, 'fs");
+ break;
+ case FLOOR_L_D:
+ Format(instr, "floor.l.d 'fd, 'fs");
+ break;
+ case CEIL_W_D:
+ Format(instr, "ceil.w.d 'fd, 'fs");
+ break;
+ case CEIL_L_D:
+ Format(instr, "ceil.l.d 'fd, 'fs");
+ break;
+ case CVT_S_D:
+ Format(instr, "cvt.s.d 'fd, 'fs");
+ break;
+ case C_F_D:
+ Format(instr, "c.f.d 'fs, 'ft, 'Cc");
+ break;
+ case C_UN_D:
+ Format(instr, "c.un.d 'fs, 'ft, 'Cc");
+ break;
+ case C_EQ_D:
+ Format(instr, "c.eq.d 'fs, 'ft, 'Cc");
+ break;
+ case C_UEQ_D:
+ Format(instr, "c.ueq.d 'fs, 'ft, 'Cc");
+ break;
+ case C_OLT_D:
+ Format(instr, "c.olt.d 'fs, 'ft, 'Cc");
+ break;
+ case C_ULT_D:
+ Format(instr, "c.ult.d 'fs, 'ft, 'Cc");
+ break;
+ case C_OLE_D:
+ Format(instr, "c.ole.d 'fs, 'ft, 'Cc");
+ break;
+ case C_ULE_D:
+ Format(instr, "c.ule.d 'fs, 'ft, 'Cc");
+ break;
+ default:
+ Format(instr, "unknown.cop1.d");
+ break;
+ }
+}
+
+
+void Decoder::DecodeTypeRegisterLRsType(Instruction* instr) {
+ switch (instr->FunctionFieldRaw()) {
+ case CVT_D_L:
+ Format(instr, "cvt.d.l 'fd, 'fs");
+ break;
+ case CVT_S_L:
+ Format(instr, "cvt.s.l 'fd, 'fs");
+ break;
+ case CMP_UN:
+ Format(instr, "cmp.un.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_EQ:
+ Format(instr, "cmp.eq.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_UEQ:
+ Format(instr, "cmp.ueq.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_LT:
+ Format(instr, "cmp.lt.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_ULT:
+ Format(instr, "cmp.ult.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_LE:
+ Format(instr, "cmp.le.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_ULE:
+ Format(instr, "cmp.ule.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_OR:
+ Format(instr, "cmp.or.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_UNE:
+ Format(instr, "cmp.une.d 'fd, 'fs, 'ft");
+ break;
+ case CMP_NE:
+ Format(instr, "cmp.ne.d 'fd, 'fs, 'ft");
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Decoder::DecodeTypeRegisterCOP1(Instruction* instr) {
+ switch (instr->RsFieldRaw()) {
+ case MFC1:
+ Format(instr, "mfc1 'rt, 'fs");
+ break;
+ case DMFC1:
+ Format(instr, "dmfc1 'rt, 'fs");
+ break;
+ case MFHC1:
+ Format(instr, "mfhc1 'rt, 'fs");
+ break;
+ case MTC1:
+ Format(instr, "mtc1 'rt, 'fs");
+ break;
+ case DMTC1:
+ Format(instr, "dmtc1 'rt, 'fs");
+ break;
+ // These are called "fs" too, although they are not FPU registers.
+ case CTC1:
+ Format(instr, "ctc1 'rt, 'fs");
+ break;
+ case CFC1:
+ Format(instr, "cfc1 'rt, 'fs");
+ break;
+ case MTHC1:
+ Format(instr, "mthc1 'rt, 'fs");
+ break;
+ case D:
+ DecodeTypeRegisterDRsType(instr);
+ break;
+ case W:
switch (instr->FunctionFieldRaw()) {
- case MADD_D:
- Format(instr, "madd.d 'fd, 'fr, 'fs, 'ft");
+ case CVT_D_W: // Convert word to double.
+ Format(instr, "cvt.d.w 'fd, 'fs");
break;
default:
UNREACHABLE();
}
break;
- case SPECIAL:
- switch (instr->FunctionFieldRaw()) {
- case JR:
- Format(instr, "jr 'rs");
- break;
- case JALR:
- Format(instr, "jalr 'rs");
- break;
- case SLL:
- if (0x0 == static_cast<int>(instr->InstructionBits()))
- Format(instr, "nop");
- else
- Format(instr, "sll 'rd, 'rt, 'sa");
- break;
- case DSLL:
- Format(instr, "dsll 'rd, 'rt, 'sa");
- break;
- case D_MUL_MUH: // Equals to DMUL.
- if (kArchVariant != kMips64r6) {
- Format(instr, "dmult 'rs, 'rt");
- } else {
- if (instr->SaValue() == MUL_OP) {
- Format(instr, "dmul 'rd, 'rs, 'rt");
- } else {
- Format(instr, "dmuh 'rd, 'rs, 'rt");
- }
- }
- break;
- case DSLL32:
- Format(instr, "dsll32 'rd, 'rt, 'sa");
- break;
- case SRL:
- if (instr->RsValue() == 0) {
- Format(instr, "srl 'rd, 'rt, 'sa");
- } else {
- if (kArchVariant == kMips64r2) {
- Format(instr, "rotr 'rd, 'rt, 'sa");
- } else {
- Unknown(instr);
- }
- }
- break;
- case DSRL:
- if (instr->RsValue() == 0) {
- Format(instr, "dsrl 'rd, 'rt, 'sa");
- } else {
- if (kArchVariant == kMips64r2) {
- Format(instr, "drotr 'rd, 'rt, 'sa");
- } else {
- Unknown(instr);
- }
- }
- break;
- case DSRL32:
- Format(instr, "dsrl32 'rd, 'rt, 'sa");
- break;
- case SRA:
- Format(instr, "sra 'rd, 'rt, 'sa");
- break;
- case DSRA:
- Format(instr, "dsra 'rd, 'rt, 'sa");
- break;
- case DSRA32:
- Format(instr, "dsra32 'rd, 'rt, 'sa");
- break;
- case SLLV:
- Format(instr, "sllv 'rd, 'rt, 'rs");
- break;
- case DSLLV:
- Format(instr, "dsllv 'rd, 'rt, 'rs");
- break;
- case SRLV:
- if (instr->SaValue() == 0) {
- Format(instr, "srlv 'rd, 'rt, 'rs");
- } else {
- if (kArchVariant == kMips64r2) {
- Format(instr, "rotrv 'rd, 'rt, 'rs");
- } else {
- Unknown(instr);
- }
- }
- break;
- case DSRLV:
- if (instr->SaValue() == 0) {
- Format(instr, "dsrlv 'rd, 'rt, 'rs");
- } else {
- if (kArchVariant == kMips64r2) {
- Format(instr, "drotrv 'rd, 'rt, 'rs");
- } else {
- Unknown(instr);
- }
- }
- break;
- case SRAV:
- Format(instr, "srav 'rd, 'rt, 'rs");
- break;
- case DSRAV:
- Format(instr, "dsrav 'rd, 'rt, 'rs");
- break;
- case MFHI:
- if (instr->Bits(25, 16) == 0) {
- Format(instr, "mfhi 'rd");
- } else {
- if ((instr->FunctionFieldRaw() == CLZ_R6)
- && (instr->FdValue() == 1)) {
- Format(instr, "clz 'rd, 'rs");
- } else if ((instr->FunctionFieldRaw() == CLO_R6)
- && (instr->FdValue() == 1)) {
- Format(instr, "clo 'rd, 'rs");
- }
- }
- break;
- case MFLO:
- Format(instr, "mflo 'rd");
- break;
- case D_MUL_MUH_U: // Equals to DMULTU.
- if (kArchVariant != kMips64r6) {
- Format(instr, "dmultu 'rs, 'rt");
- } else {
- if (instr->SaValue() == MUL_OP) {
- Format(instr, "dmulu 'rd, 'rs, 'rt");
- } else {
- Format(instr, "dmuhu 'rd, 'rs, 'rt");
- }
- }
- break;
- case MULT: // @Mips64r6 == MUL_MUH.
- if (kArchVariant != kMips64r6) {
- Format(instr, "mult 'rs, 'rt");
- } else {
- if (instr->SaValue() == MUL_OP) {
- Format(instr, "mul 'rd, 'rs, 'rt");
- } else {
- Format(instr, "muh 'rd, 'rs, 'rt");
- }
- }
- break;
- case MULTU: // @Mips64r6 == MUL_MUH_U.
- if (kArchVariant != kMips64r6) {
- Format(instr, "multu 'rs, 'rt");
- } else {
- if (instr->SaValue() == MUL_OP) {
- Format(instr, "mulu 'rd, 'rs, 'rt");
- } else {
- Format(instr, "muhu 'rd, 'rs, 'rt");
- }
- }
+ case L:
+ DecodeTypeRegisterLRsType(instr);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
- break;
- case DIV: // @Mips64r6 == DIV_MOD.
- if (kArchVariant != kMips64r6) {
- Format(instr, "div 'rs, 'rt");
- } else {
- if (instr->SaValue() == DIV_OP) {
- Format(instr, "div 'rd, 'rs, 'rt");
- } else {
- Format(instr, "mod 'rd, 'rs, 'rt");
- }
- }
- break;
- case DDIV: // @Mips64r6 == D_DIV_MOD.
- if (kArchVariant != kMips64r6) {
- Format(instr, "ddiv 'rs, 'rt");
- } else {
- if (instr->SaValue() == DIV_OP) {
- Format(instr, "ddiv 'rd, 'rs, 'rt");
- } else {
- Format(instr, "dmod 'rd, 'rs, 'rt");
- }
- }
- break;
- case DIVU: // @Mips64r6 == DIV_MOD_U.
- if (kArchVariant != kMips64r6) {
- Format(instr, "divu 'rs, 'rt");
- } else {
- if (instr->SaValue() == DIV_OP) {
- Format(instr, "divu 'rd, 'rs, 'rt");
- } else {
- Format(instr, "modu 'rd, 'rs, 'rt");
- }
- }
- break;
- case DDIVU: // @Mips64r6 == D_DIV_MOD_U.
- if (kArchVariant != kMips64r6) {
- Format(instr, "ddivu 'rs, 'rt");
- } else {
- if (instr->SaValue() == DIV_OP) {
- Format(instr, "ddivu 'rd, 'rs, 'rt");
- } else {
- Format(instr, "dmodu 'rd, 'rs, 'rt");
- }
- }
- break;
- case ADD:
- Format(instr, "add 'rd, 'rs, 'rt");
- break;
- case DADD:
- Format(instr, "dadd 'rd, 'rs, 'rt");
- break;
- case ADDU:
- Format(instr, "addu 'rd, 'rs, 'rt");
- break;
- case DADDU:
- Format(instr, "daddu 'rd, 'rs, 'rt");
- break;
- case SUB:
- Format(instr, "sub 'rd, 'rs, 'rt");
- break;
- case DSUB:
- Format(instr, "dsub 'rd, 'rs, 'rt");
- break;
- case SUBU:
- Format(instr, "subu 'rd, 'rs, 'rt");
- break;
- case DSUBU:
- Format(instr, "dsubu 'rd, 'rs, 'rt");
- break;
- case AND:
- Format(instr, "and 'rd, 'rs, 'rt");
- break;
- case OR:
- if (0 == instr->RsValue()) {
- Format(instr, "mov 'rd, 'rt");
- } else if (0 == instr->RtValue()) {
- Format(instr, "mov 'rd, 'rs");
- } else {
- Format(instr, "or 'rd, 'rs, 'rt");
- }
- break;
- case XOR:
- Format(instr, "xor 'rd, 'rs, 'rt");
- break;
- case NOR:
- Format(instr, "nor 'rd, 'rs, 'rt");
- break;
- case SLT:
- Format(instr, "slt 'rd, 'rs, 'rt");
- break;
- case SLTU:
- Format(instr, "sltu 'rd, 'rs, 'rt");
- break;
- case BREAK:
- return DecodeBreakInstr(instr);
- case TGE:
- Format(instr, "tge 'rs, 'rt, code: 'code");
- break;
- case TGEU:
- Format(instr, "tgeu 'rs, 'rt, code: 'code");
- break;
- case TLT:
- Format(instr, "tlt 'rs, 'rt, code: 'code");
- break;
- case TLTU:
- Format(instr, "tltu 'rs, 'rt, code: 'code");
- break;
- case TEQ:
- Format(instr, "teq 'rs, 'rt, code: 'code");
- break;
- case TNE:
- Format(instr, "tne 'rs, 'rt, code: 'code");
- break;
- case MOVZ:
- Format(instr, "movz 'rd, 'rs, 'rt");
- break;
- case MOVN:
- Format(instr, "movn 'rd, 'rs, 'rt");
- break;
- case MOVCI:
- if (instr->Bit(16)) {
- Format(instr, "movt 'rd, 'rs, 'bc");
- } else {
- Format(instr, "movf 'rd, 'rs, 'bc");
- }
- break;
- case SELEQZ_S:
- Format(instr, "seleqz 'rd, 'rs, 'rt");
- break;
- case SELNEZ_S:
- Format(instr, "selnez 'rd, 'rs, 'rt");
- break;
- default:
- UNREACHABLE();
+
+void Decoder::DecodeTypeRegisterCOP1X(Instruction* instr) {
+ switch (instr->FunctionFieldRaw()) {
+ case MADD_D:
+ Format(instr, "madd.d 'fd, 'fr, 'fs, 'ft");
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Decoder::DecodeTypeRegisterSPECIAL(Instruction* instr) {
+ switch (instr->FunctionFieldRaw()) {
+ case JR:
+ Format(instr, "jr 'rs");
+ break;
+ case JALR:
+ Format(instr, "jalr 'rs");
+ break;
+ case SLL:
+ if (0x0 == static_cast<int>(instr->InstructionBits()))
+ Format(instr, "nop");
+ else
+ Format(instr, "sll 'rd, 'rt, 'sa");
+ break;
+ case DSLL:
+ Format(instr, "dsll 'rd, 'rt, 'sa");
+ break;
+ case D_MUL_MUH: // Equals to DMUL.
+ if (kArchVariant != kMips64r6) {
+ Format(instr, "dmult 'rs, 'rt");
+ } else {
+ if (instr->SaValue() == MUL_OP) {
+ Format(instr, "dmul 'rd, 'rs, 'rt");
+ } else {
+ Format(instr, "dmuh 'rd, 'rs, 'rt");
+ }
}
break;
- case SPECIAL2:
- switch (instr->FunctionFieldRaw()) {
- case MUL:
- Format(instr, "mul 'rd, 'rs, 'rt");
- break;
- case CLZ:
- if (kArchVariant != kMips64r6) {
- Format(instr, "clz 'rd, 'rs");
- }
- break;
- default:
- UNREACHABLE();
+ case DSLL32:
+ Format(instr, "dsll32 'rd, 'rt, 'sa");
+ break;
+ case SRL:
+ if (instr->RsValue() == 0) {
+ Format(instr, "srl 'rd, 'rt, 'sa");
+ } else {
+ if (kArchVariant == kMips64r2) {
+ Format(instr, "rotr 'rd, 'rt, 'sa");
+ } else {
+ Unknown(instr);
+ }
}
break;
- case SPECIAL3:
- switch (instr->FunctionFieldRaw()) {
- case INS: {
- Format(instr, "ins 'rt, 'rs, 'sa, 'ss2");
- break;
+ case DSRL:
+ if (instr->RsValue() == 0) {
+ Format(instr, "dsrl 'rd, 'rt, 'sa");
+ } else {
+ if (kArchVariant == kMips64r2) {
+ Format(instr, "drotr 'rd, 'rt, 'sa");
+ } else {
+ Unknown(instr);
}
- case EXT: {
- Format(instr, "ext 'rt, 'rs, 'sa, 'ss1");
- break;
+ }
+ break;
+ case DSRL32:
+ Format(instr, "dsrl32 'rd, 'rt, 'sa");
+ break;
+ case SRA:
+ Format(instr, "sra 'rd, 'rt, 'sa");
+ break;
+ case DSRA:
+ Format(instr, "dsra 'rd, 'rt, 'sa");
+ break;
+ case DSRA32:
+ Format(instr, "dsra32 'rd, 'rt, 'sa");
+ break;
+ case SLLV:
+ Format(instr, "sllv 'rd, 'rt, 'rs");
+ break;
+ case DSLLV:
+ Format(instr, "dsllv 'rd, 'rt, 'rs");
+ break;
+ case SRLV:
+ if (instr->SaValue() == 0) {
+ Format(instr, "srlv 'rd, 'rt, 'rs");
+ } else {
+ if (kArchVariant == kMips64r2) {
+ Format(instr, "rotrv 'rd, 'rt, 'rs");
+ } else {
+ Unknown(instr);
}
- case DEXT: {
- Format(instr, "dext 'rt, 'rs, 'sa, 'ss1");
- break;
+ }
+ break;
+ case DSRLV:
+ if (instr->SaValue() == 0) {
+ Format(instr, "dsrlv 'rd, 'rt, 'rs");
+ } else {
+ if (kArchVariant == kMips64r2) {
+ Format(instr, "drotrv 'rd, 'rt, 'rs");
+ } else {
+ Unknown(instr);
+ }
+ }
+ break;
+ case SRAV:
+ Format(instr, "srav 'rd, 'rt, 'rs");
+ break;
+ case DSRAV:
+ Format(instr, "dsrav 'rd, 'rt, 'rs");
+ break;
+ case MFHI:
+ if (instr->Bits(25, 16) == 0) {
+ Format(instr, "mfhi 'rd");
+ } else {
+ if ((instr->FunctionFieldRaw() == CLZ_R6) && (instr->FdValue() == 1)) {
+ Format(instr, "clz 'rd, 'rs");
+ } else if ((instr->FunctionFieldRaw() == CLO_R6) &&
+ (instr->FdValue() == 1)) {
+ Format(instr, "clo 'rd, 'rs");
}
+ }
+ break;
+ case MFLO:
+ Format(instr, "mflo 'rd");
+ break;
+ case D_MUL_MUH_U: // Equals to DMULTU.
+ if (kArchVariant != kMips64r6) {
+ Format(instr, "dmultu 'rs, 'rt");
+ } else {
+ if (instr->SaValue() == MUL_OP) {
+ Format(instr, "dmulu 'rd, 'rs, 'rt");
+ } else {
+ Format(instr, "dmuhu 'rd, 'rs, 'rt");
+ }
+ }
+ break;
+ case MULT: // @Mips64r6 == MUL_MUH.
+ if (kArchVariant != kMips64r6) {
+ Format(instr, "mult 'rs, 'rt");
+ } else {
+ if (instr->SaValue() == MUL_OP) {
+ Format(instr, "mul 'rd, 'rs, 'rt");
+ } else {
+ Format(instr, "muh 'rd, 'rs, 'rt");
+ }
+ }
+ break;
+ case MULTU: // @Mips64r6 == MUL_MUH_U.
+ if (kArchVariant != kMips64r6) {
+ Format(instr, "multu 'rs, 'rt");
+ } else {
+ if (instr->SaValue() == MUL_OP) {
+ Format(instr, "mulu 'rd, 'rs, 'rt");
+ } else {
+ Format(instr, "muhu 'rd, 'rs, 'rt");
+ }
+ }
+
+ break;
+ case DIV: // @Mips64r6 == DIV_MOD.
+ if (kArchVariant != kMips64r6) {
+ Format(instr, "div 'rs, 'rt");
+ } else {
+ if (instr->SaValue() == DIV_OP) {
+ Format(instr, "div 'rd, 'rs, 'rt");
+ } else {
+ Format(instr, "mod 'rd, 'rs, 'rt");
+ }
+ }
+ break;
+ case DDIV: // @Mips64r6 == D_DIV_MOD.
+ if (kArchVariant != kMips64r6) {
+ Format(instr, "ddiv 'rs, 'rt");
+ } else {
+ if (instr->SaValue() == DIV_OP) {
+ Format(instr, "ddiv 'rd, 'rs, 'rt");
+ } else {
+ Format(instr, "dmod 'rd, 'rs, 'rt");
+ }
+ }
+ break;
+ case DIVU: // @Mips64r6 == DIV_MOD_U.
+ if (kArchVariant != kMips64r6) {
+ Format(instr, "divu 'rs, 'rt");
+ } else {
+ if (instr->SaValue() == DIV_OP) {
+ Format(instr, "divu 'rd, 'rs, 'rt");
+ } else {
+ Format(instr, "modu 'rd, 'rs, 'rt");
+ }
+ }
+ break;
+ case DDIVU: // @Mips64r6 == D_DIV_MOD_U.
+ if (kArchVariant != kMips64r6) {
+ Format(instr, "ddivu 'rs, 'rt");
+ } else {
+ if (instr->SaValue() == DIV_OP) {
+ Format(instr, "ddivu 'rd, 'rs, 'rt");
+ } else {
+ Format(instr, "dmodu 'rd, 'rs, 'rt");
+ }
+ }
+ break;
+ case ADD:
+ Format(instr, "add 'rd, 'rs, 'rt");
+ break;
+ case DADD:
+ Format(instr, "dadd 'rd, 'rs, 'rt");
+ break;
+ case ADDU:
+ Format(instr, "addu 'rd, 'rs, 'rt");
+ break;
+ case DADDU:
+ Format(instr, "daddu 'rd, 'rs, 'rt");
+ break;
+ case SUB:
+ Format(instr, "sub 'rd, 'rs, 'rt");
+ break;
+ case DSUB:
+ Format(instr, "dsub 'rd, 'rs, 'rt");
+ break;
+ case SUBU:
+ Format(instr, "subu 'rd, 'rs, 'rt");
+ break;
+ case DSUBU:
+ Format(instr, "dsubu 'rd, 'rs, 'rt");
+ break;
+ case AND:
+ Format(instr, "and 'rd, 'rs, 'rt");
+ break;
+ case OR:
+ if (0 == instr->RsValue()) {
+ Format(instr, "mov 'rd, 'rt");
+ } else if (0 == instr->RtValue()) {
+ Format(instr, "mov 'rd, 'rs");
+ } else {
+ Format(instr, "or 'rd, 'rs, 'rt");
+ }
+ break;
+ case XOR:
+ Format(instr, "xor 'rd, 'rs, 'rt");
+ break;
+ case NOR:
+ Format(instr, "nor 'rd, 'rs, 'rt");
+ break;
+ case SLT:
+ Format(instr, "slt 'rd, 'rs, 'rt");
+ break;
+ case SLTU:
+ Format(instr, "sltu 'rd, 'rs, 'rt");
+ break;
+ case TGE:
+ Format(instr, "tge 'rs, 'rt, code: 'code");
+ break;
+ case TGEU:
+ Format(instr, "tgeu 'rs, 'rt, code: 'code");
+ break;
+ case TLT:
+ Format(instr, "tlt 'rs, 'rt, code: 'code");
+ break;
+ case TLTU:
+ Format(instr, "tltu 'rs, 'rt, code: 'code");
+ break;
+ case TEQ:
+ Format(instr, "teq 'rs, 'rt, code: 'code");
+ break;
+ case TNE:
+ Format(instr, "tne 'rs, 'rt, code: 'code");
+ break;
+ case MOVZ:
+ Format(instr, "movz 'rd, 'rs, 'rt");
+ break;
+ case MOVN:
+ Format(instr, "movn 'rd, 'rs, 'rt");
+ break;
+ case MOVCI:
+ if (instr->Bit(16)) {
+ Format(instr, "movt 'rd, 'rs, 'bc");
+ } else {
+ Format(instr, "movf 'rd, 'rs, 'bc");
+ }
+ break;
+ case SELEQZ_S:
+ Format(instr, "seleqz 'rs, 'rt, 'rd");
+ break;
+ case SELNEZ_S:
+ Format(instr, "selnez 'rs, 'rt, 'rd");
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Decoder::DecodeTypeRegisterSPECIAL2(Instruction* instr) {
+ switch (instr->FunctionFieldRaw()) {
+ case MUL:
+ Format(instr, "mul 'rd, 'rs, 'rt");
+ break;
+ case CLZ:
+ if (kArchVariant != kMips64r6) {
+ Format(instr, "clz 'rd, 'rs");
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Decoder::DecodeTypeRegisterSPECIAL3(Instruction* instr) {
+ switch (instr->FunctionFieldRaw()) {
+ case INS: {
+ Format(instr, "ins 'rt, 'rs, 'sa, 'ss2");
+ break;
+ }
+ case EXT: {
+ Format(instr, "ext 'rt, 'rs, 'sa, 'ss1");
+ break;
+ }
+ case DEXT: {
+ Format(instr, "dext 'rt, 'rs, 'sa, 'ss1");
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+int Decoder::DecodeTypeRegister(Instruction* instr) {
+ switch (instr->OpcodeFieldRaw()) {
+ case COP1: // Coprocessor instructions.
+ DecodeTypeRegisterCOP1(instr);
+ break;
+ case COP1X:
+ DecodeTypeRegisterCOP1X(instr);
+ break;
+ case SPECIAL:
+ switch (instr->FunctionFieldRaw()) {
+ case BREAK:
+ return DecodeBreakInstr(instr);
default:
- UNREACHABLE();
+ DecodeTypeRegisterSPECIAL(instr);
+ break;
}
break;
+ case SPECIAL2:
+ DecodeTypeRegisterSPECIAL2(instr);
+ break;
+ case SPECIAL3:
+ DecodeTypeRegisterSPECIAL3(instr);
+ break;
default:
UNREACHABLE();
}
}
+void Decoder::DecodeTypeImmediateCOP1D(Instruction* instr) {
+ switch (instr->FunctionValue()) {
+ case SEL:
+ Format(instr, "sel.D 'ft, 'fs, 'fd");
+ break;
+ case SELEQZ_C:
+ Format(instr, "seleqz.D 'ft, 'fs, 'fd");
+ break;
+ case SELNEZ_C:
+ Format(instr, "selnez.D 'ft, 'fs, 'fd");
+ break;
+ case MIN:
+ Format(instr, "min.D 'ft, 'fs, 'fd");
+ break;
+ case MINA:
+ Format(instr, "mina.D 'ft, 'fs, 'fd");
+ break;
+ case MAX:
+ Format(instr, "max.D 'ft, 'fs, 'fd");
+ break;
+ case MAXA:
+ Format(instr, "maxa.D 'ft, 'fs, 'fd");
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Decoder::DecodeTypeImmediateCOP1L(Instruction* instr) {
+ switch (instr->FunctionValue()) {
+ case CMP_AF:
+ Format(instr, "cmp.af.D 'ft, 'fs, 'fd");
+ break;
+ case CMP_UN:
+ Format(instr, "cmp.un.D 'ft, 'fs, 'fd");
+ break;
+ case CMP_EQ:
+ Format(instr, "cmp.eq.D 'ft, 'fs, 'fd");
+ break;
+ case CMP_UEQ:
+ Format(instr, "cmp.ueq.D 'ft, 'fs, 'fd");
+ break;
+ case CMP_LT:
+ Format(instr, "cmp.lt.D 'ft, 'fs, 'fd");
+ break;
+ case CMP_ULT:
+ Format(instr, "cmp.ult.D 'ft, 'fs, 'fd");
+ break;
+ case CMP_LE:
+ Format(instr, "cmp.le.D 'ft, 'fs, 'fd");
+ break;
+ case CMP_ULE:
+ Format(instr, "cmp.ule.D 'ft, 'fs, 'fd");
+ break;
+ case CMP_OR:
+ Format(instr, "cmp.or.D 'ft, 'fs, 'fd");
+ break;
+ case CMP_UNE:
+ Format(instr, "cmp.une.D 'ft, 'fs, 'fd");
+ break;
+ case CMP_NE:
+ Format(instr, "cmp.ne.D 'ft, 'fs, 'fd");
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Decoder::DecodeTypeImmediateCOP1S(Instruction* instr) {
+ switch (instr->FunctionValue()) {
+ case SEL:
+ Format(instr, "sel.S 'ft, 'fs, 'fd");
+ break;
+ case SELEQZ_C:
+ Format(instr, "seleqz.S 'ft, 'fs, 'fd");
+ break;
+ case SELNEZ_C:
+ Format(instr, "selnez.S 'ft, 'fs, 'fd");
+ break;
+ case MIN:
+ Format(instr, "min.S 'ft, 'fs, 'fd");
+ break;
+ case MINA:
+ Format(instr, "mina.S 'ft, 'fs, 'fd");
+ break;
+ case MAX:
+ Format(instr, "max.S 'ft, 'fs, 'fd");
+ break;
+ case MAXA:
+ Format(instr, "maxa.S 'ft, 'fs, 'fd");
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Decoder::DecodeTypeImmediateCOP1W(Instruction* instr) {
+ switch (instr->FunctionValue()) {
+ case CMP_AF:
+ Format(instr, "cmp.af.S 'ft, 'fs, 'fd");
+ break;
+ case CMP_UN:
+ Format(instr, "cmp.un.S 'ft, 'fs, 'fd");
+ break;
+ case CMP_EQ:
+ Format(instr, "cmp.eq.S 'ft, 'fs, 'fd");
+ break;
+ case CMP_UEQ:
+ Format(instr, "cmp.ueq.S 'ft, 'fs, 'fd");
+ break;
+ case CMP_LT:
+ Format(instr, "cmp.lt.S 'ft, 'fs, 'fd");
+ break;
+ case CMP_ULT:
+ Format(instr, "cmp.ult.S 'ft, 'fs, 'fd");
+ break;
+ case CMP_LE:
+ Format(instr, "cmp.le.S 'ft, 'fs, 'fd");
+ break;
+ case CMP_ULE:
+ Format(instr, "cmp.ule.S 'ft, 'fs, 'fd");
+ break;
+ case CMP_OR:
+ Format(instr, "cmp.or.S 'ft, 'fs, 'fd");
+ break;
+ case CMP_UNE:
+ Format(instr, "cmp.une.S 'ft, 'fs, 'fd");
+ break;
+ case CMP_NE:
+ Format(instr, "cmp.ne.S 'ft, 'fs, 'fd");
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Decoder::DecodeTypeImmediateCOP1(Instruction* instr) {
+ switch (instr->RsFieldRaw()) {
+ case BC1:
+ if (instr->FBtrueValue()) {
+ Format(instr, "bc1t 'bc, 'imm16u");
+ } else {
+ Format(instr, "bc1f 'bc, 'imm16u");
+ }
+ break;
+ case BC1EQZ:
+ Format(instr, "bc1eqz 'ft, 'imm16u");
+ break;
+ case BC1NEZ:
+ Format(instr, "bc1nez 'ft, 'imm16u");
+ break;
+ case W: // CMP.S instruction.
+ DecodeTypeImmediateCOP1W(instr);
+ break;
+ case L: // CMP.D instruction.
+ DecodeTypeImmediateCOP1L(instr);
+ break;
+ case S:
+ DecodeTypeImmediateCOP1S(instr);
+ break;
+ case D:
+ DecodeTypeImmediateCOP1D(instr);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Decoder::DecodeTypeImmediateREGIMM(Instruction* instr) {
+ switch (instr->RtFieldRaw()) {
+ case BLTZ:
+ Format(instr, "bltz 'rs, 'imm16u");
+ break;
+ case BLTZAL:
+ Format(instr, "bltzal 'rs, 'imm16u");
+ break;
+ case BGEZ:
+ Format(instr, "bgez 'rs, 'imm16u");
+ break;
+ case BGEZAL:
+ Format(instr, "bgezal 'rs, 'imm16u");
+ break;
+ case BGEZALL:
+ Format(instr, "bgezall 'rs, 'imm16u");
+ break;
+ case DAHI:
+ Format(instr, "dahi 'rs, 'imm16u");
+ break;
+ case DATI:
+ Format(instr, "dati 'rs, 'imm16u");
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
void Decoder::DecodeTypeImmediate(Instruction* instr) {
switch (instr->OpcodeFieldRaw()) {
case COP1:
- switch (instr->RsFieldRaw()) {
- case BC1:
- if (instr->FBtrueValue()) {
- Format(instr, "bc1t 'bc, 'imm16u");
- } else {
- Format(instr, "bc1f 'bc, 'imm16u");
- }
- break;
- case BC1EQZ:
- Format(instr, "bc1eqz 'ft, 'imm16u");
- break;
- case BC1NEZ:
- Format(instr, "bc1nez 'ft, 'imm16u");
- break;
- case W: // CMP.S instruction.
- switch (instr->FunctionValue()) {
- case CMP_AF:
- Format(instr, "cmp.af.S 'ft, 'fs, 'fd");
- break;
- case CMP_UN:
- Format(instr, "cmp.un.S 'ft, 'fs, 'fd");
- break;
- case CMP_EQ:
- Format(instr, "cmp.eq.S 'ft, 'fs, 'fd");
- break;
- case CMP_UEQ:
- Format(instr, "cmp.ueq.S 'ft, 'fs, 'fd");
- break;
- case CMP_LT:
- Format(instr, "cmp.lt.S 'ft, 'fs, 'fd");
- break;
- case CMP_ULT:
- Format(instr, "cmp.ult.S 'ft, 'fs, 'fd");
- break;
- case CMP_LE:
- Format(instr, "cmp.le.S 'ft, 'fs, 'fd");
- break;
- case CMP_ULE:
- Format(instr, "cmp.ule.S 'ft, 'fs, 'fd");
- break;
- case CMP_OR:
- Format(instr, "cmp.or.S 'ft, 'fs, 'fd");
- break;
- case CMP_UNE:
- Format(instr, "cmp.une.S 'ft, 'fs, 'fd");
- break;
- case CMP_NE:
- Format(instr, "cmp.ne.S 'ft, 'fs, 'fd");
- break;
- default:
- UNREACHABLE();
- }
- break;
- case L: // CMP.D instruction.
- switch (instr->FunctionValue()) {
- case CMP_AF:
- Format(instr, "cmp.af.D 'ft, 'fs, 'fd");
- break;
- case CMP_UN:
- Format(instr, "cmp.un.D 'ft, 'fs, 'fd");
- break;
- case CMP_EQ:
- Format(instr, "cmp.eq.D 'ft, 'fs, 'fd");
- break;
- case CMP_UEQ:
- Format(instr, "cmp.ueq.D 'ft, 'fs, 'fd");
- break;
- case CMP_LT:
- Format(instr, "cmp.lt.D 'ft, 'fs, 'fd");
- break;
- case CMP_ULT:
- Format(instr, "cmp.ult.D 'ft, 'fs, 'fd");
- break;
- case CMP_LE:
- Format(instr, "cmp.le.D 'ft, 'fs, 'fd");
- break;
- case CMP_ULE:
- Format(instr, "cmp.ule.D 'ft, 'fs, 'fd");
- break;
- case CMP_OR:
- Format(instr, "cmp.or.D 'ft, 'fs, 'fd");
- break;
- case CMP_UNE:
- Format(instr, "cmp.une.D 'ft, 'fs, 'fd");
- break;
- case CMP_NE:
- Format(instr, "cmp.ne.D 'ft, 'fs, 'fd");
- break;
- default:
- UNREACHABLE();
- }
- break;
- case S:
- switch (instr->FunctionValue()) {
- case SEL:
- Format(instr, "sel.S 'ft, 'fs, 'fd");
- break;
- case SELEQZ_C:
- Format(instr, "seleqz.S 'ft, 'fs, 'fd");
- break;
- case SELNEZ_C:
- Format(instr, "selnez.S 'ft, 'fs, 'fd");
- break;
- case MIN:
- Format(instr, "min.S 'ft, 'fs, 'fd");
- break;
- case MINA:
- Format(instr, "mina.S 'ft, 'fs, 'fd");
- break;
- case MAX:
- Format(instr, "max.S 'ft, 'fs, 'fd");
- break;
- case MAXA:
- Format(instr, "maxa.S 'ft, 'fs, 'fd");
- break;
- default:
- UNREACHABLE();
- }
- break;
- case D:
- switch (instr->FunctionValue()) {
- case SEL:
- Format(instr, "sel.D 'ft, 'fs, 'fd");
- break;
- case SELEQZ_C:
- Format(instr, "seleqz.D 'ft, 'fs, 'fd");
- break;
- case SELNEZ_C:
- Format(instr, "selnez.D 'ft, 'fs, 'fd");
- break;
- case MIN:
- Format(instr, "min.D 'ft, 'fs, 'fd");
- break;
- case MINA:
- Format(instr, "mina.D 'ft, 'fs, 'fd");
- break;
- case MAX:
- Format(instr, "max.D 'ft, 'fs, 'fd");
- break;
- case MAXA:
- Format(instr, "maxa.D 'ft, 'fs, 'fd");
- break;
- default:
- UNREACHABLE();
- }
- break;
- default:
- UNREACHABLE();
- }
-
+ DecodeTypeImmediateCOP1(instr);
break; // Case COP1.
// ------------- REGIMM class.
case REGIMM:
- switch (instr->RtFieldRaw()) {
- case BLTZ:
- Format(instr, "bltz 'rs, 'imm16u");
- break;
- case BLTZAL:
- Format(instr, "bltzal 'rs, 'imm16u");
- break;
- case BGEZ:
- Format(instr, "bgez 'rs, 'imm16u");
- break;
- case BGEZAL:
- Format(instr, "bgezal 'rs, 'imm16u");
- break;
- case BGEZALL:
- Format(instr, "bgezall 'rs, 'imm16u");
- break;
- case DAHI:
- Format(instr, "dahi 'rs, 'imm16u");
- break;
- case DATI:
- Format(instr, "dati 'rs, 'imm16u");
- break;
- default:
- UNREACHABLE();
- }
+
break; // Case REGIMM.
// ------------- Branch instructions.
case BEQ:
case DDIVU:
// div and divu never raise exceptions.
break;
+ case SELEQZ_S:
+ case SELNEZ_S:
+ break;
default:
UNREACHABLE();
}
}
+void Simulator::DecodeTypeRegisterSRsType(Instruction* instr,
+ const int32_t& fs_reg,
+ const int64_t& fd_reg) {
+ float f;
+ switch (instr->FunctionFieldRaw()) {
+ case CVT_D_S:
+ f = get_fpu_register_float(fs_reg);
+ set_fpu_register_double(fd_reg, static_cast<double>(f));
+ break;
+ default:
+ // CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S
+ // CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented.
+ UNREACHABLE();
+ }
+}
+
+
+void Simulator::DecodeTypeRegisterDRsType(Instruction* instr,
+ const int32_t& fs_reg,
+ const int64_t& ft_reg,
+ const int32_t& fd_reg) {
+ double ft, fs;
+ uint32_t cc, fcsr_cc;
+ fs = get_fpu_register_double(fs_reg);
+ ft = get_fpu_register_double(ft_reg);
+ cc = instr->FCccValue();
+ fcsr_cc = get_fcsr_condition_bit(cc);
+ int64_t ft_int = static_cast<int64_t>(ft);
+ switch (instr->FunctionFieldRaw()) {
+ case SELEQZ_C:
+ DCHECK(kArchVariant == kMips64r6);
+ set_fpu_register_double(fd_reg, (ft_int & 0x1) == 0 ? fs : 0.0);
+ break;
+ case SELNEZ_C:
+ DCHECK(kArchVariant == kMips64r6);
+ set_fpu_register_double(fd_reg, (ft_int & 0x1) != 0 ? fs : 0.0);
+ break;
+ case ADD_D:
+ set_fpu_register_double(fd_reg, fs + ft);
+ break;
+ case SUB_D:
+ set_fpu_register_double(fd_reg, fs - ft);
+ break;
+ case MUL_D:
+ set_fpu_register_double(fd_reg, fs * ft);
+ break;
+ case DIV_D:
+ set_fpu_register_double(fd_reg, fs / ft);
+ break;
+ case ABS_D:
+ set_fpu_register_double(fd_reg, fabs(fs));
+ break;
+ case MOV_D:
+ set_fpu_register_double(fd_reg, fs);
+ break;
+ case NEG_D:
+ set_fpu_register_double(fd_reg, -fs);
+ break;
+ case SQRT_D:
+ set_fpu_register_double(fd_reg, fast_sqrt(fs));
+ break;
+ case C_UN_D:
+ set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
+ break;
+ case C_EQ_D:
+ set_fcsr_bit(fcsr_cc, (fs == ft));
+ break;
+ case C_UEQ_D:
+ set_fcsr_bit(fcsr_cc, (fs == ft) || (std::isnan(fs) || std::isnan(ft)));
+ break;
+ case C_OLT_D:
+ set_fcsr_bit(fcsr_cc, (fs < ft));
+ break;
+ case C_ULT_D:
+ set_fcsr_bit(fcsr_cc, (fs < ft) || (std::isnan(fs) || std::isnan(ft)));
+ break;
+ case C_OLE_D:
+ set_fcsr_bit(fcsr_cc, (fs <= ft));
+ break;
+ case C_ULE_D:
+ set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
+ break;
+ case CVT_W_D: // Convert double to word.
+ // Rounding modes are not yet supported.
+ DCHECK((FCSR_ & 3) == 0);
+ // In rounding mode 0 it should behave like ROUND.
+ // No break.
+ case ROUND_W_D: // Round double to word (round half to even).
+ {
+ double rounded = std::floor(fs + 0.5);
+ int32_t result = static_cast<int32_t>(rounded);
+ if ((result & 1) != 0 && result - fs == 0.5) {
+ // If the number is halfway between two integers,
+ // round to the even one.
+ result--;
+ }
+ set_fpu_register_word(fd_reg, result);
+ if (set_fcsr_round_error(fs, rounded)) {
+ set_fpu_register(fd_reg, kFPUInvalidResult);
+ }
+ } break;
+ case TRUNC_W_D: // Truncate double to word (round towards 0).
+ {
+ double rounded = trunc(fs);
+ int32_t result = static_cast<int32_t>(rounded);
+ set_fpu_register_word(fd_reg, result);
+ if (set_fcsr_round_error(fs, rounded)) {
+ set_fpu_register(fd_reg, kFPUInvalidResult);
+ }
+ } break;
+ case FLOOR_W_D: // Round double to word towards negative infinity.
+ {
+ double rounded = std::floor(fs);
+ int32_t result = static_cast<int32_t>(rounded);
+ set_fpu_register_word(fd_reg, result);
+ if (set_fcsr_round_error(fs, rounded)) {
+ set_fpu_register(fd_reg, kFPUInvalidResult);
+ }
+ } break;
+ case CEIL_W_D: // Round double to word towards positive infinity.
+ {
+ double rounded = std::ceil(fs);
+ int32_t result = static_cast<int32_t>(rounded);
+ set_fpu_register_word(fd_reg, result);
+ if (set_fcsr_round_error(fs, rounded)) {
+ set_fpu_register(fd_reg, kFPUInvalidResult);
+ }
+ } break;
+ case CVT_S_D: // Convert double to float (single).
+ set_fpu_register_float(fd_reg, static_cast<float>(fs));
+ break;
+ case CVT_L_D: // Mips64r2: Truncate double to 64-bit long-word.
+ // Rounding modes are not yet supported.
+ DCHECK((FCSR_ & 3) == 0);
+ // In rounding mode 0 it should behave like ROUND.
+ // No break.
+ case ROUND_L_D: { // Mips64r2 instruction.
+ // check error cases
+ double rounded = fs > 0 ? floor(fs + 0.5) : ceil(fs - 0.5);
+ int64_t result = static_cast<int64_t>(rounded);
+ set_fpu_register(fd_reg, result);
+ if (set_fcsr_round64_error(fs, rounded)) {
+ set_fpu_register(fd_reg, kFPU64InvalidResult);
+ }
+ break;
+ }
+ case TRUNC_L_D: { // Mips64r2 instruction.
+ double rounded = trunc(fs);
+ int64_t result = static_cast<int64_t>(rounded);
+ set_fpu_register(fd_reg, result);
+ if (set_fcsr_round64_error(fs, rounded)) {
+ set_fpu_register(fd_reg, kFPU64InvalidResult);
+ }
+ break;
+ }
+ case FLOOR_L_D: { // Mips64r2 instruction.
+ double rounded = floor(fs);
+ int64_t result = static_cast<int64_t>(rounded);
+ set_fpu_register(fd_reg, result);
+ if (set_fcsr_round64_error(fs, rounded)) {
+ set_fpu_register(fd_reg, kFPU64InvalidResult);
+ }
+ break;
+ }
+ case CEIL_L_D: { // Mips64r2 instruction.
+ double rounded = ceil(fs);
+ int64_t result = static_cast<int64_t>(rounded);
+ set_fpu_register(fd_reg, result);
+ if (set_fcsr_round64_error(fs, rounded)) {
+ set_fpu_register(fd_reg, kFPU64InvalidResult);
+ }
+ break;
+ }
+ case C_F_D:
+ UNIMPLEMENTED_MIPS();
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Simulator::DecodeTypeRegisterWRsType(Instruction* instr,
+ const int32_t& fs_reg,
+ const int32_t& fd_reg,
+ int64_t& alu_out) {
+ switch (instr->FunctionFieldRaw()) {
+ case CVT_S_W: // Convert word to float (single).
+ alu_out = get_fpu_register_signed_word(fs_reg);
+ set_fpu_register_float(fd_reg, static_cast<float>(alu_out));
+ break;
+ case CVT_D_W: // Convert word to double.
+ alu_out = get_fpu_register_signed_word(fs_reg);
+ set_fpu_register_double(fd_reg, static_cast<double>(alu_out));
+ break;
+ default: // Mips64r6 CMP.S instructions unimplemented.
+ UNREACHABLE();
+ }
+}
+
+
+void Simulator::DecodeTypeRegisterLRsType(Instruction* instr,
+ const int32_t& fs_reg,
+ const int32_t& fd_reg,
+ const int32_t& ft_reg) {
+ double fs = get_fpu_register_double(fs_reg);
+ double ft = get_fpu_register_double(ft_reg);
+ int64_t i64;
+ switch (instr->FunctionFieldRaw()) {
+ case CVT_D_L: // Mips32r2 instruction.
+ i64 = get_fpu_register(fs_reg);
+ set_fpu_register_double(fd_reg, static_cast<double>(i64));
+ break;
+ case CVT_S_L:
+ UNIMPLEMENTED_MIPS();
+ break;
+ case CMP_AF: // Mips64r6 CMP.D instructions.
+ UNIMPLEMENTED_MIPS();
+ break;
+ case CMP_UN:
+ if (std::isnan(fs) || std::isnan(ft)) {
+ set_fpu_register(fd_reg, -1);
+ } else {
+ set_fpu_register(fd_reg, 0);
+ }
+ break;
+ case CMP_EQ:
+ if (fs == ft) {
+ set_fpu_register(fd_reg, -1);
+ } else {
+ set_fpu_register(fd_reg, 0);
+ }
+ break;
+ case CMP_UEQ:
+ if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) {
+ set_fpu_register(fd_reg, -1);
+ } else {
+ set_fpu_register(fd_reg, 0);
+ }
+ break;
+ case CMP_LT:
+ if (fs < ft) {
+ set_fpu_register(fd_reg, -1);
+ } else {
+ set_fpu_register(fd_reg, 0);
+ }
+ break;
+ case CMP_ULT:
+ if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) {
+ set_fpu_register(fd_reg, -1);
+ } else {
+ set_fpu_register(fd_reg, 0);
+ }
+ break;
+ case CMP_LE:
+ if (fs <= ft) {
+ set_fpu_register(fd_reg, -1);
+ } else {
+ set_fpu_register(fd_reg, 0);
+ }
+ break;
+ case CMP_ULE:
+ if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) {
+ set_fpu_register(fd_reg, -1);
+ } else {
+ set_fpu_register(fd_reg, 0);
+ }
+ break;
+ default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED
+ UNREACHABLE();
+ }
+}
+
+
+void Simulator::DecodeTypeRegisterCOP1(
+ Instruction* instr, const int64_t& rs_reg, const int64_t& rs,
+ const uint64_t& rs_u, const int64_t& rt_reg, const int64_t& rt,
+ const uint64_t& rt_u, const int64_t& rd_reg, const int32_t& fr_reg,
+ const int32_t& fs_reg, const int32_t& ft_reg, const int64_t& fd_reg,
+ int64_t& alu_out) {
+ switch (instr->RsFieldRaw()) {
+ case BC1: // Branch on coprocessor condition.
+ case BC1EQZ:
+ case BC1NEZ:
+ UNREACHABLE();
+ break;
+ case CFC1:
+ set_register(rt_reg, alu_out);
+ break;
+ case MFC1:
+ case DMFC1:
+ case MFHC1:
+ set_register(rt_reg, alu_out);
+ break;
+ case CTC1:
+ // At the moment only FCSR is supported.
+ DCHECK(fs_reg == kFCSRRegister);
+ FCSR_ = registers_[rt_reg];
+ break;
+ case MTC1:
+ // Hardware writes upper 32-bits to zero on mtc1.
+ set_fpu_register_hi_word(fs_reg, 0);
+ set_fpu_register_word(fs_reg, registers_[rt_reg]);
+ break;
+ case DMTC1:
+ set_fpu_register(fs_reg, registers_[rt_reg]);
+ break;
+ case MTHC1:
+ set_fpu_register_hi_word(fs_reg, registers_[rt_reg]);
+ break;
+ case S:
+ DecodeTypeRegisterSRsType(instr, fs_reg, fd_reg);
+ break;
+ case D:
+ DecodeTypeRegisterDRsType(instr, fs_reg, ft_reg, fd_reg);
+ break;
+ case W:
+ DecodeTypeRegisterWRsType(instr, fs_reg, fd_reg, alu_out);
+ break;
+ case L:
+ DecodeTypeRegisterLRsType(instr, fs_reg, fd_reg, ft_reg);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Simulator::DecodeTypeRegisterCOP1X(Instruction* instr,
+ const int32_t& fr_reg,
+ const int32_t& fs_reg,
+ const int32_t& ft_reg,
+ const int64_t& fd_reg) {
+ switch (instr->FunctionFieldRaw()) {
+ case MADD_D:
+ double fr, ft, fs;
+ fr = get_fpu_register_double(fr_reg);
+ fs = get_fpu_register_double(fs_reg);
+ ft = get_fpu_register_double(ft_reg);
+ set_fpu_register_double(fd_reg, fs * ft + fr);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Simulator::DecodeTypeRegisterSPECIAL(
+ Instruction* instr, const int64_t& rs_reg, const int64_t& rs,
+ const uint64_t& rs_u, const int64_t& rt_reg, const int64_t& rt,
+ const uint64_t& rt_u, const int64_t& rd_reg, const int32_t& fr_reg,
+ const int32_t& fs_reg, const int32_t& ft_reg, const int64_t& fd_reg,
+ int64_t& i64hilo, uint64_t& u64hilo, int64_t& alu_out, bool& do_interrupt,
+ int64_t& current_pc, int64_t& next_pc, int64_t& return_addr_reg,
+ int64_t& i128resultH, int64_t& i128resultL) {
+ switch (instr->FunctionFieldRaw()) {
+ case SELEQZ_S:
+ DCHECK(kArchVariant == kMips64r6);
+ set_register(rd_reg, rt == 0 ? rs : 0);
+ break;
+ case SELNEZ_S:
+ DCHECK(kArchVariant == kMips64r6);
+ set_register(rd_reg, rt != 0 ? rs : 0);
+ break;
+ case JR: {
+ Instruction* branch_delay_instr =
+ reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize);
+ BranchDelayInstructionDecode(branch_delay_instr);
+ set_pc(next_pc);
+ pc_modified_ = true;
+ break;
+ }
+ case JALR: {
+ Instruction* branch_delay_instr =
+ reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize);
+ BranchDelayInstructionDecode(branch_delay_instr);
+ set_register(return_addr_reg, current_pc + 2 * Instruction::kInstrSize);
+ set_pc(next_pc);
+ pc_modified_ = true;
+ break;
+ }
+ // Instructions using HI and LO registers.
+ case MULT:
+ if (kArchVariant != kMips64r6) {
+ set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff));
+ set_register(HI, static_cast<int32_t>(i64hilo >> 32));
+ } else {
+ switch (instr->SaValue()) {
+ case MUL_OP:
+ set_register(rd_reg, static_cast<int32_t>(i64hilo & 0xffffffff));
+ break;
+ case MUH_OP:
+ set_register(rd_reg, static_cast<int32_t>(i64hilo >> 32));
+ break;
+ default:
+ UNIMPLEMENTED_MIPS();
+ break;
+ }
+ }
+ break;
+ case MULTU:
+ set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff));
+ set_register(HI, static_cast<int32_t>(u64hilo >> 32));
+ break;
+ case DMULT: // DMULT == D_MUL_MUH.
+ if (kArchVariant != kMips64r6) {
+ set_register(LO, static_cast<int64_t>(i128resultL));
+ set_register(HI, static_cast<int64_t>(i128resultH));
+ } else {
+ switch (instr->SaValue()) {
+ case MUL_OP:
+ set_register(rd_reg, static_cast<int64_t>(i128resultL));
+ break;
+ case MUH_OP:
+ set_register(rd_reg, static_cast<int64_t>(i128resultH));
+ break;
+ default:
+ UNIMPLEMENTED_MIPS();
+ break;
+ }
+ }
+ break;
+ case DMULTU:
+ UNIMPLEMENTED_MIPS();
+ break;
+ case DSLL:
+ set_register(rd_reg, alu_out);
+ break;
+ case DIV:
+ case DDIV:
+ switch (kArchVariant) {
+ case kMips64r2:
+ // Divide by zero and overflow was not checked in the
+ // configuration step - div and divu do not raise exceptions. On
+ // division by 0 the result will be UNPREDICTABLE. On overflow
+ // (INT_MIN/-1), return INT_MIN which is what the hardware does.
+ if (rs == INT_MIN && rt == -1) {
+ set_register(LO, INT_MIN);
+ set_register(HI, 0);
+ } else if (rt != 0) {
+ set_register(LO, rs / rt);
+ set_register(HI, rs % rt);
+ }
+ break;
+ case kMips64r6:
+ switch (instr->SaValue()) {
+ case DIV_OP:
+ if (rs == INT_MIN && rt == -1) {
+ set_register(rd_reg, INT_MIN);
+ } else if (rt != 0) {
+ set_register(rd_reg, rs / rt);
+ }
+ break;
+ case MOD_OP:
+ if (rs == INT_MIN && rt == -1) {
+ set_register(rd_reg, 0);
+ } else if (rt != 0) {
+ set_register(rd_reg, rs % rt);
+ }
+ break;
+ default:
+ UNIMPLEMENTED_MIPS();
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case DIVU:
+ if (rt_u != 0) {
+ set_register(LO, rs_u / rt_u);
+ set_register(HI, rs_u % rt_u);
+ }
+ break;
+ // Break and trap instructions.
+ case BREAK:
+ case TGE:
+ case TGEU:
+ case TLT:
+ case TLTU:
+ case TEQ:
+ case TNE:
+ if (do_interrupt) {
+ SoftwareInterrupt(instr);
+ }
+ break;
+ // Conditional moves.
+ case MOVN:
+ if (rt) {
+ set_register(rd_reg, rs);
+ TraceRegWr(rs);
+ }
+ break;
+ case MOVCI: {
+ uint32_t cc = instr->FBccValue();
+ uint32_t fcsr_cc = get_fcsr_condition_bit(cc);
+ if (instr->Bit(16)) { // Read Tf bit.
+ if (test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs);
+ } else {
+ if (!test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs);
+ }
+ break;
+ }
+ case MOVZ:
+ if (!rt) {
+ set_register(rd_reg, rs);
+ TraceRegWr(rs);
+ }
+ break;
+ default: // For other special opcodes we do the default operation.
+ set_register(rd_reg, alu_out);
+ TraceRegWr(alu_out);
+ }
+}
+
+
+void Simulator::DecodeTypeRegisterSPECIAL2(Instruction* instr,
+ const int64_t& rd_reg,
+ int64_t& alu_out) {
+ switch (instr->FunctionFieldRaw()) {
+ case MUL:
+ set_register(rd_reg, alu_out);
+ TraceRegWr(alu_out);
+ // HI and LO are UNPREDICTABLE after the operation.
+ set_register(LO, Unpredictable);
+ set_register(HI, Unpredictable);
+ break;
+ default: // For other special2 opcodes we do the default operation.
+ set_register(rd_reg, alu_out);
+ }
+}
+
+
+void Simulator::DecodeTypeRegisterSPECIAL3(Instruction* instr,
+ const int64_t& rt_reg,
+ int64_t& alu_out) {
+ switch (instr->FunctionFieldRaw()) {
+ case INS:
+ // Ins instr leaves result in Rt, rather than Rd.
+ set_register(rt_reg, alu_out);
+ TraceRegWr(alu_out);
+ break;
+ case EXT:
+ case DEXT:
+ // Dext/Ext instr leaves result in Rt, rather than Rd.
+ set_register(rt_reg, alu_out);
+ TraceRegWr(alu_out);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
void Simulator::DecodeTypeRegister(Instruction* instr) {
// Instruction fields.
const Opcode op = instr->OpcodeFieldRaw();
// ---------- Execution.
switch (op) {
case COP1:
- switch (instr->RsFieldRaw()) {
- case BC1: // Branch on coprocessor condition.
- case BC1EQZ:
- case BC1NEZ:
- UNREACHABLE();
- break;
- case CFC1:
- set_register(rt_reg, alu_out);
- break;
- case MFC1:
- case DMFC1:
- case MFHC1:
- set_register(rt_reg, alu_out);
- break;
- case CTC1:
- // At the moment only FCSR is supported.
- DCHECK(fs_reg == kFCSRRegister);
- FCSR_ = registers_[rt_reg];
- break;
- case MTC1:
- // Hardware writes upper 32-bits to zero on mtc1.
- set_fpu_register_hi_word(fs_reg, 0);
- set_fpu_register_word(fs_reg, registers_[rt_reg]);
- break;
- case DMTC1:
- set_fpu_register(fs_reg, registers_[rt_reg]);
- break;
- case MTHC1:
- set_fpu_register_hi_word(fs_reg, registers_[rt_reg]);
- break;
- case S:
- float f;
- switch (instr->FunctionFieldRaw()) {
- case CVT_D_S:
- f = get_fpu_register_float(fs_reg);
- set_fpu_register_double(fd_reg, static_cast<double>(f));
- break;
- default:
- // CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S
- // CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented.
- UNREACHABLE();
- }
- break;
- case D:
- double ft, fs;
- uint32_t cc, fcsr_cc;
- int64_t i64;
- fs = get_fpu_register_double(fs_reg);
- ft = get_fpu_register_double(ft_reg);
- cc = instr->FCccValue();
- fcsr_cc = get_fcsr_condition_bit(cc);
- switch (instr->FunctionFieldRaw()) {
- case ADD_D:
- set_fpu_register_double(fd_reg, fs + ft);
- break;
- case SUB_D:
- set_fpu_register_double(fd_reg, fs - ft);
- break;
- case MUL_D:
- set_fpu_register_double(fd_reg, fs * ft);
- break;
- case DIV_D:
- set_fpu_register_double(fd_reg, fs / ft);
- break;
- case ABS_D:
- set_fpu_register_double(fd_reg, fabs(fs));
- break;
- case MOV_D:
- set_fpu_register_double(fd_reg, fs);
- break;
- case NEG_D:
- set_fpu_register_double(fd_reg, -fs);
- break;
- case SQRT_D:
- set_fpu_register_double(fd_reg, fast_sqrt(fs));
- break;
- case C_UN_D:
- set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
- break;
- case C_EQ_D:
- set_fcsr_bit(fcsr_cc, (fs == ft));
- break;
- case C_UEQ_D:
- set_fcsr_bit(fcsr_cc,
- (fs == ft) || (std::isnan(fs) || std::isnan(ft)));
- break;
- case C_OLT_D:
- set_fcsr_bit(fcsr_cc, (fs < ft));
- break;
- case C_ULT_D:
- set_fcsr_bit(fcsr_cc,
- (fs < ft) || (std::isnan(fs) || std::isnan(ft)));
- break;
- case C_OLE_D:
- set_fcsr_bit(fcsr_cc, (fs <= ft));
- break;
- case C_ULE_D:
- set_fcsr_bit(fcsr_cc,
- (fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
- break;
- case CVT_W_D: // Convert double to word.
- // Rounding modes are not yet supported.
- DCHECK((FCSR_ & 3) == 0);
- // In rounding mode 0 it should behave like ROUND.
- // No break.
- case ROUND_W_D: // Round double to word (round half to even).
- {
- double rounded = std::floor(fs + 0.5);
- int32_t result = static_cast<int32_t>(rounded);
- if ((result & 1) != 0 && result - fs == 0.5) {
- // If the number is halfway between two integers,
- // round to the even one.
- result--;
- }
- set_fpu_register_word(fd_reg, result);
- if (set_fcsr_round_error(fs, rounded)) {
- set_fpu_register(fd_reg, kFPUInvalidResult);
- }
- }
- break;
- case TRUNC_W_D: // Truncate double to word (round towards 0).
- {
- double rounded = trunc(fs);
- int32_t result = static_cast<int32_t>(rounded);
- set_fpu_register_word(fd_reg, result);
- if (set_fcsr_round_error(fs, rounded)) {
- set_fpu_register(fd_reg, kFPUInvalidResult);
- }
- }
- break;
- case FLOOR_W_D: // Round double to word towards negative infinity.
- {
- double rounded = std::floor(fs);
- int32_t result = static_cast<int32_t>(rounded);
- set_fpu_register_word(fd_reg, result);
- if (set_fcsr_round_error(fs, rounded)) {
- set_fpu_register(fd_reg, kFPUInvalidResult);
- }
- }
- break;
- case CEIL_W_D: // Round double to word towards positive infinity.
- {
- double rounded = std::ceil(fs);
- int32_t result = static_cast<int32_t>(rounded);
- set_fpu_register_word(fd_reg, result);
- if (set_fcsr_round_error(fs, rounded)) {
- set_fpu_register(fd_reg, kFPUInvalidResult);
- }
- }
- break;
- case CVT_S_D: // Convert double to float (single).
- set_fpu_register_float(fd_reg, static_cast<float>(fs));
- break;
- case CVT_L_D: // Mips64r2: Truncate double to 64-bit long-word.
- // Rounding modes are not yet supported.
- DCHECK((FCSR_ & 3) == 0);
- // In rounding mode 0 it should behave like ROUND.
- // No break.
- case ROUND_L_D: { // Mips64r2 instruction.
- // check error cases
- double rounded = fs > 0 ? floor(fs + 0.5) : ceil(fs - 0.5);
- int64_t result = static_cast<int64_t>(rounded);
- set_fpu_register(fd_reg, result);
- if (set_fcsr_round64_error(fs, rounded)) {
- set_fpu_register(fd_reg, kFPU64InvalidResult);
- }
- break;
- }
- case TRUNC_L_D: { // Mips64r2 instruction.
- double rounded = trunc(fs);
- int64_t result = static_cast<int64_t>(rounded);
- set_fpu_register(fd_reg, result);
- if (set_fcsr_round64_error(fs, rounded)) {
- set_fpu_register(fd_reg, kFPU64InvalidResult);
- }
- break;
- }
- case FLOOR_L_D: { // Mips64r2 instruction.
- double rounded = floor(fs);
- int64_t result = static_cast<int64_t>(rounded);
- set_fpu_register(fd_reg, result);
- if (set_fcsr_round64_error(fs, rounded)) {
- set_fpu_register(fd_reg, kFPU64InvalidResult);
- }
- break;
- }
- case CEIL_L_D: { // Mips64r2 instruction.
- double rounded = ceil(fs);
- int64_t result = static_cast<int64_t>(rounded);
- set_fpu_register(fd_reg, result);
- if (set_fcsr_round64_error(fs, rounded)) {
- set_fpu_register(fd_reg, kFPU64InvalidResult);
- }
- break;
- }
- case C_F_D:
- UNIMPLEMENTED_MIPS();
- break;
- default:
- UNREACHABLE();
- }
- break;
- case W:
- switch (instr->FunctionFieldRaw()) {
- case CVT_S_W: // Convert word to float (single).
- alu_out = get_fpu_register_signed_word(fs_reg);
- set_fpu_register_float(fd_reg, static_cast<float>(alu_out));
- break;
- case CVT_D_W: // Convert word to double.
- alu_out = get_fpu_register_signed_word(fs_reg);
- set_fpu_register_double(fd_reg, static_cast<double>(alu_out));
- break;
- default: // Mips64r6 CMP.S instructions unimplemented.
- UNREACHABLE();
- }
- break;
- case L:
- fs = get_fpu_register_double(fs_reg);
- ft = get_fpu_register_double(ft_reg);
- switch (instr->FunctionFieldRaw()) {
- case CVT_D_L: // Mips32r2 instruction.
- i64 = get_fpu_register(fs_reg);
- set_fpu_register_double(fd_reg, static_cast<double>(i64));
- break;
- case CVT_S_L:
- UNIMPLEMENTED_MIPS();
- break;
- case CMP_AF: // Mips64r6 CMP.D instructions.
- UNIMPLEMENTED_MIPS();
- break;
- case CMP_UN:
- if (std::isnan(fs) || std::isnan(ft)) {
- set_fpu_register(fd_reg, -1);
- } else {
- set_fpu_register(fd_reg, 0);
- }
- break;
- case CMP_EQ:
- if (fs == ft) {
- set_fpu_register(fd_reg, -1);
- } else {
- set_fpu_register(fd_reg, 0);
- }
- break;
- case CMP_UEQ:
- if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) {
- set_fpu_register(fd_reg, -1);
- } else {
- set_fpu_register(fd_reg, 0);
- }
- break;
- case CMP_LT:
- if (fs < ft) {
- set_fpu_register(fd_reg, -1);
- } else {
- set_fpu_register(fd_reg, 0);
- }
- break;
- case CMP_ULT:
- if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) {
- set_fpu_register(fd_reg, -1);
- } else {
- set_fpu_register(fd_reg, 0);
- }
- break;
- case CMP_LE:
- if (fs <= ft) {
- set_fpu_register(fd_reg, -1);
- } else {
- set_fpu_register(fd_reg, 0);
- }
- break;
- case CMP_ULE:
- if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) {
- set_fpu_register(fd_reg, -1);
- } else {
- set_fpu_register(fd_reg, 0);
- }
- break;
- default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED
- UNREACHABLE();
- }
- break;
- default:
- UNREACHABLE();
- }
+ DecodeTypeRegisterCOP1(instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u, rd_reg,
+ fr_reg, fs_reg, ft_reg, fd_reg, alu_out);
break;
case COP1X:
- switch (instr->FunctionFieldRaw()) {
- case MADD_D:
- double fr, ft, fs;
- fr = get_fpu_register_double(fr_reg);
- fs = get_fpu_register_double(fs_reg);
- ft = get_fpu_register_double(ft_reg);
- set_fpu_register_double(fd_reg, fs * ft + fr);
- break;
- default:
- UNREACHABLE();
- }
+ DecodeTypeRegisterCOP1X(instr, fr_reg, fs_reg, ft_reg, fd_reg);
break;
case SPECIAL:
- switch (instr->FunctionFieldRaw()) {
- case JR: {
- Instruction* branch_delay_instr = reinterpret_cast<Instruction*>(
- current_pc+Instruction::kInstrSize);
- BranchDelayInstructionDecode(branch_delay_instr);
- set_pc(next_pc);
- pc_modified_ = true;
- break;
- }
- case JALR: {
- Instruction* branch_delay_instr = reinterpret_cast<Instruction*>(
- current_pc+Instruction::kInstrSize);
- BranchDelayInstructionDecode(branch_delay_instr);
- set_register(return_addr_reg,
- current_pc + 2 * Instruction::kInstrSize);
- set_pc(next_pc);
- pc_modified_ = true;
- break;
- }
- // Instructions using HI and LO registers.
- case MULT:
- if (kArchVariant != kMips64r6) {
- set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff));
- set_register(HI, static_cast<int32_t>(i64hilo >> 32));
- } else {
- switch (instr->SaValue()) {
- case MUL_OP:
- set_register(rd_reg,
- static_cast<int32_t>(i64hilo & 0xffffffff));
- break;
- case MUH_OP:
- set_register(rd_reg, static_cast<int32_t>(i64hilo >> 32));
- break;
- default:
- UNIMPLEMENTED_MIPS();
- break;
- }
- }
- break;
- case MULTU:
- set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff));
- set_register(HI, static_cast<int32_t>(u64hilo >> 32));
- break;
- case DMULT: // DMULT == D_MUL_MUH.
- if (kArchVariant != kMips64r6) {
- set_register(LO, static_cast<int64_t>(i128resultL));
- set_register(HI, static_cast<int64_t>(i128resultH));
- } else {
- switch (instr->SaValue()) {
- case MUL_OP:
- set_register(rd_reg, static_cast<int64_t>(i128resultL));
- break;
- case MUH_OP:
- set_register(rd_reg, static_cast<int64_t>(i128resultH));
- break;
- default:
- UNIMPLEMENTED_MIPS();
- break;
- }
- }
- break;
- case DMULTU:
- UNIMPLEMENTED_MIPS();
- break;
- case DSLL:
- set_register(rd_reg, alu_out);
- break;
- case DIV:
- case DDIV:
- switch (kArchVariant) {
- case kMips64r2:
- // Divide by zero and overflow was not checked in the
- // configuration step - div and divu do not raise exceptions. On
- // division by 0 the result will be UNPREDICTABLE. On overflow
- // (INT_MIN/-1), return INT_MIN which is what the hardware does.
- if (rs == INT_MIN && rt == -1) {
- set_register(LO, INT_MIN);
- set_register(HI, 0);
- } else if (rt != 0) {
- set_register(LO, rs / rt);
- set_register(HI, rs % rt);
- }
- break;
- case kMips64r6:
- switch (instr->SaValue()) {
- case DIV_OP:
- if (rs == INT_MIN && rt == -1) {
- set_register(rd_reg, INT_MIN);
- } else if (rt != 0) {
- set_register(rd_reg, rs / rt);
- }
- break;
- case MOD_OP:
- if (rs == INT_MIN && rt == -1) {
- set_register(rd_reg, 0);
- } else if (rt != 0) {
- set_register(rd_reg, rs % rt);
- }
- break;
- default:
- UNIMPLEMENTED_MIPS();
- break;
- }
- break;
- default:
- break;
- }
- break;
- case DIVU:
- if (rt_u != 0) {
- set_register(LO, rs_u / rt_u);
- set_register(HI, rs_u % rt_u);
- }
- break;
- // Break and trap instructions.
- case BREAK:
- case TGE:
- case TGEU:
- case TLT:
- case TLTU:
- case TEQ:
- case TNE:
- if (do_interrupt) {
- SoftwareInterrupt(instr);
- }
- break;
- // Conditional moves.
- case MOVN:
- if (rt) {
- set_register(rd_reg, rs);
- TraceRegWr(rs);
- }
- break;
- case MOVCI: {
- uint32_t cc = instr->FBccValue();
- uint32_t fcsr_cc = get_fcsr_condition_bit(cc);
- if (instr->Bit(16)) { // Read Tf bit.
- if (test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs);
- } else {
- if (!test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs);
- }
- break;
- }
- case MOVZ:
- if (!rt) {
- set_register(rd_reg, rs);
- TraceRegWr(rs);
- }
- break;
- default: // For other special opcodes we do the default operation.
- set_register(rd_reg, alu_out);
- TraceRegWr(alu_out);
- }
+ DecodeTypeRegisterSPECIAL(
+ instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u, rd_reg, fr_reg, fs_reg,
+ ft_reg, fd_reg, i64hilo, u64hilo, alu_out, do_interrupt, current_pc,
+ next_pc, return_addr_reg, i128resultH, i128resultL);
break;
case SPECIAL2:
- switch (instr->FunctionFieldRaw()) {
- case MUL:
- set_register(rd_reg, alu_out);
- TraceRegWr(alu_out);
- // HI and LO are UNPREDICTABLE after the operation.
- set_register(LO, Unpredictable);
- set_register(HI, Unpredictable);
- break;
- default: // For other special2 opcodes we do the default operation.
- set_register(rd_reg, alu_out);
- }
+ DecodeTypeRegisterSPECIAL2(instr, rd_reg, alu_out);
break;
case SPECIAL3:
- switch (instr->FunctionFieldRaw()) {
- case INS:
- // Ins instr leaves result in Rt, rather than Rd.
- set_register(rt_reg, alu_out);
- TraceRegWr(alu_out);
- break;
- case EXT:
- case DEXT:
- // Dext/Ext instr leaves result in Rt, rather than Rd.
- set_register(rt_reg, alu_out);
- TraceRegWr(alu_out);
- break;
- default:
- UNREACHABLE();
- }
+ DecodeTypeRegisterSPECIAL3(instr, rt_reg, alu_out);
break;
// Unimplemented opcodes raised an error in the configuration step before,
// so we can use the default here to set the destination register in common
inline int32_t SetDoubleHIW(double* addr);
inline int32_t SetDoubleLOW(double* addr);
+ // functions called from DecodeTypeRegister
+ void DecodeTypeRegisterCOP1(Instruction* instr, const int64_t& rs_reg,
+ const int64_t& rs, const uint64_t& rs_u,
+ const int64_t& rt_reg, const int64_t& rt,
+ const uint64_t& rt_u, const int64_t& rd_reg,
+ const int32_t& fr_reg, const int32_t& fs_reg,
+ const int32_t& ft_reg, const int64_t& fd_reg,
+ int64_t& alu_out);
+
+ void DecodeTypeRegisterCOP1X(Instruction* instr, const int32_t& fr_reg,
+ const int32_t& fs_reg, const int32_t& ft_reg,
+ const int64_t& fd_reg);
+
+ void DecodeTypeRegisterSPECIAL(
+ Instruction* instr, const int64_t& rs_reg, const int64_t& rs,
+ const uint64_t& rs_u, const int64_t& rt_reg, const int64_t& rt,
+ const uint64_t& rt_u, const int64_t& rd_reg, const int32_t& fr_reg,
+ const int32_t& fs_reg, const int32_t& ft_reg, const int64_t& fd_reg,
+ int64_t& i64hilo, uint64_t& u64hilo, int64_t& alu_out, bool& do_interrupt,
+ int64_t& current_pc, int64_t& next_pc, int64_t& return_addr_reg,
+ int64_t& i128resultH, int64_t& i128resultL);
+
+ void DecodeTypeRegisterSPECIAL2(Instruction* instr, const int64_t& rd_reg,
+ int64_t& alu_out);
+
+ void DecodeTypeRegisterSPECIAL3(Instruction* instr, const int64_t& rt_reg,
+ int64_t& alu_out);
+
+ void DecodeTypeRegisterSRsType(Instruction* instr, const int32_t& fs_reg,
+ const int64_t& fd_reg);
+
+ void DecodeTypeRegisterDRsType(Instruction* instr, const int32_t& fs_reg,
+ const int64_t& ft_reg, const int32_t& fd_reg);
+
+ void DecodeTypeRegisterWRsType(Instruction* instr, const int32_t& fs_reg,
+ const int32_t& fd_reg, int64_t& alu_out);
+
+ void DecodeTypeRegisterLRsType(Instruction* instr, const int32_t& fs_reg,
+ const int32_t& fd_reg, const int32_t& ft_reg);
// Executing is handled based on the instruction type.
void DecodeTypeRegister(Instruction* instr);
#define __ assm.
+TEST(MIPS16) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ MacroAssembler assm(isolate, NULL, 0);
+
+ typedef struct test {
+ int a;
+ int b;
+ int c;
+ int d;
+ double e;
+ double f;
+ double g;
+ double h;
+ double i;
+ double j;
+ double k;
+ double l;
+ } Test;
+
+ Test test;
+ // integer part of test
+ __ addiu(t1, zero_reg, 1); // t1=1
+ __ seleqz(t1, zero_reg, t3); // t3=1
+ __ sw(t3, MemOperand(a0, OFFSET_OF(Test, a))); // a=1
+ __ seleqz(t1, t1, t2); // t2=0
+ __ sw(t2, MemOperand(a0, OFFSET_OF(Test, b))); // b=0
+ __ selnez(t1, zero_reg, t3); // t3=1;
+ __ sw(t3, MemOperand(a0, OFFSET_OF(Test, c))); // c=0
+ __ selnez(t1, t1, t3); // t3=1
+ __ sw(t3, MemOperand(a0, OFFSET_OF(Test, d))); // d=1
+ // floating point part of test S format
+ __ li(t0, 0x80);
+ __ mtc1(t0, f4);
+ __ cvt_d_w(f4, f4); // f4=0x80
+ __ li(t0, 0xf3);
+ __ mtc1(t0, f6);
+ __ cvt_d_w(f6, f6); // f6=0xf3
+ __ seleqz(S, f8, f4, f6); // f8=0xf3
+ __ seleqz(S, f10, f6, f6); // f10=0
+ __ sdc1(f8, MemOperand(a0, OFFSET_OF(Test, e))); // e=0xf3
+ __ sdc1(f10, MemOperand(a0, OFFSET_OF(Test, f))); // f=0
+ __ selnez(S, f8, f4, f6); // f8=0
+ __ selnez(S, f10, f6, f6); // f10=0xf3*/
+ __ sdc1(f8, MemOperand(a0, OFFSET_OF(Test, g))); // g=0
+ __ sdc1(f10, MemOperand(a0, OFFSET_OF(Test, h))); // h=0xf3
+
+ __ li(t0, 0x80);
+ __ mtc1(t0, f4);
+ __ cvt_d_w(f4, f4); // f4=0x80
+ __ li(t0, 0xf3);
+ __ mtc1(t0, f6);
+ __ cvt_d_w(f6, f6); // f6=0xf3
+ __ seleqz(D, f8, f4, f6); // f8=0xf3
+ __ seleqz(D, f10, f6, f6); // f10=0
+ __ sdc1(f8, MemOperand(a0, OFFSET_OF(Test, i))); // i=0xf3
+ __ sdc1(f10, MemOperand(a0, OFFSET_OF(Test, j))); // j=0
+ __ selnez(S, f8, f4, f6); // f8=0
+ __ selnez(S, f10, f6, f6); // f10=0xf3*/
+ __ sdc1(f8, MemOperand(a0, OFFSET_OF(Test, k))); // k=0
+ __ sdc1(f10, MemOperand(a0, OFFSET_OF(Test, l))); // l=0xf3
+ __ jr(ra);
+ __ nop();
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+
+ (CALL_GENERATED_CODE(f, &test, 0, 0, 0, 0));
+
+ CHECK_EQ(test.a, 1);
+ CHECK_EQ(test.b, 0);
+ CHECK_EQ(test.c, 0);
+ CHECK_EQ(test.d, 1);
+
+ CHECK_EQ(test.e, 0xf3);
+ CHECK_EQ(test.f, 0x0);
+ CHECK_EQ(test.g, 0);
+ CHECK_EQ(test.h, 0xf3);
+
+ CHECK_EQ(test.i, 0xf3);
+ CHECK_EQ(test.j, 0x0);
+ CHECK_EQ(test.k, 0);
+ CHECK_EQ(test.l, 0xf3);
+}
TEST(MIPS0) {
#define __ assm.
+TEST(MIPS17) {
+ if (kArchVariant == kMips64r6) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ MacroAssembler assm(isolate, NULL, 0);
+
+ typedef struct test {
+ int a;
+ int b;
+ int c;
+ int d;
+ double i;
+ double j;
+ double k;
+ double l;
+ } Test;
+
+ Test test;
+ // integer part of test
+ __ addiu(t1, zero_reg, 1); // t1=1
+ __ seleqz(t1, zero_reg, t3); // t3=1
+ __ sw(t3, MemOperand(a0, OFFSET_OF(Test, a))); // a=1
+ __ seleqz(t1, t1, t2); // t2=0
+ __ sw(t2, MemOperand(a0, OFFSET_OF(Test, b))); // b=0
+ __ selnez(t1, zero_reg, t3); // t3=1;
+ __ sw(t3, MemOperand(a0, OFFSET_OF(Test, c))); // c=0
+ __ selnez(t1, t1, t3); // t3=1
+ __ sw(t3, MemOperand(a0, OFFSET_OF(Test, d))); // d=1
+ // floating point part of test
+ __ li(t0, 0x80);
+ __ mtc1(t0, f4);
+ __ cvt_d_w(f4, f4); // f4=0x80
+ __ li(t0, 0xf3);
+ __ mtc1(t0, f6);
+ __ cvt_d_w(f6, f6); // f6=0xf3
+ __ seleqz(D, f8, f4, f6); // f8=0xf3
+ __ seleqz(D, f10, f6, f6); // f10=0
+ __ sdc1(f8, MemOperand(a0, OFFSET_OF(Test, i))); // i=0xf3
+ __ sdc1(f10, MemOperand(a0, OFFSET_OF(Test, j))); // j=0
+ __ selnez(D, f8, f4, f6); // f8=0
+ __ selnez(D, f10, f6, f6); // f10=0xf3*/
+ __ sdc1(f8, MemOperand(a0, OFFSET_OF(Test, k))); // k=0
+ __ sdc1(f10, MemOperand(a0, OFFSET_OF(Test, l))); // l=0xf3
+ __ jr(ra);
+ __ nop();
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+
+ (CALL_GENERATED_CODE(f, &test, 0, 0, 0, 0));
+
+ CHECK_EQ(test.a, 1);
+ CHECK_EQ(test.b, 0);
+ CHECK_EQ(test.c, 0);
+ CHECK_EQ(test.d, 1);
+
+ CHECK_EQ(test.i, 0xf3);
+ CHECK_EQ(test.j, 0x0);
+ CHECK_EQ(test.k, 0);
+ CHECK_EQ(test.l, 0xf3);
+ }
+}
+
TEST(MIPS0) {
CcTest::InitializeVM();
if (failure) { \
V8_Fatal(__FILE__, __LINE__, "MIPS Disassembler tests failed.\n"); \
}
+// tests only seleqz, selnez, seleqz.fmt and selnez.fmt
+TEST(Type1) {
+ SET_UP();
+ if (IsMipsArchVariant(kMips32r6)) {
+ COMPARE(seleqz(a0, a1, a2), "00853035 seleqz a0, a1, a2");
+ COMPARE(selnez(a0, a1, a2), "00853037 selnez a0, a1, a2");
+
+ COMPARE(seleqz(S, f0, f1, f2), "45000894 seleqz.S f0, f1, f2");
+ COMPARE(selnez(S, f0, f1, f2), "45000897 selnez.S f0, f1, f2");
+ COMPARE(seleqz(D, f3, f4, f5), "00853035 seleqz.D f3, f4, f5");
+ COMPARE(selnez(D, f3, f4, f5), "00853037 selnez.D f3, f4, f5");
+ }
+ VERIFY_RUN();
+}
TEST(Type0) {
}
+TEST(Type1) {
+ if (kArchVariant == kMips64r6) {
+ SET_UP();
+ COMPARE(seleqz(a0, a1, a2), "00853035 seleqz a0, a1, a2");
+ COMPARE(selnez(a0, a1, a2), "00853037 selnez a0, a1, a2");
+
+
+ COMPARE(seleqz(D, f3, f4, f5), "462428d4 seleqz.D f4, f5, f3");
+ COMPARE(selnez(D, f3, f4, f5), "462428d7 selnez.D f4, f5, f3");
+
+ /*COMPARE(min(D, f3, f4, f5),
+ "462428dc min.D f4, f5, f3");
+ COMPARE(max(D, f3, f4, f5),
+ "462428de max.D f4, f5, f3");*/
+ VERIFY_RUN();
+ }
+}
+
+
TEST(Type0) {
SET_UP();