Пример #1
0
 def __concatenate_instruction(self, part_0, part_1):
     part_0 = bin(part_0, self._size)
     part_1 = bin(part_1, self._size)
     self._log.buffer(self, "concatenating {:} and {:}"
                      .format(part_0[2:], part_1[2:]), level.FINEST)
     instruction = part_0[2:] + part_1[2:]
     return int(instruction, 2)
Пример #2
0
    def addImmediate(self, args, instruction_decoded, **named_args):
        """Adds a register to an immediate value and stores the
        result in a second register.

        addImmediate performs twos complement arithmetic, so
        the value stored as a result of -1 + -1 will be 0xfffffffe.

        For unsigned calculations, use addImmediateUnsigned.

        Arguments:
            Args 1 and 2 should be the numbers of registers.
            args[0]:int (register)
            args[1]:int (register)
            args[2]:int (immediate)

        State changed:
             register[a]['value'] <- register[b]['value'] + c:int

        Returns:
            Always returns True

        Raises:
            RegisterReferenceException
        """
        self.log.buffer('addImmediate called', level.FINER)
        a = self._decode_register_reference(args[0], instruction_decoded)
        b = self._decode_register_reference(args[1], instruction_decoded)
        # This will be a signed immediate value.
        c = int(instruction_decoded[args[2]], 2, signed=True)
        self.log.buffer('args 0:{0}, 1:{1}, 2:{2}'.format(a, b, c),
                        level.FINEST)
        for operand in [a, b]:
            if operand not in self._register.keys():
                raise RegisterReferenceException
        # Convert to binary to get the correct 2's comp value.
        b_size = self._register.get_size(b)
        bin_bvalue = bin(self._register.get_value(b), size=b_size)
        bvalue = int(bin_bvalue[2:], 2, signed=True)
        #result = int(bin(self._register.get_value(b))[2:], 2, signed=True)
        result = bvalue + c
        # Need size of a, the register to be written to, for correct sign.
        size = self._register.get_size(a)
        result = int(bin(result, size), 2)
        self.log.buffer('result is {0}'.format(result), level.FINEST)
        self._register.set_value(a, result)
        return True
Пример #3
0
    def addRegisters(self, args, instruction_decoded, **named_args):
        """Adds two registers and stores the result in a third.

        Args:
            Each argument should be the number of a register.
            args[0]:int (result)
            args[1]:int (operand)
            args[2]:int (operand)

        State changed:
            register[a]['value'] <-
                register[b]['value'] + register[c]['value']

        Returns:
            Always returns True

        Raises:
            RegisterReferenceException
        """
        self.log.buffer('addRegisters called', level.FINER)
        a = self._decode_register_reference(args[0], instruction_decoded)
        b = self._decode_register_reference(args[1], instruction_decoded)
        c = self._decode_register_reference(args[2], instruction_decoded)
        self.log.buffer('args 0:{0}, 1:{1}, 2:{2}'.format(a,b,c),
                        level.FINEST)
        for operand in [a, b, c]:
            if operand not in self._register.keys():
                raise RegisterReferenceException
        b_size = self._register.get_size(b)
        c_size = self._register.get_size(c)
        # We want 2's comp values for b and c...
        bin_bvalue = bin(self._register.get_value(b), size=b_size)
        bin_cvalue = bin(self._register.get_value(c), size=c_size)
        bvalue = int(bin_bvalue[2:], 2, signed=True)
        cvalue = int(bin_cvalue[2:], 2, signed=True)
        result = bvalue + cvalue
        # ... and we want to store the result as a signed number.
        a_size = self._register.get_size(a)
        # NB: we don't use signed arg to give correct 2's comp result
        result = int(bin(result, a_size), 2)
        self.log.buffer('result is {0}'.format(result), level.FINEST)
        self._register.set_value(a, result)
        return True
