Beispiel #1
0
 def __init__(self, ctl, addr_str, operation):
     self.ctl = ctl
     if addr_str[0].isdigit():
         self.addr_str = addr_str
         self.addr_base = BASE_10
     else:
         self.addr_str = addr_str[1:]
         self.addr_base = BASE_16
     self.address = parse_int(addr_str)
     self.operation = operation
     self.container = None
     self.reference = None
     self.mid_block_comment = None
     self.comment = None
     self.referrers = []
     self.asm_label = None
     self.nolabel = False
     self.org = None
     # If this instruction has no address, it was inserted between
     # @rsub+begin and @rsub+end; in that case, mark it as a subbed
     # instruction already
     if self.address is None:
         self.sub = operation
     else:
         self.sub = None
     self.keep = False
     self.warn = True
     self.ignoreua = False
     self.ignoremrcua = False
Beispiel #2
0
 def __init__(self, ctl, addr_str, operation):
     self.ctl = ctl
     if addr_str[0].isdigit():
         self.addr_str = addr_str
         self.addr_base = BASE_10
     else:
         self.addr_str = addr_str[1:]
         self.addr_base = BASE_16
     self.address = parse_int(addr_str)
     self.operation = operation
     self.container = None
     self.reference = None
     self.mid_block_comment = None
     self.comment = None
     self.referrers = []
     self.asm_label = None
     self.nolabel = False
     self.org = None
     # If this instruction has no address, it was inserted between
     # @rsub+begin and @rsub+end; in that case, mark it as a subbed
     # instruction already
     if self.address is None:
         self.sub = operation
     else:
         self.sub = None
     self.keep = False
     self.warn = True
     self.ignoreua = False
     self.ignoremrcua = False
Beispiel #3
0
def calculate_references(entries, operations):
    """
    For each instruction address in a memory map entry, calculate a list of the
    entries containing instructions that jump to, call or otherwise refer to
    that address.

    :param entries: A collection of memory map entries.
    :param operations: A tuple of regular expression patterns. The address
                       operand of any instruction whose operation matches one
                       of these patterns identifies an entry point that will be
                       marked with an asterisk in the skool file.
    :return: A dictionary of entry point addresses.
    """
    instructions = {i.address: (i, e) for e in entries for i in e.instructions}
    referrers = defaultdict(list)
    for entry in entries:
        for instruction in entry.instructions:
            operation = instruction.operation
            if any(re.match(op, operation.upper()) for op in operations):
                addr_str = get_address(operation)
                if addr_str:
                    ref_addr = parse_int(addr_str)
                    ref_i, ref_e = instructions.get(ref_addr, (None, None))
                    if ref_i and entry.address not in ref_i.rrefs and ref_i.label != '' and (
                            entry.ctl != 'u' or entry is ref_e):
                        referrers[ref_addr].append(entry)
            for ref_addr in instruction.refs:
                referrer = instructions.get(ref_addr, (None, None))[1]
                if referrer and referrer not in referrers[instruction.address]:
                    referrers[instruction.address].append(referrer)
    return referrers
Beispiel #4
0
def calculate_references(entries, operations):
    """
    For each entry point in each routine, calculate a list of the entries
    containing instructions that jump to or call that entry point.

    :param entries: A collection of memory map entries.
    :param operations: A tuple of operation prefixes. Any instruction whose
                       operation starts with one of these prefixes is regarded
                       as a jump or call operation, and therefore identifies an
                       entry point.
    :return: A dictionary of entry point addresses.
    """
    instructions = {i.address: (i, e) for e in entries for i in e.instructions}
    referrers = defaultdict(list)
    for entry in entries:
        for instruction in entry.instructions:
            operation = instruction.operation
            if operation.upper().startswith(operations):
                addr_str = get_address(operation)
                if addr_str:
                    ref_addr = parse_int(addr_str)
                    ref_i, ref_e = instructions.get(ref_addr, (None, None))
                    if ref_i and entry.address not in ref_i.rrefs and ref_i.label != '' and (entry.ctl != 'u' or entry is ref_e):
                        referrers[ref_addr].append(entry)
            for ref_addr in instruction.refs:
                referrer = instructions.get(ref_addr, (None, None))[1]
                if referrer and referrer not in referrers[instruction.address]:
                    referrers[instruction.address].append(referrer)
    return referrers
