* Enable simple ARM register concretization for Unicorn * Add canonical_registers property to abstractcpu * cpu to self * Check for regs_access better * Emulate a single instruction * Bypass capstone 3.0.4 arm bug * Dealing with capstone * Temporary disable ASR and remobe BitVec.Bool from test * WIP WIP debug prints WIP WIP * Unicorn fallback working (using unicorn master) * HAck to support unicorn 1.0.0 * WIP * Unicorn hack to handle PC updates * [WIP] do not do anything with this commit; for debugging only * Adding before clean up * emulation more or less works; need to work out more unicorn bugs * clean up emulate() caller code * move hooks to methods; cleanup * Concretize memory when emulating * Re-add Bool() * Update tests to start at offset 4 When an instruction branches to the previous instruction, Unicorn attempts to dereference that memory. We'd like to use unit tests to also make sure Unicorn emulation is in line with our own semantics. If we start all tests at offset 4, we can jump to a previous instruction and not fault when Unicorn dereferences it. * Fix concretization * Clean up test imports; upper-case Cpu * Unicorn tests * Add tests for all the ARM semantics, but make sure they're equivalent on unicorn. * Add a few tests to make sure unicorn correctly concretizes the memory it references * Fix broken import * Add symbolic register tests * Re-introduce the unicorn hack * Add the 'ONE' concretization policy * Rm unused function * Update concretization; add comments * Add ONE policy test * Create a base class for all concretization exceptions * Remove Armv7Cpu._concretize_registers * Check for enabled logging in a more idiomatic way * [wip] intermediate testing commit * Reimplement hooks and execution with unicorn * Add a DMB (mem barrier) instruction; nop * simplify instruction resolution * improve unicorn error handling * explicitly delete emu * Handle ARM helpers inline * map fetched memory * Narrow exception handling * Update DMB docs; make __kuser_dmb match real implementation * Fix typo; add comment; remove extraneous parameter * typos++
1355 lines
42 KiB
Python
1355 lines
42 KiB
Python
import unittest
|
|
import struct
|
|
from functools import wraps
|
|
|
|
from manticore.core.cpu.arm import Armv7Cpu as Cpu, Mask, Interruption
|
|
from manticore.core.memory import Memory32
|
|
|
|
from capstone.arm import *
|
|
from keystone import Ks, KS_ARCH_ARM, KS_MODE_ARM
|
|
|
|
ks = Ks(KS_ARCH_ARM, KS_MODE_ARM)
|
|
|
|
import logging
|
|
|
|
logger = logging.getLogger("ARM_TESTS")
|
|
|
|
|
|
def assemble(asm):
|
|
ords = ks.asm(asm)[0]
|
|
if not ords:
|
|
raise Exception('bad assembly: {}'.format(asm))
|
|
return ''.join(map(chr, ords))
|
|
|
|
|
|
class Armv7CpuTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.c = Cpu(Memory32(), 'armv7')
|
|
self.rf = self.c.regfile
|
|
self._setupStack()
|
|
|
|
def _setupStack(self):
|
|
self.stack = self.c.memory.mmap(0xf000, 0x1000, 'rw')
|
|
self.rf.write('SP', self.stack + 0x1000)
|
|
|
|
def test_rd(self):
|
|
self.assertEqual(self.rf.read('R0'), 0)
|
|
|
|
def test_rd2(self):
|
|
self.c.STACK = 0x1337
|
|
self.assertEqual(self.rf.read('SP'), 0x1337)
|
|
|
|
def test_stack_set_get(self):
|
|
self.c.STACK = 0x1337
|
|
self.assertEqual(self.c.STACK, 0x1337)
|
|
|
|
def test_rd3(self):
|
|
self.c.STACK = 0x1337 - 1
|
|
self.assertEqual(self.rf.read('SP'), 0x1336)
|
|
|
|
def test_rd4(self):
|
|
self.c.STACK = 0x1337 + 1
|
|
self.assertEqual(self.rf.read('SP'), 0x1338)
|
|
|
|
def test_stack_push(self):
|
|
self.c.stack_push(42)
|
|
self.c.stack_push(44)
|
|
self.assertItemsEqual(self.c.read(self.c.STACK, 4), '\x2c\x00\x00\x00')
|
|
self.assertItemsEqual(self.c.read(self.c.STACK + 4, 4), '\x2a\x00\x00\x00')
|
|
|
|
def test_stack_pop(self):
|
|
v = 0x55
|
|
v_bytes = struct.pack('<I', v)
|
|
self.c.stack_push(v)
|
|
val = self.c.stack_pop()
|
|
self.assertItemsEqual(self.c.read(self.c.STACK - 4, 4), v_bytes)
|
|
|
|
def test_stack_peek(self):
|
|
self.c.stack_push(42)
|
|
self.assertItemsEqual(self.c.stack_peek(), '\x2a\x00\x00\x00')
|
|
|
|
def test_readwrite_int(self):
|
|
self.c.STACK -= 4
|
|
self.c.write_int(self.c.STACK, 0x4242, 32)
|
|
self.assertEqual(self.c.read_int(self.c.STACK), 0x4242)
|
|
|
|
|
|
def itest_failing(asm):
|
|
def instr_dec(assertions_func):
|
|
@wraps(assertions_func)
|
|
def wrapper(self):
|
|
self._setupCpu(asm)
|
|
self.cpu.instruction = "\x00\x00\x00\x00"
|
|
# self.cpu.execute()
|
|
assertions_func(self)
|
|
|
|
return wrapper
|
|
|
|
return instr_dec
|
|
|
|
|
|
def itest(asm):
|
|
def instr_dec(assertions_func):
|
|
@wraps(assertions_func)
|
|
def wrapper(self):
|
|
self._setupCpu(asm)
|
|
self.cpu.execute()
|
|
assertions_func(self)
|
|
|
|
return wrapper
|
|
|
|
return instr_dec
|
|
|
|
|
|
def itest_setregs(*preds):
|
|
def instr_dec(custom_func):
|
|
@wraps(custom_func)
|
|
def wrapper(self):
|
|
for p in preds:
|
|
dest, src = p.split('=')
|
|
|
|
try:
|
|
src = int(src, 0)
|
|
except:
|
|
pass
|
|
|
|
self.rf.write(dest.upper(), src)
|
|
custom_func(self)
|
|
|
|
return wrapper
|
|
|
|
return instr_dec
|
|
|
|
def itest_custom(asm):
|
|
def instr_dec(custom_func):
|
|
@wraps(custom_func)
|
|
def wrapper(self):
|
|
self._setupCpu(asm)
|
|
custom_func(self)
|
|
|
|
return wrapper
|
|
|
|
return instr_dec
|
|
|
|
|
|
class Armv7CpuInstructions(unittest.TestCase):
|
|
def setUp(self):
|
|
self.cpu = Cpu(Memory32(), 'armv7')
|
|
self.mem = self.cpu.memory
|
|
self.rf = self.cpu.regfile
|
|
|
|
def _setupCpu(self, asm):
|
|
self.code = self.mem.mmap(0x1000, 0x1000, 'rwx')
|
|
self.data = self.mem.mmap(0xd000, 0x1000, 'rw')
|
|
self.stack = self.mem.mmap(0xf000, 0x1000, 'rw')
|
|
start = self.code + 4
|
|
self.mem.write(start, assemble(asm))
|
|
self.rf.write('PC', start)
|
|
self.rf.write('SP', self.stack + 0x1000)
|
|
|
|
def _checkFlagsNZCV(self, n, z, c, v):
|
|
self.assertEqual(self.rf.read('APSR_N'), n)
|
|
self.assertEqual(self.rf.read('APSR_Z'), z)
|
|
self.assertEqual(self.rf.read('APSR_C'), c)
|
|
self.assertEqual(self.rf.read('APSR_V'), v)
|
|
|
|
# MOV
|
|
|
|
@itest("mov r0, 0x0")
|
|
def test_mov_imm_min(self):
|
|
self.assertEqual(self.rf.read('R0'), 0x0)
|
|
|
|
@itest("mov r0, 42")
|
|
def test_mov_imm_norm(self):
|
|
self.assertEqual(self.rf.read('R0'), 42)
|
|
|
|
@itest("mov r0, 0x100")
|
|
def test_mov_imm_modified_imm_min(self):
|
|
self.assertEqual(self.rf.read('R0'), 0x100)
|
|
|
|
@itest("mov r0, 0xff000000")
|
|
def test_mov_imm_modified_imm_max(self):
|
|
self.assertEqual(self.rf.read('R0'), 0xff000000)
|
|
|
|
@itest_custom("mov r0, r1")
|
|
def test_mov_immreg(self):
|
|
self.rf.write('R1', 0)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R0'), 0)
|
|
|
|
@itest_custom("mov r0, r1")
|
|
def test_mov_immreg1(self):
|
|
self.rf.write('R1', 2 ** 32)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R0'), 0)
|
|
|
|
@itest_custom("mov r0, r1")
|
|
def test_mov_immreg2(self):
|
|
self.rf.write('R1', 0xffffffff)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R0'), 0xffffffff)
|
|
|
|
@itest_custom("mov r0, r1")
|
|
def test_mov_immreg3(self):
|
|
self.rf.write('R1', 42)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R0'), 42)
|
|
|
|
# MOVW
|
|
|
|
@itest("movw r0, 0")
|
|
def test_movw_imm_min(self):
|
|
self.assertEqual(self.rf.read('R0'), 0x0)
|
|
|
|
@itest("movw r0, 0xffff")
|
|
def test_movw_imm_max(self):
|
|
self.assertEqual(self.rf.read('R0'), 0xffff)
|
|
|
|
# MOVS
|
|
|
|
@itest_custom("movs r0, 0")
|
|
def test_movs_imm_min(self):
|
|
pre_c = self.rf.read('APSR_C')
|
|
pre_v = self.rf.read('APSR_V')
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R0'), 0)
|
|
self._checkFlagsNZCV(0, 1, pre_c, pre_v)
|
|
|
|
@itest_custom("movs r0, 42")
|
|
def test_movs_imm_norm(self):
|
|
pre_c = self.rf.read('APSR_C')
|
|
pre_v = self.rf.read('APSR_V')
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R0'), 42)
|
|
self._checkFlagsNZCV(0, 0, pre_c, pre_v)
|
|
|
|
@itest_custom("movs r0, 0x100")
|
|
def test_movs_imm_modified_imm_min(self):
|
|
pre_c = self.rf.read('APSR_C')
|
|
pre_v = self.rf.read('APSR_V')
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R0'), 0x100)
|
|
self._checkFlagsNZCV(0, 0, pre_c, pre_v)
|
|
|
|
@itest_custom("movs r0, 0xff000000")
|
|
def test_movs_imm_modified_imm_max(self):
|
|
pre_v = self.rf.read('APSR_V')
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R0'), 0xff000000)
|
|
self._checkFlagsNZCV(1, 0, 1, pre_v)
|
|
|
|
@itest_custom("movs r0, 0x0e000000")
|
|
def test_movs_imm_modified_imm_sans_carry(self):
|
|
pre_v = self.rf.read('APSR_V')
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R0'), 0x0e000000)
|
|
self._checkFlagsNZCV(0, 0, 0, pre_v)
|
|
|
|
@itest_custom("movs r0, r1")
|
|
def test_movs_reg(self):
|
|
self.rf.write('R1', 0)
|
|
pre_c = self.rf.read('APSR_C')
|
|
pre_v = self.rf.read('APSR_V')
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R0'), 0)
|
|
self._checkFlagsNZCV(0, 1, pre_c, pre_v)
|
|
|
|
@itest_custom("movs r0, r1")
|
|
def test_movs_reg1(self):
|
|
self.rf.write('R1', 2 ** 32)
|
|
pre_c = self.rf.read('APSR_C')
|
|
pre_v = self.rf.read('APSR_V')
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R0'), 0)
|
|
self._checkFlagsNZCV(0, 1, pre_c, pre_v)
|
|
|
|
@itest_custom("movs r0, r1")
|
|
def test_movs_reg2(self):
|
|
self.rf.write('R1', 2 ** 32 - 1)
|
|
pre_c = self.rf.read('APSR_C')
|
|
pre_v = self.rf.read('APSR_V')
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R0'), 2 ** 32 - 1)
|
|
self._checkFlagsNZCV(1, 0, pre_c, pre_v)
|
|
|
|
@itest_custom("movs r0, r1")
|
|
def test_movs_reg3(self):
|
|
self.rf.write('R1', 42)
|
|
pre_c = self.rf.read('APSR_C')
|
|
pre_v = self.rf.read('APSR_V')
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R0'), 42)
|
|
self._checkFlagsNZCV(0, 0, pre_c, pre_v)
|
|
|
|
# ADD
|
|
|
|
@itest_custom("add r3, r1, 55")
|
|
def test_add_imm_norm(self):
|
|
self.rf.write('R1', 44)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 99)
|
|
|
|
@itest_custom("add r3, r1, 0x100")
|
|
def test_add_imm_mod_imm_min(self):
|
|
self.rf.write('R1', 44)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 44 + 0x100)
|
|
|
|
@itest_custom("add r3, r1, 0xff000000")
|
|
def test_add_imm_mod_imm_max(self):
|
|
self.rf.write('R1', 44)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 44 + 0xff000000)
|
|
|
|
@itest_custom("add r3, r1, 0x1000000")
|
|
def test_add_imm_carry(self):
|
|
self.rf.write('R1', 0xff000001)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 1)
|
|
|
|
@itest_custom("add r3, r1, 0x1")
|
|
def test_add_imm_overflow(self):
|
|
self.rf.write('R1', (2 ** 31 - 1))
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 0x80000000)
|
|
|
|
@itest_custom("add r3, r1, r2")
|
|
def test_add_reg_norm(self):
|
|
self.rf.write('R1', 44)
|
|
self.rf.write('R2', 55)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 99)
|
|
|
|
@itest_custom("add r3, r1, r2")
|
|
def test_add_reg_mod_imm_min(self):
|
|
self.rf.write('R1', 44)
|
|
self.rf.write('R2', 0x100)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 44 + 0x100)
|
|
|
|
@itest_custom("add r3, r1, r2")
|
|
def test_add_reg_mod_imm_max(self):
|
|
self.rf.write('R1', 44)
|
|
self.rf.write('R2', 0xff000000)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 44 + 0xff000000)
|
|
|
|
@itest_custom("add r3, r1, r2")
|
|
def test_add_reg_carry(self):
|
|
self.rf.write('R1', 0x1000000)
|
|
self.rf.write('R2', 0xff000001)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 1)
|
|
|
|
@itest_custom("add r3, r1, r2")
|
|
def test_add_reg_overflow(self):
|
|
self.rf.write('R1', (2 ** 31 - 1))
|
|
self.rf.write('R2', 1)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), (1 << 31))
|
|
|
|
@itest_custom("add r3, r1, r2, lsl #3")
|
|
def test_add_reg_sft_lsl(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R2', 0x1)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), (1 << 3))
|
|
|
|
@itest_custom("add r3, r1, r2, lsr #3")
|
|
def test_add_reg_sft_lsr(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R2', 0x8)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), (0x8 >> 3))
|
|
|
|
@itest_custom("add r3, r1, r2, asr #3")
|
|
def test_add_reg_sft_asr(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R2', 0x80000000)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 0xf0000000)
|
|
|
|
@itest_custom("add r3, r1, r2, asr #3")
|
|
def test_add_reg_sft_asr2(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R2', 0x40000000)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), (0x40000000 >> 3))
|
|
|
|
@itest_custom("add r3, r1, r2, ror #3")
|
|
def test_add_reg_sft_ror_norm(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R2', 0x8)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 0x1)
|
|
|
|
@itest_custom("add r3, r1, r2, ror #3")
|
|
def test_add_reg_sft_ror(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R2', 0x3)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 0x60000000)
|
|
|
|
@itest_custom("adc r3, r1, r2")
|
|
@itest_setregs("R1=1", "R2=2", "APSR_C=1")
|
|
def test_adc_basic(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 4)
|
|
|
|
@itest_custom("adc r3, r1, r2, ror #3")
|
|
@itest_setregs("R1=1", "R2=2", "APSR_C=1")
|
|
def test_adc_reg_sft_ror(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R2', 0x3)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 0x60000001)
|
|
|
|
# TODO what is shifter_carry_out in the manual, A8-291? it gets set to
|
|
# Bit[0] presumably, but i have no clue what it is. Not mentioned again in
|
|
# manual.
|
|
@itest_custom("add r3, r1, r2, rrx")
|
|
def test_add_reg_sft_rrx(self):
|
|
self.rf.write('APSR_C', 0x0)
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R2', 2 ** 32 - 1)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 2 ** 31 - 1)
|
|
|
|
@itest_custom("add r3, r1, r2, rrx")
|
|
def test_add_reg_sft_rrx2(self):
|
|
self.rf.write('APSR_C', 0x1)
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R2', 2 ** 32 - 1)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 2 ** 32 - 1)
|
|
|
|
@itest_custom("add r3, r1, r2, lsl r4")
|
|
def test_add_reg_sft_lsl_reg(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R4', 0x3)
|
|
self.rf.write('R2', 0x1)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), (1 << 3))
|
|
|
|
@itest_custom("add r3, r1, r2, lsr r4")
|
|
def test_add_reg_sft_lsr_reg(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R4', 0x3)
|
|
self.rf.write('R2', 0x8)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), (0x8 >> 3))
|
|
|
|
@itest_custom("add r3, r1, r2, asr r4")
|
|
def test_add_reg_sft_asr_reg(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R4', 0x3)
|
|
self.rf.write('R2', 0x80000000)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 0xf0000000)
|
|
|
|
@itest_custom("add r3, r1, r2, asr r4")
|
|
def test_add_reg_sft_asr2_reg(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R4', 0x3)
|
|
self.rf.write('R2', 0x40000000)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), (0x40000000 >> 3))
|
|
|
|
@itest_custom("add r3, r1, r2, ror r4")
|
|
def test_add_reg_sft_ror_norm_reg(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R4', 0x3)
|
|
self.rf.write('R2', 0x8)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 1)
|
|
|
|
@itest_custom("add r3, r1, r2, ror r4")
|
|
def test_add_reg_sft_ror_reg(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R4', 0x3)
|
|
self.rf.write('R2', 0x3)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 0x60000000)
|
|
|
|
@itest_custom("add r3, r1, r2, rrx")
|
|
def test_add_reg_sft_rrx_reg(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('APSR_C', 0x0)
|
|
self.rf.write('R2', 2 ** 32 - 1)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 2 ** 31 - 1)
|
|
|
|
@itest_custom("add r3, r1, r2, rrx")
|
|
def test_add_reg_sft_rrx2_reg(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('APSR_C', 0x1)
|
|
self.rf.write('R2', 2 ** 32 - 1)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 2 ** 32 - 1)
|
|
|
|
# ADDS
|
|
|
|
@itest_custom("adds r3, r1, 55")
|
|
def test_adds_imm_norm(self):
|
|
self.rf.write('R1', 44)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 99)
|
|
self._checkFlagsNZCV(0, 0, 0, 0)
|
|
|
|
@itest_custom("adds r3, r1, 0x100")
|
|
def test_adds_imm_mod_imm_min(self):
|
|
self.rf.write('R1', 44)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 44 + 0x100)
|
|
self._checkFlagsNZCV(0, 0, 0, 0)
|
|
|
|
@itest_custom("adds r3, r1, 0xff000000")
|
|
def test_adds_imm_mod_imm_max(self):
|
|
self.rf.write('R1', 44)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 44 + 0xff000000)
|
|
self._checkFlagsNZCV(1, 0, 0, 0)
|
|
|
|
@itest_custom("adds r3, r1, 0x1000000")
|
|
def test_adds_imm_carry(self):
|
|
self.rf.write('R1', 0xff000001)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 1)
|
|
self._checkFlagsNZCV(0, 0, 1, 0)
|
|
|
|
@itest_custom("adds r3, r1, 0x80000000")
|
|
def test_adds_imm_carry_overflow(self):
|
|
self.rf.write('R1', 0x80000001)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 1)
|
|
self._checkFlagsNZCV(0, 0, 1, 1)
|
|
|
|
@itest_custom("adds r3, r1, 0x1")
|
|
def test_adds_imm_overflow(self):
|
|
self.rf.write('R1', (2 ** 31 - 1))
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 0x80000000)
|
|
self._checkFlagsNZCV(1, 0, 0, 1)
|
|
|
|
@itest_custom("adds r3, r3, 0x0")
|
|
def test_adds_imm_zf(self):
|
|
self.rf.write('R3', 0)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 0)
|
|
self._checkFlagsNZCV(0, 1, 0, 0)
|
|
|
|
@itest_custom("adds r3, r1, r2")
|
|
def test_adds_reg_norm(self):
|
|
self.rf.write('R1', 44)
|
|
self.rf.write('R2', 55)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 99)
|
|
self._checkFlagsNZCV(0, 0, 0, 0)
|
|
|
|
@itest_custom("adds r3, r1, r2")
|
|
def test_adds_reg_mod_imm_min(self):
|
|
self.rf.write('R1', 44)
|
|
self.rf.write('R2', 0x100)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 44 + 0x100)
|
|
self._checkFlagsNZCV(0, 0, 0, 0)
|
|
|
|
@itest_custom("adds r3, r1, r2")
|
|
def test_adds_reg_mod_imm_max(self):
|
|
self.rf.write('R1', 44)
|
|
self.rf.write('R2', 0xff000000)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 44 + 0xff000000)
|
|
self._checkFlagsNZCV(1, 0, 0, 0)
|
|
|
|
@itest_custom("adds r3, r1, r2")
|
|
def test_adds_reg_carry(self):
|
|
self.rf.write('R1', 0x1000000)
|
|
self.rf.write('R2', 0xff000001)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 1)
|
|
self._checkFlagsNZCV(0, 0, 1, 0)
|
|
|
|
@itest_custom("adds r3, r1, r2")
|
|
def test_adds_reg_overflow(self):
|
|
self.rf.write('R1', (2 ** 31 - 1))
|
|
self.rf.write('R2', 1)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), (1 << 31))
|
|
self._checkFlagsNZCV(1, 0, 0, 1)
|
|
|
|
@itest_custom("adds r3, r1, r2")
|
|
def test_adds_reg_carry_overflow(self):
|
|
self.rf.write('R1', 0x80000001)
|
|
self.rf.write('R2', 0x80000000)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 1)
|
|
self._checkFlagsNZCV(0, 0, 1, 1)
|
|
|
|
@itest_custom("adds r3, r1, r2")
|
|
def test_adds_reg_zf(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R2', 0x0)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 0)
|
|
self._checkFlagsNZCV(0, 1, 0, 0)
|
|
|
|
@itest_custom("adds r3, r1, r2, asr #3")
|
|
def test_adds_reg_sft_asr(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R2', 0x80000000)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 0xf0000000)
|
|
self._checkFlagsNZCV(1, 0, 0, 0)
|
|
|
|
@itest_custom("adds r3, r1, r2, asr #3")
|
|
def test_adds_reg_sft_asr2(self):
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R2', 0x40000000)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), (0x40000000 >> 3))
|
|
self._checkFlagsNZCV(0, 0, 0, 0)
|
|
|
|
@itest_custom("adds r3, r1, r2, rrx")
|
|
def test_adds_reg_sft_rrx(self):
|
|
self.rf.write('APSR_C', 0x0)
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R2', 2 ** 32 - 1)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 2 ** 31 - 1)
|
|
self._checkFlagsNZCV(0, 0, 0, 0)
|
|
|
|
@itest_custom("adds r3, r1, r2, rrx")
|
|
def test_adds_reg_sft_rrx2(self):
|
|
self.rf.write('APSR_C', 0x1)
|
|
self.rf.write('R1', 0x0)
|
|
self.rf.write('R2', 2 ** 32 - 1)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 2 ** 32 - 1)
|
|
self._checkFlagsNZCV(1, 0, 0, 0)
|
|
|
|
# LDR imm
|
|
|
|
@itest_custom("ldr r1, [sp]")
|
|
def test_ldr_imm_off_none(self):
|
|
self.cpu.stack_push(42)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 42)
|
|
|
|
@itest_custom("ldr r1, [sp, #4]")
|
|
def test_ldr_imm_off_pos(self):
|
|
self.cpu.stack_push(42)
|
|
self.cpu.stack_push(41)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 42)
|
|
|
|
@itest_custom("ldr r1, [sp, #-4]")
|
|
def test_ldr_imm_off_neg(self):
|
|
self.cpu.stack_push(42)
|
|
self.cpu.stack_push(41)
|
|
self.cpu.STACK += 4
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 41)
|
|
|
|
@itest_custom("ldr r1, [sp, #4]!")
|
|
def test_ldr_imm_preind_pos(self):
|
|
self.cpu.stack_push(42)
|
|
self.cpu.stack_push(41)
|
|
pre_stack = self.cpu.STACK
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 42)
|
|
self.assertEqual(self.rf.read('SP'), pre_stack + 4)
|
|
|
|
@itest_custom("ldr r1, [sp, #-4]!")
|
|
def test_ldr_imm_preind_neg(self):
|
|
self.cpu.stack_push(42)
|
|
self.cpu.stack_push(41)
|
|
self.cpu.STACK += 4
|
|
pre_stack = self.cpu.STACK
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 41)
|
|
self.assertEqual(self.rf.read('SP'), pre_stack - 4)
|
|
|
|
@itest_custom("ldr r1, [sp], #5")
|
|
def test_ldr_imm_postind_pos(self):
|
|
self.cpu.stack_push(42)
|
|
pre_stack = self.cpu.STACK
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 42)
|
|
self.assertEqual(self.rf.read('SP'), pre_stack + 5)
|
|
|
|
@itest_custom("ldr r1, [sp], #-5")
|
|
def test_ldr_imm_postind_neg(self):
|
|
self.cpu.stack_push(42)
|
|
pre_stack = self.cpu.STACK
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 42)
|
|
self.assertEqual(self.rf.read('SP'), pre_stack - 5)
|
|
|
|
# LDR reg
|
|
|
|
@itest_custom("ldr r1, [sp, r2]")
|
|
def test_ldr_reg_off(self):
|
|
self.cpu.regfile.write('R2', 4)
|
|
self.cpu.stack_push(42)
|
|
self.cpu.stack_push(48)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 42)
|
|
|
|
@itest_custom("ldr r1, [sp, -r2]")
|
|
def test_ldr_reg_off_neg(self):
|
|
self.cpu.regfile.write('R2', 4)
|
|
self.cpu.stack_push(42)
|
|
self.cpu.stack_push(48)
|
|
self.cpu.STACK += 4
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 48)
|
|
|
|
@itest_custom("ldr r1, [sp, r2, lsl #3]")
|
|
def test_ldr_reg_off_shift(self):
|
|
self.cpu.regfile.write('R2', 1)
|
|
self.cpu.stack_push(42)
|
|
self.cpu.stack_push(48)
|
|
self.cpu.stack_push(40)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 42)
|
|
|
|
@itest_custom("ldr r1, [sp, -r2, lsl #3]")
|
|
def test_ldr_reg_off_neg_shift(self):
|
|
self.cpu.regfile.write('R2', 1)
|
|
self.cpu.stack_push(42)
|
|
self.cpu.stack_push(48)
|
|
self.cpu.STACK += 8
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 48)
|
|
|
|
@itest_custom("ldr r1, [sp, r2]!")
|
|
def test_ldr_reg_preind(self):
|
|
self.cpu.regfile.write('R2', 4)
|
|
self.cpu.stack_push(42)
|
|
self.cpu.stack_push(48)
|
|
pre_stack = self.cpu.STACK
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 42)
|
|
self.assertEqual(self.rf.read('SP'), pre_stack + 4)
|
|
|
|
@itest_custom("ldr r1, [sp, -r2, lsl #3]!")
|
|
def test_ldr_reg_preind_shift(self):
|
|
self.cpu.regfile.write('R2', 1)
|
|
self.cpu.stack_push(42)
|
|
self.cpu.stack_push(48)
|
|
self.cpu.STACK += 8
|
|
pre_stack = self.cpu.STACK
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 48)
|
|
self.assertEqual(self.rf.read('SP'), pre_stack - 8)
|
|
|
|
@itest_custom("ldr r1, [sp], r2")
|
|
def test_ldr_reg_postind(self):
|
|
self.cpu.regfile.write('R2', 4)
|
|
self.cpu.stack_push(42)
|
|
pre_stack = self.cpu.STACK
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 42)
|
|
self.assertEqual(self.rf.read('SP'), pre_stack + 4)
|
|
|
|
@itest_custom("ldr r1, [sp], -r2, lsl #3")
|
|
def test_ldr_reg_postind_neg_shift(self):
|
|
self.cpu.regfile.write('R2', 1)
|
|
self.cpu.stack_push(42)
|
|
pre_stack = self.cpu.STACK
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 42)
|
|
self.assertEqual(self.rf.read('SP'), pre_stack - 8)
|
|
|
|
@itest_custom("pop {r1}")
|
|
def test_pop_one_reg(self):
|
|
self.cpu.stack_push(0x55)
|
|
pre_stack = self.cpu.STACK
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 0x55)
|
|
self.assertEqual(self.rf.read('SP'), pre_stack + 4)
|
|
|
|
@itest_custom("pop {r1, r2, r3}")
|
|
def test_pop_multops(self):
|
|
vals = [0x01, 0x55, 0xAA]
|
|
for v in vals:
|
|
self.cpu.stack_push(v)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 0xAA)
|
|
self.assertEqual(self.rf.read('R2'), 0x55)
|
|
self.assertEqual(self.rf.read('R3'), 0x01)
|
|
|
|
@itest_custom("push {r1}")
|
|
@itest_setregs("R1=3")
|
|
def test_push_one_reg(self):
|
|
self.cpu.execute()
|
|
self.assertItemsEqual(self.cpu.stack_peek(), struct.pack('<I', 3))
|
|
|
|
@itest_custom("push {r1, r2, r3}")
|
|
@itest_setregs("R1=3", "R2=0x55", "R3=0xffffffff")
|
|
def test_push_multi_reg(self):
|
|
pre_sp = self.cpu.STACK
|
|
self.cpu.execute()
|
|
sp = self.cpu.STACK
|
|
self.assertEqual(self.rf.read('SP'), pre_sp - (3 * 4))
|
|
self.assertItemsEqual(self.cpu.stack_peek(), struct.pack('<I', 3))
|
|
self.assertEqual(self.cpu.read_int(sp + 4, self.cpu.address_bit_size), 0x55)
|
|
self.assertEqual(self.cpu.read_int(sp + 8, self.cpu.address_bit_size), 0xffffffff)
|
|
|
|
@itest_custom("str SP, [R1]")
|
|
@itest_setregs("R1=0xd000")
|
|
def test_str_basic(self):
|
|
r1 = self.rf.read('R1')
|
|
sp = self.rf.read('SP')
|
|
self.cpu.execute()
|
|
dr1 = self.cpu.read_int(r1, self.cpu.address_bit_size)
|
|
self.assertEqual(sp, dr1)
|
|
|
|
@itest_custom("str R1, [R2, R3]")
|
|
@itest_setregs("R1=34", "R2=0xD000", "R3=8")
|
|
def test_str_index(self):
|
|
r1 = self.rf.read('R1')
|
|
r2 = self.rf.read('R2')
|
|
r3 = self.rf.read('R3')
|
|
self.cpu.execute()
|
|
retrieved = self.cpu.read_int(r2 + r3, self.cpu.address_bit_size)
|
|
self.assertEqual(retrieved, r1)
|
|
|
|
@itest_custom("str R1, [R2, R3, LSL #3]")
|
|
@itest_setregs("R1=34", "R2=0xD000", "R3=1")
|
|
def test_str_index_w_shift(self):
|
|
r1 = self.rf.read('R1')
|
|
r2 = self.rf.read('R2')
|
|
r3 = self.rf.read('R3')
|
|
r3 = r3 << 3
|
|
self.cpu.execute()
|
|
retrieved = self.cpu.read_int(r2 + r3, self.cpu.address_bit_size)
|
|
self.assertEqual(retrieved, r1)
|
|
|
|
@itest_custom("str R1, [R2], #3")
|
|
@itest_setregs("R1=34", "R2=0xD000")
|
|
def test_str_postindex(self):
|
|
r1 = self.rf.read('R1')
|
|
r2 = self.rf.read('R2')
|
|
self.cpu.execute()
|
|
# check store results
|
|
data = self.cpu.read_int(r2, self.cpu.address_bit_size)
|
|
self.assertEqual(data, r1)
|
|
# check writeback results
|
|
new_r2 = self.rf.read('R2')
|
|
self.assertEqual(new_r2, r2 + 3)
|
|
|
|
@itest_custom("str R1, [R2, #3]!")
|
|
@itest_setregs("R1=34", "R2=0xD000")
|
|
def test_str_index_writeback(self):
|
|
r1 = self.rf.read('R1')
|
|
r2 = self.rf.read('R2')
|
|
self.cpu.execute()
|
|
# check store results
|
|
data = self.cpu.read_int(r2 + 3, self.cpu.address_bit_size)
|
|
self.assertEqual(data, r1)
|
|
# check writeback results
|
|
new_r2 = self.rf.read('R2')
|
|
self.assertEqual(new_r2, r2 + 3)
|
|
|
|
# BL
|
|
|
|
@itest_custom("bl 0x170")
|
|
def test_bl(self):
|
|
pre_pc = self.rf.read('PC')
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('PC'), pre_pc + 0x170)
|
|
self.assertEqual(self.rf.read('LR'), pre_pc + 4)
|
|
|
|
@itest_custom("bl #-4")
|
|
def test_bl_neg(self):
|
|
pre_pc = self.rf.read('PC')
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('PC'), pre_pc - 4)
|
|
self.assertEqual(self.rf.read('LR'), pre_pc + 4)
|
|
|
|
# CMP
|
|
|
|
@itest_setregs("R0=3")
|
|
@itest("cmp r0, 3")
|
|
def test_cmp_eq(self):
|
|
self._checkFlagsNZCV(0, 1, 1, 0)
|
|
|
|
@itest_setregs("R0=3")
|
|
@itest("cmp r0, 5")
|
|
def test_cmp_lt(self):
|
|
self._checkFlagsNZCV(1, 0, 0, 0)
|
|
|
|
@itest_setregs("R0=3")
|
|
@itest("cmp r0, 2")
|
|
def test_cmp_gt(self):
|
|
self._checkFlagsNZCV(0, 0, 1, 0)
|
|
|
|
@itest_setregs("R0=0")
|
|
@itest("cmp r0, 0")
|
|
def test_cmp_carry(self):
|
|
self._checkFlagsNZCV(0, 1, 1, 0)
|
|
|
|
@itest_setregs("R0=0x40000000")
|
|
@itest("cmp r0, 0xa0000000")
|
|
def test_cmp_ovf(self):
|
|
self._checkFlagsNZCV(1, 0, 0, 1)
|
|
|
|
@itest_setregs("R0=0x80000000")
|
|
@itest("cmp r0, 0x40000000")
|
|
def test_cmp_carry_ovf(self):
|
|
self._checkFlagsNZCV(0, 0, 1, 1)
|
|
|
|
# CLZ
|
|
|
|
@itest_custom("clz r1, r2")
|
|
@itest_setregs("R2=0xFFFF")
|
|
def test_clz_sixteen_zeroes(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 16)
|
|
|
|
@itest_custom("clz r1, r2")
|
|
@itest_setregs("R2=0")
|
|
def test_clz_all_zero(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), self.cpu.address_bit_size)
|
|
|
|
@itest_custom("clz r1, r2")
|
|
@itest_setregs("R2=0xffffffff")
|
|
def test_clz_no_leading_zeroes(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 0)
|
|
|
|
@itest_custom("clz r1, r2")
|
|
@itest_setregs("R2=0x7fffffff")
|
|
def test_clz_one_leading_zero(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 1)
|
|
|
|
@itest_custom("clz r1, r2")
|
|
@itest_setregs("R2=0x7f7fffff")
|
|
def test_clz_lead_zero_then_more_zeroes(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 1)
|
|
|
|
@itest_custom("sub r3, r1, r2")
|
|
@itest_setregs("R1=4", "R2=2")
|
|
def test_sub_basic(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 2)
|
|
|
|
@itest_custom("sub r3, r1, #5")
|
|
@itest_setregs("R1=10")
|
|
def test_sub_imm(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 5)
|
|
|
|
@itest_custom("sbc r3, r1, #5")
|
|
@itest_setregs("R1=10")
|
|
def test_sbc_imm(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R3'), 4)
|
|
|
|
@itest_custom("ldm sp, {r1, r2, r3}")
|
|
def test_ldm(self):
|
|
self.cpu.stack_push(0x41414141)
|
|
self.cpu.stack_push(2)
|
|
self.cpu.stack_push(42)
|
|
pre_sp = self.cpu.STACK
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 42)
|
|
self.assertEqual(self.rf.read('R2'), 2)
|
|
self.assertEqual(self.rf.read('R3'), 0x41414141)
|
|
self.assertEqual(self.cpu.STACK, pre_sp)
|
|
|
|
@itest_custom("ldm sp!, {r1, r2, r3}")
|
|
def test_ldm_wb(self):
|
|
self.cpu.stack_push(0x41414141)
|
|
self.cpu.stack_push(2)
|
|
self.cpu.stack_push(42)
|
|
pre_sp = self.cpu.STACK
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 42)
|
|
self.assertEqual(self.rf.read('R2'), 2)
|
|
self.assertEqual(self.rf.read('R3'), 0x41414141)
|
|
self.assertEqual(self.cpu.STACK, pre_sp + 12)
|
|
|
|
@itest_setregs("R1=2", "R2=42", "R3=0x42424242")
|
|
@itest_custom("stm sp, {r1, r2, r3}")
|
|
def test_stm(self):
|
|
self.cpu.STACK -= 12
|
|
pre_sp = self.cpu.STACK
|
|
self.cpu.execute()
|
|
self.assertEqual(self.cpu.read_int(pre_sp, self.cpu.address_bit_size), 2)
|
|
self.assertEqual(self.cpu.read_int(pre_sp + 4, self.cpu.address_bit_size), 42)
|
|
self.assertEqual(self.cpu.read_int(pre_sp + 8, self.cpu.address_bit_size),
|
|
0x42424242)
|
|
self.assertEqual(self.cpu.STACK, pre_sp)
|
|
|
|
@itest_setregs("R1=2", "R2=42", "R3=0x42424242")
|
|
@itest_custom("stm sp!, {r1, r2, r3}")
|
|
def test_stm_wb(self):
|
|
self.cpu.STACK -= 12
|
|
pre_sp = self.cpu.STACK
|
|
self.cpu.execute()
|
|
self.assertEqual(self.cpu.read_int(pre_sp, self.cpu.address_bit_size), 2)
|
|
self.assertEqual(self.cpu.read_int(pre_sp + 4, self.cpu.address_bit_size), 42)
|
|
self.assertEqual(self.cpu.read_int(pre_sp + 8, self.cpu.address_bit_size),
|
|
0x42424242)
|
|
self.assertEqual(self.cpu.STACK, pre_sp + 12)
|
|
|
|
@itest_custom("stmib r3, {r2, r4}")
|
|
@itest_setregs("R1=1", "R2=2", "R4=4", "R3=0xd100")
|
|
def test_stmib_basic(self):
|
|
self.cpu.execute()
|
|
addr = self.rf.read('R3')
|
|
self.assertEqual(self.cpu.read_int(addr + 4, self.cpu.address_bit_size), 2)
|
|
self.assertEqual(self.cpu.read_int(addr + 8, self.cpu.address_bit_size), 4)
|
|
|
|
@itest_custom("bx r1")
|
|
@itest_setregs("R1=0x1008")
|
|
def test_bx_basic(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('PC'), 0x1008)
|
|
|
|
@itest_custom("bx r1")
|
|
@itest_setregs("R1=0x1009")
|
|
def test_bx_thumb(self):
|
|
pre_pc = self.rf.read('PC')
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('PC'), pre_pc + 4)
|
|
|
|
# ORR
|
|
|
|
@itest_custom("orr r2, r3, #5")
|
|
@itest_setregs("R3=0x1000")
|
|
def test_orr_imm(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R2'), 0x1005)
|
|
|
|
@itest_custom("orrs r2, r3")
|
|
@itest_setregs("R2=0x5", "R3=0x80000000")
|
|
def test_orrs_imm_flags(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R2'), 0x80000005)
|
|
self.assertEqual(self.rf.read('APSR_N'), True)
|
|
|
|
@itest_custom("orr r2, r3")
|
|
@itest_setregs("R2=0x5", "R3=0x80000000")
|
|
def test_orr_reg_w_flags(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R2'), 0x80000005)
|
|
# self.assertEqual(self.rf.read('APSR_N'), 1)
|
|
|
|
@itest_custom("orr r2, r3, r4")
|
|
@itest_setregs("R3=0x5", "R4=0x80000000")
|
|
def test_orr_reg_two_op(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R2'), 0x80000005)
|
|
# self.assertEqual(self.rf.read('APSR_N'), 1)
|
|
|
|
@itest_custom("orr r2, r3, r4, LSL #4")
|
|
@itest_setregs("R3=0x5", "R4=0xF")
|
|
def test_orr_reg_two_op_shifted(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R2'), 0xF5)
|
|
|
|
# EOR
|
|
|
|
@itest_custom("eor r2, r3, #5")
|
|
@itest_setregs("R3=0xA")
|
|
def test_eor_imm(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R2'), 0xF)
|
|
|
|
@itest_custom("eors r2, r3")
|
|
@itest_setregs("R2=0xAA", "R3=0x80000000")
|
|
def test_eors_imm_flags(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R2'), 0x800000AA)
|
|
self.assertEqual(self.rf.read('APSR_N'), True)
|
|
|
|
@itest_custom("eors r2, r3")
|
|
@itest_setregs("R2=0x5", "R3=0x80000005")
|
|
def test_eor_reg_w_flags(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R2'), 0x80000000)
|
|
self.assertEqual(self.rf.read('APSR_N'), 1)
|
|
|
|
@itest_custom("eor r2, r3, r4")
|
|
@itest_setregs("R3=0x80000005", "R4=0x80000005")
|
|
def test_eor_reg_two_op(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R2'), 0)
|
|
|
|
@itest_custom("eor r2, r3, r4, LSL #4")
|
|
@itest_setregs("R3=0x55", "R4=0x5")
|
|
def test_eor_reg_two_op_shifted(self):
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R2'), 0x5)
|
|
|
|
# LDRH - see also LDR tests
|
|
|
|
@itest_custom("ldrh r1, [sp]")
|
|
def test_ldrh_imm_off_none(self):
|
|
self.cpu.stack_push(0x41410041)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 0x41)
|
|
|
|
@itest_custom("ldrh r1, [sp, r2]")
|
|
@itest_setregs("R2=4")
|
|
def test_ldrh_reg_off(self):
|
|
self.cpu.stack_push(0x41410041)
|
|
self.cpu.stack_push(48)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 0x41)
|
|
|
|
# LDRSH - see also LDR tests
|
|
|
|
@itest_custom("ldrsh r1, [sp]")
|
|
def test_ldrsh_imm_off_none_neg(self):
|
|
self.cpu.stack_push(0x2ff0f)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 0xffffff0f)
|
|
|
|
@itest_custom("ldrsh r1, [sp]")
|
|
def test_ldrsh_imm_off_none_pos(self):
|
|
self.cpu.stack_push(0xff0fff)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 0x0fff)
|
|
|
|
@itest_custom("ldrsh r1, [sp, r2]")
|
|
@itest_setregs("R2=4")
|
|
def test_ldrsh_reg_off_neg(self):
|
|
self.cpu.stack_push(0x2ff0f)
|
|
self.cpu.stack_push(48)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 0xffffff0f)
|
|
|
|
@itest_custom("ldrsh r1, [sp, r2]")
|
|
@itest_setregs("R2=4")
|
|
def test_ldrsh_reg_off_pos(self):
|
|
self.cpu.stack_push(0xff0fff)
|
|
self.cpu.stack_push(48)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 0x0fff)
|
|
|
|
# LDRB - see also LDR tests
|
|
|
|
@itest_custom("ldrb r1, [sp]")
|
|
def test_ldrb_imm_off_none(self):
|
|
self.cpu.stack_push(0x41)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 0x41)
|
|
|
|
@itest_custom("ldrb r1, [sp, r2]")
|
|
@itest_setregs("R2=4")
|
|
def test_ldrb_reg_off(self):
|
|
self.cpu.stack_push(0x41)
|
|
self.cpu.stack_push(48)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 0x41)
|
|
|
|
# LDRSB - see also LDR tests
|
|
|
|
@itest_custom("ldrsb r1, [sp]")
|
|
def test_ldrsb_imm_off_none_neg(self):
|
|
self.cpu.stack_push(0x2ff)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), Mask(32))
|
|
|
|
@itest_custom("ldrsb r1, [sp]")
|
|
def test_ldrsb_imm_off_none_pos(self):
|
|
self.cpu.stack_push(0xff0f)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 0xf)
|
|
|
|
@itest_custom("ldrsb r1, [sp, r2]")
|
|
@itest_setregs("R2=4")
|
|
def test_ldrsb_reg_off_neg(self):
|
|
self.cpu.stack_push(0x2ff)
|
|
self.cpu.stack_push(48)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), Mask(32))
|
|
|
|
@itest_custom("ldrsb r1, [sp, r2]")
|
|
@itest_setregs("R2=4")
|
|
def test_ldrsb_reg_off_pos(self):
|
|
self.cpu.stack_push(0xff0f)
|
|
self.cpu.stack_push(48)
|
|
self.cpu.execute()
|
|
self.assertEqual(self.rf.read('R1'), 0xf)
|
|
|
|
# TST
|
|
@itest_setregs("R1=1", "R3=0")
|
|
@itest("tst r3, r1")
|
|
def test_tst(self):
|
|
self._checkFlagsNZCV(0, 1, 0, 0)
|
|
|
|
# AND
|
|
@itest_setregs("R2=5")
|
|
@itest("and r2, r2, #1")
|
|
def test_and_imm(self):
|
|
self.assertEqual(self.rf.read('R2'), 1)
|
|
|
|
@itest_setregs("R1=5", "R2=3")
|
|
@itest("and r1, r1, r2")
|
|
def test_and_reg(self):
|
|
self.assertEqual(self.rf.read('R1'), 3 & 5)
|
|
|
|
@itest_setregs("R1=5", "R2=3", "APSR_C=1")
|
|
@itest("and r1, r1, r2")
|
|
def test_and_reg_carry(self):
|
|
self.assertEqual(self.rf.read('R1'), 3 & 5)
|
|
self.assertEqual(self.rf.read('APSR_C'), 1)
|
|
|
|
# svc
|
|
|
|
def test_svc(self):
|
|
with self.assertRaises(Interruption):
|
|
self._setupCpu("svc #0")
|
|
self.cpu.execute()
|
|
|
|
# lsl
|
|
|
|
@itest_setregs("R3=0x11")
|
|
@itest("lsls r4, r3, 1")
|
|
def test_lsl_imm_min(self):
|
|
self.assertEqual(self.rf.read('R4'), 0x11 << 1)
|
|
self._checkFlagsNZCV(0, 0, 0, 0)
|
|
|
|
@itest_setregs("R3=0x11")
|
|
@itest("lsls r4, r3, 31")
|
|
def test_lsl_imm_max(self):
|
|
self.assertEqual(self.rf.read('R4'), 1 << 31)
|
|
self._checkFlagsNZCV(1, 0, 0, 0)
|
|
|
|
@itest_setregs("R3=0x11", "R2=0xff01")
|
|
@itest("lsls r4, r3, r2")
|
|
def test_lsl_reg_min(self):
|
|
self.assertEqual(self.rf.read('R4'), 0x11 << 1)
|
|
self._checkFlagsNZCV(0, 0, 0, 0)
|
|
|
|
@itest_setregs("R3=0x11", "R2=0xff1f")
|
|
@itest("lsls r4, r3, r2")
|
|
def test_lsl_reg_max(self):
|
|
self.assertEqual(self.rf.read('R4'), 0x1 << 31)
|
|
self._checkFlagsNZCV(1, 0, 0, 0)
|
|
|
|
# lsr
|
|
@itest_setregs("R0=0x1000", "R2=3")
|
|
@itest("lsr r0, r0, r2")
|
|
def test_lsr_reg(self):
|
|
self.assertEqual(self.rf.read('R0'), 0x1000 >> 3)
|
|
|
|
@itest_setregs("R0=0x1000")
|
|
@itest("lsr r0, r0, #3")
|
|
def test_lsr_reg_imm(self):
|
|
self.assertEqual(self.rf.read('R0'), 0x1000 >> 3)
|
|
|
|
@itest_setregs("R2=29")
|
|
@itest("RSB r2, r2, #31")
|
|
def test_rsb_imm(self):
|
|
# Diverging instruction from trace
|
|
self.assertEqual(self.rf.read('R2'), 2)
|
|
|
|
def test_flag_state_continuity(self):
|
|
'''If an instruction only partially updates flags, cpu.setFlags should
|
|
ensure unupdated flags are preserved.
|
|
|
|
For example:
|
|
r1 = 2**31 - 1
|
|
add r2, r1, 0x1 // overflow = 1
|
|
mov r1, 1
|
|
mov r3, 0
|
|
tst r3, r1 // does not change overflow flag
|
|
// ovf should still be 1
|
|
'''
|
|
|
|
self.rf.write('R1', (2 ** 31 - 1))
|
|
self._setupCpu("adds r2, r1, #0x1")
|
|
self.cpu.execute()
|
|
self.rf.write('R1', 1)
|
|
self.rf.write('R3', 0)
|
|
self.mem.write(self.cpu.PC, assemble("tst r3, r1"))
|
|
self.cpu.execute()
|
|
self._checkFlagsNZCV(0, 1, 0, 1)
|
|
|
|
@itest_setregs("R1=30", "R2=10")
|
|
@itest("MUL R1, R2")
|
|
def test_mul_reg(self):
|
|
self.assertEqual(self.rf.read('R1'), 300)
|
|
|
|
@itest_setregs("R1=30", "R2=10")
|
|
@itest("MUL R3, R1, R2")
|
|
def test_mul_reg_w_dest(self):
|
|
self.assertEqual(self.rf.read('R3'), 300)
|
|
|
|
@itest_setregs("R2=10", "R3=15", "R4=7")
|
|
@itest("MLA R1, R2, R3, R4")
|
|
def test_mla_reg(self):
|
|
self.assertEqual(self.rf.read('R1'), 157)
|
|
|
|
@itest_setregs("R1=0xFF")
|
|
@itest("BIC R2, R1, #0x10")
|
|
def test_bic_reg_imm(self):
|
|
self.assertEqual(self.rf.read('R2'), 0xEF)
|
|
|
|
@itest_setregs("R1=0x1008")
|
|
@itest("BLX R1")
|
|
def test_blx_reg(self):
|
|
self.assertEqual(self.rf.read('PC'), 0x1008)
|
|
self.assertEqual(self.rf.read('LR'), 0x1008)
|
|
|
|
@itest_setregs("R1=0x1009")
|
|
@itest("BLX R1")
|
|
def test_blx_reg_thumb(self):
|
|
self.assertEqual(self.rf.read('PC'), 0x1008)
|
|
self.assertEqual(self.rf.read('LR'), 0x1008)
|
|
|
|
@itest_setregs("R1=0xffffffff", "R2=2")
|
|
@itest("UMULLS R1, R2, R1, R2")
|
|
def test_umull(self):
|
|
mul = 0xffffffff * 2
|
|
pre_c = self.rf.read('APSR_C')
|
|
pre_v = self.rf.read('APSR_V')
|
|
self.assertEqual(self.rf.read('R1'), mul & Mask(32))
|
|
self.assertEqual(self.rf.read('R2'), mul >> 32)
|
|
self._checkFlagsNZCV(0, 0, pre_c, pre_v)
|
|
|
|
@itest_setregs("R1=2", "R2=2")
|
|
@itest("UMULLS R1, R2, R1, R2")
|
|
def test_umull_still32(self):
|
|
mul = 2 * 2
|
|
pre_c = self.rf.read('APSR_C')
|
|
pre_v = self.rf.read('APSR_V')
|
|
self.assertEqual(self.rf.read('R1'), mul & Mask(32))
|
|
self.assertEqual(self.rf.read('R2'), mul >> 32)
|
|
self._checkFlagsNZCV(0, 0, pre_c, pre_v)
|
|
|
|
@itest_setregs("R1=0xfffffffe", "R2=0xfffffffe")
|
|
@itest("UMULLS R1, R2, R1, R2")
|
|
def test_umull_max(self):
|
|
mul = 0xfffffffe ** 2
|
|
pre_c = self.rf.read('APSR_C')
|
|
pre_v = self.rf.read('APSR_V')
|
|
self.assertEqual(self.rf.read('R1'), mul & Mask(32))
|
|
self.assertEqual(self.rf.read('R2'), mul >> 32)
|
|
self._checkFlagsNZCV(1, 0, pre_c, pre_v)
|
|
|
|
@itest_setregs("R1=3", "R2=0")
|
|
@itest("UMULLS R1, R2, R1, R2")
|
|
def test_umull_z(self):
|
|
mul = 3 * 0
|
|
pre_c = self.rf.read('APSR_C')
|
|
pre_v = self.rf.read('APSR_V')
|
|
self.assertEqual(self.rf.read('R1'), mul & Mask(32))
|
|
self.assertEqual(self.rf.read('R2'), (mul >> 32) & Mask(32))
|
|
self._checkFlagsNZCV(0, 1, pre_c, pre_v)
|
|
|
|
@itest("dmb ish")
|
|
def test_dmb(self):
|
|
# This is a nop, ensure that the instruction exists
|
|
self.assertTrue(True)
|