Пример #4
0
    def setBitInRegister(self, args, instruction_decoded, **named_args):
        """Sets one bit in a register value to on or off.

        Description:
            (register:str, bit:int, value:int) -> return:bool

        Purpose:
            Can be used to alter registers which are commonly bit-stuffed,
            like the [E]FLAGS register of 808x ISAs.

            This call is not expected to read an instruction, so it is best
            used to generate the results of other operations, for example
            setting a carry flag as the result of an addition.

        Restrictions:
            If the register does not contain a bit_n, the result of this
            call is undefined. Registers are padded with zeros depending
            on their size specified in the ISA. This means a decimal value
            of 10 in an 0 bit register will be treated as 0b00001010.

            If the call does not contain a '0' or '1' value in the `value'
            field, the result of this call is undefined.

        Exceptions:
            This call has undefined behaviour and may not handle exceptions
            raised in the event of error.

        Returns:
            Always returns True
        """
        self.log.buffer('setBitInRegister called', level.FINER)
        a = args[0]
        b = int(args[1])
        c = str(args[2])
        self.log.buffer('args 0:{:}, 1:{:}, 2:{:}'.format(a, b, c), level.FINEST)

        a_size = self._register.get_size(a)

        value = list(bin(self._register.get_value(a), a_size)[2:])
        value[b] = c
        value = int(''.join(value), 2)
        self._register.set_value(a, value)

        return True
Пример #5
0
    def __decode(self, index):
        # Data required to decode instruction
        #Format-related data
        bit_ranges  = self._isa.get_format_bit_ranges()
        cycles      = self._isa.get_format_cycles()
        # Instruction-related data
        signatures  = self._isa.getSignatures()
        mappings    = self._isa.get_instruction_to_format_map()

        # Get the instruction to decode
        instruction = bin(self._pipeline[index][0],self._size)[2:]
        self._log.buffer(self, "decoding {0}".format(instruction), level.FINER)

        # The following block identifies the instruction being decoded.
        # It tells us the instruction's format, signature and the number
        # of parts it was broken into.
        test={}
        # Test each type of instruction
        for format_type in bit_ranges:
            # against each of the relevant signatures.
            for signature in signatures:
                # Do no work at all if the instruction is obviously
                # not a candidate.
                if format_type in mappings[signature]:
                    test.clear()

                    # We might have to deal with a multi-field signature.
                    for field in signatures[signature]:
                        start= bit_ranges[format_type][field][0]
                        end  = bit_ranges[format_type][field][1]+1
                        test[field]=int(instruction[start:end],2)

                    if test == signatures[signature]:
                        number_of_parts = cycles[format_type]
                        self._log.buffer(self, "decoded `{0}' type instruction, {1}"
                                         .format(format_type, signature),
                                         level.FINER)
                        return (format_type, signature, number_of_parts)
Пример #6
0
    def testBitIsOff(self, args, instruction_decoded, **named_args):
        """Returns true if register has bit_n set to 0b0

        Description:
            (register:str, int:int) -> return:bool
            testBitIsOff inspects one bit of a binary value stored in
            a register and returns True or False. This call behaves
            as if the storage were big-endian, regardless of the underlying
            ISA and other organization.


        Purpose:
            Can be used to examine registers which are commonly bit-stuffed,
            like the [E]FLAGS register of 808x ISAs.

        Restrictions:
            If the register does not contain a bit_n, the result of this
            call is undefined. Registers are padded with zeros depending
            on their size specified in the ISA. This means a value of 0b1
            in a 0 bit register will be examined as 0b00000001, so bit
            0..6 will appear to be off and 7 will appear on.

        Exceptions:
            This call has undefined behaviour and may not handle exceptions
            raised in the event of error.

        Returns:
            True or False
        """
        a = self._decode_register_reference(args[0], instruction_decoded)
        b = args[1]

        size  = self._register.get_size(a)
        value = list(bin(self._register.get_value(a), size)[2:])

        return value[b] == '0'
Пример #7
0
 def evaluate(self, lines):
     expression = self.simulation.evaluate(lines, self.connected,
                                           self.client)
     if self.display_eval:
         for line in expression:
             print bin(int(line),self.size)[2:]