Beispiel #5
0
 def apply_base(self, addr_str, operation):
     address = parse_int(addr_str)
     if self.decimal:
         if address is not None:
             addr_str = '{:05d}'.format(address)
         if operation:
             operation = self.convert(operation)
     elif self.hexadecimal:
         if address is not None:
             addr_str = self.hex4fmt.format(address)
         if operation:
             operation = self.convert(operation)
     return addr_str, operation
Beispiel #6
0
 def replace_number(self, text, digits):
     num_str = get_address(text)
     if num_str is None or num_str.startswith('%'):
         return text
     num = parse_int(num_str)
     if self.decimal:
         return text.replace(num_str, str(num))
     if self.hexadecimal:
         if digits <= 2 and num < 256:
             hex_fmt = self.hex2fmt
         else:
             hex_fmt = self.hex4fmt
         return text.replace(num_str, hex_fmt.format(num))
Beispiel #7
0
 def _calculate_references(self):
     for entry in self.entries:
         for instruction in entry.instructions:
             instruction.referrers = []
     for entry in self.entries:
         for instruction in entry.instructions:
             operation = instruction.operation
             if operation.upper().startswith(('DJ', 'JR', 'JP', 'CA', 'RS')):
                 addr_str = get_address(operation)
                 if addr_str:
                     callee = self.instructions.get(parse_int(addr_str))
                     if callee:
                         callee.add_referrer(entry)
Beispiel #8
0
 def _calculate_references(self):
     for entry in self.entries:
         for instruction in entry.instructions:
             instruction.referrers = []
     for entry in self.entries:
         for instruction in entry.instructions:
             operation = instruction.operation
             if operation.upper().startswith(('DJ', 'JR', 'JP', 'CA', 'RS')):
                 addr_str = get_address(operation)
                 if addr_str:
                     callee = self.instructions.get(parse_int(addr_str))
                     if callee:
                         callee.add_referrer(entry)
Beispiel #9
0
 def apply_base(self, addr_str, operation):
     address = parse_int(addr_str)
     if self.decimal:
         if address is not None:
             addr_str = '{:05d}'.format(address)
         if operation:
             operation = self.convert(operation)
     elif self.hexadecimal:
         if address is not None:
             addr_str = self.hex4fmt.format(address)
         if operation:
             operation = self.convert(operation, self.hex2fmt, self.hex4fmt)
     return addr_str, operation
Beispiel #10
0
 def _parse_asm_directive(self, address, directive, removed):
     if directive.startswith(
         ('isub=', 'ssub=', 'rsub=', 'ofix=', 'bfix=', 'rfix=')):
         weight = self.weights[directive[:4]]
         if weight > (0, 0):
             value = directive[5:].rstrip()
             if value.startswith('!'):
                 removed.update(parse_address_range(value[1:]))
             else:
                 self.subs[weight].append(value)
     elif directive.startswith('if('):
         try:
             address = self._parse_asm_directive(
                 address,
                 parse_if(self.fields, directive, 2)[1], removed)
         except MacroParsingError:
             pass
     elif directive.startswith('org'):
         org = directive.rstrip().partition('=')[2]
         if org:
             try:
                 address = get_int_param(org)
             except ValueError:
                 raise SkoolParsingError(
                     "Invalid org address: {}".format(org))
         else:
             address = None
     elif directive.startswith('keep'):
         self.keep = parse_asm_keep_directive(directive)
     elif directive.startswith('nowarn'):
         self.nowarn = parse_asm_nowarn_directive(directive)
     elif self.data is not None and directive.startswith(
         ('defb=', 'defs=', 'defw=')):
         self.data.append(directive)
     elif directive.startswith('remote='):
         addrs = [
             parse_int(a)
             for a in directive[7:].partition(':')[-1].split(',')
         ]
         if addrs[0] is not None:
             self.remote_entries.append(
                 Entry(None,
                       [Instruction(a) for a in addrs if a is not None]))
     return address
