def get_ctl_parser(ctls, infile, start, end, def_start, def_end): if infile[-4:].lower() in ('.bin', '.sna', '.szx', '.z80'): prefix = infile[:-4] else: prefix = infile if not ctls: ctls.extend(sorted(glob.glob(prefix + '*.ctl'))) ctlfiles = [] if ctls and '0' not in ctls: # Use control file(s) for ctl in ctls: if os.path.isdir(ctl): ctlfiles.extend(sorted(glob.glob(os.path.join(ctl, '*.ctl')))) else: ctlfiles.append(ctl) if ctlfiles: if len(ctlfiles) > 1: suffix = 's' else: suffix = '' info('Using control file{}: {}'.format(suffix, ', '.join(ctlfiles))) ctl_parser = CtlParser() ctl_parser.parse_ctls(ctlfiles, start, end) else: ctl_parser = CtlParser({def_start: 'c', def_end: 'i'}) return ctl_parser
def run(snafile, options, config): snapshot, start, end = make_snapshot(snafile, options.org, options.start, options.end, options.page) if options.ctlfiles: # Use control file(s) if len(options.ctlfiles) > 1: suffix = 's' else: suffix = '' info('Using control file{}: {}'.format(suffix, ', '.join(options.ctlfiles))) ctl_parser = CtlParser() ctl_parser.parse_ctls(options.ctlfiles, options.start, options.end) else: ctl_parser = CtlParser({start: 'c', end: 'i'}) writer = SkoolWriter(snapshot, ctl_parser, options, config) writer.write_skool(config['ListRefs'], config['Text'])
def run(snafile, options, config): # Read the snapshot file if snafile[-4:].lower() in ('.sna', '.szx', '.z80'): snapshot = get_snapshot(snafile, options.page) start = max(START, options.start) else: ram = read_bin_file(snafile, 65536) if options.org is None: org = 65536 - len(ram) else: org = options.org snapshot = [0] * org snapshot.extend(ram) start = max(org, options.start) end = min(options.end, len(snapshot)) snapshot += [0] * (65536 - len(snapshot)) if options.sftfile: # Use a skool file template info('Using skool file template: {}'.format(options.sftfile)) writer = SftParser(snapshot, options.sftfile, options.zfill, options.base == 16, options.case == 1) writer.write_skool(options.start, options.end) return if options.genctlfile: # Generate a control file ctls = generate_ctls(snapshot, start, end, options.code_map) write_ctl(options.genctlfile, ctls, options.ctl_hex) ctl_parser = CtlParser(ctls) elif options.ctlfile: # Use a control file info('Using control file: {}'.format(options.ctlfile)) ctl_parser = CtlParser() ctl_parser.parse_ctl(options.ctlfile, options.start, options.end) else: ctl_parser = CtlParser({start: 'c', end: 'i'}) writer = SkoolWriter(snapshot, ctl_parser, options, config) writer.write_skool(options.write_refs, options.text)
def _generate_ctls_with_code_map(snapshot, start, end, config, code_map): # (1) Use the code map to create an initial set of 'c' ctls, and mark all # unexecuted blocks as 'U' (unknown) # (2) Where a 'c' block doesn't end with a RET/JP/JR, extend it up to the # next RET/JP/JR in the following 'U' blocks, or up to the next 'c' # block # (3) Mark entry points in 'U' blocks that are CALLed or JPed to from 'c' # blocks with 'c' # (4) Split 'c' blocks on RET/JP/JR # (5) Scan the disassembly for pairs of adjacent blocks where the start # address of the second block is JRed or JPed to from the first block, # and join such pairs # (6) Examine the remaining 'U' blocks for text # (7) Mark data blocks of all zeroes with 's' # (1) Mark all executed blocks as 'c' and unexecuted blocks as 'U' # (unknown) ctls = {start: 'U', end: 'i'} for address, length in _get_code_blocks(snapshot, start, end, code_map): ctls[address] = 'c' if address + length < end: ctls[address + length] = 'U' # (2) Where a 'c' block doesn't end with a RET/JP/JR, extend it up to the # next RET/JP/JR in the following 'U' blocks, or up to the next 'c' block while 1: done = True for ctl, b_start, b_end in _get_blocks(ctls): if ctl == 'c': last_op_id = list(decode(snapshot, b_start, b_end))[-1][3] if last_op_id == END: continue if _find_terminal_instruction(snapshot, ctls, b_end, end) < end: done = False break if done: break # (3) Mark entry points in 'U' blocks that are CALLed or JPed to from 'c' # blocks with 'c' ctl_parser = CtlParser(ctls) disassembly = Disassembly(snapshot, ctl_parser) while 1: disassembly.build(True) done = True for entry in disassembly.entries: if entry.ctl == 'U': for instruction in entry.instructions: for referrer in instruction.referrers: if ctls[referrer] == 'c': ctls[instruction.address] = 'c' if entry.next: e_end = entry.next.address else: e_end = 65536 _find_terminal_instruction(snapshot, ctls, instruction.address, e_end, entry.ctl) disassembly.remove_entry(entry.address) done = False break if not done: break if not done: break if done: break # (4) Split 'c' blocks on RET/JP/JR for ctl, b_address, b_end in _get_blocks(ctls): if ctl == 'c': next_address = _find_terminal_instruction(snapshot, ctls, b_address, b_end, 'c') if next_address < b_end: disassembly.remove_entry(b_address) while next_address < b_end: next_address = _find_terminal_instruction( snapshot, ctls, next_address, b_end, 'c') # (5) Scan the disassembly for pairs of adjacent blocks where the start # address of the second block is JRed or JPed to from the first block, and # join such pairs while 1: disassembly.build() done = True for entry in disassembly.entries[:-1]: if entry.ctl == 'c': for instruction in entry.instructions: operation = instruction.operation if operation[:2] in ('JR', 'JP') and operation[-5:] == str( entry.next.address): del ctls[entry.next.address] disassembly.remove_entry(entry.address) disassembly.remove_entry(entry.next.address) done = False break if done: break # (6) Examine the 'U' blocks for text/data for ctl, b_start, b_end in _get_blocks(ctls): if ctl == 'U': ctls[b_start] = 'b' for t_start, t_end in _get_text_blocks(snapshot, b_start, b_end, config): ctls[t_start] = 't' if t_end < b_end: ctls[t_end] = 'b' # (7) Mark data blocks of all zeroes with 's' for ctl, b_start, b_end in _get_blocks(ctls): if ctl == 'b' and sum(snapshot[b_start:b_end]) == 0: ctls[b_start] = 's' return ctls
def _generate_ctls_without_code_map(snapshot, start, end): ctls = {start: 'c', end: 'i'} # Look for potential 'RET', 'JR d' and 'JP nn' instructions and assume that # they end a block (after which another block follows); note that we don't # bother examining the final byte because no block can follow it for address in range(start, end - 1): b = snapshot[address] if b == 201: ctls[address + 1] = 'c' elif b == 195 and address < end - 3: ctls[address + 3] = 'c' elif b == 24 and address < end - 2: ctls[address + 2] = 'c' ctl_parser = CtlParser(ctls) disassembly = Disassembly(snapshot, ctl_parser) # Scan the disassembly for pairs of adjacent blocks that overlap, and join # such pairs while True: done = True for entry in disassembly.entries[:-1]: if entry.bad_blocks: del ctls[entry.next.address] disassembly.remove_entry(entry.address) disassembly.remove_entry(entry.next.address) done = False if done: break disassembly.build() # Scan the disassembly for blocks that don't end in a 'RET', 'JP nn' or # 'JR d' instruction, and join them to the next block changed = False for entry in disassembly.entries[:-1]: last_instr = entry.instructions[-1].operation if last_instr != 'RET' and not (last_instr[:2] in ('JP', 'JR') and last_instr[3:].isdigit()): next_address = entry.next.address if next_address < end: del ctls[entry.next.address] disassembly.remove_entry(entry.address) disassembly.remove_entry(entry.next.address) changed = True if changed: disassembly.build() # Scan the disassembly for pairs of adjacent blocks where the start address # of the second block is JRed or JPed to from the first block, and join # such pairs while True: done = True for entry in disassembly.entries[:-1]: for instruction in entry.instructions: operation = instruction.operation if operation[:2] in ('JR', 'JP') and operation[-5:] == str( entry.next.address): del ctls[entry.next.address] disassembly.remove_entry(entry.address) disassembly.remove_entry(entry.next.address) done = False break if done: break disassembly.build() # Mark any NOP sequences at the beginning of a block as a separate zero # block for entry in disassembly.entries: if entry.instructions[0].operation != 'NOP': continue for instruction in entry.instructions[1:]: if instruction.operation != 'NOP': break ctls[entry.address] = 's' if entry.instructions[-1].operation != 'NOP': ctls[instruction.address] = 'c' # See which blocks marked as code look like text or data _analyse_blocks(disassembly, ctls) return ctls