def __init__(self, ctl, address, addr_str, operation, comment_index, comment, preserve_base): self.ctl = ctl self.address = address self.addr_str = addr_str self.comment_index = comment_index self.comment = comment self.operation = operation if operation: self.inst_ctl = get_instruction_ctl(operation) if self.inst_ctl == 'C': size = get_size(operation, address) length = [get_operand_bases(operation, preserve_base), size] elif self.inst_ctl == 'B': size, length = get_defb_length(self.operation, preserve_base) elif self.inst_ctl == 'T': size, length = get_defb_length(self.operation, preserve_base) elif self.inst_ctl == 'W': size, length = get_defw_length(self.operation, preserve_base) else: size, length = get_defs_length(self.operation, preserve_base) self.end = address + size self.lengths = [length] else: self.inst_ctl = 'I'
def write_sub_block(self, ctl, entry_ctl, comment, instructions, lengths): length = 0 sublengths = [] address = instructions[0].address if ctl == 'C': # Compute the sublengths for a 'C' sub-block for i, instruction in enumerate(instructions): addr = instruction.address if i < len(instructions) - 1: sublength = instructions[i + 1].address - addr else: sublength = get_size(instruction.operation, addr) if sublength > 0: length += sublength bases = instruction.bases if sublengths and bases == sublengths[-1][0]: sublengths[-1][1] += sublength else: sublengths.append([bases, sublength]) if not any(comment) and len(sublengths) > 1 and entry_ctl == 'c': if not sublengths[-1][0]: length -= sublengths.pop()[1] if not sublengths[0][0]: sublength = sublengths.pop(0)[1] length -= sublength address += sublength lengths = ','.join(['{}{}'.format(*s) for s in sublengths]) if len(sublengths) > 1: lengths = '{},{}'.format(length, lengths) elif ctl in 'BSTW': # Compute the sublengths for a 'B', 'S', 'T' or 'W' sub-block for statement in instructions: length += statement.size sublengths.append(statement.length) while len(sublengths) > 1 and sublengths[-1] == sublengths[-2]: sublengths.pop() lengths = '{},{}'.format(length, get_lengths(sublengths)) addr_str = self.addr_str(address) if lengths: lengths = ',{}'.format(lengths) if isinstance(comment, str): write_line('{} {}{} {}'.format(ctl, addr_str, lengths, comment).rstrip()) else: # Remove redundant trailing blank lines min_comments = min(len(instructions) - 1, 1) while len(comment) > min_comments and comment[-1] == ['']: comment.pop() self._write_lines(comment, ctl, addr_str + lengths, True)
def write_sub_block(self, ctl, entry_ctl, comment, instructions, lengths): length = 0 sublengths = [] address = instructions[0].address if ctl == 'c': # Compute the sublengths for a 'C' sub-block for i, instruction in enumerate(instructions): addr = instruction.address if i < len(instructions) - 1: sublength = instructions[i + 1].address - addr else: sublength = get_size(instruction.operation, addr) if sublength > 0: length += sublength bases = instruction.bases if sublengths and bases == sublengths[-1][0]: sublengths[-1][1] += sublength else: sublengths.append([bases, sublength]) if not comment and len(sublengths) > 1 and entry_ctl == 'c': if not sublengths[-1][0]: length -= sublengths.pop()[1] if not sublengths[0][0]: sublength = sublengths.pop(0)[1] length -= sublength address += sublength lengths = ','.join(['{}{}'.format(*s) for s in sublengths]) if len(sublengths) > 1: lengths = '{},{}'.format(length, lengths) elif ctl in 'bstw': # Compute the sublengths for a 'B', 'S', 'T' or 'W' sub-block for statement in instructions: length += statement.size sublengths.append(statement.length) while len(sublengths) > 1 and sublengths[-1] == sublengths[-2]: sublengths.pop() lengths = '{},{}'.format(length, get_lengths(sublengths)) if ctl == entry_ctl: sub_block_ctl = ' ' else: sub_block_ctl = ctl.upper() addr_str = self.addr_str(address) if lengths: lengths = ',{}'.format(lengths) write_line('{} {}{} {}'.format(sub_block_ctl, addr_str, lengths, comment).rstrip())
def _parse_skool(self, skoolfile, min_address, max_address): map_entry = None instruction = None comments = [] ignores = [] address_comments = [] for line in skoolfile: if line.startswith(';'): if self.mode.include: comments.append(line[2:].rstrip()) instruction = None address_comments.append((None, None)) continue if line.startswith('@'): self._parse_asm_directive(line[1:].rstrip(), ignores, len(comments)) continue if not self.mode.include: continue s_line = line.strip() if not s_line: instruction = None address_comments.append((None, None)) if comments and map_entry: map_entry.end_comment = join_comments(comments, True) comments[:] = [] map_entry = None continue if s_line.startswith(';'): if map_entry and instruction: # This is an instruction comment continuation line address_comments[-1][1] = '{} {}'.format(address_comments[-1][1], s_line[1:].lstrip()) continue # pragma: no cover # This line contains an instruction instruction, address_comment = self._parse_instruction(line) address = instruction.address if address < min_address: continue if address >= max_address: map_entry = None break ctl = instruction.ctl if ctl in DIRECTIVES: start_comment, desc, details, registers = parse_comment_block(comments, ignores, self.mode) map_entry = Entry(ctl, desc, details, registers, self.mode.entry_ignoreua) instruction.mid_block_comment = start_comment map_entry.asm_directives = extract_entry_asm_directives(instruction.asm_directives) self.memory_map.append(map_entry) comments[:] = [] instruction.ignoremrcua = self.mode.ignoremrcua elif ctl in 'dr': # This is a data definition entry or a remote entry map_entry = None if map_entry: address_comments.append([instruction, address_comment]) map_entry.add_instruction(instruction) if comments: instruction.mid_block_comment = join_comments(comments, True) comments[:] = [] instruction.ignoremrcua = 0 in ignores instruction.ignoreua = any(ignores) elif ignores: instruction.ignoreua = True ignores[:] = [] if comments and map_entry: map_entry.end_comment = join_comments(comments, True) map_entry.ignoreua[END] = len(ignores) > 0 last_entry = None last_instruction = None for entry in self.memory_map: entry.sort_instructions() if last_entry is None or last_entry.address < entry.address: last_entry = entry end_instruction = entry.instructions[-1] if last_instruction is None or last_instruction.address < end_instruction.address: last_instruction = end_instruction if last_entry is not None and last_entry.ctl != 'i': address = last_instruction.address self.end_address = address + (get_size(last_instruction.operation, address) or 1) parse_address_comments(address_comments)
def _calculate_entry_sizes(self): for entry in self.memory_map: address = max([i.address for i in entry.instructions if i.address is not None]) last_instruction = self.get_instruction(address) entry.size = address + (get_size(last_instruction.operation, address) or 1) - entry.address
def _parse_skool(self, skoolfile, min_address, max_address): map_entry = None instruction = None address_comments = [] for line in skoolfile: if line.startswith(';'): if self.mode.started and self.mode.include: self.comments.append(line[2:].rstrip()) self.mode.ignoreua = False instruction = None address_comments.append((None, None)) continue if line.startswith('@'): self._parse_asm_directive(line[1:].rstrip()) continue if not self.mode.include: continue s_line = line.strip() if not s_line: instruction = None address_comments.append((None, None)) if self.comments: if map_entry: self._add_end_comment(map_entry) else: self.header += self.comments self.comments[:] = [] self.ignores[:] = [] map_entry = None continue if s_line[0] == ';' and map_entry and instruction: # This is an instruction comment continuation line address_comments[-1][1] = '{0} {1}'.format(address_comments[-1][1], s_line[1:].lstrip()) continue # This line contains an instruction instruction, address_comment = self._parse_instruction(line) address = instruction.address addr_str = instruction.addr_str ctl = instruction.ctl if ctl in DIRECTIVES: if address is None: raise SkoolParsingError("Invalid address: '{}'".format(addr_str)) start_comment, desc, details, registers = parse_comment_block(self.comments, self.ignores, self.mode) map_entry = SkoolEntry(address, addr_str, ctl, desc, details, registers) instruction.mid_block_comment = start_comment map_entry.ignoreua.update(self.mode.entry_ignoreua) self.mode.reset_entry_ignoreua() self._entries[address] = map_entry self.memory_map.append(map_entry) self.comments[:] = [] self.base_address = min((address, self.base_address)) elif ctl == 'd': # This is a data definition entry map_entry = None elif ctl == 'r': # This is a remote entry map_entry = RemoteEntry(instruction.operation, address) if map_entry: address_comments.append([instruction, address_comment]) if address is not None: self._instructions.setdefault(address, []).append(instruction) map_entry.add_instruction(instruction) if self.comments: instruction.mid_block_comment = join_comments(self.comments, split=True, html=self.mode.html) self.comments[:] = [] self.mode.ignoremrcua = 0 in self.ignores self.mode.apply_asm_attributes(instruction) self.ignores[:] = [] # Set bytes in the snapshot if the instruction is DEF{B,M,S,W} if address is not None: operation = instruction.operation if self.mode.assemble or operation.upper().startswith(('DEFB ', 'DEFM ', 'DEFS ', 'DEFW ')): set_bytes(self.snapshot, address, operation) if self.comments and map_entry: self._add_end_comment(map_entry) if min_address > 0 or max_address < 65536: self.memory_map = [e for e in self.memory_map if min_address <= e.address < max_address] self._entries = {k: v for k, v in self._entries.items() if min_address <= k < max_address} if self._entries: self.base_address = min(self._entries) last_entry = self._entries[max(self._entries)] last_entry.instructions = [i for i in last_entry.instructions if i.address is None or i.address < max_address] else: self.base_address = max_address self._instructions = {k: v for k, v in self._instructions.items() if self.base_address <= k < max_address} address_comments = [c for c in address_comments if c[0] is None or c[0].address is None or self.base_address <= c[0].address < max_address] if self.memory_map: end_address = max([i.address for e in self.memory_map for i in e.instructions if i.address is not None]) last_instruction = self.get_instruction(end_address) self.end_address = end_address + (get_size(last_instruction.operation, end_address) or 1) # Do some post-processing parse_address_comments(address_comments, self.mode.html) self.make_replacements(self) self._calculate_references() if self.mode.asm_labels: self._generate_labels() if self.mode.html: self._calculate_entry_sizes() self._escape_instructions() else: self._substitute_labels()
def _test_operation(self, operation, exp_data, address): data = assemble(operation, address) self.assertEqual(exp_data, data, "assemble('{}', {}) failed".format(operation, address)) exp_length = len(data) length = get_size(operation, address) self.assertEqual(exp_length, length, "get_size('{}', {}) failed".format(operation, address))
def _parse_skool(self, skoolfile, min_address, max_address): address_comments = [] non_entries = [] for non_entry, block in read_skool(skoolfile, 1): if non_entry: non_entries.append(block) continue map_entry = None instruction = None comments = [] ignores = [] address_comments.append((None, None, None)) for line in block: if line.startswith(';'): comments.append(line[1:]) instruction = None address_comments.append((None, None, None)) continue if line.startswith('@'): self._parse_asm_directive(line[1:], ignores, len(comments)) continue s_line = line.lstrip() if s_line.startswith(';'): if map_entry and instruction: # This is an instruction comment continuation line address_comments[-1][1].append(s_line[1:].lstrip()) continue # This line contains an instruction instruction, address_comment = self._parse_instruction(line) address = instruction.address if address < min_address: continue if address >= max_address: map_entry = None break ctl = instruction.ctl if ctl in DIRECTIVES: start_comment, desc, details, registers = parse_comment_block(comments, ignores, self.mode) map_entry = Entry(ctl, desc, details, registers, self.mode.entry_ignoreua) instruction.mid_block_comment = start_comment map_entry.asm_directives = extract_entry_asm_directives(instruction.asm_directives) self.memory_map.append(map_entry) comments[:] = [] instruction.ignoremrcua = self.mode.ignoremrcua if map_entry: address_comments.append((instruction, [address_comment], [])) map_entry.add_instruction(instruction) if comments: instruction.mid_block_comment = join_comments(comments, True) comments[:] = [] instruction.ignoremrcua = 0 in ignores instruction.ignoreua = any(ignores) elif ignores: instruction.ignoreua = True ignores[:] = [] if map_entry: if comments: map_entry.end_comment = join_comments(comments, True) map_entry.ignoreua[END] = len(ignores) > 0 map_entry.header = non_entries non_entries = [] if self.memory_map: self.memory_map[-1].footer = non_entries last_entry = None last_instruction = None for entry in self.memory_map: entry.sort_instructions() if last_entry is None or last_entry.address < entry.address: last_entry = entry end_instruction = entry.instructions[-1] if last_instruction is None or last_instruction.address < end_instruction.address: last_instruction = end_instruction if last_entry is not None and last_entry.ctl != 'i': address = last_instruction.address self.end_address = address + (get_size(last_instruction.operation, address) or 1) parse_address_comments(address_comments)
def _parse_skool(self, skoolfile, min_address, max_address): address_comments = [] non_entries = [] done = False for non_entry, block in read_skool(skoolfile, 1): if non_entry: non_entries.append(block) continue map_entry = None instruction = None comments = [] ignores = [] address_comments.append((None, None, None)) for line in block: if line.startswith(';'): self._parse_comment_line(comments, line) instruction = None address_comments.append((None, None, None)) continue if line.startswith('@'): self._parse_asm_directive(line[1:], ignores, len(comments)) continue s_line = line.lstrip() if s_line.startswith(';'): if map_entry and instruction: # This is an instruction comment continuation line self._parse_comment_line(address_comments[-1][1], s_line) continue # This line contains an instruction instruction, address_comment = self._parse_instruction(line) address = instruction.address if address < min_address: non_entries.clear() break if address >= max_address: non_entries.clear() map_entry = None done = True break ctl = instruction.ctl if ctl in DIRECTIVES: start_comment, title, description, registers = parse_comment_block(comments, ignores, self.mode, self.keep_lines) map_entry = Entry(ctl, title, description, registers, self.mode.entry_ignoreua) instruction.mid_block_comment = start_comment map_entry.asm_directives = extract_entry_asm_directives(instruction.asm_directives) self.memory_map.append(map_entry) comments[:] = [] instruction.ignoremrcua = self.mode.ignoremrcua if map_entry: address_comments.append((instruction, [address_comment], [])) map_entry.add_instruction(instruction) if comments: instruction.mid_block_comment = join_comments(comments, True, self.keep_lines) comments = [] instruction.ignoremrcua = 0 in ignores instruction.ignoreua = any(ignores) elif ignores: instruction.ignoreua = True ignores[:] = [] if map_entry: if comments: map_entry.end_comment = join_comments(comments, True, self.keep_lines) map_entry.ignoreua[END] = len(ignores) > 0 map_entry.header = non_entries non_entries = [] if done: break if self.memory_map: self.memory_map[-1].footer = non_entries last_entry = None last_instruction = None for entry in self.memory_map: entry.sort_instructions() if last_entry is None or last_entry.address < entry.address: last_entry = entry end_instruction = entry.instructions[-1] if last_instruction is None or last_instruction.address < end_instruction.address: last_instruction = end_instruction if last_entry is not None and last_entry.ctl != 'i': address = last_instruction.address self.end_address = address + (get_size(last_instruction.operation, address) or 1) parse_address_comments(address_comments, self.keep_lines)
def _parse_skool(self, skoolfile, min_address, max_address): map_entry = None instruction = None comments = [] ignores = [] address_comments = [] for line in skoolfile: if line.startswith(';'): if self.mode.include: comments.append(line[2:].rstrip()) instruction = None address_comments.append((None, None)) continue if line.startswith('@'): self._parse_asm_directive(line[1:].rstrip(), ignores, len(comments)) continue if not self.mode.include: continue s_line = line.strip() if not s_line: instruction = None address_comments.append((None, None)) if comments and map_entry: map_entry.end_comment = join_comments(comments, True) comments[:] = [] map_entry = None continue if s_line.startswith(';'): if map_entry and instruction: # This is an instruction comment continuation line address_comments[-1][1] = '{} {}'.format( address_comments[-1][1], s_line[1:].lstrip()) continue # pragma: no cover # This line contains an instruction instruction, address_comment = self._parse_instruction(line) address = instruction.address if address < min_address: continue if address >= max_address: map_entry = None break ctl = instruction.ctl if ctl in DIRECTIVES: start_comment, desc, details, registers = parse_comment_block( comments, ignores, self.mode) map_entry = Entry(ctl, desc, details, registers, self.mode.entry_ignoreua) instruction.mid_block_comment = start_comment map_entry.asm_directives = extract_entry_asm_directives( instruction.asm_directives) self.memory_map.append(map_entry) comments[:] = [] instruction.ignoremrcua = self.mode.ignoremrcua elif ctl in 'dr': # This is a data definition entry or a remote entry map_entry = None if map_entry: address_comments.append([instruction, address_comment]) map_entry.add_instruction(instruction) if comments: instruction.mid_block_comment = join_comments( comments, True) comments[:] = [] instruction.ignoremrcua = 0 in ignores instruction.ignoreua = any(ignores) elif ignores: instruction.ignoreua = True ignores[:] = [] if comments and map_entry: map_entry.end_comment = join_comments(comments, True) map_entry.ignoreua[END] = len(ignores) > 0 last_entry = None last_instruction = None for entry in self.memory_map: entry.sort_instructions() if last_entry is None or last_entry.address < entry.address: last_entry = entry end_instruction = entry.instructions[-1] if last_instruction is None or last_instruction.address < end_instruction.address: last_instruction = end_instruction if last_entry is not None and last_entry.ctl != 'i': address = last_instruction.address self.end_address = address + (get_size(last_instruction.operation, address) or 1) parse_address_comments(address_comments)