class TestInstructions(unittest.TestCase):

    def setUp(self):
        self.cpu = CPU()
        self.emulator = Emulator(self.cpu)

    def test_add(self):
        """ Sets REG.A to REG.A + REG.B """
        self.cpu.registers[REG.A].value = 0x12
        self.cpu.registers[REG.B].value = 0x34

        # ADD A, B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.ADD,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0x46, "Add failed")
        self.assertTrue(self.cpu.EX.value == 0x0, "Ex flag not reset")

    def test_add_overflow(self):
        """ Sets REG.A to REG.A + REG.B, overflows """
        self.cpu.registers[REG.A].value = 0xffff
        self.cpu.registers[REG.B].value = 0x0001

        # ADD A, B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.ADD,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0x0000, "Add failed")
        self.assertTrue(self.cpu.EX.value == 0xffff, "Ex flag not reset")

    def test_sub(self):
        """ Sets REG.A to REG.A - REG.B """
        self.cpu.registers[REG.A].value = 0xffff
        self.cpu.registers[REG.B].value = 0x0001

        # SUB A, B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SUB,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0xfffe, "Sub failed")
        self.assertTrue(self.cpu.EX.value == 0x0, "Ex flag not reset")

    def test_sub_underflow(self):
        """ Sets REG.A to REG.A - REG.B, underflows """
        self.cpu.registers[REG.A].value = 0x0001
        self.cpu.registers[REG.B].value = 0x0002

        # SUB A, B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SUB,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0x0001, "Sub failed")
        self.assertTrue(self.cpu.EX.value == 0xffff, "Ex flag not set")

    def test_mul(self):
        """ Sets REG.A to REG.A * REG.B """
        self.cpu.registers[REG.A].value = 0x0002
        self.cpu.registers[REG.B].value = 0x0003

        # MUL A, B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.MUL,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0x6, "Mul failed")
        self.assertTrue(self.cpu.EX.value == 0x0, "Ex flag not reset")

    def test_mul_overflows(self):
        """ Sets REG.A to REG.A * REG.B, overflows """
        self.cpu.registers[REG.A].value = 0xffff
        self.cpu.registers[REG.B].value = 0x0002

        # MUL A, B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.MUL,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0xfffe, "Mul failed")
        self.assertTrue(self.cpu.EX.value == 0x1, "Ex flag not set")

    def test_mli(self):
        """ Sets REG.A to REG.A * REG.B, treats A,B as signed """
        self.cpu.registers[REG.A].value = -5
        self.cpu.registers[REG.B].value = -4

        # MLI A, B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.MLI,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        signed_result = c_int16(self.cpu.registers[REG.A].value)
        self.assertEqual(signed_result.value, 20, "MLI failed")
        self.assertTrue(self.cpu.EX.value == 0x0, "Ex flag not reset")

    def test_div(self):
        """ Sets REG.A to REG.A / REG.B """
        self.cpu.registers[REG.A].value = 0x0009
        self.cpu.registers[REG.B].value = 0x0002

        # DIV A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.DIV,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0x4, "Div failed")

    def test_dvi(self):
        """ Sets REG.A to REG.A / REG.B """
        self.cpu.registers[REG.A].value = -4
        self.cpu.registers[REG.B].value = -2

        # DVI A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.DVI,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 2, "Div failed")

    def test_mod(self):
        """ Sets REG.A to REG.A % REG.B """
        self.cpu.registers[REG.A].value = 0x0009
        self.cpu.registers[REG.B].value = 0x0002

        # MOD A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.MOD,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0x1, "Mod failed")

    def test_mdi(self):
        """ Sets REG.A to REG.A % REG.B """
        self.cpu.registers[REG.A].value = -7
        self.cpu.registers[REG.B].value = 16

        # MDI A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.MDI,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        signed_result = c_int16(self.cpu.registers[REG.A].value)
        self.assertEqual(signed_result.value, -7, "MDI failed")

    def test_shl(self):
        """ Sets REG.A to REG.A << REG.B """
        self.cpu.registers[REG.A].value = 0x0008
        self.cpu.registers[REG.B].value = 0x0002

        # SHL A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SHL,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0x20, "shl failed")

    def test_asr(self):
        """ Sets REG.A to REG.A << REG.B """
        self.cpu.registers[REG.A].value = -3
        self.cpu.registers[REG.B].value = 2

        # ASR A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.ASR,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        signed_result = c_int16(self.cpu.registers[REG.A].value)
        self.assertEqual(signed_result.value, -1, "ASR failed")

    def test_shr(self):
        """ Sets REG.A to REGREG.> REG.B """
        self.cpu.registers[REG.A].value = 0x0008
        self.cpu.registers[REG.B].value = 0x0002

        # SHR A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SHR,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0x2, "shr failed")

    def test_and(self):
        """ Sets REG.A to REG.A & REG.B """
        self.cpu.registers[REG.A].value = 0x00ff
        self.cpu.registers[REG.B].value = 0x000f

        # AND A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.AND,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0xf, "and failed")

    def test_bor(self):
        """ Sets REG.A to REG.A | REG.B """
        self.cpu.registers[REG.A].value = 0x00ff
        self.cpu.registers[REG.B].value = 0x000f

        # BOR A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.BOR,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0xff, "bor failed")

    def test_xor(self):
        """ Sets REG.A to REG.A ^ REG.B """
        self.cpu.registers[REG.A].value = 0x00ff
        self.cpu.registers[REG.B].value = 0x000f

        # XOR A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.XOR,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0xf0, "xor failed")

    def test_ife_equal(self):
        self.cpu.registers[REG.A].value = 0x00ff
        self.cpu.registers[REG.B].value = 0x00ff

        # IFE A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.IFE,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.PC.value == 1, "PC not set right")

    def test_ife_notequal(self):
        self.cpu.registers[REG.A].value = 0x00ff
        self.cpu.registers[REG.B].value = 0x00ef

        # IFE A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.IFE,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.skip_instruction, True, "Skip error")

    def test_ifc_equal(self):
        self.cpu.registers[REG.A].value = 0x0000
        self.cpu.registers[REG.B].value = 0x00ff

        # IFC A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.IFC,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.skip_instruction, False, "PC not set right")


    def test_ifn_false(self):
        self.cpu.registers[REG.A].value = 0x00ff
        self.cpu.registers[REG.B].value = 0x00ff

        # IFN A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.IFN,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.skip_instruction, True, "Skip error")

    def test_ifn_true(self):
        self.cpu.registers[REG.A].value = 0x00ff
        self.cpu.registers[REG.B].value = 0x00ef

        # IFN A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.IFN,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.skip_instruction, False, "Skip error")

    def test_ifg_false(self):
        self.cpu.registers[REG.A].value = 0x00fe
        self.cpu.registers[REG.B].value = 0x00ff

        # IFG A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.IFG,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.skip_instruction, True, "Skip error")

    def test_ifg_true(self):
        self.cpu.registers[REG.A].value = 0x00ff
        self.cpu.registers[REG.B].value = 0x00ef

        # IFG A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.IFG,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.skip_instruction, False, "Skip error")

    def test_ifb_true(self):
        self.cpu.registers[REG.A].value = 0x00ff
        self.cpu.registers[REG.B].value = 0x00ff

        # IFB A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.IFB,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.skip_instruction, False, "Skip error")

    def test_ifb_notequal(self):
        self.cpu.registers[REG.A].value = 0x00ff
        self.cpu.registers[REG.B].value = 0x0000

        # IFB A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.IFB,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.skip_instruction, True, "Skip error")

    def test_ifa_true(self):
        self.cpu.registers[REG.A].value = 3
        self.cpu.registers[REG.B].value = -4

        # IFN A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.IFA,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.skip_instruction, False, "IFA failed")

    def test_ifl(self):
        self.cpu.registers[REG.A].value = 3
        self.cpu.registers[REG.B].value = -4

        # IFL A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.IFA,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.skip_instruction, False, "IFL failed")

    def test_ifu(self):
        self.cpu.registers[REG.A].value = 3
        self.cpu.registers[REG.B].value = -4

        # IFU A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.IFU,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.skip_instruction, True, "IFU failed")

    def test_adx(self):
        self.cpu.registers[REG.A].value = 0xfffe
        self.cpu.registers[REG.B].value = 0x0002
        self.cpu.EX.value = 0

        # ADX A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.ADX,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.EX.value, 1, "ADX failed")

    def test_sbx(self):
        self.cpu.registers[REG.A].value = 0x0003
        self.cpu.registers[REG.B].value = 0x0008
        self.cpu.EX.value = 0

        # SBX A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SBX,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.EX.value, 1, "SBX failed")

    def test_sti(self):
        self.cpu.registers[REG.A].value = 0x0003
        self.cpu.registers[REG.B].value = 0x0008
        self.cpu.registers[REG.I].value = 0x000a
        self.cpu.registers[REG.J].value = 0x000b

        # STI A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.STI,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.registers[REG.A].value, 0xb, "STI fail")
        self.assertEqual(self.cpu.registers[REG.I].value, 0xb, "STI fail")
        self.assertEqual(self.cpu.registers[REG.J].value, 0xc, "STI fail")

    def test_std(self):
        self.cpu.registers[REG.A].value = 0x0003
        self.cpu.registers[REG.B].value = 0x0008
        self.cpu.registers[REG.I].value = 0x000a
        self.cpu.registers[REG.J].value = 0x000b

        # STD A,B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.STD,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.registers[REG.A].value, 0xb, "STD fail")
        self.assertEqual(self.cpu.registers[REG.I].value, 0x9, "STD fail")
        self.assertEqual(self.cpu.registers[REG.J].value, 0xa, "STD fail")

    def test_jsr(self):
        self.cpu.registers[REG.I].value = 0x2323 # PC should be set to this

        # JSR I
        self.cpu.ram[0].value = pack_special_instruction(op_code=OPCODE.JSR,
                                                         a=REG.I)
        self.emulator.dispatch()

        self.assertEqual(self.cpu.PC.value, 0x2323, "PC not right")
        self.assertEqual(self.cpu.ram[self.cpu.SP.value].value, 0x1, "Stack not right")

    def test_pack_unpack(self):
        packed = pack_instruction(op_code=OPCODE.IFG,
                                  a=Value.reg(REG.A),
                                  b=Value.reg(REG.B))
        op_code, b, a = unpack_instruction(packed)

        self.assertEqual(op_code, OPCODE.IFG)
        self.assertEqual(b, Value.reg(REG.A))
        self.assertEqual(a, Value.reg(REG.B))

    def test_pack_unpack_special(self):
        packed = pack_special_instruction(op_code=OPCODE.JSR,
                                          a=Value.reg(REG.A))
        op_code, a = unpack_special_instruction(packed)

        self.assertEqual(op_code, OPCODE.JSR)
        self.assertEqual(a, Value.reg(REG.A))