Пример #8
0
    def set_word(self, offset, value, size, aligned=True):
        """Inserts a word at the given memory offset

        Description:
            (offset:int, value:int, size:int, aligned:bool)
                -> **memory{offset:value}:dict**
            set_word uses _set_byte to do its work by calling it repeatedly.
            It will store a word in such a way that it remains addressable
            in byte-sized portions. 

        Purpose:
            Handles the storage of a word, ensuring addressing is correct
            for the scheme in operation (eg. byte-addressing)

            This is an important interface to the memory system and should
            be given preference over other methods when a word needs to be
            stored.

            The expected use of this method is in the implementation of
            APIs and simulation clients.

        Values:
            offset  -- the address in memory
            value   -- the value to be stored
            size    -- the word size to set
            aligned -- is word alligment enforced?

        Restrictions:
            N/A

        Exceptions:
            Raises : AddressingError
                     AlignmentError
            Allows : SegmentationFaultException
            Masks  : None

        Returns:
            N/A
        """
        #We want to prevent addressing violations
        if size < self._addressable:
            self.log.buffer('Addressing error: store {:} at {:}'
                            .format(bin(value, self._size)[2:].lstrip('0'),
                                    hex(offset).replace('L','')),
                            level.ERROR)
            raise AddressingError('Tried to store {:} at {:}'
                                  .format(bin(value, self._size)[2:],
                                          hex(offset).replace('L','')))

        #We want to prevent bad allignment
        if aligned and int(offset) % (size / self._addressable) != 0:
            self.log.buffer('Alignment error: store {:} at {:}'
                            .format(bin(value, self._size)[2:],
                                    hex(offset).replace('L','')),
                            level.ERROR)
            raise AlignmentError('Tried to store {:} at {:}'
                                 .format(bin(value, self._size)[2:],
                                         hex(offset).replace('L','')),
                                 level.ERROR)

        if self._endian == self._types.Little:
            offset = offset - (size/self._addressable)

        orig_offset = offset
        bitmap=bin(value, size)[2:]
        start=0
        end=self._addressable
        for i in range(size/self._addressable):
            self._set_byte(offset, int(bitmap[start:end],2))
            start=end
            end=end+self._addressable
            if self._endian == self._types.Big:
                offset = offset + 1
            else:
                offset = offset - 1
        self.log.buffer('stored {:} at {:}'
                        .format(bitmap,hex(orig_offset)),
                        level.FINER)
Пример #9
0
    def get_word(self, offset, size, aligned=True, quietly=False):
        """(offset:int,
            size:int
            aligned:bool) -> value:int

        Returns a tuple containing address offset and
        the decimal value of a word at that location.

        Values:
            offset  -- the address in memory
            size    -- the word size to get
            aligned -- is word alligment enforced?

        Raises:
            AddressingError
            AlignmentError
        Allows:
            SegmentationFaultException
        Masks:
            KeyValue (in _get_byte)
        """
        #We want to prevent addressing violations
        if size < self._addressable:
            if not quietly:
                message='Tried to address {:}-bytes at {:}'.format(
                    size/self._addressable, hex(offset))
                self.log.buffer('Addressing error: {:}'.format(message),
                                level.ERROR)
            raise AddressingError(message)

        #We want to prevent bad alignment
        #if aligned and int(offset) % size != 0:
        if aligned and int(offset) % (size / self._addressable) != 0:
            if not quietly:
                message='Tried to load {:}-bytes from {:}'.format(
                    size/self._addressable, hex(offset))
                self.log.buffer('Alignment error: {:}'.format(message),
                                level.ERROR)
            raise AlignmentError(message)

        if self._endian == self._types.Little:
            offset = offset - (size/self._addressable)

        bitmap=[]
        orig_offset=offset
        start=0
        end=self._addressable

        for i in range(size/self._addressable):

            byte = self._get_byte(offset)
            bitmap[start:end] = bin(byte, self._addressable)[2:]

            if self._endian == self._types.Big:
                offset = offset + 1
            else:
                offset = offset - 1

            start=end
            end=end+self._addressable

        bitmap=''.join(bitmap)
        if not quietly:
            self.log.buffer('loaded {:} from {:}'
                            .format(bitmap,
                                    hex(orig_offset).replace('L', '')),
                            level.FINER)
        return int(bitmap, 2)
