Initial support for MIPS16e in virtualmips emulator

Also:
- A few bugfixes in virtualmips
- Compile Smaller C (smlrc) using MIPS16e
  (this frees up ~20KB of space for smlrc improvements)
TBD:
- MIPS16e disassembler in virtualmips
This commit is contained in:
Alexey Frunze
2015-10-25 02:40:23 -07:00
parent c1ff69edd2
commit f8bde663cf
5 changed files with 663 additions and 19 deletions

View File

@@ -436,7 +436,7 @@ void mips_trigger_exception (cpu_mips_t * cpu, u_int exc_code, int bd_slot)
/* we don't set EPC if EXL is set */
if (!(cp0->reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_EXL)) {
cp0->reg[MIPS_CP0_EPC] = cpu->pc;
cp0->reg[MIPS_CP0_EPC] = cpu->pc | cpu->is_mips16e;
/*Cause BD is not update. MIPS VOLUME V3 P65 */
cause &= ~MIPS_CP0_CAUSE_BD_SLOT; //clear bd
if (bd_slot)
@@ -446,6 +446,7 @@ void mips_trigger_exception (cpu_mips_t * cpu, u_int exc_code, int bd_slot)
}
cpu->is_mips16e = 0;
cause &= ~MIPS_CP0_CAUSE_EXC_MASK; //clear exec-code
cause |= (exc_code << 2);
cp0->reg[MIPS_CP0_CAUSE] = cause;
@@ -535,6 +536,8 @@ void fastcall mips_exec_eret (cpu_mips_t * cpu)
/* We have to clear the LLbit */
cpu->ll_bit = 0;
cpu->is_mips16e = cpu->pc & 1;
cpu->pc &= 0xFFFFFFFE;
}
/* Execute BREAK instruction */

View File

