#!/usr/bin/python

import unittest
import io
from ppci.binutils.layout import Layout
from test_asm import AsmTestCaseBase
from ppci.arch.msp430 import instructions, registers


class Msp430AssemblerTestCase(AsmTestCaseBase):
    """ Test the msp430 assembler """
    march = 'msp430'

    def test_mov(self):
        """ Test move """
        self.feed("mov.w r14, r15")
        self.check('0F4E')

    def test_mov_b(self):
        """ Test move """
        self.feed("mov.b r14, r15")
        self.check('4F4E')

    def test_mov_1337(self):
        """ Test the move of an absolute value """
        self.feed("mov.w #0x1337, r12")
        self.check('3C40 3713')

    def test_mov_indirect(self):
        """ Test the move of memory values """
        self.feed("mov.w #0x1337, 0x123(r12)")
        self.check('bC40 3713 2301')

    def test_mov_global(self):
        """ Store at global location (absolute address) """
        self.feed('a:')
        self.feed('dw 0')
        self.feed('b:')
        self.feed('dw 0')
        self.feed('mov.w &a, r0')
        self.feed('mov.w r0, &b')
        self.feed('mov.w &a, &b')
        spec = "MEMORY flash LOCATION=0xf800 SIZE=0x100 {SECTION(code)}"
        layout = Layout.load(io.StringIO(spec))
        self.check('0000 0000 1042 00f8 8240 02f8 9242 00f8 02f8', layout)

    def test_add(self):
        """ Test add instruction """
        self.feed("add.w r15, r13")
        self.check('0D5F')

    def test_sub(self):
        """ Test sub instruction """
        self.feed("sub.w r4, r5")
        self.check('0584')

    def test_cmp(self):
        """ Test sub instruction """
        self.feed("cmp.w r6, r7")
        self.check('0796')

    def test_bit(self):
        """ Test bit instruction """
        self.feed("bit.w r8, r9")
        self.check('09b8')

    def test_clrc(self):
        """ Test clear carry """
        self.feed("clrc")
        self.check('12c3')

    def test_clrn(self):
        """ Test clear negative flag """
        self.feed("clrn")
        self.check('22c2')

    def test_clrz(self):
        """ Test clear zero flag """
        self.feed("clrz")
        self.check('22c3')

    def test_rrc(self):
        """ Test rrc """
        self.feed("rrc r7")
        self.check('0710')

    def test_rra(self):
        """ Test rra """
        self.feed("rra 0x1234(r6)")
        self.check('1611 3412')

    def test_sxt(self):
        """ Test sxt """
        self.feed("sxt @r4")
        self.check('a411')

    def test_push(self):
        """ Test push """
        self.feed("push @r13+")
        self.check('3d12')

    def test_pop(self):
        """ Test pop (emulated as mov @sp+, dst ) """
        self.feed("pop r6")
        self.check('3641')

    def test_nop(self):
        """ Test nop ( mov #0, r3 ) """
        self.feed("nop")
        self.check('0343')

    def test_ret(self):
        """ Test ret ( mov @sp+, pc ) """
        self.feed("ret")
        self.check('3041')

    def test_call(self):
        """ Test call """
        self.feed("call #a")
        self.feed("call #a")
        self.feed("a: call #a")
        self.check('b0120800 b0120800 b0120800')

    def test_reti(self):
        """ Test return from interrupt """
        self.feed("reti")
        self.check('0013')

    def test_jne(self):
        """ Test jumping around """
        self.feed('jne a')
        self.feed('jne a')
        self.feed('a:')
        self.feed('jne a')
        self.feed('jne a')
        self.check('0120 0020 ff23 fe23')


class Msp430Syntax(unittest.TestCase):
    def test_add(self):
        add = instructions.Addw(
            instructions.RegSrc(registers.r2),
            instructions.RegDst(registers.r3))
        self.assertEqual('add.w r2, r3', str(add))


class Msp430InstructionUseDef(unittest.TestCase):
    """ Test instruction use def info """
    def test_cmp(self):
        cp = instructions.Cmp(
            instructions.RegSrc(registers.r4),
            instructions.RegDst(registers.r5))
        self.assertEqual([registers.r4, registers.r5], cp.used_registers)
        self.assertEqual([], cp.defined_registers)

    def test_mov_reg_mem(self):
        mv = instructions.Mov(
            instructions.RegSrc(registers.r4),
            instructions.MemDst(10, registers.r5))
        self.assertEqual([registers.r4, registers.r5], mv.used_registers)
        self.assertEqual([], mv.defined_registers)

    def test_mov_regs(self):
        mv = instructions.Mov(
            instructions.RegSrc(registers.r4),
            instructions.RegDst(registers.r5))
        self.assertEqual([registers.r4], mv.used_registers)
        self.assertEqual([registers.r5], mv.defined_registers)

    def test_mov_mem_mem(self):
        mv = instructions.Mov(
            instructions.MemSrcOffset(20, registers.r4),
            instructions.MemDst(10, registers.r5))
        self.assertEqual([registers.r4, registers.r5], mv.used_registers)
        self.assertEqual([], mv.defined_registers)

    def test_add_regs(self):
        mv = instructions.Addw(
            instructions.RegSrc(registers.r4),
            instructions.RegDst(registers.r5))
        self.assertEqual([registers.r4, registers.r5], mv.used_registers)
        self.assertEqual([registers.r5], mv.defined_registers)


class Msp430ConstructorTestCase(unittest.TestCase):
    """ Test instruction use def info """
    def test_cmp(self):
        reg_src = instructions.RegSrc(registers.r4)
        reg_dst = instructions.RegDst(registers.r5)
        cp = instructions.Cmp(reg_src, reg_dst)
        self.assertSequenceEqual([cp, reg_src, reg_dst], list(cp.non_leaves))


if __name__ == '__main__':
    unittest.main()
