flycast/tests/src/AicaArmTest.cpp

1090 lines
24 KiB
C++

#include "types.h"
#if FEAT_AREC != DYNAREC_NONE
#include "hw/mem/addrspace.h"
#include "hw/arm7/arm7.h"
#include "hw/aica/aica_if.h"
#include "hw/arm7/arm7_rec.h"
#include "emulator.h"
#include "gtest/gtest.h"
static const u32 N_FLAG = 1 << 31;
static const u32 Z_FLAG = 1 << 30;
static const u32 C_FLAG = 1 << 29;
static const u32 V_FLAG = 1 << 28;
static const u32 NZCV_MASK = N_FLAG | Z_FLAG | C_FLAG | V_FLAG;
namespace aica::arm::recompiler {
extern void (*EntryPoints[])();
class AicaArmTest : public ::testing::Test {
protected:
void SetUp() override
{
if (!addrspace::reserve())
die("addrspace::reserve failed");
emu.init();
emu.dc_reset(true);
Arm7Enabled = true;
}
void PrepareOp(u32 op)
{
PrepareOps(1, &op);
}
void PrepareOps(int count, u32 *ops)
{
arm_Reg[R15_ARM_NEXT].I = 0x1000;
for (int i = 0; i < count; i++)
*(u32*)&aica_ram[0x1000 + i * 4] = ops[i];
*(u32*)&aica_ram[0x1000 + count * 4] = 0xea000000 | ((u32)(-count * 4 - 8) << 2); // b pc+8-12
flush();
compile();
}
void RunOp()
{
arm_Reg[R15_ARM_NEXT].I = 0x1000;
arm_Reg[CYCL_CNT].I = 1;
arm_mainloop(arm_Reg, EntryPoints);
}
void ResetNZCV()
{
arm_Reg[RN_PSR_FLAGS].I &= ~NZCV_MASK;
}
};
#define ASSERT_NZCV_EQ(expected) ASSERT_EQ(arm_Reg[RN_PSR_FLAGS].I & NZCV_MASK, (expected));
TEST_F(AicaArmTest, ArithmeticOpsTest)
{
PrepareOp(0xe0810002); // add r0, r1, r2
arm_Reg[0].I = 0;
arm_Reg[1].I = 0xbaad0000;
arm_Reg[2].I = 0x0000cafe;
arm_Reg[RN_PSR_FLAGS].I |= NZCV_MASK;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xbaadcafe);
ASSERT_NZCV_EQ(NZCV_MASK);
PrepareOp(0xe0810000); // add r0, r1, r0
arm_Reg[0].I = 11;
arm_Reg[1].I = 22;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 33);
PrepareOp(0xe0410002); // sub r0, r1, r2
arm_Reg[0].I = 0;
arm_Reg[1].I = 0xbaadcafe;
arm_Reg[2].I = 0x0000cafe;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xbaad0000);
ASSERT_NZCV_EQ(NZCV_MASK);
PrepareOp(0xe0410000); // sub r0, r1, r0
arm_Reg[0].I = 11;
arm_Reg[1].I = 22;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 11);
PrepareOp(0xe0910002); // adds r0, r1, r2
arm_Reg[0].I = 0;
arm_Reg[1].I = 0x80000000;
arm_Reg[2].I = 0x80000000;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG | V_FLAG); // Z,C,V
PrepareOp(0xe0510002); // subs r0, r1, r2
arm_Reg[0].I = 0;
arm_Reg[1].I = 0xbaadcafe;
arm_Reg[2].I = 0x0000cafe;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xbaad0000);
ASSERT_NZCV_EQ(N_FLAG | C_FLAG); // N,C
arm_Reg[0].I = 0;
arm_Reg[1].I = 0xbaadcafe;
arm_Reg[2].I = 0xbaadcafe;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG); // Z,C
arm_Reg[0].I = 0;
arm_Reg[1].I = 0x80000000;
arm_Reg[2].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x7fffffff);
ASSERT_NZCV_EQ(C_FLAG | V_FLAG); // C,V
arm_Reg[0].I = 0;
arm_Reg[1].I = 0xffffffff;
arm_Reg[2].I = 0xffffffff;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG); // Z,C
PrepareOp(0xe0b10002); // adcs r0, r1, r2
arm_Reg[RN_PSR_FLAGS].I &= ~NZCV_MASK;
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 2;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 3);
ASSERT_NZCV_EQ(0); //
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // set C
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 4);
ASSERT_NZCV_EQ(0); //
// from armwrestler
PrepareOp(0xe0d22002); // sbcs r2,r2,r2
ResetNZCV();
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // set C
arm_Reg[2].I = 0xFFFFFFFF;
RunOp();
ASSERT_EQ(arm_Reg[2].I, 0);
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG);
// from armwrestler
PrepareOp(0xe2d22000); // sbcs r2,r2,#0
ResetNZCV();
arm_Reg[2].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[2].I, -1);
ASSERT_NZCV_EQ(N_FLAG);
PrepareOp(0xe0d10002); // sbcs r0, r1, r2
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 2;
RunOp();
ASSERT_EQ(arm_Reg[0].I, -2);
ASSERT_NZCV_EQ(N_FLAG); // N
PrepareOp(0xe0710002); // rsbs r0, r1, r2
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 2;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
ASSERT_NZCV_EQ(C_FLAG); // C, confirmed by interpreter and online arm sim
PrepareOp(0xe0f10002); // rscs r0, r1, r2
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 2;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG); // Z,C
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // C set
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 2;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
ASSERT_NZCV_EQ(C_FLAG); // C
PrepareOp(0xe1500001); // cmp r0, r1
ResetNZCV();
arm_Reg[0].I = 2;
arm_Reg[1].I = 1;
RunOp();
ASSERT_NZCV_EQ(C_FLAG); // C
PrepareOp(0xe1700001); // cmn r0, r1
ResetNZCV();
arm_Reg[0].I = 2;
arm_Reg[1].I = -1;
RunOp();
ASSERT_NZCV_EQ(C_FLAG); // C
}
TEST_F(AicaArmTest, LogicOpsTest)
{
PrepareOp(0xe1a00001); // mov r0, r1
arm_Reg[0].I = 0;
arm_Reg[1].I = 0xbaadcafe;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xbaadcafe);
PrepareOp(0xe3c100ff); // bic r0, r1, 0xff
arm_Reg[0].I = 0;
arm_Reg[1].I = 0xffff;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xff00);
PrepareOp(0xe0010002); // and r0, r1, r2
arm_Reg[0].I = 0;
arm_Reg[1].I = 0xff0f;
arm_Reg[2].I = 0xf0f0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xf000);
PrepareOp(0xe0010000); // and r0, r1, r0
arm_Reg[0].I = 0xf0f0f0f0;
arm_Reg[1].I = 0xffffffff;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xf0f0f0f0);
PrepareOp(0xe1a00251); // asr r0, r1, r2
arm_Reg[0].I = 0;
arm_Reg[1].I = 0xfffffff8;
arm_Reg[2].I = 2;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xfffffffe);
PrepareOp(0xe1a00241); // asr r0, r1, 4
arm_Reg[0].I = 0;
arm_Reg[1].I = 0x0ffffff0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x00ffffff);
PrepareOp(0xe0210002); // eor r0, r1, r2
arm_Reg[0].I = 0;
arm_Reg[1].I = 0xffff0000;
arm_Reg[2].I = 0xf0f0f0f0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x0f0ff0f0);
PrepareOp(0xe0210000); // eor r0, r1, r0
arm_Reg[0].I = 0xf0f0f0f0;
arm_Reg[1].I = 0xffffffff;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x0f0f0f0f);
PrepareOp(0xe1810000); // orr r0, r1, r0
arm_Reg[0].I = 0xf0f0f0f0;
arm_Reg[1].I = 0x0f0f0f0f;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xffffffff);
PrepareOp(0xe1a00211); // lsl r0, r1, r2
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 8;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x100);
PrepareOp(0xe1a00231); // lsr r0, r1, r2
arm_Reg[0].I = 0;
arm_Reg[1].I = 0x100;
arm_Reg[2].I = 4;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x10);
PrepareOp(0xe1a00271); // ror r0, r1, r2
arm_Reg[0].I = 0;
arm_Reg[1].I = 0x12345678;
arm_Reg[2].I = 16;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x56781234);
PrepareOp(0xe1300001); // teq r0, r1
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_NZCV_EQ(0);
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG | V_FLAG; // set C,V
arm_Reg[0].I = 1;
arm_Reg[1].I = 1;
RunOp();
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG | V_FLAG); // Z,C,V
PrepareOp(0xe1100001); // tst r0, r1
ResetNZCV();
arm_Reg[0].I = 1;
arm_Reg[1].I = 2;
RunOp();
ASSERT_NZCV_EQ(0x40000000); // Z
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG | V_FLAG; // set C,V
arm_Reg[0].I = 3;
arm_Reg[1].I = 2;
RunOp();
ASSERT_NZCV_EQ(C_FLAG | V_FLAG); // C,V
}
TEST_F(AicaArmTest, Operand2ImmTest)
{
PrepareOp(0xe2810003); // add r0, r1, #3
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 4);
PrepareOp(0xe2a10003); // adc r0, r1, #3
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 2;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 5);
PrepareOp(0xe2410003); // sub r0, r1, #3
arm_Reg[0].I = 0;
arm_Reg[1].I = 7;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 4);
PrepareOp(0xe2610003); // rsb r0, r1, #3
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 2);
PrepareOp(0xe2c10003); // sbc r0, r1, #3
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 10;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 6);
PrepareOp(0xe2e10010); // rsc r0, r1, #16
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 6;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 9);
PrepareOp(0xe3500010); // cmp r0, #16
ResetNZCV();
arm_Reg[0].I = 16;
RunOp();
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG);
PrepareOp(0xe3700001); // cmn r0, #1
ResetNZCV();
arm_Reg[0].I = 0xffffffff;
RunOp();
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG);
PrepareOp(0xe2010001); // and r0, r1, #1
arm_Reg[0].I = 0;
arm_Reg[1].I = 3;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
PrepareOp(0xe3810001); // orr r0, r1, #1
arm_Reg[0].I = 0;
arm_Reg[1].I = 2;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 3);
PrepareOp(0xe2210001); // eor r0, r1, #1
arm_Reg[0].I = 0;
arm_Reg[1].I = 3;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 2);
PrepareOp(0xe3c10001); // bic r0, r1, #1
arm_Reg[0].I = 0;
arm_Reg[1].I = 3;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 2);
PrepareOp(0xe3100001); // tst r0, #1
ResetNZCV();
arm_Reg[0].I = 2;
RunOp();
ASSERT_NZCV_EQ(Z_FLAG);
ResetNZCV();
arm_Reg[0].I = 1;
RunOp();
ASSERT_NZCV_EQ(0);
PrepareOp(0xe3300001); // teq r0, #1
ResetNZCV();
arm_Reg[0].I = 1;
RunOp();
ASSERT_NZCV_EQ(Z_FLAG);
ResetNZCV();
arm_Reg[0].I = 2;
RunOp();
ASSERT_NZCV_EQ(0);
PrepareOp(0xe2522001); // subs r2, #1
ResetNZCV();
arm_Reg[2].I = 1;
RunOp();
ASSERT_NZCV_EQ(Z_FLAG | C_FLAG);
ResetNZCV();
arm_Reg[2].I = 2;
RunOp();
ASSERT_NZCV_EQ(C_FLAG);
ResetNZCV();
arm_Reg[2].I = 0;
RunOp();
ASSERT_NZCV_EQ(N_FLAG);
}
TEST_F(AicaArmTest, Operand2ShiftImmTest)
{
PrepareOp(0xe0810202); // add r0, r1, r2, LSL #4
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 2;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x21);
PrepareOp(0xe0810142); // add r0, r1, r2, ASR #2
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = -8;
RunOp();
ASSERT_EQ(arm_Reg[0].I, -1);
PrepareOp(0xe08100a2); // add r0, r1, r2, LSR #1
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 0x80000000;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x40000001);
PrepareOp(0xe0810862); // add r0, r1, r2, ROR #16
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 0x56771234;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x12345678);
PrepareOp(0xe0810062); // add r0, r1, r2, RRX
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 0x22222221;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x11111111);
ASSERT_NZCV_EQ(0); //
PrepareOp(0xe1910062); // orrs r0, r1, r2, RRX
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // set C
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 0x22222221;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x91111111);
ASSERT_NZCV_EQ(N_FLAG | C_FLAG); // N,C
// When an Operand2 constant is used with the instructions MOVS, MVNS, ANDS, ORRS, ORNS, EORS, BICS, TEQ or TST, the carry flag is updated to bit[31] of the constant,
// if the constant is greater than 255 and can be produced by shifting an 8-bit value.
PrepareOp(0xe3b00102); // movs r0, #0x80000000
ResetNZCV();
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x80000000);
ASSERT_NZCV_EQ(N_FLAG | C_FLAG); // N,C
PrepareOp(0xe3f00000); // mvns r0, #0
arm_Reg[RN_PSR_FLAGS].I &= ~NZCV_MASK;
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xffffffff);
ASSERT_NZCV_EQ(N_FLAG); // N
}
TEST_F(AicaArmTest, Operand2RegShiftTest)
{
PrepareOp(0xe1810312); // orr r0, r1, r2, LSL r3
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 0x10;
arm_Reg[3].I = 8;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x1001);
PrepareOp(0xe1810352); // orr r0, r1, r2, ASR r3
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 0x80000000;
arm_Reg[3].I = 30;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xffffffff);
PrepareOp(0xe1810332); // orr r0, r1, r2, LSR r3
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 0x80000000;
arm_Reg[3].I = 30;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 3);
PrepareOp(0xe1810372); // orr r0, r1, r2, ROR r3
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 0x43208765;
arm_Reg[3].I = 16;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x87654321);
PrepareOp(0xe1b0431f); // movs r4, r15, lsl r3
ResetNZCV();
arm_Reg[3].I = 0;
arm_Reg[4].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[4].I, 0x1000 + 12);
ASSERT_NZCV_EQ(0);
PrepareOp(0xe1b0400f); // movs r4, r15, lsl #0
ResetNZCV();
arm_Reg[3].I = 0;
arm_Reg[4].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[4].I, 0x1000 + 8);
ASSERT_NZCV_EQ(0);
}
TEST_F(AicaArmTest, CarryTest)
{
PrepareOp(0xe1910022); // orrs r0, r1, r2, LSR #32
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 0x80000000;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
ASSERT_NZCV_EQ(C_FLAG); // C
PrepareOp(0xe1910822); // orrs r0, r1, r2, LSR #16
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 0x80008000;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x8001);
ASSERT_NZCV_EQ(C_FLAG); // C
PrepareOp(0xe1910042); // orrs r0, r1, r2, ASR #32
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 0x80000000;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xffffffff);
ASSERT_NZCV_EQ(N_FLAG | C_FLAG); // N,C
PrepareOp(0xe1910842); // orrs r0, r1, r2, ASR #16
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 0x00008000;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
ASSERT_NZCV_EQ(C_FLAG); // C
arm_Reg[RN_PSR_FLAGS].I &= ~NZCV_MASK;
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 0x00004000;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
ASSERT_NZCV_EQ(0); //
PrepareOp(0xe1910802); // orrs r0, r1, r2, LSL #16
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
arm_Reg[2].I = 0x00010001;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x10001);
ASSERT_NZCV_EQ(C_FLAG); // C
}
TEST_F(AicaArmTest, MemoryTest)
{
PrepareOp(0xe5910004); // ldr r0, [r1, #4]
arm_Reg[0].I = 0;
arm_Reg[1].I = 0x10000;
*(u32*)&aica_ram[0x10004] = 0xbaadcafe;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xbaadcafe);
PrepareOp(0xe5010008); // str r0, [r1, #-8]
arm_Reg[0].I = 0xbaadcafe;
arm_Reg[1].I = 0x10008;
*(u32*)&aica_ram[0x10000] = 0;
RunOp();
ASSERT_EQ(*(u32*)&aica_ram[0x10000], 0xbaadcafe);
PrepareOp(0xe5b10004); // ldr r0, [r1, #4]!
arm_Reg[0].I = 0;
arm_Reg[1].I = 0x10000;
*(u32*)&aica_ram[0x10004] = 0xbaadcafe;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xbaadcafe);
ASSERT_EQ(arm_Reg[1].I, 0x10004);
PrepareOp(0xe4110004); // ldr r0, [r1], #-4
arm_Reg[0].I = 0;
arm_Reg[1].I = 0x10004;
*(u32*)&aica_ram[0x10004] = 0xbaadcafe;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xbaadcafe);
ASSERT_EQ(arm_Reg[1].I, 0x10000);
PrepareOp(0xe4900004); // ldr r0, [r0], #4
arm_Reg[0].I = 0x10004;
*(u32*)&aica_ram[0x10004] = 0xbaadcafe;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xbaadcafe);
PrepareOp(0xe5b00004); // ldr r0, [r0, #4]!
arm_Reg[0].I = 0x10000;
*(u32*)&aica_ram[0x10000] = 0xbaadcafe;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0xbaadcafe);
PrepareOp(0xe51f0004); // ldr r0, [r15, #-4]
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
PrepareOp(0xe79000a4); // ldr r0, [r0, r4, LSR #1]
arm_Reg[0].I = 0x1003;
arm_Reg[4].I = 2;
RunOp();
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
PrepareOp(0xe7900084); // ldr r0, [r0, r4, LSL #1]
arm_Reg[0].I = 0x1002;
arm_Reg[4].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
PrepareOp(0xe7900024); // ldr r0, [r0, r4, LSR #32]
arm_Reg[0].I = 0x1004;
arm_Reg[4].I = 0x12345678;
RunOp();
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
ASSERT_EQ(arm_Reg[4].I, 0x12345678);
PrepareOp(0xe7900044); // ldr r0, [r0, r4, ASR #32]
arm_Reg[0].I = 0x1005;
arm_Reg[4].I = 0x80000000;
RunOp();
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
ASSERT_EQ(arm_Reg[4].I, 0x80000000);
PrepareOp(0xe7920002); // ldr r0,[r2,r2]
arm_Reg[0].I = 5;
arm_Reg[2].I = 0x1004 / 2;
RunOp();
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
ASSERT_EQ(arm_Reg[2].I, 0x1004 / 2);
PrepareOp(0xe7920063); // ldr r0,[r2,r3, rrx]
ResetNZCV();
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // set C
arm_Reg[0].I = 5;
arm_Reg[2].I = 0x1006;
arm_Reg[3].I = -4;
RunOp();
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
ASSERT_EQ(arm_Reg[2].I, 0x1006);
ASSERT_EQ(arm_Reg[3].I, -4);
// unaligned read
PrepareOp(0xe7900002); // ldr r0,[r0,r2]
arm_Reg[0].I = 0x1004;
arm_Reg[2].I = 2;
RunOp();
ASSERT_EQ(arm_Reg[0].I, (*(u32*)&aica_ram[0x1004]) >> 16 | (*(u32*)&aica_ram[0x1004]) << 16);
ASSERT_EQ(arm_Reg[2].I, 2);
PrepareOp(0xe69000a4); // ldr r0,[r0],r4, lsr #1
arm_Reg[0].I = 0x1004;
arm_Reg[4].I = 2;
RunOp();
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
ASSERT_EQ(arm_Reg[4].I, 2);
PrepareOp(0xe6920104); // ldr r0,[r2],r4, lsl #2
arm_Reg[0].I = 0;
arm_Reg[2].I = 0x1004;
arm_Reg[4].I = 2;
RunOp();
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
ASSERT_EQ(arm_Reg[2].I, 0x1004 + 8);
ASSERT_EQ(arm_Reg[4].I, 2);
PrepareOp(0xe6920000); // ldr r0,[r2],r0
arm_Reg[0].I = 123;
arm_Reg[2].I = 0x1004;
RunOp();
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
ASSERT_EQ(arm_Reg[2].I, 0x1004 + 123);
PrepareOp(0xe6920043); // ldr r0,[r2],r3, asr #32
arm_Reg[0].I = 0;
arm_Reg[2].I = 0x1004;
arm_Reg[3].I = 0xc0000000;
RunOp();
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
ASSERT_EQ(arm_Reg[2].I, 0x1004 - 1);
ASSERT_EQ(arm_Reg[3].I, 0xc0000000);
PrepareOp(0xe6920063); // ldr r0,[r2],r3, rrx
ResetNZCV();
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG; // set C
arm_Reg[0].I = 5;
arm_Reg[2].I = 0x1004;
arm_Reg[3].I = 0xfffffffc;
RunOp();
ASSERT_EQ(arm_Reg[0].I, *(u32*)&aica_ram[0x1004]);
ASSERT_EQ(arm_Reg[2].I, 0x1002);
ASSERT_NZCV_EQ(C_FLAG); // C
ASSERT_EQ(arm_Reg[3].I, 0xfffffffc);
// unaligned read
PrepareOp(0xe6900002); // ldr r0,[r0],r2
arm_Reg[0].I = 0x1006;
arm_Reg[2].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, (*(u32*)&aica_ram[0x1004]) >> 16 | (*(u32*)&aica_ram[0x1004]) << 16);
ASSERT_EQ(arm_Reg[2].I, 1);
// conditional with write-back, false condition
PrepareOp(0x04910004); // ldreq r0, [r1], #4
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 0x1004;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
ASSERT_EQ(arm_Reg[1].I, 0x1004);
}
TEST_F(AicaArmTest, PcRelativeTest)
{
PrepareOp(0xe38f0010); // orr r0, r15, #16
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x1008 + 16);
PrepareOp(0xe180011f); // orr r0, r15, LSL r1
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0x100C << 1);
PrepareOp(0xe28f5c7d); // add r5, r15, #32000
arm_Reg[5].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[5].I, 32000 + 0x1008);
}
TEST_F(AicaArmTest, ConditionalTest)
{
PrepareOp(0x01a00001); // moveq r0, r1
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
arm_Reg[RN_PSR_FLAGS].I |= Z_FLAG;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
PrepareOp(0x11a00001); // movne r0, r1
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
arm_Reg[RN_PSR_FLAGS].I |= Z_FLAG;
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
PrepareOp(0x21a00001); // movcs r0, r1
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
PrepareOp(0x31a00001); // movcc r0, r1
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG;
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
PrepareOp(0x41a00001); // movmi r0, r1
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
arm_Reg[RN_PSR_FLAGS].I |= N_FLAG;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
PrepareOp(0x51a00001); // movpl r0, r1
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
arm_Reg[RN_PSR_FLAGS].I |= N_FLAG;
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
PrepareOp(0x61a00001); // movvs r0, r1
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
arm_Reg[RN_PSR_FLAGS].I |= V_FLAG;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
PrepareOp(0x71a00001); // movvc r0, r1
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
arm_Reg[RN_PSR_FLAGS].I |= V_FLAG;
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
PrepareOp(0x81a00001); // movhi r0, r1
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
arm_Reg[RN_PSR_FLAGS].I |= Z_FLAG;
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
PrepareOp(0x91a00001); // movls r0, r1
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
arm_Reg[RN_PSR_FLAGS].I |= C_FLAG;
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
arm_Reg[RN_PSR_FLAGS].I = Z_FLAG;
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
PrepareOp(0xa1a00001); // movge r0, r1
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
arm_Reg[RN_PSR_FLAGS].I |= N_FLAG;
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
arm_Reg[RN_PSR_FLAGS].I |= V_FLAG;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
PrepareOp(0xb1a00001); // movlt r0, r1
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
arm_Reg[RN_PSR_FLAGS].I |= N_FLAG;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
arm_Reg[RN_PSR_FLAGS].I |= V_FLAG;
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
PrepareOp(0xc1a00001); // movgt r0, r1
// Z==0 && N==V
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
ResetNZCV();
arm_Reg[RN_PSR_FLAGS].I |= Z_FLAG;
arm_Reg[0].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
ResetNZCV();
arm_Reg[RN_PSR_FLAGS].I |= V_FLAG;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
arm_Reg[RN_PSR_FLAGS].I |= N_FLAG;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
PrepareOp(0xd1a00001); // movle r0, r1
// Z==1 || N!=V
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[1].I = 1;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
arm_Reg[RN_PSR_FLAGS].I |= Z_FLAG;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
ResetNZCV();
arm_Reg[0].I = 0;
arm_Reg[RN_PSR_FLAGS].I |= V_FLAG;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
arm_Reg[0].I = 0;
arm_Reg[RN_PSR_FLAGS].I |= N_FLAG;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 0);
}
TEST_F(AicaArmTest, JumpTest)
{
PrepareOp(0xea00003e); // b +248
RunOp();
ASSERT_EQ(arm_Reg[R15_ARM_NEXT].I, 0x1100);
PrepareOp(0xeb00003e); // bl +248
arm_Reg[14].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[R15_ARM_NEXT].I, 0x1100);
ASSERT_EQ(arm_Reg[14].I, 0x1004);
PrepareOp(0xe1a0f000); // mov pc, r0
arm_Reg[0].I = 0x100;
RunOp();
ASSERT_EQ(arm_Reg[R15_ARM_NEXT].I, 0x100);
PrepareOp(0xc1a0f000); // movgt pc, r0
ResetNZCV();
arm_Reg[RN_PSR_FLAGS].I |= N_FLAG;
arm_Reg[0].I = 0x100;
RunOp();
ASSERT_EQ(arm_Reg[R15_ARM_NEXT].I, 0x1004);
ResetNZCV();
RunOp();
ASSERT_EQ(arm_Reg[R15_ARM_NEXT].I, 0x100);
PrepareOp(0xe590f000); // ldr r15, [r0]
arm_Reg[0].I = 0x10000;
*(u32*)&aica_ram[0x10000] = 0xbaadcafc;
RunOp();
ASSERT_EQ(arm_Reg[R15_ARM_NEXT].I, 0xbaadcafc);
PrepareOp(0x1b00003e); // blne +248
ResetNZCV();
arm_Reg[14].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[R15_ARM_NEXT].I, 0x1100);
ASSERT_EQ(arm_Reg[14].I, 0x1004);
ResetNZCV();
arm_Reg[RN_PSR_FLAGS].I |= Z_FLAG;
arm_Reg[14].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[R15_ARM_NEXT].I, 0x1004);
ASSERT_EQ(arm_Reg[14].I, 0);
}
TEST_F(AicaArmTest, LdmStmTest)
{
PrepareOp(0xe8bd8000); // ldm sp!, {pc}
arm_Reg[13].I = 0x1100;
*(u32*)&aica_ram[0x1100] = 0x1234;
RunOp();
ASSERT_EQ(arm_Reg[R15_ARM_NEXT].I, 0x1234);
PrepareOp(0xe92d8000); // stmdb sp!, {pc}
arm_Reg[13].I = 0x1104;
RunOp();
ASSERT_EQ(arm_Reg[13].I, 0x1100);
ASSERT_EQ(*(u32*)&aica_ram[0x1100], 0x1000 + 12);
}
TEST_F(AicaArmTest, RegAllocTest)
{
u32 ops[] = {
0xe3a00000, // mov r0, #0
0xe3a01001, // mov r1, #1
0xe3a02002, // mov r2, #2
0xe3a03003, // mov r3, #3
0xe3a04004, // mov r4, #4
0xe3a05005, // mov r5, #5
0xe3a06006, // mov r6, #6
0xe0800001, // add r0, r0, r1
0xe0811002, // add r1, r1, r2
0xe0822003, // add r2, r2, r3
0xe0833004, // add r3, r3, r4
0xe0844005, // add r4, r4, r5
0xe0855006, // add r5, r5, r6
0xe0866000, // add r6, r6, r0
};
PrepareOps(std::size(ops), ops);
for (int i = 0; i < 15; i++)
arm_Reg[i].I = 0;
RunOp();
ASSERT_EQ(arm_Reg[0].I, 1);
ASSERT_EQ(arm_Reg[1].I, 3);
ASSERT_EQ(arm_Reg[2].I, 5);
ASSERT_EQ(arm_Reg[3].I, 7);
ASSERT_EQ(arm_Reg[4].I, 9);
ASSERT_EQ(arm_Reg[5].I, 11);
ASSERT_EQ(arm_Reg[6].I, 7);
}
TEST_F(AicaArmTest, ConditionRegAllocTest)
{
u32 ops1[] = {
0x03a0004d, // moveq r0, #77
0xe1a01000 // mov r1, r0
};
PrepareOps(std::size(ops1), ops1);
arm_Reg[0].I = 22;
arm_Reg[1].I = 22;
ResetNZCV();
RunOp();
ASSERT_EQ(arm_Reg[0].I, 22);
ASSERT_EQ(arm_Reg[1].I, 22);
u32 ops2[] = {
0x01a01000, // moveq r1, r0
0xe1a02000 // mov r2, r0
};
PrepareOps(std::size(ops2), ops2);
arm_Reg[0].I = 22;
arm_Reg[1].I = 0;
arm_Reg[2].I = 0;
ResetNZCV();
RunOp();
ASSERT_EQ(arm_Reg[0].I, 22);
ASSERT_EQ(arm_Reg[1].I, 0);
ASSERT_EQ(arm_Reg[2].I, 22);
}
}
#endif