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
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
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
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
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))
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)
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
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
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
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)
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)
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
def _item_value(item, limit=256): value = parse_int(item, 0) if 0 <= value < limit: return value return 0