Пример #10
0
    def _encode(self, lines):
        """Takes a list of assembly instructions (with decoded identifiers)
        and returns a list of binary machine instructions.

        Description:
            [lines:str]:list -> [lines:str]:list

            Once an assembly program has been through the linker and all
            the identifiers (label references) have been replaced with
            numerical values, the encoder can convert the instruction
            to binary using instruction and format data from the config.

        Purpose:
            Encodes assembly instructions as binary: the final step in
            converting an assembly program into machine code.

            Encoding gurantees that instruction syntax is correct and
            that identifiers and register references are valid.

        Restrictions:
            The result of processing identifiers which have not been
            converter is undefined.

        Exceptions:
            Raises:
                BadInstructionOrSyntax

        Returns:
            A list of binary manchine instructions.
        """

        self.log.buffer("entering encoder", level.FINER)
        # We will return output and use instruction_fields to build up
        # each instruction.
        output             = []
        instruction_fields = {}
        for line in lines:
            #
            # Here we will read the instruction on each line, try to fetch
            # a regular expression for it, and split it into groups.
            #
            instruction = line.split()[0]
            if instruction in self._format_mappings:
                syntax     = self._instruction_syntax[instruction]
                expression = '^' + syntax['expression'] + '$'
                self.log.buffer("matching `{0}' instruction"
                                .format(instruction), level.FINER)
                match = re.search(expression, line)
                if match:
                    # Here we are looping over fields in the instruction
                    # format and determining their values.
                    for i in range(len(match.groups())):
                        field = syntax['symbols'][i][0]
                        value = match.groups()[i]

                        # This block deals with the possibility that the
                        # symbol is a hex number.
                        if value not in self._registers:
                            try:
                                if value[:2] == '0x':
                                    value = int(value, 16)
                                elif value.endswith(self._hex_pattern):
                                    value = value.replace(self._hex_pattern, '')
                                    value = int(value, 16)
                                else:
                                    value=int(value)
                            except:
                                line = "`" + line + "'"
                                raise BadInstructionOrSyntax(
                                    "{:} on line {:}:Non-ref or digit.\n{:}"
                                    .format(BAD, i+1, lines[i]))
                        # We have identified the field. Log it...
                        self.log.buffer("`{0}' is {1}"
                                        .format(field, value), level.FINEST)
                        # ...and add it to the instruction.
                        instruction_fields[field] = value

                    # This block adds the preset field values.
                    values = self._instruction_values[instruction]
                    for field in values:
                        instruction_fields[field] = values[field]
                        self.log.buffer("`{0}' is {1}"
                                        .format(field, values[field]),
                                        level.FINEST)

                    #print("instruction: {:}".format(instruction_fields))


                    # Here we binary-encode the instruction.
                    format_name = self._format_mappings[instruction]
                    fetch_cycles = self._isa.get_format_cycles()[format_name]
                    instruction_length = self._isa_size * fetch_cycles
                    # Creates a list of 0s which we can edit to match the
                    # instruction.
                    instruction_raw = instruction_length * '0'.split()
                    for field in instruction_fields:
                        start = self._format_properties[format_name][field][0]
                        end   = self._format_properties[format_name][field][1]+1
                        # If the value is a register reference, encode the
                        # register number, otherwise encode literal.
                        if instruction_fields[field] in self._registers:
                            value = self._registers[instruction_fields[field]]
                        else:
                            value = instruction_fields[field]
                        # Now insert the encoded field into the instruction.
                        width = end - start
                        value = bin(value, size = width)[2:]
                        instruction_raw[start:end] = value
                        self.log.buffer("{:}:{:} is {:}"
                                        .format(start, end, value),
                                        level.FINEST)

                    # Finally convert the instruction from a list to a string.
                    instruction_raw = "".join(instruction_raw)
                    # Bon.
                    self.log.buffer("encoded {0}".format(instruction_raw),
                                    level.FINER)

                    # Split the instruction if it spans multiple words.
                    # eg. 8085 Direct Addressing uses three 8 bit parts
                    # despite being an 8 bit ISA.
                    self.log.buffer("splitting into {:}-bit chunks"
                                    .format(self._isa_size),
                                    level.FINER)
                    start = 0
                    end   = 1
                    for i in range(len(instruction_raw)):
                        if end % self._isa_size == 0:
                            part = instruction_raw[start:end]
                            # Log entry is indented for readability.
                            self.log.buffer(
                                "  split {:}".format(part),
                             level.FINER)
                            output.append(part)
                            start = end
                        end = end + 1
                    #output.append(instruction_raw)
                else:
                    raise BadInstructionOrSyntax(
                        "{:} on line {:}:\n{:}"
                        .format(BAD, i+1, lines[i]))
            # TODO: Keep this block under review. Probably kept for a reason.
            #(2011-08-18)
            #elif instruction in self._special_instructions:
                #
                #we aren't dealing with specials yet
                #this will probably come into play with assembler directives
                #
                #output.append(instruction)
                #pass
            else:
                raise BadInstructionOrSyntax(
                    "{:} on line {:}:\n{:}"
                    .format(BAD, i+1, lines[i]))
            instruction_fields.clear()
        self.log.buffer("leaving encoder", level.FINER)
        return output
