#! /usr/bin/env python
class SerialALU:
    """Serial ALU of CSCI 320"""
    # The following operations are not implemented in SystemVerilog
    # They are the mutator and accessor methods
    # Wow....  They take a lot of lines
    def __init__(self):
        ## just random values
        self._regs = [0, 1066, 1776, 2016]
        self._instruction = 4
        self._input = 42
        self._status = 3
        self._output = 42
    def set_instruction(self, new_instruction):
        if (type(new_instruction) is int
                and new_instruction >= 0 and new_instruction <= 127):
            self._instruction = new_instruction
        else:
            raise ValueError(
                'Invalid instruction: {0}'.format(new_instruction))
    def get_instruction(self):
        return self._instruction
    def set_input(self, new_input):
        if (type(new_input) is int
                and -32768 <= new_input and new_input < 65536):
            if new_input < 0:
                new_input = new_input & 0xFFFF ;
            self._input = new_input
        else:
            raise ValueError(
                'Invalid input: {0}'.format(new_input))
    def get_input(self):
        return self._input
    def get_input_signed(self):
        if self._input >= 32768:
            return self._input - 65536
        else:
            return self._input
    def get_reg(self, index):
        if type(index) is int and 1 <= index and index <= 3:
            return self._regs[index]
        else:
            raise ValueError(
                'Invalid index: {0}'.format(index))
    def get_status(self):
        return self._status
    def get_output(self):
        return self._output
    def get_output_signed(self):
        if self._output >= 32768:
            return self._output - 65536
        else:
            return self._output

    ## finally....  The one method to be implemented
    def clock(self):
        # Decode instruction fields
        # This is a one-liner in SystemVerilog
        opcode = (self._instruction & 0x70) >> 4
        reg_source = (self._instruction & 0xC) >> 2
        reg_target = (self._instruction & 0x3)
        # get source value
        if reg_source == 0:
            val_source = self._input
        else:
            val_source = self._regs[reg_source]
        # get target value
        if reg_target == 0:
            val_target = self._input
        else:
            val_target = self._regs[reg_target]
        # Do the operation, + and - are hard
        if opcode==0:     # unsigned +
            val_result = (val_target + val_source) & 0xFFFF
            c_bit = ((val_target + val_source)>>16) & 1
            v_bit = c_bit
        elif opcode==1:   # unsigned -
            val_result = (val_target - val_source) & 0xFFFF
            c_bit = ((val_target - val_source)>>16) & 1
            v_bit = c_bit
        elif opcode==2:   # signed +
            val_result = (val_target + val_source) & 0xFFFF
            c_bit = ((val_target + val_source)>>16) & 1
            neg_source = val_source >= 32768
            neg_target = val_target >= 32768
            neg_result = val_result >= 32768
            v_bit = 1 if (neg_source==neg_target) and (neg_result!=neg_source) else 0
        elif opcode==3:   # signed -
            val_result = (val_target - val_source) &0xFFFF
            c_bit = ((val_target - val_source)>>16) & 1
            neg_source = val_source >= 32768
            neg_target = val_target >= 32768
            neg_result = val_result >= 32768
            v_bit = 1 if (neg_source!=neg_target) and (neg_result!=neg_target) else 0
        elif opcode==4:   # &
            val_result = val_target & val_source
            c_bit = 0
            v_bit = 0
        elif opcode==5:   # |
            val_result = val_target | val_source
            c_bit = 0
            v_bit = 0
        elif opcode==6:   # ^
            val_result = val_target ^ val_source
            c_bit = 0
            v_bit = 0
        else:             # ld/st [transfer source to target]
            val_result = val_source
            c_bit = 0
            v_bit = 0
        z_bit = 1 if val_result == 0 else 0
        # Set the status... simpler in SystemVerilog
        self._status = 4*z_bit + 2*c_bit + v_bit
        # Set the output
        self._output = val_result
        # Set the register, when appropriate
        if reg_target != 0:
            self._regs[reg_target] = val_result
