ARMv7: BX, MOV_IMM, IT (ITSTATE register)

This commit is contained in:
Nekotekina 2014-10-31 04:12:07 +03:00
parent de156c59e4
commit 3895c083cb
5 changed files with 239 additions and 49 deletions

View File

@ -18,11 +18,11 @@ public:
const u32 code0 = vm::psv::read16(address & ~1);
const u32 code1 = vm::psv::read16(address + 2 & ~1);
const u32 data = code0 << 16 | code1;
const u32 arg = address & 1 ? data : code1 << 16 | code0;
const u32 arg = address & 1 ? code1 << 16 | code0 : data;
for (auto& opcode : ARMv7_opcode_table)
{
if ((opcode.type >= A1) == ((address & 1) == 0) && (data & opcode.mask) == opcode.code)
if ((opcode.type < A1) == ((address & 1) == 0) && (arg & opcode.mask) == opcode.code)
{
(m_op.*opcode.func)(opcode.length == 2 ? code0 : arg, opcode.type);
return opcode.length;
@ -30,6 +30,6 @@ public:
}
m_op.UNK(data);
return 2;
return address & 1 ? 4 : 2;
}
};

View File

@ -150,15 +150,20 @@ void ARMv7Interpreter::ASR_REG(const u32 data, const ARMv7_encoding type)
void ARMv7Interpreter::B(const u32 data, const ARMv7_encoding type)
{
u32 cond = 0xf;
u32 cond = CPU.ITSTATE.advance();
u32 jump = 0; // jump = instr_size + imm32
switch (type)
{
case T1:
{
cond = (data >> 8) & 0xf;
if (cond == 0xf)
{
throw "SVC";
}
jump = 2 + sign<9, u32>((data & 0xff) << 1);
cond = (data >> 8) & 0xf; if (cond == 0xf) throw "SVC";
break;
}
case T2:
@ -168,11 +173,16 @@ void ARMv7Interpreter::B(const u32 data, const ARMv7_encoding type)
}
case T3:
{
cond = (data >> 6) & 0xf;
if (cond >= 0xe)
{
throw "Related encodings";
}
u32 s = (data >> 26) & 0x1;
u32 j1 = (data >> 13) & 0x1;
u32 j2 = (data >> 11) & 0x1;
jump = 4 + sign<21, u32>(s << 20 | j2 << 19 | j1 << 18 | (data & 0x3f0000) >> 4 | (data & 0x7ff) << 1);
cond = (data >> 6) & 0xf; if (cond >= 0xe) throw "Related encodings";
break;
}
case T4:
@ -185,8 +195,8 @@ void ARMv7Interpreter::B(const u32 data, const ARMv7_encoding type)
}
case A1:
{
jump = 1 + 4 + sign<26, u32>((data & 0xffffff) << 2);
cond = (data >> 28) & 0xf;
jump = 1 + 4 + sign<26, u32>((data & 0xffffff) << 2);
break;
}
}
@ -257,7 +267,9 @@ void ARMv7Interpreter::BKPT(const u32 data, const ARMv7_encoding type)
void ARMv7Interpreter::BL(const u32 data, const ARMv7_encoding type)
{
u32 jump = 0;
u32 cond = CPU.ITSTATE.advance();
u32 newLR = CPU.PC;
u32 imm32 = 0;
switch (type)
{
@ -266,30 +278,39 @@ void ARMv7Interpreter::BL(const u32 data, const ARMv7_encoding type)
u32 s = (data >> 26) & 0x1;
u32 i1 = (data >> 13) & 0x1 ^ s ^ 1;
u32 i2 = (data >> 11) & 0x1 ^ s ^ 1;
jump = 4 + sign<25, u32>(s << 24 | i2 << 23 | i1 << 22 | (data & 0x3ff0000) >> 4 | (data & 0x7ff) << 1);
imm32 = 4 + sign<25, u32>(s << 24 | i2 << 23 | i1 << 22 | (data & 0x3ff0000) >> 4 | (data & 0x7ff) << 1);
newLR = (CPU.PC + 4) | 1;
break;
}
case A1:
{
jump = 4 + sign<26, u32>((data & 0xffffff) << 2);
cond = data >> 28;
imm32 = 4 + sign<26, u32>((data & 0xffffff) << 2);
newLR = (CPU.PC + 4) - 4;
break;
}
default: throw __FUNCTION__;
}
CPU.LR = (CPU.PC & 1) ? CPU.PC - 4 : CPU.PC;
CPU.SetBranch(CPU.PC + jump);
if (ConditionPassed(cond))
{
CPU.LR = newLR;
CPU.SetBranch(CPU.PC + imm32);
}
}
void ARMv7Interpreter::BLX(const u32 data, const ARMv7_encoding type)
{
u32 target;
u32 cond = CPU.ITSTATE.advance();
u32 newLR = CPU.PC;
u32 target = 0;
switch (type)
{
case T1:
{
target = CPU.GPR[(data >> 3) & 0xf];
target = CPU.read_gpr((data >> 3) & 0xf);
newLR = ((CPU.PC + 2) - 2) | 1; // ???
break;
}
case T2:
@ -297,34 +318,75 @@ void ARMv7Interpreter::BLX(const u32 data, const ARMv7_encoding type)
u32 s = (data >> 26) & 0x1;
u32 i1 = (data >> 13) & 0x1 ^ s ^ 1;
u32 i2 = (data >> 11) & 0x1 ^ s ^ 1;
target = CPU.PC + 4 + sign<25, u32>(s << 24 | i2 << 23 | i1 << 22 | (data & 0x3ff0000) >> 4 | (data & 0x7ff) << 1) & ~1;
target = (CPU.PC + 4 & ~3) + sign<25, u32>(s << 24 | i2 << 23 | i1 << 22 | (data & 0x3ff0000) >> 4 | (data & 0x7ff) << 1);
newLR = (CPU.PC + 4) | 1;
break;
}
case A1:
{
target = CPU.GPR[data & 0xf];
if (!ConditionPassed(data >> 28)) return;
cond = data >> 28;
target = CPU.read_gpr(data & 0xf);
newLR = (CPU.PC + 4) - 4;
break;
}
case A2:
{
target = CPU.PC + 5 + sign<25, u32>((data & 0xffffff) << 2 | (data & 0x1000000) >> 23);
target = (CPU.PC + 4 | 1) + sign<25, u32>((data & 0xffffff) << 2 | (data & 0x1000000) >> 23);
newLR = (CPU.PC + 4) - 4;
break;
}
default: throw __FUNCTION__;
}
CPU.LR = (CPU.PC & 1) ? CPU.PC - (type == T1 ? 2 : 4) : CPU.PC - 4; // ???
CPU.SetBranch(target);
if (ConditionPassed(cond))
{
CPU.LR = newLR;
if (target & 1)
{
CPU.ISET = Thumb;
CPU.SetBranch(target & ~1);
}
else
{
CPU.ISET = ARM;
CPU.SetBranch(target);
}
}
}
void ARMv7Interpreter::BX(const u32 data, const ARMv7_encoding type)
{
u32 cond = CPU.ITSTATE.advance();
u32 target = 0;
switch (type)
{
case A1: throw __FUNCTION__;
case T1:
{
target = CPU.read_gpr((data >> 3) & 0xf);
break;
}
case A1:
{
cond = data >> 28;
target = CPU.read_gpr(data & 0xf);
}
default: throw __FUNCTION__;
}
if (ConditionPassed(cond))
{
if (target & 1)
{
CPU.ISET = Thumb;
CPU.SetBranch(target & ~1);
}
else
{
CPU.ISET = ARM;
CPU.SetBranch(target);
}
}
}
@ -336,7 +398,7 @@ void ARMv7Interpreter::CB_Z(const u32 data, const ARMv7_encoding type)
default: throw __FUNCTION__;
}
if ((CPU.GPR[data & 0x7] == 0) ^ (data & 0x800))
if ((CPU.read_gpr(data & 0x7) == 0) ^ (data & 0x800))
{
CPU.SetBranch(CPU.PC + 2 + ((data & 0xf8) >> 2) + ((data & 0x200) >> 3));
}
@ -441,7 +503,16 @@ void ARMv7Interpreter::IT(const u32 data, const ARMv7_encoding type)
{
switch (type)
{
case A1: throw __FUNCTION__;
case T1:
{
if ((data & 0xf) == 0)
{
throw "Related encodings";
}
CPU.ITSTATE.IT = data & 0xff;
return;
}
default: throw __FUNCTION__;
}
}
@ -711,16 +782,48 @@ void ARMv7Interpreter::MLS(const u32 data, const ARMv7_encoding type)
void ARMv7Interpreter::MOV_IMM(const u32 data, const ARMv7_encoding type)
{
u32 d;
u32 imm;
bool set_flags = CPU.ITSTATE;
bool carry = CPU.APSR.C;
u32 cond = CPU.ITSTATE.advance();
u32 d = 0;
u32 imm32 = 0;
switch (type)
{
case T1: d = (data >> 8) & 0x7; imm = sign<8, u32>(data & 0xff); break;
case T1:
{
d = (data >> 8) & 0x7;
imm32 = sign<8, u32>(data & 0xff);
break;
}
//case T2:
//{
// set_flags = data & 0x100000;
// d = (data >> 8) & 0xf;
// imm32 = ThumbExpandImm_C((data & 0x4000000) >> 15 | (data & 0x7000) >> 4 | (data & 0xff), carry);
// break;
//}
case T3:
{
set_flags = false;
d = (data >> 8) & 0xf;
imm32 = (data & 0xf0000) >> 4 | (data & 0x4000000) >> 15 | (data & 0x7000) >> 4 | (data & 0xff);
break;
}
default: throw __FUNCTION__;
}
CPU.write_gpr(d, imm);
if (ConditionPassed(cond))
{
CPU.write_gpr(d, imm32);
if (set_flags)
{
CPU.APSR.N = imm32 >> 31;
CPU.APSR.Z = imm32 == 0;
CPU.APSR.C = carry;
}
}
}
void ARMv7Interpreter::MOV_REG(const u32 data, const ARMv7_encoding type)
@ -810,13 +913,29 @@ void ARMv7Interpreter::MVN_RSR(const u32 data, const ARMv7_encoding type)
void ARMv7Interpreter::NOP(const u32 data, const ARMv7_encoding type)
{
u32 cond = CPU.ITSTATE.advance();
switch (type)
{
case T1: break;
case T2: break;
case A1: break;
case T1:
{
break;
}
case T2:
{
break;
}
case A1:
{
cond = data >> 28;
break;
}
default: throw __FUNCTION__;
}
if (ConditionPassed(cond))
{
}
}
@ -879,6 +998,7 @@ void ARMv7Interpreter::PKH(const u32 data, const ARMv7_encoding type)
void ARMv7Interpreter::POP(const u32 data, const ARMv7_encoding type)
{
u32 cond = CPU.ITSTATE.advance();
u16 reg_list = 0;
switch (type)
@ -900,31 +1020,39 @@ void ARMv7Interpreter::POP(const u32 data, const ARMv7_encoding type)
}
case A1:
{
reg_list = data & 0xffff; if (BitCount(reg_list) < 2) throw "LDM / LDMIA / LDMFD";
if (!ConditionPassed(data >> 28)) return;
cond = data >> 28;
reg_list = data & 0xffff;
if (BitCount(reg_list) < 2)
{
throw "LDM / LDMIA / LDMFD";
}
break;
}
case A2:
{
cond = data >> 28;
reg_list = 1 << ((data >> 12) & 0xf);
if (!ConditionPassed(data >> 28)) return;
break;
}
default: throw __FUNCTION__;
}
for (u16 mask = 1, i = 0; mask; mask <<= 1, i++)
if (ConditionPassed(cond))
{
if (reg_list & mask)
for (u16 mask = 1, i = 0; mask; mask <<= 1, i++)
{
CPU.write_gpr(i, vm::psv::read32(CPU.SP));
CPU.SP += 4;
if (reg_list & mask)
{
CPU.write_gpr(i, vm::psv::read32(CPU.SP));
CPU.SP += 4;
}
}
}
}
void ARMv7Interpreter::PUSH(const u32 data, const ARMv7_encoding type)
{
u32 cond = CPU.ITSTATE.advance();
u16 reg_list = 0;
switch (type)
@ -946,25 +1074,32 @@ void ARMv7Interpreter::PUSH(const u32 data, const ARMv7_encoding type)
}
case A1:
{
reg_list = data & 0xffff; if (BitCount(reg_list) < 2) throw "STMDB / STMFD";
if (!ConditionPassed(data >> 28)) return;
cond = data >> 28;
reg_list = data & 0xffff;
if (BitCount(reg_list) < 2)
{
throw "STMDB / STMFD";
}
break;
}
case A2:
{
cond = data >> 28;
reg_list = 1 << ((data >> 12) & 0xf);
if (!ConditionPassed(data >> 28)) return;
break;
}
default: throw __FUNCTION__;
}
for (u16 mask = 1 << 15, i = 15; mask; mask >>= 1, i--)
if (ConditionPassed(cond))
{
if (reg_list & mask)
for (u16 mask = 1 << 15, i = 15; mask; mask >>= 1, i--)
{
CPU.SP -= 4;
vm::psv::write32(CPU.SP, CPU.read_gpr(i));
if (reg_list & mask)
{
CPU.SP -= 4;
vm::psv::write32(CPU.SP, CPU.read_gpr(i));
}
}
}
}
@ -1657,9 +1792,11 @@ void ARMv7Interpreter::SUB_RSR(const u32 data, const ARMv7_encoding type)
void ARMv7Interpreter::SUB_SPI(const u32 data, const ARMv7_encoding type)
{
u32 cond = CPU.ITSTATE.advance();
switch (type)
{
case T1: CPU.SP -= (data & 0x7f) << 2; return;
case T1: if (ConditionPassed(cond)) CPU.SP -= (data & 0x7f) << 2; return;
default: throw __FUNCTION__;
}
}

