class TestControlUnitV(TBCU2): """Test case for Model Machine Variable Control Unit.""" def setup(self): """Init state.""" super().setup() self.ram = RandomAccessMemory(BYTE_SIZE, 256, 'big', is_protected=True) self.control_unit = ControlUnitV(WORD_SIZE, BYTE_SIZE, self.registers, self.ram, self.alu, WORD_SIZE) assert self.control_unit.opcodes == {0x00, 0x01, 0x02, 0x03, 0x04, 0x13, 0x14, 0x05, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x93, 0x94, 0x95, 0x96, 0x99} def test_fetch_and_decode(self): """Right fetch and decode is a half of business.""" for opcode in set(range(2 ** BYTE_SIZE)) - self.control_unit.opcodes: with raises(ValueError): self.run_fetch(opcode, opcode, BYTE_SIZE) for opcode in ARITHMETIC_OPCODES | {OP_COMP, OP_MOVE}: self.control_unit.address1, self.control_unit.address2 = None, None self.run_fetch(opcode << 16 | 0x0203, opcode, 24) assert self.control_unit.address1 == 0x02 assert self.control_unit.address2 == 0x03 for opcode in CONDJUMP_OPCODES | {OP_JUMP}: self.control_unit.address1, self.control_unit.address2 = None, None self.run_fetch(opcode << 8 | 0x02, opcode, 16) assert self.control_unit.address1 == 0x02 assert self.control_unit.address2 is None for opcode in {OP_HALT}: self.control_unit.address1, self.control_unit.address2 = None, None self.run_fetch(opcode, opcode, 8) assert self.control_unit.address1 is None assert self.control_unit.address2 is None def test_load(self): """R1 := [A1], R2 := [A2].""" addr1, val1 = 5, 123456 addr2, val2 = 10, 654321 self.ram.put(addr1, val1, WORD_SIZE) self.ram.put(addr2, val2, WORD_SIZE) self.control_unit.address1 = addr1 self.control_unit.address2 = addr2 for opcode in ARITHMETIC_OPCODES | {OP_COMP}: self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() self.registers.put.assert_has_calls([call("R1", val1, WORD_SIZE), call("R2", val2, WORD_SIZE)]) for opcode in {OP_MOVE}: self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() self.registers.put.assert_called_once_with("R1", val2, WORD_SIZE) for opcode in CONDJUMP_OPCODES | {OP_JUMP}: self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() self.registers.put.assert_called_once_with("ADDR", addr1, BYTE_SIZE) for opcode in {OP_HALT}: self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() assert not self.registers.put.called def test_step(self): """Test step cycle.""" self.control_unit.registers = self.registers = RegisterMemory() self.registers.add_register('RI', WORD_SIZE) self.alu = ArithmeticLogicUnit(self.registers, self.control_unit.register_names, WORD_SIZE, BYTE_SIZE) self.control_unit.alu = self.alu self.ram.put(0x00, 0x01080c, 3 * BYTE_SIZE) self.ram.put(0x03, 0x050310, 3 * BYTE_SIZE) self.ram.put(0x06, 0x8614, 2 * BYTE_SIZE) self.ram.put(0x08, 12, WORD_SIZE) self.ram.put(0x0c, 10, WORD_SIZE) self.ram.put(0x10, 20, WORD_SIZE) self.ram.put(0x14, 0x99, BYTE_SIZE) self.registers.put("PC", 0, BYTE_SIZE) self.control_unit.step() assert self.ram.fetch(0x08, WORD_SIZE) == 22 assert self.registers.fetch("PC", BYTE_SIZE) == 0x03 assert self.control_unit.get_status() == RUNNING self.control_unit.step() assert self.ram.fetch(0x08, WORD_SIZE) == 22 assert self.registers.fetch("PC", BYTE_SIZE) == 0x06 assert self.control_unit.get_status() == RUNNING self.control_unit.step() assert self.registers.fetch("PC", BYTE_SIZE) == 0x14 assert self.control_unit.get_status() == RUNNING self.control_unit.step() assert self.registers.fetch("PC", BYTE_SIZE) == 0x15 assert self.control_unit.get_status() == HALTED def test_run(self): """Very simple program.""" self.control_unit.registers = self.registers = RegisterMemory() self.registers.add_register('RI', WORD_SIZE) self.alu = ArithmeticLogicUnit(self.registers, self.control_unit.register_names, WORD_SIZE, BYTE_SIZE) self.control_unit.alu = self.alu self.ram.put(0x00, 0x01080c, 3 * BYTE_SIZE) self.ram.put(0x03, 0x050310, 3 * BYTE_SIZE) self.ram.put(0x06, 0x8614, 2 * BYTE_SIZE) self.ram.put(0x08, 12, WORD_SIZE) self.ram.put(0x0c, 10, WORD_SIZE) self.ram.put(0x10, 20, WORD_SIZE) self.ram.put(0x14, 0x99, BYTE_SIZE) self.registers.put("PC", 0, BYTE_SIZE) self.control_unit.run() assert self.ram.fetch(0x08, WORD_SIZE) == 22 assert self.registers.fetch("PC", BYTE_SIZE) == 0x15 assert self.control_unit.get_status() == HALTED def test_minimal_run(self): """Minimal program.""" self.control_unit.registers = self.registers = RegisterMemory() self.registers.add_register('RI', WORD_SIZE) self.alu = ArithmeticLogicUnit(self.registers, self.control_unit.register_names, WORD_SIZE, BYTE_SIZE) self.control_unit.alu = self.alu self.ram.put(0x00, 0x99, BYTE_SIZE) self.registers.put("PC", 0, BYTE_SIZE) self.control_unit.run() assert self.registers.fetch("PC", BYTE_SIZE) == 0x01 assert self.control_unit.get_status() == HALTED
class TestControlUnitM(TBCU2): """Test case for Address Modification Model Machine Control Unit.""" def setup(self): """Init state.""" super().setup() self.ram = RandomAccessMemory(HALF_SIZE, 2 ** HALF_SIZE, 'big', is_protected=True) self.control_unit = ControlUnitM(WORD_SIZE, HALF_SIZE, self.registers, self.ram, self.alu, WORD_SIZE) self.operand_size = WORD_SIZE self.address_size = 2 * BYTE_SIZE assert self.control_unit.opcodes == {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x10, 0x11, 0x13, 0x14, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x33, 0x34, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x93, 0x94, 0x95, 0x96, 0x99} def test_const(self): super().test_const() assert self.control_unit.OPCODES["addr"] == OP_ADDR assert self.control_unit.OPCODES["rmove"] == OP_RMOVE assert self.control_unit.OPCODES["radd"] == OP_RADD assert self.control_unit.OPCODES["rsub"] == OP_RSUB assert self.control_unit.OPCODES["rsmul"] == OP_RSMUL assert self.control_unit.OPCODES["rsdivmod"] == OP_RSDIVMOD assert self.control_unit.OPCODES["rcomp"] == OP_RCOMP assert self.control_unit.OPCODES["rumul"] == OP_RUMUL assert self.control_unit.OPCODES["rudivmod"] == OP_RUDIVMOD def run_fetch(self, value, opcode, instruction_size, r2=True): """Run one fetch test.""" address1 = 10 address2 = 42 self.ram.put(address1, value, instruction_size) increment = instruction_size // self.ram.word_size # pylint: disable=no-member self.registers.fetch.reset_mock() self.registers.put.reset_mock() def get_register(name, size): """Get PC.""" if name == "PC": assert size == 2 * BYTE_SIZE return address1 elif name == "R2": assert size == WORD_SIZE return address2 else: raise KeyError() self.registers.fetch.side_effect = get_register self.control_unit.fetch_and_decode() if r2: self.registers.fetch.assert_has_calls([call("PC", 2 * BYTE_SIZE), call("R2", WORD_SIZE)]) else: self.registers.fetch.assert_any_call("PC", 2 * BYTE_SIZE) self.registers.put.assert_has_calls([call("RI", value, WORD_SIZE), call("PC", address1 + increment, 2 * BYTE_SIZE)]) assert self.control_unit.opcode == opcode def test_fetch_and_decode(self): """Right fetch and decode is a half of business.""" for opcode in set(range(2 ** BYTE_SIZE)) - self.control_unit.opcodes: with raises(ValueError): self.run_fetch(opcode << BYTE_SIZE, opcode, 2 * BYTE_SIZE) for opcode in ARITHMETIC_OPCODES | JUMP_OPCODES | {OP_COMP, OP_LOAD, OP_ADDR, OP_STORE}: self.control_unit.register1 = None self.control_unit.register2 = None self.control_unit.address = None self.run_fetch(opcode << 24 | 0x120014, opcode, 32) assert self.control_unit.register1 == 'R1' assert self.control_unit.register2 is None assert self.control_unit.address == 0x14 + 42 for opcode in REGISTER_OPCODES: self.control_unit.register1 = None self.control_unit.register2 = None self.control_unit.address = None self.run_fetch(opcode << 8 | 0x12, opcode, 16, r2=False) assert self.control_unit.register1 == 'R1' assert self.control_unit.register2 == 'R2' assert self.control_unit.address is None for opcode in {OP_HALT}: self.control_unit.register1 = None self.control_unit.register2 = None self.control_unit.address = None self.run_fetch(opcode << 8 | 0x12, opcode, 16, r2=False) assert self.control_unit.register1 is None assert self.control_unit.register2 is None assert self.control_unit.address is None def test_load(self): """R1 := [A1], R2 := [A2].""" register1, val1 = 'R3', 123456 register2, val2 = 'R4', 654321 address, val3 = 10, 111111 def get_register(name, size): """Get PC.""" assert size == WORD_SIZE if name == register1: return val1 elif name == register2: return val2 else: raise KeyError() self.registers.fetch.side_effect = get_register self.control_unit.address = address self.control_unit.register1 = register1 self.control_unit.register2 = register2 self.ram.put(address, val3, WORD_SIZE) # pylint: disable=no-member for opcode in ARITHMETIC_OPCODES | {OP_LOAD, OP_COMP}: self.registers.fetch.reset_mock() self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() self.registers.fetch.assert_called_once_with(register1, WORD_SIZE) self.registers.put.assert_has_calls([call("S", val1, WORD_SIZE), call("RZ", val3, WORD_SIZE)]) for opcode in {OP_STORE}: self.registers.fetch.reset_mock() self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() self.registers.fetch.assert_called_once_with(register1, WORD_SIZE) self.registers.put.assert_called_once_with("S", val1, WORD_SIZE) for opcode in REGISTER_OPCODES: self.registers.fetch.reset_mock() self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() self.registers.fetch.assert_has_calls([call(register1, WORD_SIZE), call(register2, WORD_SIZE)]) self.registers.put.assert_has_calls([call("S", val1, WORD_SIZE), call("RZ", val2, WORD_SIZE)]) for opcode in {OP_ADDR}: self.registers.fetch.reset_mock() self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() assert not self.registers.fetch.called self.registers.put.assert_called_once_with("S", address, WORD_SIZE) for opcode in CONDJUMP_OPCODES | {OP_JUMP}: self.registers.fetch.reset_mock() self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() assert not self.registers.fetch.called self.registers.put.assert_called_once_with("ADDR", address, 2 * BYTE_SIZE) for opcode in {OP_HALT}: self.registers.fetch.reset_mock() self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() assert not self.registers.fetch.called assert not self.registers.put.called def test_basic_execute(self, should_move=None): """Test basic operations.""" super().test_basic_execute(should_move=should_move) # pylint: disable=no-member self.control_unit.opcode = OP_MOVE self.alu.move.reset_mock() self.control_unit.execute() self.alu.move.assert_called_once_with('R2', 'S') self.control_unit.opcode = OP_ADDR self.alu.move.reset_mock() self.control_unit.execute() assert not self.alu.move.called def run_write_back(self, should, opcode): """Run write back method for specific opcode.""" print(hex(opcode), should) register1, next_register1, register2 = 'R5', 'R6', 'R8' res_register1, val1 = 'S', 123456 res_register2, val2 = 'RZ', 654321 address, canary = 10, 0 def get_register(name, size): """Get PC.""" assert size == self.operand_size if name == res_register1: return val1 elif name == res_register2: return val2 else: raise KeyError() self.registers.fetch.side_effect = get_register self.control_unit.address = address self.control_unit.register1 = register1 self.control_unit.register2 = register2 self.ram.put(address, canary, self.operand_size) # pylint: disable=no-member self.registers.fetch.reset_mock() self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.write_back() if should == 'two_registers': self.registers.fetch.assert_has_calls([call(res_register1, self.operand_size), call(res_register2, self.operand_size)]) self.registers.put.assert_has_calls([call(register1, val1, self.operand_size), call(next_register1, val2, self.operand_size)]) assert self.ram.fetch(address, self.operand_size) == canary elif should == 'register': self.registers.fetch.assert_called_once_with(res_register1, self.operand_size) self.registers.put.assert_called_once_with(register1, val1, self.operand_size) assert self.ram.fetch(address, self.operand_size) == canary elif should == 'memory': self.registers.fetch.assert_called_once_with(res_register1, self.operand_size) assert not self.registers.put.called assert self.ram.fetch(address, self.operand_size) == val1 else: assert not self.registers.fetch.called assert not self.registers.put.called assert self.ram.fetch(address, self.operand_size) == canary def test_write_back(self): """Test write back result to the memory.""" for opcode in {OP_SDIVMOD, OP_UDIVMOD}: self.run_write_back('two_registers', opcode) for opcode in (ARITHMETIC_OPCODES | {OP_ADDR, OP_LOAD}) - \ {OP_SDIVMOD, OP_UDIVMOD}: self.run_write_back('register', opcode) for opcode in {OP_STORE}: self.run_write_back('memory', opcode) for opcode in (CONDJUMP_OPCODES | {OP_HALT, OP_JUMP, OP_COMP}): self.run_write_back('nothing', opcode) def test_step(self): """Test step cycle.""" self.control_unit.registers = self.registers = RegisterMemory() for register in {'RI', 'RZ', 'S', 'R0', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8', 'R9', 'RA', 'RB', 'RC', 'RD', 'RE', 'RF'}: self.registers.add_register(register, self.operand_size) self.alu = ArithmeticLogicUnit(self.registers, self.control_unit.register_names, self.operand_size, self.address_size) self.control_unit.alu = self.alu canary = 0 self.ram.put(0x0000, 0x00000100, WORD_SIZE) self.ram.put(0x0002, 0x0300000C, WORD_SIZE) self.ram.put(0x0004, 0x0400000E, WORD_SIZE) self.ram.put(0x0006, 0x02100102, WORD_SIZE) self.ram.put(0x0008, 0x2311, 2 * BYTE_SIZE) self.ram.put(0x0009, 0x10100104, WORD_SIZE) self.ram.put(0x000B, 0x9900, 2 * BYTE_SIZE) self.ram.put(0x000C, 0xffffffeb, WORD_SIZE) self.ram.put(0x000E, 0x00000032, WORD_SIZE) self.ram.put(0x0100, -123 % 2 ** WORD_SIZE, WORD_SIZE) self.ram.put(0x0102, 456, WORD_SIZE) self.ram.put(0x0104, canary, WORD_SIZE) self.registers.put("PC", 0, 2 * BYTE_SIZE) self.control_unit.step() assert self.registers.fetch("R0", WORD_SIZE) == -123 % 2 ** WORD_SIZE assert self.registers.fetch("R1", WORD_SIZE) == 0 assert self.registers.fetch("PC", 2 * BYTE_SIZE) == 0x02 assert self.ram.fetch(0x0104, WORD_SIZE) == canary assert self.control_unit.get_status() == RUNNING self.control_unit.step() assert self.registers.fetch("R0", WORD_SIZE) == (21 * 123) assert self.registers.fetch("R1", WORD_SIZE) == 0 assert self.registers.fetch("PC", 2 * BYTE_SIZE) == 0x04 assert self.ram.fetch(0x0104, WORD_SIZE) == canary assert self.control_unit.get_status() == RUNNING self.control_unit.step() assert self.registers.fetch("R0", WORD_SIZE) == (21 * 123) // 50 x = 21 * 123 % 50 assert self.registers.fetch("R1", WORD_SIZE) == x assert self.registers.fetch("PC", 2 * BYTE_SIZE) == 0x06 assert self.ram.fetch(0x0104, WORD_SIZE) == canary assert self.control_unit.get_status() == RUNNING self.control_unit.step() assert self.registers.fetch("R0", WORD_SIZE) == (21 * 123) // 50 assert self.registers.fetch("R1", WORD_SIZE) == (x - 456) % 2 ** WORD_SIZE assert self.registers.fetch("PC", 2 * BYTE_SIZE) == 0x08 assert self.ram.fetch(0x0104, WORD_SIZE) == canary assert self.control_unit.get_status() == RUNNING self.control_unit.step() assert self.registers.fetch("R0", WORD_SIZE) == (21 * 123) // 50 assert self.registers.fetch("R1", WORD_SIZE) == (x - 456) ** 2 assert self.registers.fetch("PC", 2 * BYTE_SIZE) == 0x09 assert self.ram.fetch(0x0104, WORD_SIZE) == canary assert self.control_unit.get_status() == RUNNING self.control_unit.step() assert self.registers.fetch("R0", WORD_SIZE) == (21 * 123) // 50 assert self.registers.fetch("R1", WORD_SIZE) == (x - 456) ** 2 assert self.registers.fetch("PC", 2 * BYTE_SIZE) == 0x0b assert self.ram.fetch(0x0104, WORD_SIZE) == (x - 456) ** 2 assert self.control_unit.get_status() == RUNNING self.control_unit.step() assert self.registers.fetch("R0", WORD_SIZE) == (21 * 123) // 50 assert self.registers.fetch("R1", WORD_SIZE) == (x - 456) ** 2 assert self.registers.fetch("PC", 2 * BYTE_SIZE) == 0x0C assert self.control_unit.get_status() == HALTED def test_run(self): """Very simple program.""" self.control_unit.registers = self.registers = RegisterMemory() for register in {'RI', 'RZ', 'S', 'R0', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8', 'R9', 'RA', 'RB', 'RC', 'RD', 'RE', 'RF'}: self.registers.add_register(register, self.operand_size) self.alu = ArithmeticLogicUnit(self.registers, self.control_unit.register_names, self.operand_size, self.address_size) self.control_unit.alu = self.alu self.ram.put(0x0000, 0x00000100, WORD_SIZE) self.ram.put(0x0002, 0x0300000C, WORD_SIZE) self.ram.put(0x0004, 0x0400000E, WORD_SIZE) self.ram.put(0x0006, 0x02100102, WORD_SIZE) self.ram.put(0x0008, 0x2311, 2 * BYTE_SIZE) self.ram.put(0x0009, 0x10100104, WORD_SIZE) self.ram.put(0x000B, 0x9900, 2 * BYTE_SIZE) self.ram.put(0x000C, 0xffffffeb, WORD_SIZE) self.ram.put(0x000E, 0x00000032, WORD_SIZE) self.ram.put(0x0100, 0xffffff85, WORD_SIZE) self.ram.put(0x0102, 0x000001c8, WORD_SIZE) self.registers.put("PC", 0, 2 * BYTE_SIZE) self.control_unit.run() assert self.ram.fetch(0x0104, WORD_SIZE) == 178929 assert self.registers.fetch("PC", 2 * BYTE_SIZE) == 0x000C assert self.control_unit.get_status() == HALTED def test_minimal_run(self): """Minimal program.""" self.control_unit.registers = self.registers = RegisterMemory() self.registers.add_register('RI', self.operand_size) self.alu = ArithmeticLogicUnit(self.registers, self.control_unit.register_names, self.operand_size, self.address_size) self.control_unit.alu = self.alu self.ram.put(0x00, 0x9900, 2 * BYTE_SIZE) self.registers.put("PC", 0, 2 * BYTE_SIZE) self.control_unit.run() assert self.registers.fetch("PC", 2 * BYTE_SIZE) == 0x01 assert self.control_unit.get_status() == HALTED
class TestControlUnit3(TBCU): """Test case for Mode Machine 3 Control Unit.""" def setup(self): """Init state.""" super().setup() self.ram = RandomAccessMemory(WORD_SIZE, 256, 'big') self.control_unit = ControlUnit3(WORD_SIZE, BYTE_SIZE, self.registers, self.ram, self.alu, WORD_SIZE) assert self.control_unit.opcodes == {0x00, 0x01, 0x02, 0x03, 0x04, 0x13, 0x14, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x93, 0x94, 0x95, 0x96, 0x99} def test_fetch_and_decode(self): """Right fetch and decode is a half of business.""" for opcode in self.control_unit.opcodes: self.control_unit.address1, self.control_unit.address2 = None, None self.run_fetch(opcode << 24 | 0x020304, opcode, WORD_SIZE) assert self.control_unit.address1 == 0x02 assert self.control_unit.address2 == 0x03 assert self.control_unit.address3 == 0x04 for opcode in set(range(2 ** BYTE_SIZE)) - self.control_unit.opcodes: with raises(ValueError): self.run_fetch(opcode << 24 | 0x020304, opcode, WORD_SIZE) def test_load(self): """R1 := [A1], R2 := [A2].""" addr1, val1 = 5, 123456 addr2, val2 = 10, 654321 addr3 = 15 self.ram.put(addr1, val1, WORD_SIZE) self.ram.put(addr2, val2, WORD_SIZE) self.control_unit.address1 = addr1 self.control_unit.address2 = addr2 self.control_unit.address3 = addr3 for opcode in ARITHMETIC_OPCODES: self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() self.registers.put.assert_has_calls([call("R1", val1, WORD_SIZE), call("R2", val2, WORD_SIZE)]) for opcode in CONDJUMP_OPCODES: self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() self.registers.put.assert_has_calls([call("R1", val1, WORD_SIZE), call("R2", val2, WORD_SIZE), call("ADDR", addr3, BYTE_SIZE)]) for opcode in {OP_MOVE}: self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() self.registers.put.assert_called_once_with("R1", val1, WORD_SIZE) for opcode in {OP_JUMP}: self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() self.registers.put.assert_called_once_with("ADDR", addr3, BYTE_SIZE) for opcode in {OP_HALT}: self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.load() assert not self.registers.put.called def test_basic_execute(self, should_move=True): """Test basic operations.""" super().test_basic_execute(should_move) for opcode in range(0, 256): if not opcode in self.control_unit.opcodes: with raises(ValueError): self.control_unit.opcode = opcode self.control_unit.execute() def run_cond_jump(self, opcode, signed, mol, equal): """Run one conditional jump test.""" self.alu.cond_jump.reset_mock() self.alu.sub.reset_mock() self.registers.put.reset_mock() self.control_unit.opcode = opcode self.control_unit.execute() self.alu.sub.assert_called_once_with() assert not self.registers.put.called self.alu.cond_jump.assert_called_once_with(signed, mol, equal) def test_execute_cond_jumps(self): """Test for jumps.""" self.run_cond_jump(OP_JEQ, True, EQUAL, True) self.run_cond_jump(OP_JNEQ, True, EQUAL, False) self.run_cond_jump(OP_SJL, True, LESS, False) self.run_cond_jump(OP_SJGEQ, True, GREATER, True) self.run_cond_jump(OP_SJLEQ, True, LESS, True) self.run_cond_jump(OP_SJG, True, GREATER, False) self.run_cond_jump(OP_UJL, False, LESS, False) self.run_cond_jump(OP_UJGEQ, False, GREATER, True) self.run_cond_jump(OP_UJLEQ, False, LESS, True) self.run_cond_jump(OP_UJG, False, GREATER, False) def test_execute_jump_halt(self): """Test for jump and halt.""" self.alu.cond_jump.reset_mock() self.alu.sub.reset_mock() self.registers.put.reset_mock() self.control_unit.opcode = OP_JUMP self.control_unit.execute() assert not self.alu.sub.called assert not self.registers.put.called self.alu.jump.assert_called_once_with() self.control_unit.opcode = OP_HALT self.control_unit.execute() assert not self.alu.sub.called assert not self.registers.put.called self.alu.halt.assert_called_once_with() def run_write_back(self, should, opcode): """Run write back method for specific opcode.""" first, second, third = 11111111, 22222222, 33333333 size = WORD_SIZE // self.ram.word_size def get_register(name, size): """Get result.""" assert name in {"S", "R1"} assert size == WORD_SIZE if name == "S": return second elif name == "R1": return third self.registers.fetch.side_effect = get_register for address in (10, 2 ** BYTE_SIZE - size): next_address = (address + size) % 2 ** BYTE_SIZE self.ram.put(address, first, WORD_SIZE) self.ram.put(next_address, first, WORD_SIZE) self.control_unit.address3 = address self.control_unit.opcode = opcode self.control_unit.write_back() if should: assert self.ram.fetch(address, WORD_SIZE) == second if opcode in {OP_SDIVMOD, OP_UDIVMOD}: assert self.ram.fetch(next_address, WORD_SIZE) == third else: assert self.ram.fetch(next_address, WORD_SIZE) == first else: assert self.ram.fetch(address, WORD_SIZE) == first def test_write_back(self): """Test write back result to the memory.""" for opcode in ARITHMETIC_OPCODES | {OP_MOVE}: self.run_write_back(True, opcode) for opcode in (CONDJUMP_OPCODES | {OP_HALT, OP_JUMP}): self.run_write_back(False, opcode) def test_step(self): """Test step cycle.""" self.control_unit.registers = self.registers = RegisterMemory() self.registers.add_register('RI', WORD_SIZE) self.alu = ArithmeticLogicUnit(self.registers, self.control_unit.register_names, WORD_SIZE, BYTE_SIZE) self.control_unit.alu = self.alu self.ram.put(0, 0x01020304, WORD_SIZE) self.ram.put(1, 0x82020305, WORD_SIZE) self.ram.put(2, 12, WORD_SIZE) self.ram.put(3, 10, WORD_SIZE) self.ram.put(5, 0x99000000, WORD_SIZE) self.registers.put("PC", 0, BYTE_SIZE) self.control_unit.step() assert self.ram.fetch(4, WORD_SIZE) == 22 assert self.registers.fetch("PC", BYTE_SIZE) == 1 assert self.control_unit.get_status() == RUNNING self.control_unit.step() assert self.registers.fetch("PC", BYTE_SIZE) == 5 assert self.control_unit.get_status() == RUNNING self.control_unit.step() assert self.registers.fetch("PC", BYTE_SIZE) == 6 assert self.control_unit.get_status() == HALTED def test_run(self): """Very simple program.""" self.control_unit.registers = self.registers = RegisterMemory() self.registers.add_register('RI', WORD_SIZE) self.alu = ArithmeticLogicUnit(self.registers, self.control_unit.register_names, WORD_SIZE, BYTE_SIZE) self.control_unit.alu = self.alu self.ram.put(0, 0x01020304, WORD_SIZE) self.ram.put(1, 0x82020305, WORD_SIZE) self.ram.put(2, 12, WORD_SIZE) self.ram.put(3, 10, WORD_SIZE) self.ram.put(5, 0x99000000, WORD_SIZE) self.registers.put("PC", 0, BYTE_SIZE) self.control_unit.run() assert self.ram.fetch(4, WORD_SIZE) == 22 assert self.registers.fetch("PC", BYTE_SIZE) == 6 assert self.control_unit.get_status() == HALTED
class TestControlUnit: """Test case for abstract bordachenkova control unit.""" ram = None registers = None alu = None control_unit = None arithmetic_opcodes = None condjump_opcodes = None ir_size = 32 operand_size = WORD_SIZE address_size = BYTE_SIZE def setup(self): """Init state.""" self.ram = RandomAccessMemory(WORD_SIZE, 256, 'big') self.registers = create_autospec(RegisterMemory, True, True) self.alu = create_autospec(ArithmeticLogicUnit, True, True) self.control_unit = ControlUnit(WORD_SIZE, BYTE_SIZE, self.registers, self.ram, self.alu, WORD_SIZE) self.test_const() def test_const(self): """Test internal constants.""" assert isinstance(self.control_unit, AbstractControlUnit) assert isinstance(self.control_unit, ControlUnit) assert self.control_unit.ir_size == self.ir_size assert self.control_unit.operand_size == self.operand_size assert self.control_unit.address_size == self.address_size assert self.control_unit.OPCODE_SIZE == BYTE_SIZE assert self.control_unit.OPCODES["move"] == OP_MOVE assert self.control_unit.OPCODES["load"] == OP_LOAD assert self.control_unit.OPCODES["store"] == OP_STORE assert self.control_unit.OPCODES["swap"] == OP_SWAP assert self.control_unit.OPCODES["add"] == OP_ADD assert self.control_unit.OPCODES["sub"] == OP_SUB assert self.control_unit.OPCODES["smul"] == OP_SMUL assert self.control_unit.OPCODES["sdivmod"] == OP_SDIVMOD assert self.control_unit.OPCODES["umul"] == OP_UMUL assert self.control_unit.OPCODES["udivmod"] == OP_UDIVMOD assert self.control_unit.OPCODES["comp"] == OP_COMP assert self.control_unit.OPCODES["stpush"] == OP_STPUSH assert self.control_unit.OPCODES["stpop"] == OP_STPOP assert self.control_unit.OPCODES["stdup"] == OP_STDUP assert self.control_unit.OPCODES["stswap"] == OP_STSWAP assert self.control_unit.OPCODES["jump"] == OP_JUMP assert self.control_unit.OPCODES["jeq"] == OP_JEQ assert self.control_unit.OPCODES["jneq"] == OP_JNEQ assert self.control_unit.OPCODES["sjl"] == OP_SJL assert self.control_unit.OPCODES["sjgeq"] == OP_SJGEQ assert self.control_unit.OPCODES["sjleq"] == OP_SJLEQ assert self.control_unit.OPCODES["sjg"] == OP_SJG assert self.control_unit.OPCODES["ujl"] == OP_UJL assert self.control_unit.OPCODES["ujgeq"] == OP_UJGEQ assert self.control_unit.OPCODES["ujleq"] == OP_UJLEQ assert self.control_unit.OPCODES["ujg"] == OP_UJG assert self.control_unit.OPCODES["halt"] == OP_HALT def test_fetch_and_decode(self): """Abstract class.""" with raises(NotImplementedError): self.control_unit.fetch_and_decode() def test_load(self): """Abstract class.""" with raises(NotImplementedError): self.control_unit.load() def test_write_back(self): """Abstract class.""" with raises(NotImplementedError): self.control_unit.write_back() def run_fetch(self, value, opcode, instruction_size, and_decode=True, address_size=BYTE_SIZE, ir_size=WORD_SIZE): """Run one fetch test.""" address = 10 self.ram.put(address, value, instruction_size) increment = instruction_size // self.ram.word_size self.registers.fetch.reset_mock() self.registers.put.reset_mock() def get_register(name, size): """Get PC.""" assert name == "PC" assert size == self.control_unit.address_size return address self.registers.fetch.side_effect = get_register if and_decode: self.control_unit.fetch_and_decode() else: self.control_unit.fetch_instruction(instruction_size) self.registers.fetch.assert_any_call("PC", address_size) self.registers.put.assert_has_calls([call("RI", value, ir_size), call("PC", address + increment, address_size)]) assert self.control_unit.opcode == opcode def test_fetch_instruction(self): """Right fetch and decode is a half of business.""" self.run_fetch(0x01020304, 0x01, WORD_SIZE, False) def test_basic_execute(self, should_move=True): """Test basic operations.""" self.registers.put.reset_mock() self.registers.fetch.reset_mock() if should_move is not None: self.control_unit.opcode = OP_MOVE self.alu.move.reset_mock() self.control_unit.execute() if should_move: self.alu.move.assert_called_once_with() else: assert not self.alu.move.called self.control_unit.opcode = OP_ADD self.alu.add.reset_mock() self.control_unit.execute() self.alu.add.assert_called_once_with() self.control_unit.opcode = OP_SUB self.alu.sub.reset_mock() self.control_unit.execute() self.alu.sub.assert_called_once_with() self.control_unit.opcode = OP_SMUL self.alu.smul.reset_mock() self.control_unit.execute() self.alu.smul.assert_called_once_with() self.control_unit.opcode = OP_UMUL self.alu.umul.reset_mock() self.control_unit.execute() self.alu.umul.assert_called_once_with() self.control_unit.opcode = OP_SDIVMOD self.alu.sdivmod.reset_mock() self.control_unit.execute() self.alu.sdivmod.assert_called_once_with() self.control_unit.opcode = OP_UDIVMOD self.alu.udivmod.reset_mock() self.control_unit.execute() self.alu.udivmod.assert_called_once_with() self.control_unit.opcode = OP_HALT self.alu.halt.reset_mock() self.control_unit.execute() self.alu.halt.assert_called_once_with() with raises(ValueError): self.control_unit.opcode = 0x98 self.control_unit.execute() assert not self.registers.fetch.called assert not self.registers.put.called
class TestRandomAccessMemory: """Test case for RAM.""" ram = None def setup(self): """Init state.""" self.ram = RandomAccessMemory(WORD_SIZE, 512, endianess='big') assert self.ram.word_size == WORD_SIZE assert self.ram.memory_size == 512 assert len(self.ram) == 512 assert self.ram.is_protected is True def test_check_address(self): """It a second part of information protection.""" for i in range(len(self.ram)): self.ram.check_address(i) for i in range(len(self.ram), 2 * len(self.ram)): with raises(KeyError): self.ram.check_address(i) with raises(TypeError): self.ram.check_address('R1') def test_setitem(self): """Address should be checked.""" for i in range(len(self.ram)): self.ram[i] = len(self.ram) + i assert i in self.ram for i in range(len(self.ram), 2 * len(self.ram)): with raises(KeyError): self.ram[i] = i assert i not in self.ram with raises(TypeError): self.ram['R1'] = 10 assert 'R1' not in self.ram with raises(ValueError): self.ram[2] = 2 ** WORD_SIZE def test_getitem(self): """Address should be checked.""" for i in range(2 * len(self.ram)): with raises(KeyError): self.ram.__getitem__(i) with raises(TypeError): self.ram.__getitem__('R1') for i in range(len(self.ram) // 2): self.ram[i] = i assert self.ram[i] == i def test_not_protected_getitem(self): """Test if programmer can shut in his leg.""" self.ram = RandomAccessMemory(WORD_SIZE, 512, 'big', is_protected=False) for i in range(len(self.ram)): assert self.ram[i] == 0 for i in range(len(self.ram), 2 * len(self.ram)): with raises(KeyError): self.ram.__getitem__(i) def test_fetch(self): """Fetch is basic operation of transfer data.""" for i in range(5, 9): self.ram[i] = i assert self.ram.fetch(i, WORD_SIZE) == i assert (self.ram.fetch(5, 4 * WORD_SIZE) == 0x00000005000000060000000700000008) assert (self.ram.fetch(5, 4 * WORD_SIZE) == big_endian_decode([5, 6, 7, 8], WORD_SIZE)) self.ram[5] = 0 assert (self.ram.fetch(5, 4 * WORD_SIZE) == big_endian_decode([0, 6, 7, 8], WORD_SIZE)) assert (self.ram.fetch(5, 4 * WORD_SIZE) == 0x00000000000000060000000700000008) with raises(KeyError): self.ram.fetch(5, 4 * WORD_SIZE - 1) with raises(KeyError): self.ram.fetch(4, 4 * WORD_SIZE) self.ram = RandomAccessMemory(WORD_SIZE, 512, endianess="little") for i in range(5, 9): self.ram[i] = i assert self.ram.fetch(i, WORD_SIZE) == i assert (self.ram.fetch(5, 4 * WORD_SIZE) == 0x00000008000000070000000600000005) assert (self.ram.fetch(5, 4 * WORD_SIZE) == little_endian_decode([5, 6, 7, 8], WORD_SIZE)) def test_put(self): """Test put operation.""" value_list = [0, 6, 7, 0] value = big_endian_decode(value_list, WORD_SIZE) self.ram.put(5, value, 4 * WORD_SIZE) with raises(ValueError): self.ram.put(5, 2 ** WORD_SIZE, WORD_SIZE) self.ram.put(4, 4, WORD_SIZE) for i in range(5, 9): assert self.ram[i] == value_list[i - 5] self.ram = RandomAccessMemory(WORD_SIZE, 512, endianess="little") value = little_endian_decode(value_list, WORD_SIZE) self.ram.put(5, value, 4 * WORD_SIZE) for i in range(5, 9): assert self.ram[i] == value_list[i - 5]