def _parse_skool(self, skoolfile): entry_ctl = None f = open_file(skoolfile) for line in f: if line.startswith(';'): continue if line.startswith('@'): self._parse_asm_directive(line[1:].rstrip()) continue if not self.include: continue s_line = line.strip() if not s_line: # This line is blank entry_ctl = None continue # Check whether we're in a block that can be skipped if entry_ctl is None and line.startswith(SKIP_BLOCKS): entry_ctl = line[0] if entry_ctl in SKIP_BLOCKS: continue if s_line.startswith(';'): # This line is a continuation of an instruction comment continue if line[0] in VALID_CTLS: # This line contains an instruction self._parse_instruction(line) f.close()
def __init__(self, skoolfile, case=None, base=None, asm_mode=0, warnings=False, fix_mode=0, html=False, create_labels=False, asm_labels=True, min_address=0, max_address=65536): self.skoolfile = skoolfile self.mode = Mode(case, base, asm_mode, warnings, fix_mode, html, create_labels, asm_labels) self.case = case self.base = base self.snapshot = [0] * 65536 # 64K of Spectrum memory self._instructions = {} # address -> [Instructions] self._entries = {} # address -> SkoolEntry self.memory_map = [] # SkoolEntry instances self.base_address = 65536 self.end_address = 0 self.header = [] self.stack = [] self.comments = [] self.ignores = [] self.asm_writer_class = None self.properties = {} self._replacements = [] self.equs = [] self._equ_values = {} with open_file(skoolfile) as f: self._parse_skool(f, min_address, max_address)
def _get_tape(urlstring, member=None): url = urlparse(urlstring) if url.scheme: write_line('Downloading {0}'.format(urlstring)) u = urlopen(urlstring, timeout=30) f = tempfile.NamedTemporaryFile(prefix='tap2sna-') while 1: data = bytearray(u.read(4096)) if data: f.write(data) else: break elif url.path: f = open_file(url.path, 'rb') if urlstring.lower().endswith('.zip'): z = zipfile.ZipFile(f) if member is None: for name in z.namelist(): if name.lower().endswith(('.tap', '.tzx')): member = name break else: raise TapeError('No TAP or TZX file found') write_line('Extracting {0}'.format(member)) tape = z.open(member) data = bytearray(tape.read()) tape_type = member[-3:] else: tape_type = urlstring[-3:] f.seek(0) data = bytearray(f.read()) f.close() return tape_type, data
def __init__(self, skoolfile, preserve_base, min_address, max_address): self.skoolfile = skoolfile self.preserve_base = preserve_base self.mode = Mode() self.memory_map = [] self.end_address = 65536 with open_file(skoolfile) as f: self._parse_skool(f, min_address, max_address)
def __init__(self, skoolfile, preserve_base, min_address, max_address): self.skoolfile = skoolfile self.preserve_base = preserve_base self.mode = Mode() self.memory_map = [] self.stack = [] self.end_address = 65536 with open_file(skoolfile) as f: self._parse_skool(f, min_address, max_address)
def _parse_ctl_file(self, ctlfile, ctl_lines, min_address, max_address): with open_file(ctlfile) as f: for line in f: s_line = line.rstrip() if s_line: ctl_lines.append(s_line) if s_line.startswith(('b', 'c', 'g', 'i', 's', 't', 'u', 'w')): try: address = get_int_param(s_line[1:].lstrip().split(' ', 1)[0]) if min_address <= address < max_address: self._ctls[address] = s_line[0] except ValueError: pass
def __init__(self, skoolfile, preserve_base, assembler, min_address, max_address, keep_lines): self.skoolfile = skoolfile self.mode = Mode() self.memory_map = [] self.end_address = 65536 self.keep_lines = keep_lines self.assembler = assembler self.composer = get_component('ControlDirectiveComposer', preserve_base) with open_file(skoolfile) as f: self._parse_skool(f, min_address, max_address)
def _parse_skool(self, skoolfile): f = open_file(skoolfile) removed = set() for non_entry, block in read_skool(f, 2, self.asm_mode, self.fix_mode): if non_entry: continue for line in block: if line.startswith('@'): self._parse_asm_directive(line[1:]) elif not line.lstrip().startswith( ';') and line[0] in VALID_CTLS: self._parse_instruction(line, removed) f.close()
def _get_code_blocks(snapshot, start, end, fname): if os.path.isdir(fname): raise SkoolKitError('{0} is a directory'.format(fname)) try: size = os.path.getsize(fname) except OSError as e: if e.errno == 2: raise SkoolKitError('{0}: file not found'.format(fname)) raise SkoolKitError('Failed to get size of {}: {}'.format( fname, e.strerror)) if size == 8192: # Assume this is a Z80 map file sys.stderr.write('Reading {0}'.format(fname)) sys.stderr.flush() addresses = [] data = read_bin_file(fname) address = start & 65528 for b in data[start // 8:end // 8 + 1]: for i in range(8): if b & 1 and start <= address < end: addresses.append(address) b >>= 1 address += 1 elif size == 65536: # Assume this is a SpecEmu map file sys.stderr.write('Reading {}'.format(fname)) sys.stderr.flush() addresses = [] data = read_bin_file(fname) for address in range(start, end): if data[address] & 1: addresses.append(address) else: sys.stderr.write('Reading {0}: '.format(fname)) sys.stderr.flush() with open_file(fname) as f: addresses = _get_addresses(f, fname, size, start, end) sys.stderr.write('\n') code_blocks = [] disassembler = Disassembler(snapshot) for address in addresses: size = disassembler.disassemble(address, address + 1)[0].size() if code_blocks and address <= sum(code_blocks[-1]): if address == sum(code_blocks[-1]): code_blocks[-1][1] += size else: code_blocks.append([address, size]) return code_blocks
def _get_code_blocks(snapshot, start, end, fname): if os.path.isdir(fname): raise SkoolKitError('{0} is a directory'.format(fname)) try: size = os.path.getsize(fname) except OSError as e: if e.errno == 2: raise SkoolKitError('{0}: file not found'.format(fname)) raise # pragma: no cover if size == 8192: # Assume this is a Z80 map file sys.stderr.write('Reading {0}'.format(fname)) sys.stderr.flush() addresses = [] data = read_bin_file(fname) address = start & 65528 for b in data[start // 8:end // 8 + 1]: for i in range(8): if b & 1 and start <= address < end: addresses.append(address) b >>= 1 address += 1 elif size == 65536: # Assume this is a SpecEmu map file sys.stderr.write('Reading {}'.format(fname)) sys.stderr.flush() addresses = [] data = read_bin_file(fname) for address in range(start, end): if data[address] & 1: addresses.append(address) else: sys.stderr.write('Reading {0}: '.format(fname)) sys.stderr.flush() with open_file(fname) as f: addresses = _get_addresses(f, fname, size, start, end) sys.stderr.write('\n') code_blocks = [] disassembler = Disassembler(snapshot) for address in addresses: size = disassembler.disassemble(address, address + 1)[0].size() if code_blocks and address <= sum(code_blocks[-1]): if address == sum(code_blocks[-1]): code_blocks[-1][1] += size else: code_blocks.append([address, size]) return code_blocks
def _parse_skool(self, min_address, max_address): sft = [] f = open_file(self.skoolfile) for non_entry, block in read_skool(f): lines = [] for line in block: if line.startswith(';'): lines.append(VerbatimLine(line)) continue if line.startswith('@'): lines.append(VerbatimLine(line)) self._parse_asm_directive(line[1:]) continue if self.verbatim: # This line is inside a '+' block, so include it as is lines.append(VerbatimLine(line)) continue s_line = line.lstrip() if not s_line: # This line is blank lines.append(VerbatimLine(line)) continue if s_line.startswith(';'): # This line is a continuation of an instruction comment comment_index = line.index(';') lines.append( VerbatimLine(" ;{} {}".format( comment_index, line[comment_index + 1:].lstrip()))) elif line[0] in VALID_CTLS: # This line contains an instruction ctl_line = self._parse_instruction(line) if ctl_line.address >= max_address: while lines and lines[-1].is_trimmable(): lines.pop() if lines: lines.append(VerbatimLine('')) break if ctl_line.address < min_address: lines[:] = [] break lines.append(ctl_line) else: lines.append(VerbatimLine(line)) sft.extend(lines) f.close() while sft and sft[-1].is_blank(): sft.pop() return self._compress_blocks(sft)
def write(self, binfile, start, end): if start is None: base_address = self.base_address else: base_address = start if end is None: end_address = self.end_address else: end_address = end data = self.snapshot[base_address:end_address] with open_file(binfile, 'wb') as f: f.write(bytearray(data)) if binfile == '-': binfile = 'stdout' info("Wrote {}: start={}, end={}, size={}".format(binfile, base_address, end_address, len(data)))
def write(self, binfile): if self.start < 0: base_address = self.base_address else: base_address = max(self.start, self.base_address) if self.end > 65536: end_address = self.end_address else: end_address = min(self.end, self.end_address) base_address = min(base_address, end_address) data = self.snapshot[base_address:end_address] with open_file(binfile, 'wb') as f: f.write(bytearray(data)) if binfile == '-': binfile = 'stdout' info("Wrote {}: start={}, end={}, size={}".format( binfile, base_address, end_address, len(data)))
def run(snafile, options, config): words = set() dict_fname = config['Dictionary'] if dict_fname: if find_file(dict_fname): info("Using dictionary file: {}".format(dict_fname)) with open_file(config['Dictionary']) as f: for line in f: word = line.strip().lower() if word: words.add(word) else: info("Dictionary file '{}' not found".format(dict_fname)) ctl_config = Config(config['TextChars'], config['TextMinLengthCode'], config['TextMinLengthData'], words) snapshot, start, end = make_snapshot(snafile, options.org, options.start, options.end, options.page) ctls = get_component('ControlFileGenerator').generate_ctls(snapshot, start, end, options.code_map, ctl_config) write_ctl(ctls, options.ctl_hex)
def _parse_skool(self, skoolfile): f = open_file(skoolfile) address = None for non_entry, block in read_skool(f, 2, self.asm_mode, self.fix_mode): if non_entry: continue removed = set() for line in block: if line.startswith('@'): address = self._parse_asm_directive( address, line[1:], removed) elif not line.lstrip().startswith( ';') and line[0] in VALID_CTLS: address = self._parse_instruction(address, line, removed) self.entries.append(Entry(self.entry_ctl, self.instructions)) self.entry_ctl = None self.instructions = [] f.close()
def parse(self, reffile): """Parse a ref file. This method may be called as many times as required to collect sections from multiple ref files. :param reffile: The name of the ref file to parse. """ section_name = None section_lines = [] infile = open_file(reffile) for line in infile: s_line = line.rstrip() if s_line.startswith(('[[', ';;')): section_lines.append(s_line[1:]) elif s_line.startswith('[') and s_line.endswith(']'): self._add_section(section_name, section_lines) section_name = s_line[1:-1] section_lines = [] elif not s_line.startswith(';'): section_lines.append(s_line) infile.close() self._add_section(section_name, section_lines)
def _get_tape(urlstring, member=None): url = urlparse(urlstring) if url.scheme: write_line('Downloading {0}'.format(urlstring)) r = Request(urlstring, headers={'User-Agent': ''}) u = urlopen(r, timeout=30) f = tempfile.NamedTemporaryFile(prefix='tap2sna-') while 1: data = bytearray(u.read(4096)) if data: f.write(data) else: break elif url.path: f = open_file(url.path, 'rb') if urlstring.lower().endswith('.zip'): z = zipfile.ZipFile(f) if member is None: for name in z.namelist(): if name.lower().endswith(('.tap', '.tzx')): member = name break else: f.close() raise TapeError('No TAP or TZX file found') write_line('Extracting {0}'.format(member)) tape = z.open(member) data = bytearray(tape.read()) tape_type = member[-3:] else: tape_type = urlstring[-3:] f.seek(0) data = bytearray(f.read()) f.close() return tape_type, data
def parse_ctl(self, ctlfile, min_address=0, max_address=65536): entry_ctl = None f = open_file(ctlfile) for line_no, line in enumerate(f, 1): s_line = line.rstrip() if not s_line: continue try: ctl, start, end, text, lengths, asm_directive = self._parse_ctl_line(s_line, entry_ctl) except CtlParserError as e: warn('Ignoring line {} in {} ({}):\n{}'.format(line_no, ctlfile, e.args[0], s_line)) continue if ctl: ctl = ctl.strip() if ctl.islower(): entry_ctl = ctl if not min_address <= start < max_address: continue if ctl.islower(): self._ctls[start] = ctl self._titles[start] = text elif ctl in 'D': self._descriptions.setdefault(start, []).append(text) self._subctls.setdefault(start, None) elif ctl in 'N': self._mid_block_comments.setdefault(start, []).append(text) self._subctls.setdefault(start, None) elif ctl == 'E': self._end_comments.setdefault(start, []).append(text) elif ctl == 'R': if text: fields = text.split(' ', 1) if len(fields) == 1: fields.append('') self._registers.setdefault(start, []).append(fields) elif ctl == 'M': self._multiline_comments[start] = (end, text) self._subctls.setdefault(start, None) elif ctl == 'L': count = lengths[0][0] if count > 1: if len(lengths) > 1: repeat_entries = lengths[1][0] else: repeat_entries = 0 loop_end = start + count * (end - start) if loop_end > 65536: warn('Loop crosses 64K boundary:\n{}'.format(s_line)) self._loops.append((start, end, count, repeat_entries)) self._subctls[loop_end] = None else: self._subctls[start] = ctl.lower() self._instruction_comments[start] = text if end: self._subctls[end] = None if ctl != 'L' and lengths: self._lengths[start] = lengths[0][1] if len(lengths) > 1: address = start + lengths[0][0] subctl = self._subctls[start] for length, sublengths in lengths[1:]: self._lengths[address] = sublengths self._subctls[address] = subctl address += length if text: self._multiline_comments[start] = (address, text) elif asm_directive: directive, address = asm_directive self._asm_directives.setdefault(address, []).append(directive) f.close() self._terminate_multiline_comments() self._unroll_loops(max_address) self._ctls[max_address] = 'i'
def parse_ctl(self, ctlfile, min_address=0, max_address=65536): ctl_lines = [] with open_file(ctlfile) as f: for line in f: s_line = line.rstrip() if s_line: ctl_lines.append(s_line) if s_line.startswith( ('b', 'c', 'g', 'i', 's', 't', 'u', 'w')): try: address = get_int_param(s_line[1:].lstrip().split( ' ', 1)[0]) if min_address <= address < max_address: self._ctls[address] = s_line[0] except ValueError: pass entry_addresses = sorted(self._ctls) for line_no, s_line in enumerate(ctl_lines, 1): try: ctl, start, end, text, lengths, asm_directive = self._parse_ctl_line( s_line, entry_addresses) except CtlParserError as e: warn('Ignoring line {} in {} ({}):\n{}'.format( line_no, ctlfile, e.args[0], s_line)) continue if ctl: if not min_address <= start < max_address: continue if ctl == '>': if end: self._footers[start].append(text or '') else: self._headers[start].append(text or '') elif ctl.islower(): self._titles[start] = text elif ctl == 'D': self._descriptions.setdefault(start, []).append(text) self._subctls.setdefault(start, None) elif ctl == 'N': self._mid_block_comments.setdefault(start, []).append(text) self._subctls.setdefault(start, None) elif ctl == 'E': self._end_comments.setdefault(start, []).append(text) elif ctl == 'R': if text: fields = text.split(' ', 1) if len(fields) == 1: fields.append('') self._registers.setdefault(start, []).append(fields) elif ctl == 'M': self._multiline_comments[start] = (end, text) self._subctls.setdefault(start, None) elif ctl == 'L': count = lengths[0][0] if count > 1: if len(lengths) > 1: repeat_entries = lengths[1][0] else: repeat_entries = 0 loop_end = start + count * (end - start) if loop_end > 65536: warn('Loop crosses 64K boundary:\n{}'.format( s_line)) self._loops.append((start, end, count, repeat_entries)) self._subctls[loop_end] = None else: self._subctls[start] = ctl.lower() self._instruction_comments[start] = text if end: self._subctls[end] = None if ctl != 'L' and lengths: self._lengths[start] = lengths[0][1] if len(lengths) > 1: address = start + lengths[0][0] subctl = self._subctls[start] for length, sublengths in lengths[1:]: self._lengths[address] = sublengths self._subctls[address] = subctl address += length if text: self._multiline_comments[start] = (address, text) elif asm_directive: directive, address = asm_directive self._asm_directives.setdefault(address, []).append(directive) self._terminate_multiline_comments() self._unroll_loops(max_address) self._ctls[max_address] = 'i'
def _parse_skool(self, min_address, max_address): start_index = -1 lines = [] ctl_lines = [] entry_ctl = None f = open_file(self.skoolfile) for line in f: if line.startswith(';'): lines.append(VerbatimLine(line)) continue if line.startswith('@'): lines.append(VerbatimLine(line)) self._parse_asm_directive(line[1:].rstrip()) continue if self.verbatim: # This line is inside a '+' block, so include it as is lines.append(VerbatimLine(line)) continue s_line = line.strip() if not s_line: # This line is blank lines.append(VerbatimLine(line)) entry_ctl = None continue # Check whether we're in a block that should be preserved verbatim if entry_ctl is None and line.startswith(VERBATIM_BLOCKS): entry_ctl = line[0] if entry_ctl in VERBATIM_BLOCKS: lines.append(VerbatimLine(line)) elif s_line.startswith(';'): # This line is a continuation of an instruction comment comment_index = line.index(';') lines.append(VerbatimLine(" ;{} {}".format(comment_index, line[comment_index + 1:].lstrip()))) elif line[0] in VALID_CTLS: # This line contains an instruction ctl_line = self._parse_instruction(line) if ctl_line.address >= max_address: while lines and lines[-1].is_trimmable(): lines.pop() while lines and lines[-1].is_blank(): lines.pop() break if ctl_line.address >= min_address > 0 and start_index < 0: start_index = len(lines) lines.append(ctl_line) ctl_lines.append(ctl_line) else: lines.append(VerbatimLine(line)) f.close() if min_address > 0: if start_index < 0: return [] if start_index < len(lines): if str(lines[start_index])[0] in DIRECTIVES: while start_index > 0 and not lines[start_index].is_blank(): start_index -= 1 else: while start_index < len(lines) and not lines[start_index].is_blank(): start_index += 1 return self._compress_blocks(lines[start_index + 1:]) return self._compress_blocks(lines)
def _parse_sft(self, min_address, max_address): start_index = -1 lines = [] v_block_ctl = None f = open_file(self.sftfile) for line in f: if line.startswith('#'): # This line is a skool file template comment continue if not line.strip(): # This line is blank lines.append(VerbatimLine(line)) v_block_ctl = None continue if line.startswith(';'): # This line is an entry-level comment lines.append(VerbatimLine(line)) continue if line.startswith('@'): lines.append(VerbatimLine(line)) self._parse_asm_directive(line[1:].rstrip()) continue if not self.disassemble: # This line is inside a '+' block, so include it as is lines.append(VerbatimLine(line)) continue # Check whether we're in a block that should be restored verbatim if v_block_ctl is None and line.startswith(VERBATIM_BLOCKS): v_block_ctl = line[0] if v_block_ctl: if v_block_ctl == 'd': self._set_bytes(line) lines.append(VerbatimLine(line)) continue # Check whether the line starts with a valid character if line[0] not in VALID_CTLS: lines.append(VerbatimLine(line)) continue try: ctl, inst_ctl, start, lengths, comment_index, comment = self._parse_instruction( line) except (IndexError, ValueError): raise SftParsingError("Invalid line: {0}".format( line.split()[0])) if start is not None: # This line contains a control directive if start >= min_address > 0 and start_index < 0: start_index = len(lines) instructions = [] for length, sublengths in lengths: end = start + length if inst_ctl == 'C': base = sublengths[0][1] instructions += self.disassembler.disassemble( start, end, base) elif inst_ctl == 'W': instructions += self.disassembler.defw_range( start, end, sublengths) elif inst_ctl == 'T': instructions += self.disassembler.defm_range( start, end, sublengths) elif inst_ctl == 'S': instructions.append( self.disassembler.defs(start, end, sublengths)) else: instructions += self.disassembler.defb_range( start, end, sublengths) start += length if instructions: done = False for instruction in instructions: if instruction.address >= max_address: while lines and lines[-1].is_trimmable(): lines.pop() done = True break address = self.address_fmt.format(instruction.address) lines.append( InstructionLine(ctl, address, instruction.operation, comment_index, comment)) ctl = ' ' if done: break else: lines.append( InstructionLine(ctl, start, '', comment_index, comment)) else: # This line is an instruction-level comment continuation line lines.append( InstructionLine(comment_index=comment_index, comment=comment)) f.close() if start_index < 0: return lines if start_index < len(lines): if str(lines[start_index])[0] in DIRECTIVES: while start_index > 0 and not lines[start_index].is_blank(): start_index -= 1 else: while start_index < len( lines) and not lines[start_index].is_blank(): start_index += 1 return lines[start_index + 1:] return []
self._instructions = {} # address -> [Instructions] self._entries = {} # address -> SkoolEntry self.memory_map = [] # SkoolEntry instances self.base_address = 65536 self.end_address = 0 self.header = [] self.stack = [] self.comments = [] self.ignores = [] self.asm_writer_class = None self.properties = {} self._replacements = [] self.equs = [] self._equ_values = {} with open_file(skoolfile) as f: self._parse_skool(f, min_address, max_address) def clone(self, skoolfile): return SkoolParser( skoolfile, self.case, self.base, self.mode.asm_mode, self.mode.warn, self.mode.fix_mode, self.mode.html, self.mode.create_labels, self.mode.asm_labels, snapshot=self.snapshot[:] )
def test_open_file(self): tempdir = self.make_directory() with self.assertRaises(IOError) as cm: open_file(tempdir) self.assertEqual(cm.exception.errno, ERRNO)
def _parse_sft(self, min_address, max_address): start_index = -1 lines = [] v_block_ctl = None f = open_file(self.sftfile) for line in f: if line.startswith('#'): # This line is a skool file template comment continue if not line.strip(): # This line is blank lines.append(VerbatimLine(line)) v_block_ctl = None continue if line.startswith(';'): # This line is an entry-level comment lines.append(VerbatimLine(line)) continue if line.startswith('@'): lines.append(VerbatimLine(line)) self._parse_asm_directive(line[1:].rstrip()) continue if not self.disassemble: # This line is inside a '+' block, so include it as is lines.append(VerbatimLine(line)) continue # Check whether we're in a block that should be restored verbatim if v_block_ctl is None and line[0] in 'dr' or (line[0] == 'i' and line[1] in '$0123456789'): v_block_ctl = line[0] if v_block_ctl: if v_block_ctl == 'd': self._set_bytes(line) lines.append(VerbatimLine(line)) continue # Check whether the line starts with a valid character if line[0] not in VALID_CTLS: lines.append(VerbatimLine(line)) continue try: ctl, inst_ctl, start, lengths, comment_index, comment = self._parse_instruction(line) except (IndexError, ValueError): raise SftParsingError("Invalid line: {0}".format(line.split()[0])) if start is not None: # This line contains a control directive if start >= min_address > 0 and start_index < 0: start_index = len(lines) instructions = [] for length, sublengths in lengths: end = start + length if inst_ctl == 'C': base = sublengths[0][1] instructions += self.disassembler.disassemble(start, end, base) elif inst_ctl == 'W': instructions += self.disassembler.defw_range(start, end, sublengths) elif inst_ctl == 'T': instructions += self.disassembler.defm_range(start, end, sublengths) elif inst_ctl == 'S': instructions.append(self.disassembler.defs(start, end, sublengths)) else: instructions += self.disassembler.defb_range(start, end, sublengths) start += length if instructions: done = False for instruction in instructions: if instruction.address >= max_address: while lines and lines[-1].is_trimmable(): lines.pop() done = True break address = self.address_fmt.format(instruction.address) lines.append(InstructionLine(ctl, address, instruction.operation, comment_index, comment)) ctl = ' ' if done: break else: lines.append(InstructionLine(ctl, start, '', comment_index, comment)) else: # This line is an instruction-level comment continuation line lines.append(InstructionLine(comment_index=comment_index, comment=comment)) f.close() if start_index < 0: return lines if start_index < len(lines): if str(lines[start_index])[0] in DIRECTIVES: while start_index > 0 and not lines[start_index].is_blank(): start_index -= 1 else: while start_index < len(lines) and not lines[start_index].is_blank(): start_index += 1 return lines[start_index + 1:] return []
def _parse_skool(self, min_address, max_address): start_index = -1 lines = [] ctl_lines = [] entry_ctl = None f = open_file(self.skoolfile) for line in f: if line.startswith(';'): lines.append(VerbatimLine(line)) continue if line.startswith('@'): lines.append(VerbatimLine(line)) self._parse_asm_directive(line[1:].rstrip()) continue if self.verbatim: # This line is inside a '+' block, so include it as is lines.append(VerbatimLine(line)) continue s_line = line.strip() if not s_line: # This line is blank lines.append(VerbatimLine(line)) entry_ctl = None continue # Check whether we're in a block that should be preserved verbatim if entry_ctl is None and line.startswith(VERBATIM_BLOCKS): entry_ctl = line[0] if entry_ctl in VERBATIM_BLOCKS: lines.append(VerbatimLine(line)) elif s_line.startswith(';'): # This line is a continuation of an instruction comment comment_index = line.index(';') lines.append( VerbatimLine(" ;{} {}".format( comment_index, line[comment_index + 1:].lstrip()))) elif line[0] in VALID_CTLS: # This line contains an instruction ctl_line = self._parse_instruction(line) if ctl_line.address >= max_address: while lines and lines[-1].is_trimmable(): lines.pop() while lines and lines[-1].is_blank(): lines.pop() break if ctl_line.address >= min_address > 0 and start_index < 0: start_index = len(lines) lines.append(ctl_line) ctl_lines.append(ctl_line) else: lines.append(VerbatimLine(line)) f.close() if min_address > 0: if start_index < 0: return [] if start_index < len(lines): if str(lines[start_index])[0] in DIRECTIVES: while start_index > 0 and not lines[start_index].is_blank( ): start_index -= 1 else: while start_index < len( lines) and not lines[start_index].is_blank(): start_index += 1 return self._compress_blocks(lines[start_index + 1:]) return self._compress_blocks(lines)