Пример #11
0
    def __execute(self, index):
        # A dict to hold the encoded instruction parts.
        self._pipeline[index].append({})

        # Data on the instruction format so we can decode properly.
        format_sizes      = self._isa.get_format_sizes()
        format_properties = self._isa.get_format_bit_ranges()

        # Instruction data to operate on.
        instruction_type   = self._pipeline[index][1]
        instruction_name   = self._pipeline[index][2]

        # Particularly in the case of multi-part instructions, we need
        # to ensure the correct length binary is formed. We will use the
        # format size property to achieve this.
        size = format_sizes[instruction_type]
        self._log.buffer(self, "reading {:}b {:} instruction"
                         .format(size, instruction_name), level.FINER)

        # Finally, translate the instruction into a binary representation.
        instruction_binary = bin(self._pipeline[index][0], size)[2:]

        # Begin the execution by decoding each bit-field.
        for field in format_properties[instruction_type]:
            start = format_properties[instruction_type][field][0]
            end   = format_properties[instruction_type][field][1]+1
            self._pipeline[index][3][field] = instruction_binary[start:end]
            self._log.buffer(self, "`{:}' is {:} ({:})"
                             .format(field,
                                     instruction_binary[start:end],
                                     int(instruction_binary[start:end], 2)),
                             level.FINEST)

        self._log.buffer(self, "executing {:} ({:})"
                         .format(instruction_binary, instruction_name),
                         level.FINER)

        # This next step deals with the actual state change[s] by making
        # calls to the API.
        implementation = self._isa.getImplementation()
        name           = self._pipeline[index][2]
        # The branch offset is used to calculate the address of jump
        # instructions.
        branch_offset  = index

        if self.__special_flags['increment']:
            branch_offset = branch_offset + 1

        # We also need to consider the number of fetch cycles that have
        # passed and add them to the offset calculation.
        cycles = self._isa.get_format_cycles()[instruction_type] - 1
        if cycles:
            branch_offset = branch_offset + cycles
        # If an API call returns false, the sequential flag will block
        # the next call. This is used to evaluate tests.
        sequential = True
        for method in implementation[name]:
            if sequential:
                call = getattr(self._api, method[0])
                args = method[1]
                self._log.buffer(self, "calling {0} with {1}"
                                 .format(call.__name__, args), level.FINER)
                sequential = call(args,
                                  self._pipeline[index][3],
                                  branch_offset=branch_offset)
            else:
                self._log.buffer(self, 'skipping an API call', level.FINEST)
                sequential = True
        if 'EI' in self._pipeline_flags:
            self._registers.increment(self._pc, self._word_space)
            self.__special_flags['increment'] = True