class TestValues(unittest.TestCase):

    def setUp(self):
        self.cpu = CPU()
        self.emulator = Emulator(self.cpu)

    def test_set_reg_to_literal(self):
        """ Sets a register to a literal """
        # SET A, 0x10
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SET,
                                                 a=Value.reg(REG.A),
                                                 b=Value.literal(0x10))
        self.emulator.dispatch()
        self.assertTrue(self.cpu.registers[REG.A].value == 0x10, "Register value error")

    def test_set_addr_of_reg(self):
        """ Sets ram pointed by REG.I to value of REG.B """
        # Arrange
        self.cpu.registers[REG.I].value = 0xff
        self.cpu.registers[REG.B].value = 0x11

        # SET [I] B
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SET,
                                                 a=Value.addr_reg(REG.I),
                                                 b=Value.reg(REG.B))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.ram[0xff].value == 0x11, "Ram not updated correctly")

    def test_pop_into_reg(self):
        """ Pops from stack into register """
        self.cpu.ram[self.cpu.SP.value].value = 0xdead

        # SET A POP
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SET,
                                                 a=Value.reg(REG.A),
                                                 b=Value.push_pop())
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0xdead, "Stack popping b0rked")

    def test_push_from_register(self):
        """ Pushed value in register to stack """
        self.cpu.registers[REG.A].value = 0xdead

        # SET PUSH, A
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SET,
                                                 a=Value.push_pop(),
                                                 b=Value.reg(REG.A))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.ram[self.cpu.SP.value].value == 0xdead, "Stack pushing b0rked")

    def test_peek_into_register(self):
        """ Pushed value in register to stack """
        self.cpu.ram[self.cpu.SP.value].value = 0xbeef

        # SET A, PEEK
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SET,
                                                 a=Value.reg(REG.A),
                                                 b=Value.peek())
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0xbeef, "Stack peeking b0rked")

    def test_set_sp(self):
        """ Sets SP """

        # SET SP, 0x02
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SET,
                                                 a=Value.sp(),
                                                 b=Value.literal(0x02))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.SP.value == 0x2, "SP Loading failed")

    def test_set_pc(self):
        """ Sets PC """

        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SET,
                                                 a=Value.pc(),
                                                 b=Value.literal(0x5))
        self.emulator.dispatch()

        self.assertEqual(self.cpu.PC.value, 0x5, "PC Loading failed")

    def test_read_ex(self):
        """ Reads O into register """
        self.cpu.EX.value = 0xffff

        # SET A, EX
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SET,
                                                 a=Value.reg(REG.A),
                                                 b=Value.reg(REG.EX))
        self.emulator.dispatch()

        self.assertTrue(self.cpu.EX.value == 0xffff, "O Loading failed")

    def test_next_word_register(self):
        """ [Next word + register]  into register """
        self.cpu.registers[REG.B].value = 0x10
        self.cpu.ram[0x20].value = 0xfeed

        # SET C, [B + next word]
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SET,
                                                 a=Value.reg(REG.C),
                                                 b=Value.addr_reg_next_word(REG.B))
        self.cpu.ram[1].value = 0x10
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.C].value == 0xfeed, "Register + offset failed")

    def test_next_word_register_both(self):
        """ [Next word + register] into [Next word + register] """
        self.cpu.registers[REG.B].value = 0x10
        self.cpu.registers[REG.C].value = 0x20
        self.cpu.ram[0x40].value = 0xfeed

        # SET [0x0010 + B], [0x0020 + C]
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SET,
                                                 a=Value.addr_reg_next_word(REG.B),
                                                 b=Value.addr_reg_next_word(REG.C))
        self.cpu.ram[1].value = 0x10
        self.cpu.ram[2].value = 0x20
        self.emulator.dispatch()

        self.assertEqual(self.cpu.ram[0x20].value, 0xfeed, "Register + offset failed")
        self.assertEqual(self.cpu.PC.value, 0x3, "PC not set correctly")

    def test_ram_of_next_word(self):
        """ Reads from register to ram of next word into register"""
        self.cpu.ram[0x44].value = 0x1212

        # SET X [next word]
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SET,
                                                 a=Value.reg(REG.X),
                                                 b=Value.next_word_addr())
        self.cpu.ram[1].value = 0x44
        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.X].value == 0x1212, "Ram of next word error")

    def test_next_word_literal(self):
        """ Reads next word as a literal """

        # SET A 0x1f
        self.cpu.ram[0].value = pack_instruction(op_code=OPCODE.SET,
                                                 a=Value.reg(REG.A),
                                                 b=Value.next_word_literal())
        self.cpu.ram[1].value = 0x1234

        self.emulator.dispatch()

        self.assertTrue(self.cpu.registers[REG.A].value == 0x1234, "Next word as literal error")