Beispiel #11
0
    def convert(self, operation):
        if operation.upper().startswith(('DEFB ', 'DEFM ', 'DEFS ', 'DEFW ')):
            elements = split_operation(operation, strip=False)
            if elements[0].upper() == 'DEFW':
                convert_method = self.replace_address
            else:
                convert_method = self.replace_byte
            items = []
            for item in elements[1:]:
                if item.lstrip().startswith('"'):
                    items.append(item)
                else:
                    items.append(convert_method(item))
            return '{} {}'.format(elements[0], ','.join(items))

        elements = split_operation(operation, tidy=True)
        op = elements[0]

        # Instructions containing '(I[XY]+d)'
        index = self.get_index(operation)
        if index:
            if len(elements) == 3:
                return self.replace_index(operation, index, parse_int(elements[2]))
            return self.replace_index(operation, index)

        if op in ('CALL', 'DJNZ', 'JP', 'JR'):
            return self.replace_address(operation)

        if op in ('AND', 'OR', 'XOR', 'SUB', 'CP', 'IN', 'OUT', 'ADD', 'ADC', 'SBC', 'RST'):
            return self.replace_byte(operation)

        if op == 'LD' and len(elements) == 3:
            operands = elements[1:]
            if operands[0] in ('A', 'B', 'C', 'D', 'E', 'H', 'L', 'IXL', 'IXH', 'IYL', 'IYH', '(HL)') and not operands[1].startswith('('):
                # LD r,n; LD (HL),n
                return self.replace_byte(operation)
            if not set(('A', 'BC', 'DE', 'HL', 'IX', 'IY', 'SP')).isdisjoint(operands):
                # LD A,(nn); LD (nn),A; LD rr,nn; LD rr,(nn); LD (nn),rr
                return self.replace_address(operation)

        return operation
Beispiel #12
0
 def _calculate_references(self):
     # Parse operations for routine/data addresses
     for entry in self.memory_map:
         for instruction in entry.instructions:
             if instruction.keep:
                 continue
             operation = instruction.operation.upper()
             if not operation.startswith(('CALL', 'DEFW', 'DJNZ', 'JP', 'JR', 'LD ', 'RST')) or self._is_8_bit_ld_instruction(operation):
                 continue
             addr_str = get_address(operation)
             if not addr_str:
                 continue
             address = parse_int(addr_str)
             other_instruction = self._instructions.get(address, (None,))[0]
             if other_instruction:
                 other_entry = other_instruction.container
                 if other_entry.is_ignored():
                     continue
                 if other_entry.is_remote() or operation.startswith(('DEFW', 'LD ')) or other_entry.is_routine():
                     instruction.set_reference(other_entry, address, addr_str)
                     if operation.startswith(('CALL', 'DJNZ', 'JP', 'JR', 'RST')):
                         other_instruction.add_referrer(entry)
Beispiel #13
0
 def _calculate_references(self):
     # Parse operations for routine/data addresses
     for entry in self.memory_map:
         for instruction in entry.instructions:
             if instruction.keep:
                 continue
             operation = instruction.operation.upper()
             if not operation.startswith(('CALL', 'DEFW', 'DJNZ', 'JP', 'JR', 'LD ', 'RST')) or self._is_8_bit_ld_instruction(operation):
                 continue
             addr_str = get_address(operation)
             if not addr_str:
                 continue
             address = parse_int(addr_str)
             other_instruction = self._instructions.get(address, (None,))[0]
             if other_instruction:
                 other_entry = other_instruction.container
                 if other_entry.is_ignored():
                     continue
                 if other_entry.is_remote() or operation.startswith(('DEFW', 'LD ')) or other_entry.is_routine():
                     instruction.set_reference(other_entry, address, addr_str)
                     if operation.startswith(('CALL', 'DJNZ', 'JP', 'JR', 'RST')):
                         other_instruction.add_referrer(entry)
Beispiel #14
0
 def _set_bytes(self, line):
     address = parse_int(line[1:6])
     if address is not None:
         comment_index = find_unquoted(line, ';')
         operation = line[7:comment_index].strip()
         set_bytes(self.snapshot, address, operation)
Beispiel #15
0
 def convert_address_operand(self, operand):
     if self.decimal:
         return str(parse_int(operand))
     if self.hexadecimal:
         return self.hex4fmt.format(parse_int(operand))
     return operand
Beispiel #16
0
 def _set_bytes(self, line):
     address = parse_int(line[1:6])
     if address is not None:
         comment_index = find_unquoted(line, ';')
         operation = line[7:comment_index].strip()
         set_bytes(self.snapshot, address, operation)
Beispiel #17
0
def _item_value(item, limit=256):
    value = parse_int(item, 0)
    if 0 <= value < limit:
        return value
    return 0
Beispiel #18
0
 def convert_address_operand(self, operand):
     if self.decimal:
         return str(parse_int(operand))
     if self.hexadecimal:
         return self.hex4fmt.format(parse_int(operand))
     return operand