@@ -427,6 +427,9 @@ struct cpu_mips {
u_int addr_mode;
int is_in_bdslot;
int insn_len; /* length of last fetched instruction in bytes */
int is_mips16e; /* 1 if ISA Mode is MIPS16e, 0 if MIPS32 */
int trace_syscall;
/* Current exec page (non-JIT) info */

View File

@@ -610,6 +610,10 @@ static int div_op (cpu_mips_t * cpu, mips_insn_t insn)
int rs = bits (insn, 21, 25);
int rt = bits (insn, 16, 20);
if (cpu->gpr[rt] == 0 ||
(cpu->gpr[rs] == 0x80000000 && cpu->gpr[rt] == 0xFFFFFFFF))
return (0);
cpu->lo = (m_int32_t) cpu->gpr[rs] / (m_int32_t) cpu->gpr[rt];
cpu->hi = (m_int32_t) cpu->gpr[rs] % (m_int32_t) cpu->gpr[rt];
@@ -753,7 +757,7 @@ static int jal_op (cpu_mips_t * cpu, mips_insn_t insn)
m_va_t new_pc;
/* compute the new pc */
new_pc = cpu->pc & ~((1 << 28) - 1);
new_pc = (cpu->pc + 4) & ~((1 << 28) - 1);
new_pc |= instr_index << 2;
/* set the return address (instruction after the delay slot) */
@@ -766,6 +770,27 @@ static int jal_op (cpu_mips_t * cpu, mips_insn_t insn)
return (1);
}
static int jalx_op (cpu_mips_t * cpu, mips_insn_t insn)
{
u_int instr_index = bits (insn, 0, 25);
m_va_t new_pc;
/* compute the new pc */
new_pc = (cpu->pc + 4) & ~((1 << 28) - 1);
new_pc |= instr_index << 2;
/* set the return address (instruction after the delay slot) */
cpu->reg_set (cpu, MIPS_GPR_RA, cpu->pc + 8);
int ins_res = mips_exec_bdslot (cpu);
if (likely (!ins_res)) {
cpu->is_mips16e = 1;
cpu->pc = new_pc;
}
return (1);
}
static int jalr_op (cpu_mips_t * cpu, mips_insn_t insn)
{
int rs = bits (insn, 21, 25);
@@ -779,8 +804,10 @@ static int jalr_op (cpu_mips_t * cpu, mips_insn_t insn)
new_pc = cpu->gpr[rs];
int ins_res = mips_exec_bdslot (cpu);
if (likely (!ins_res))
cpu->pc = new_pc;
if (likely (!ins_res)) {
cpu->is_mips16e = new_pc & 1;
cpu->pc = new_pc & 0xFFFFFFFE;
}
return (1);
}
@@ -794,8 +821,10 @@ static int jr_op (cpu_mips_t * cpu, mips_insn_t insn)
new_pc = cpu->gpr[rs];
int ins_res = mips_exec_bdslot (cpu);
if (likely (!ins_res))
cpu->pc = new_pc;
if (likely (!ins_res)) {
cpu->is_mips16e = new_pc & 1;
cpu->pc = new_pc & 0xFFFFFFFE;
}
return (1);
}
@@ -1775,7 +1804,7 @@ static const struct mips_op_desc mips_opcodes[] = {
{"ldl", ldl_op, 0x1A},
{"ldr", ldr_op, 0x1B},
{"spec2", spec2_op, 0x1C}, /* indexed by FUNC field */
{"undef", undef_op, 0x1D},
{"jalx", jalx_op, 0x1D},
{"undef", undef_op, 0x1E},
{"spec3", spec3_op, 0x1F}, /* indexed by FUNC field */
{"lb", lb_op, 0x20},
@@ -2167,3 +2196,553 @@ static const struct mips_op_desc mips_tlb_opcodes[] = {
{"?tlb", undef_tlb, 0x3e},
{"?tlb", undef_tlb, 0x3f},
};
static int mips_exec_mips16e(cpu_mips_t* cpu, mips_insn_t instr)
{
mips_insn_t extend = instr >> 16;
const m_va_t pc = cpu->pc;
m_va_t nextPc = pc + cpu->insn_len;
int res = 0;
instr &= 0xFFFF;
#define xlat(r) ((r) | (((r) - 2) & 16))
#define op (instr >> 11)
#define imm2 (instr & 0x3) // RRR/SHIFT-funct
#define imm3 (instr & 0x7) // MOV32R rz
#define imm4 (instr & 0xF) // SVRS framesize
#define simm4 (imm4 - ((instr & 0x8) << 1))
#define imm5 (instr & 0x1F) // MOVR32 r32
#define imm8 ((uint8_t)instr)
#define simm8 ((int8_t)instr)
#define imm11 (instr & 0x7FF)
#define simm11 (imm11 - ((instr & 0x400) << 1))
#define imm15 (((extend & 0xF) << 11) | (extend & 0x7F0) | imm4) // EXT-RRI-A addiu
#define simm15 (imm15 - ((extend & 0x8) << 12))
#define imm16 (((extend & 0x1F) << 11) | (extend & 0x7E0) | imm5)
#define simm16 ((int16_t)imm16)
#define imm26 (((extend & 0x1F) << 21) | ((extend & 0x3E0) << 11) | (uint16_t)instr) // jal(x)
#define rx ((instr >> 8) & 0x7) // funct/SVRS
#define ry ((instr >> 5) & 0x7) // RR-funct
#define rz ((instr >> 2) & 0x7) // sa
#define r32s ((instr & 0x18) | ((instr >> 5) & 0x7)) // MOV32R split/swapped r32
#define sa5 ((extend >> 6) & 0x1F) // EXT-SHIFT
#define fmsz8 ((extend & 0xF0) | imm4) // EXT-SVRS
#define aregs (extend & 0xF) // EXT-SVRS
#define xsregs ((extend >> 8) & 0x7) // EXT-SVRS
#define code6 ((instr >> 5) & 0x3F) // break, sdbbp
// TBD!!! better checks for invalid encodings
if ((extend >> 11) == 3) {
// jal(x) adr26<<2 (32-bit instruction; delay slot)
cpu->reg_set(cpu, MIPS_GPR_RA, nextPc + 3); // 2 for non-extended instruction in delay slot + 1 for ISA Mode
if (mips_exec_bdslot(cpu) == 0) {
nextPc = (nextPc & 0xF0000000) | (imm26 << 2);
cpu->pc = nextPc;
cpu->is_mips16e = (extend & 0x400) == 0; // jalx switches to MIPS32
}
res = 1;
} else if (!extend) {
switch (op) {
case 0: // addiu[sp] rx, sp, imm8
cpu->reg_set(cpu, xlat(rx), cpu->gpr[MIPS_GPR_SP] + (imm8 << 2));
break;
case 1: // addiu[pc] rx, pc, imm8
cpu->reg_set(cpu, xlat(rx), (pc + (imm8 << 2)) & 0xFFFFFFFC);
break;
case 2: // b ofs11<<1 (no delay slot)
nextPc += simm11 << 1;
cpu->pc = nextPc;
res = 1;
break;
case 4: // beqz rx, ofs8<<1 (no delay slot)
if (cpu->gpr[xlat(rx)] == 0)
nextPc += simm8 << 1;
cpu->pc = nextPc;
res = 1;
break;
case 5: // bnez rx, ofs8<<1 (no delay slot)
if (cpu->gpr[xlat(rx)] != 0)
nextPc += simm8 << 1;
cpu->pc = nextPc;
res = 1;
break;
case 6: // SHIFT
switch (imm2) {
case 0: // sll rx, ry, imm3
cpu->reg_set(cpu, xlat(rx), cpu->gpr[xlat(ry)] << (rz | ((rz - 1) & 8)));
break;
case 2: // srl rx, ry, imm3
cpu->reg_set(cpu, xlat(rx), cpu->gpr[xlat(ry)] >> (rz | ((rz - 1) & 8)));
break;
case 3: // sra rx, ry, imm3
cpu->reg_set(cpu, xlat(rx), (int32_t)cpu->gpr[xlat(ry)] >> (rz | ((rz - 1) & 8)));
break;
default:
goto lInvalidInstruction;
}
break;
case 8: // addiu ry, rx, imm4
cpu->reg_set(cpu, xlat(ry), cpu->gpr[xlat(rx)] + simm4);
break;
case 9: // addiu[8] rx, imm8
cpu->reg_set(cpu, xlat(rx), cpu->gpr[xlat(rx)] + simm8);
break;
case 10: // slti rx, imm8
cpu->reg_set(cpu, MIPS_GPR_T8, (int32_t)cpu->gpr[xlat(rx)] < (int32_t)imm8);
break;
case 11: // sltiu rx, imm8
cpu->reg_set(cpu, MIPS_GPR_T8, cpu->gpr[xlat(rx)] < imm8);
break;
case 12: // I8
switch (rx) {
case 0: // bteqz ofs8<<1 (no delay slot)
if (cpu->gpr[MIPS_GPR_T8] == 0)
nextPc += simm8 << 1;
cpu->pc = nextPc;
res = 1;
break;
case 1: // btnez ofs8<<1 (no delay slot)
if (cpu->gpr[MIPS_GPR_T8] != 0)
nextPc += simm8 << 1;
cpu->pc = nextPc;
res = 1;
break;
case 2: // sw[rasp] ra, ofs8<<2(sp)
res = mips_exec_memop2(cpu, MIPS_MEMOP_SW, MIPS_GPR_SP, imm8 << 2, MIPS_GPR_RA, FALSE);
break;
case 3: // ADJSP AKA addiu sp, imm8
cpu->reg_set(cpu, MIPS_GPR_SP, cpu->gpr[MIPS_GPR_SP] + (simm8 << 3));
break;
case 4: // SVRS
if (instr & 0x80) { // save
uint32_t temp = cpu->gpr[MIPS_GPR_SP];
cpu->reg_set(cpu, MIPS_GPR_SP, cpu->gpr[MIPS_GPR_SP] - (imm4 ? imm4 * 8 : 128));
if (instr & 0x40) // ra
res |= mips_exec_memop(cpu, MIPS_MEMOP_SW, temp -= 4, MIPS_GPR_RA, FALSE);
if (instr & 0x10) // s1
res |= mips_exec_memop(cpu, MIPS_MEMOP_SW, temp -= 4, MIPS_GPR_S1, FALSE);
if (instr & 0x20) // s0
res |= mips_exec_memop(cpu, MIPS_MEMOP_SW, temp -= 4, MIPS_GPR_S0, FALSE);
} else { // restore
uint32_t temp = cpu->gpr[MIPS_GPR_SP] + (imm4 ? imm4 * 8 : 128), temp2 = temp;
if (instr & 0x40) // ra
res |= mips_exec_memop(cpu, MIPS_MEMOP_LW, temp -= 4, MIPS_GPR_RA, TRUE);
if (instr & 0x10) // s1
res |= mips_exec_memop(cpu, MIPS_MEMOP_LW, temp -= 4, MIPS_GPR_S1, TRUE);
if (instr & 0x20) // s0
res |= mips_exec_memop(cpu, MIPS_MEMOP_LW, temp -= 4, MIPS_GPR_S0, TRUE);
cpu->reg_set(cpu, MIPS_GPR_SP, temp2);
}
break;
case 5: // move r32, rz (nop = move $0, $16)
cpu->reg_set(cpu, r32s, cpu->gpr[xlat(imm3)]);
cpu->gpr[0] = 0;
break;
case 7: // move ry, r32
cpu->reg_set(cpu, xlat(ry), cpu->gpr[imm5]);
break;
default:
goto lInvalidInstruction;
}
break;
case 13: // li rx, imm8
cpu->reg_set(cpu, xlat(rx), imm8);
break;
case 14: // cmpi rx, imm8
cpu->reg_set(cpu, MIPS_GPR_T8, cpu->gpr[xlat(rx)] ^ imm8);
break;
case 16: // lb ry, ofs5(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_LB, xlat(rx), imm5, xlat(ry), TRUE);
break;
case 17: // lh ry, ofs5<<1(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_LH, xlat(rx), imm5 << 1, xlat(ry), TRUE);
break;
case 18: // lw[sp] rx, ofs8<<2(sp)
res = mips_exec_memop2(cpu, MIPS_MEMOP_LW, MIPS_GPR_SP, imm8 << 2, xlat(rx), TRUE);
break;
case 19: // lw ry, ofs5<<2(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_LW, xlat(rx), imm5 << 2, xlat(ry), TRUE);
break;
case 20: // lbu ry, ofs5(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_LBU, xlat(rx), imm5, xlat(ry), TRUE);
break;
case 21: // lhu ry, ofs5<<1(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_LHU, xlat(rx), imm5 << 1, xlat(ry), TRUE);
break;
case 22: // lw[pc] rx, ofs8<<2(pc)
res = mips_exec_memop(cpu, MIPS_MEMOP_LW, (pc + (imm8 << 2)) & 0xFFFFFFFC, xlat(rx), TRUE);
break;
case 24: // sb ry, ofs5(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_SB, xlat(rx), imm5, xlat(ry), FALSE);
break;
case 25: // sh ry, ofs5<<1(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_SH, xlat(rx), imm5 << 1, xlat(ry), FALSE);
break;
case 26: // sw[sp] rx, ofs8<<2(sp)
res = mips_exec_memop2(cpu, MIPS_MEMOP_SW, MIPS_GPR_SP, imm8 << 2, xlat(rx), FALSE);
break;
case 27: // sw ry, ofs5<<2(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_SW, xlat(rx), imm5 << 2, xlat(ry), FALSE);
break;
case 28: // RRR
switch (imm2) {
case 1: // addu rz, rx, ry
cpu->reg_set(cpu, xlat(rz), cpu->gpr[xlat(rx)] + cpu->gpr[xlat(ry)]);
break;
case 3: // subu rz, rx, ry
cpu->reg_set(cpu, xlat(rz), cpu->gpr[xlat(rx)] - cpu->gpr[xlat(ry)]);
break;
default:
goto lInvalidInstruction;
}
break;
case 29: // RR
switch (imm5) {
case 0: // J(AL)R(C)
switch (ry) {
case 0: // jr rx (delay slot)
nextPc = cpu->gpr[xlat(rx)];
if (mips_exec_bdslot(cpu) == 0) {
cpu->pc = nextPc & 0xFFFFFFFE;
cpu->is_mips16e = nextPc & 1; // may switch to MIPS32
}
res = 1;
break;
case 1: // jr ra (delay slot)
nextPc = cpu->gpr[MIPS_GPR_RA];
if (mips_exec_bdslot(cpu) == 0) {
cpu->pc = nextPc & 0xFFFFFFFE;
cpu->is_mips16e = nextPc & 1; // may switch to MIPS32
}
res = 1;
break;
case 2: // jalr (delay slot)
cpu->reg_set(cpu, MIPS_GPR_RA, nextPc + 3); // 2 for non-extended instruction in delay slot + 1 for ISA Mode
nextPc = cpu->gpr[xlat(rx)];
if (mips_exec_bdslot(cpu) == 0) {
cpu->pc = nextPc & 0xFFFFFFFE;
cpu->is_mips16e = nextPc & 1; // may switch to MIPS32
}
res = 1;
break;
case 4: // jrc rx (no delay slot)
nextPc = cpu->gpr[xlat(rx)];
cpu->pc = nextPc & 0xFFFFFFFE;
cpu->is_mips16e = nextPc & 1; // may switch to MIPS32
res = 1;
break;
case 5: // jrc ra (no delay slot)
nextPc = cpu->gpr[MIPS_GPR_RA];
cpu->pc = nextPc & 0xFFFFFFFE;
cpu->is_mips16e = nextPc & 1; // may switch to MIPS32
res = 1;
break;
case 6: // jalrc (no delay slot)
cpu->reg_set(cpu, MIPS_GPR_RA, nextPc + 1); // 1 for ISA Mode
nextPc = cpu->gpr[xlat(rx)];
cpu->pc = nextPc & 0xFFFFFFFE;
cpu->is_mips16e = nextPc & 1; // may switch to MIPS32
res = 1;
break;
default:
goto lInvalidInstruction;
}
break;
case 1: // sdbbp imm6
goto lInvalidInstruction;
case 2: // slt rx, ry
cpu->reg_set(cpu, MIPS_GPR_T8, (int32_t)cpu->gpr[xlat(rx)] < (int32_t)cpu->gpr[xlat(ry)]);
break;
case 3: // sltu rx, ry
cpu->reg_set(cpu, MIPS_GPR_T8, cpu->gpr[xlat(rx)] < cpu->gpr[xlat(ry)]);
break;
case 4: // sllv ry, rx
cpu->reg_set(cpu, xlat(ry), cpu->gpr[xlat(ry)] << (cpu->gpr[xlat(rx)] & 31));
break;
case 5: // break imm6
mips_exec_break(cpu, code6);
res = 1;
break;
case 6: // srlv ry, rx
cpu->reg_set(cpu, xlat(ry), cpu->gpr[xlat(ry)] >> (cpu->gpr[xlat(rx)] & 31));
break;
case 7: // srav ry, rx
cpu->reg_set(cpu, xlat(ry), (int32_t)cpu->gpr[xlat(ry)] >> (cpu->gpr[xlat(rx)] & 31));
break;
case 10: // cmp rx, ry
cpu->reg_set(cpu, MIPS_GPR_T8, cpu->gpr[xlat(rx)] ^ cpu->gpr[xlat(ry)]);
break;
case 11: // neg rx, ry
cpu->reg_set(cpu, xlat(rx), -cpu->gpr[xlat(ry)]);
break;
case 12: // and rx, ry
cpu->reg_set(cpu, xlat(rx), cpu->gpr[xlat(rx)] & cpu->gpr[xlat(ry)]);
break;
case 13: // or rx, ry
cpu->reg_set(cpu, xlat(rx), cpu->gpr[xlat(rx)] | cpu->gpr[xlat(ry)]);
break;
case 14: // xor rx, ry
cpu->reg_set(cpu, xlat(rx), cpu->gpr[xlat(rx)] ^ cpu->gpr[xlat(ry)]);
break;
case 15: // not rx, ry
cpu->reg_set(cpu, xlat(rx), ~cpu->gpr[xlat(ry)]);
break;
case 16: // mfhi rx
cpu->reg_set(cpu, xlat(rx), cpu->hi);
break;
case 17: // CNVT
switch (ry) {
case 0: // zeb rx
cpu->reg_set(cpu, xlat(rx), cpu->gpr[xlat(rx)] & 0xFF);
break;
case 1: // zeh rx
cpu->reg_set(cpu, xlat(rx), cpu->gpr[xlat(rx)] & 0xFFFF);
break;
case 4: // seb rx
cpu->reg_set(cpu, xlat(rx), (int8_t)cpu->gpr[xlat(rx)]);
break;
case 5: // seh rx
cpu->reg_set(cpu, xlat(rx), (int16_t)cpu->gpr[xlat(rx)]);
break;
default:
goto lInvalidInstruction;
}
break;
case 18: // mflo rx
cpu->reg_set(cpu, xlat(rx), cpu->lo);
break;
case 24: { // mult rx, ry
int64_t p = (int64_t)(int32_t)cpu->gpr[xlat(rx)] * (int32_t)cpu->gpr[xlat(ry)];
cpu->lo = (uint32_t)p;
cpu->hi = (uint32_t)(p >> 32);
}
break;
case 25: { // multu rx, ry
uint64_t p = (uint64_t)cpu->gpr[xlat(rx)] * cpu->gpr[xlat(ry)];
cpu->lo = (uint32_t)p;
cpu->hi = (uint32_t)(p >> 32);
}
break;
case 26: // div rx, ry
if (!(cpu->gpr[xlat(ry)] == 0 ||
(cpu->gpr[xlat(rx)] == 0x80000000 && cpu->gpr[xlat(ry)] == 0xFFFFFFFF))) {
cpu->lo = (int32_t)cpu->gpr[xlat(rx)] / (int32_t)cpu->gpr[xlat(ry)];
cpu->hi = (int32_t)cpu->gpr[xlat(rx)] % (int32_t)cpu->gpr[xlat(ry)];
}
break;
case 27: // divu rx, ry
if (cpu->gpr[xlat(ry)]) {
cpu->lo = cpu->gpr[xlat(rx)] / cpu->gpr[xlat(ry)];
cpu->hi = cpu->gpr[xlat(rx)] % cpu->gpr[xlat(ry)];
}
break;
default:
goto lInvalidInstruction;
}
break;
default:
goto lInvalidInstruction;
}
// ^^^ NON-EXTENDED ^^^
} else {
// vvv EXTENDED vvv
switch (op) {
case 0: // addiu[sp] rx, sp, imm16
cpu->reg_set(cpu, xlat(rx), cpu->gpr[MIPS_GPR_SP] + simm16);
break;
case 1: // addiu[pc] rx, pc, imm16
cpu->reg_set(cpu, xlat(rx), (pc & 0xFFFFFFFC) + simm16);
break;
case 2: // b ofs16<<1 (no delay slot)
nextPc += simm16 << 1;
cpu->pc = nextPc;
res = 1;
break;
case 4: // beqz rx, ofs16<<1 (no delay slot)
if (cpu->gpr[xlat(rx)] == 0)
nextPc += simm16 << 1;
cpu->pc = nextPc;
res = 1;
break;
case 5: // bnez rx, ofs16<<1 (no delay slot)
if (cpu->gpr[xlat(rx)] != 0)
nextPc += simm16 << 1;
cpu->pc = nextPc;
res = 1;
break;
case 6: // SHIFT
switch (imm2) {
case 0: // sll rx, ry, imm5
cpu->reg_set(cpu, xlat(rx), cpu->gpr[xlat(ry)] << sa5);
break;
case 2: // srl rx, ry, imm5
cpu->reg_set(cpu, xlat(rx), cpu->gpr[xlat(ry)] >> sa5);
break;
case 3: // sra rx, ry, imm5
cpu->reg_set(cpu, xlat(rx), (int32_t)cpu->gpr[xlat(ry)] >> sa5);
break;
default:
goto lInvalidInstruction;
}
break;
case 8: // addiu ry, rx, imm15
cpu->reg_set(cpu, xlat(ry), cpu->gpr[xlat(rx)] + simm15);
break;
case 9: // addiu rx, imm16
cpu->reg_set(cpu, xlat(rx), cpu->gpr[xlat(rx)] + simm16);
break;
case 10: // slti rx, imm16
cpu->reg_set(cpu, MIPS_GPR_T8, (int32_t)cpu->gpr[xlat(rx)] < simm16);
break;
case 11: // sltiu rx, imm16
cpu->reg_set(cpu, MIPS_GPR_T8, cpu->gpr[xlat(rx)] < (uint32_t)simm16);
break;
case 12: // I8
switch (rx) {
case 0: // bteqz ofs16<<1 (no delay slot)
if (cpu->gpr[MIPS_GPR_T8] == 0)
nextPc += simm16 << 1;
cpu->pc = nextPc;
res = 1;
break;
case 1: // btnez ofs16<<1 (no delay slot)
if (cpu->gpr[MIPS_GPR_T8] != 0)
nextPc += simm16 << 1;
cpu->pc = nextPc;
res = 1;
break;
case 2: // sw[rasp] ra, ofs16(sp)
res = mips_exec_memop2(cpu, MIPS_MEMOP_SW, MIPS_GPR_SP, simm16, MIPS_GPR_RA, FALSE);
break;
case 3: // ADJSP AKA addiu sp, imm16
cpu->reg_set(cpu, MIPS_GPR_SP, cpu->gpr[MIPS_GPR_SP] + simm16);
break;
case 4: { // SVRS
uint32_t astatic = 0;
uint32_t i, temp;
switch (aregs) {
case 1: case 5: case 9: case 13: astatic = 1; break;
case 2: case 6: case 10: astatic = 2; break;
case 3: case 7: astatic = 3; break;
case 11: astatic = 4; break;
}
if (instr & 0x80) { // save
uint32_t args = 0;
switch (aregs) {
case 4: case 5: case 6: case 7: args = 1; break;
case 8: case 9: case 10: args = 2; break;
case 12: case 13: args = 3; break;
case 14: args = 4; break;
}
temp = cpu->gpr[MIPS_GPR_SP];
cpu->reg_set(cpu, MIPS_GPR_SP, cpu->gpr[MIPS_GPR_SP] - fmsz8 * 8);
for (i = 0; i < args; i++)
res |= mips_exec_memop(cpu, MIPS_MEMOP_SW, temp + i * 4, 4 + i, FALSE);
if (instr & 0x40) // ra
res |= mips_exec_memop(cpu, MIPS_MEMOP_SW, temp -= 4, MIPS_GPR_RA, FALSE);
for (i = xsregs; i; i--)
res |= mips_exec_memop(cpu, MIPS_MEMOP_SW, temp -= 4, (i == 7) ? 30 : 17 + i, FALSE);
if (instr & 0x10) // s1
res |= mips_exec_memop(cpu, MIPS_MEMOP_SW, temp -= 4, MIPS_GPR_S1, FALSE);
if (instr & 0x20) // s0
res |= mips_exec_memop(cpu, MIPS_MEMOP_SW, temp -= 4, MIPS_GPR_S0, FALSE);
for (i = 0; i < astatic; i++)
res |= mips_exec_memop(cpu, MIPS_MEMOP_SW, temp -= 4, 7 - i, FALSE);
} else { // restore
uint32_t temp2 = cpu->gpr[MIPS_GPR_SP] + fmsz8 * 8;
temp = temp2;
if (instr & 0x40) // ra
res |= mips_exec_memop(cpu, MIPS_MEMOP_LW, temp -= 4, MIPS_GPR_RA, TRUE);
for (i = xsregs; i; i--)
res |= mips_exec_memop(cpu, MIPS_MEMOP_LW, temp -= 4, (i == 7) ? 30 : 17 + i, TRUE);
if (instr & 0x10) // s1
res |= mips_exec_memop(cpu, MIPS_MEMOP_LW, temp -= 4, MIPS_GPR_S1, TRUE);
if (instr & 0x20) // s0
res |= mips_exec_memop(cpu, MIPS_MEMOP_LW, temp -= 4, MIPS_GPR_S0, TRUE);
for (i = 0; i < astatic; i++)
res |= mips_exec_memop(cpu, MIPS_MEMOP_LW, temp -= 4, 7 - i, TRUE);
cpu->reg_set(cpu, MIPS_GPR_SP, temp2);
}
}
break;
default:
goto lInvalidInstruction;
}
break;
case 13: // li rx, imm16
cpu->reg_set(cpu, xlat(rx), imm16);
break;
case 14: // cmpi rx, imm16
cpu->reg_set(cpu, MIPS_GPR_T8, cpu->gpr[xlat(rx)] ^ imm16);
break;
case 16: // lb ry, ofs16(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_LB, xlat(rx), simm16, xlat(ry), TRUE);
break;
case 17: // lh ry, ofs16(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_LH, xlat(rx), simm16, xlat(ry), TRUE);
break;
case 18: // lw[sp] rx, ofs16(sp)
res = mips_exec_memop2(cpu, MIPS_MEMOP_LW, MIPS_GPR_SP, simm16, xlat(rx), TRUE);
break;
case 19: // lw ry, ofs16(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_LW, xlat(rx), simm16, xlat(ry), TRUE);
break;
case 20: // lbu ry, ofs16(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_LBU, xlat(rx), simm16, xlat(ry), TRUE);
break;
case 21: // lhu ry, ofs16(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_LHU, xlat(rx), simm16, xlat(ry), TRUE);
break;
case 22: // lw[pc] rx, ofs16(pc)
res = mips_exec_memop(cpu, MIPS_MEMOP_LW, (pc & 0xFFFFFFFC) + simm16, xlat(rx), TRUE);
break;
case 24: // sb ry, ofs16(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_SB, xlat(rx), simm16, xlat(ry), FALSE);
break;
case 25: // sh ry, ofs16(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_SH, xlat(rx), simm16, xlat(ry), FALSE);
break;
case 26: // sw[sp] rx, ofs16(sp)
res = mips_exec_memop2(cpu, MIPS_MEMOP_SW, MIPS_GPR_SP, simm16, xlat(rx), FALSE);
break;
case 27: // sw ry, ofs16(rx)
res = mips_exec_memop2(cpu, MIPS_MEMOP_SW, xlat(rx), simm16, xlat(ry), FALSE);
break;
default:
goto lInvalidInstruction;
}
}
return res;
lInvalidInstruction:
unknown_op(cpu, (extend << 16) | instr);
return 1;
#undef xlat
#undef op
#undef imm2
#undef imm3
#undef imm4
#undef simm4
#undef imm5
#undef imm8
#undef simm8
#undef imm11
#undef simm11
#undef imm15
#undef simm15
#undef imm16
#undef simm16
#undef imm26
#undef rx
#undef ry
#undef rz
#undef r32s
#undef sa5
#undef fmsz8
#undef aregs
#undef xsregs
#undef code6
}

View File

@@ -39,6 +39,9 @@ static const struct mips_op_desc mips_spec2_opcodes[];
static const struct mips_op_desc mips_spec3_opcodes[];
static const struct mips_op_desc mips_tlb_opcodes[];
static int mips_exec_mips16e (cpu_mips_t * cpu,
mips_insn_t instruction);
extern cpu_mips_t *current_cpu;
/*for emulation performance check*/
@@ -56,11 +59,9 @@ static void forced_inline mips_main_loop_wait (cpu_mips_t * cpu,
vp_get_clock (rt_clock));
}
/* Execute a memory operation (2) */
static int forced_inline mips_exec_memop2 (cpu_mips_t * cpu, int memop,
m_va_t base, int offset, u_int dst_reg, int keep_ll_bit)
static int forced_inline mips_exec_memop (cpu_mips_t * cpu, int memop,
m_va_t vaddr, u_int dst_reg, int keep_ll_bit)
{
m_va_t vaddr = cpu->gpr[base] + sign_extend (offset, 16);
mips_memop_fn fn;
if (!keep_ll_bit)
@@ -69,8 +70,16 @@ static int forced_inline mips_exec_memop2 (cpu_mips_t * cpu, int memop,
return (fn (cpu, vaddr, dst_reg));
}
/* Execute a memory operation (2) */
static int forced_inline mips_exec_memop2 (cpu_mips_t * cpu, int memop,
u_int base_reg, int offset, u_int dst_reg, int keep_ll_bit)
{
m_va_t vaddr = cpu->gpr[base_reg] + sign_extend (offset, 16);
return mips_exec_memop (cpu, memop, vaddr, dst_reg, keep_ll_bit);
}
/* Fetch an instruction */
int mips_fetch_instruction (cpu_mips_t * cpu,
static int mips_fetch_instruction_word (cpu_mips_t * cpu,
m_va_t pc, mips_insn_t * insn)
{
m_va_t exec_page;
@@ -91,6 +100,42 @@ int mips_fetch_instruction (cpu_mips_t * cpu,
return (0);
}
int mips_fetch_instruction (cpu_mips_t * cpu,
m_va_t pc, mips_insn_t * insn)
{
int res = mips_fetch_instruction_word(cpu, pc, insn);
cpu->insn_len = 4;
if (unlikely(res)) {
return res;
}
if (unlikely(cpu->is_mips16e)) {
mips_insn_t i;
if (pc & 2) {
i = *insn >> 16;
} else {
i = *insn & 0xFFFF;
}
if (unlikely((i >> 11) == 0x1E || (i >> 11) == 3)) {
/* 4-byte extended instruction or jal(x) */
if (pc & 2) {
/* 2 more bytes needed */
res = mips_fetch_instruction_word(cpu, pc + 2, insn);
if (unlikely(res)) {
return res;
}
*insn = (i << 16) | (*insn & 0xFFFF);
} else {
*insn = (*insn << 16) | (*insn >> 16);
}
} else {
/* 2-byte instruction */
*insn = i;
cpu->insn_len = 2;
}
}
return res;
}
/* Execute a single instruction */
static forced_inline int mips_exec_single_instruction (cpu_mips_t * cpu,
mips_insn_t instruction)
@@ -111,9 +156,13 @@ static forced_inline int mips_exec_single_instruction (cpu_mips_t * cpu,
exit (1);
}
#endif
register uint op;
op = MAJOR_OP (instruction);
return mips_opcodes[op].func (cpu, instruction);
if (unlikely(cpu->is_mips16e)) {
return mips_exec_mips16e (cpu, instruction);
} else {
register uint op;
op = MAJOR_OP (instruction);
return mips_opcodes[op].func (cpu, instruction);
}
}
/* Single-step execution */
@@ -121,11 +170,12 @@ void fastcall mips_exec_single_step (cpu_mips_t * cpu,
mips_insn_t instruction)
{
int res;
int insn_len = cpu->insn_len;
res = mips_exec_single_instruction (cpu, instruction);
/* Normal flow ? */
if (likely (!res))
cpu->pc += 4;
cpu->pc += insn_len;
}
void dumpregs (cpu_mips_t *cpu)
@@ -164,6 +214,8 @@ void *mips_cpu_fdd (cpu_mips_t * cpu)
start_cpu:
for (;;) {
int insn_len;
if (unlikely (cpu->state != CPU_STATE_RUNNING))
break;
@@ -182,6 +234,7 @@ start_cpu:
}
/* Fetch the instruction */
res = mips_fetch_instruction (cpu, cpu->pc, &insn);
insn_len = cpu->insn_len;
if (cpu->vm->trace_address == cpu->pc) {
/* Trace address. */
@@ -228,11 +281,12 @@ start_cpu:
}
#endif
}
res = mips_exec_single_instruction (cpu, insn);
/* Normal flow ? */
if (likely (!res))
cpu->pc += sizeof (mips_insn_t);
cpu->pc += insn_len;
}
while (cpu->cpu_thread_running) {
@@ -266,15 +320,18 @@ static forced_inline int mips_exec_bdslot (cpu_mips_t * cpu)
{
mips_insn_t insn;
int res = 0;
int insn_len = cpu->insn_len;
cpu->is_in_bdslot = 1;
/* Fetch the instruction in delay slot */
res = mips_fetch_instruction (cpu, cpu->pc + 4, &insn);
res = mips_fetch_instruction (cpu, cpu->pc + insn_len, &insn);
if (res == 1) {
/*exception when fetching instruction */
cpu->is_in_bdslot = 0;
return (1);
}
cpu->is_in_bdslot = 1;
if (cpu->vm->debug_level > 2 || (cpu->vm->debug_level > 1 &&
@@ -282,7 +339,7 @@ static forced_inline int mips_exec_bdslot (cpu_mips_t * cpu)
! (cpu->cp0.reg[MIPS_CP0_STATUS] & MIPS_CP0_STATUS_EXL)))
{
/* Print instructions in user mode. */
printf ("%08x: %08x ", cpu->pc + 4, insn);
printf ("%08x: %08x ", cpu->pc + insn_len, insn);
print_insn_mips (cpu->pc, insn, stdout);
printf ("\n");
fflush (stdout);
@@ -290,6 +347,7 @@ static forced_inline int mips_exec_bdslot (cpu_mips_t * cpu)
/* Execute the instruction */
res = mips_exec_single_instruction (cpu, insn);
cpu->is_in_bdslot = 0;
return res;
}