View File

@ -338,7 +338,7 @@ struct ARMv7_opcode_t
static const ARMv7_opcode_t ARMv7_opcode_table[] =
{
ARMv7_OP2(0xffff, 0x0000, T1, NULL_OP),
ARMv7_OP2(0xffff, 0x0000, T1, NULL_OP), // ???
ARMv7_OP4(0xfbe0, 0x8000, 0xf140, 0x0000, T1, ADC_IMM),
ARMv7_OP4(0x0fe0, 0x0000, 0x02a0, 0x0000, A1, ADC_IMM),
@ -555,7 +555,17 @@ static const ARMv7_opcode_t ARMv7_opcode_table[] =
ARMv7_OP4(0xffff, 0xffff, 0xf3af, 0x8000, T2, NOP),
ARMv7_OP4(0x0fff, 0xffff, 0x0320, 0xf000, A1, NOP),
//
ARMv7_OP4(0xfbe0, 0x8000, 0xf060, 0x0000, T1, ORN_IMM),
ARMv7_OP4(0xffe0, 0x8000, 0xea60, 0x0000, T1, ORN_REG),
ARMv7_OP4(0xfbe0, 0x8000, 0xf040, 0x0000, T1, ORR_IMM),
ARMv7_OP4(0x0fe0, 0x0000, 0x0380, 0x0000, A1, ORR_IMM),
ARMv7_OP2(0xffc0, 0x4300, T1, ORR_REG),
ARMv7_OP4(0xffe0, 0x8000, 0xea40, 0x0000, T2, ORR_REG),
ARMv7_OP4(0x0fe0, 0x0010, 0x0180, 0x0000, A1, ORR_REG),
ARMv7_OP4(0x0fe0, 0x0090, 0x0180, 0x0010, A1, ORR_RSR),
// TODO (PKH...)
ARMv7_OP2(0xfe00, 0xbc00, T1, POP),
ARMv7_OP4(0xffff, 0x0000, 0xe8bd, 0x0000, T2, POP),
@ -569,7 +579,7 @@ static const ARMv7_opcode_t ARMv7_opcode_table[] =
ARMv7_OP4(0x0fff, 0x0000, 0x092d, 0x0000, A1, PUSH),
ARMv7_OP4(0x0fff, 0x0fff, 0x052d, 0x0004, A2, PUSH),
//
// TODO (Q*...)
ARMv7_OP2(0xf800, 0x6000, T1, STR_IMM),
ARMv7_OP2(0xf800, 0x9000, T2, STR_IMM),

View File

@ -18,7 +18,8 @@ void ARMv7Thread::InitRegs()
memset(GPR, 0, sizeof(GPR[0]) * 15);
APSR.APSR = 0;
IPSR.IPSR = 0;
PC |= 1;
ISET = Thumb;
ITSTATE.IT = 0;
SP = m_stack_addr + m_stack_size;
}

View File

@ -1,6 +1,14 @@
#pragma once
#include "Emu/CPU/CPUThread.h"
enum ARMv7InstructionSet
{
ARM,
Thumb,
Jazelle,
ThumbEE,
};
class ARMv7Thread : public CPUThread
{
public:
@ -38,6 +46,7 @@ public:
};
u32 APSR;
} APSR;
union
@ -49,8 +58,41 @@ public:
};
u32 IPSR;
} IPSR;
ARMv7InstructionSet ISET;
union
{
struct
{
u8 cond : 3;
u8 state : 5;
};
u8 IT;
u32 advance()
{
const u32 res = (state & 0xf) ? (cond << 1 | state >> 4) : 0xe /* true */;
state <<= 1;
if ((state & 0xf) == 0) // if no d
{
IT = 0; // clear ITSTATE
}
return res;
}
operator bool() const
{
return (state & 0xf) != 0;
}
} ITSTATE;
void write_gpr(u32 n, u32 value)
{
assert(n < 16);