Beispiel #1
0
class SftParser:
    def __init__(self,
                 snapshot,
                 sftfile,
                 zfill=False,
                 asm_hex=False,
                 asm_lower=False):
        self.snapshot = snapshot
        self.disassembler = Disassembler(snapshot,
                                         zfill=zfill,
                                         asm_hex=asm_hex,
                                         asm_lower=asm_lower)
        self.sftfile = sftfile
        self.address_fmt = get_address_format(asm_hex, asm_lower)
        self.stack = []
        self.disassemble = True

    def _parse_instruction(self, line):
        ctl = line[0]
        lengths = []
        if line[1] == ';':
            inst_ctl = None
            start = None
            i = line.find(' ', 2)
            if i < 0:
                i = len(line.rstrip())
            comment_index = get_int_param(line[2:i])
        else:
            inst_ctl = line[1]
            if inst_ctl == 'I':
                i = find_unquoted(line.rstrip(), ' ', 2)
                address_end = j = find_unquoted(line, ';', 2, i)
            else:
                address_end = line.index(',', 2)
                i = find_unquoted(line.rstrip(), ' ', address_end + 1)
                j = find_unquoted(line, ';', address_end + 1, i)
            start = get_int_param(line[2:address_end])
            if j == i:
                comment_index = -1
            else:
                comment_index = get_int_param(line[j + 1:i])
            if j > address_end + 1:
                params = split_unquoted(line[address_end + 1:j], ',')
                lengths = parse_params(inst_ctl, params, 0)
            elif inst_ctl != 'I':
                raise ValueError

        comment = line[i:].strip()
        return ctl, inst_ctl, start, lengths, comment_index, comment

    def _parse_asm_directive(self, directive):
        if parse_asm_block_directive(directive, self.stack):
            self.disassemble = True
            for _p, i in self.stack:
                if i != '-':
                    self.disassemble = False
                    break

    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 _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 []

    def write_skool(self, min_address=0, max_address=65536):
        for line in self._parse_sft(min_address, max_address):
            write_line(str(line))
Beispiel #2
0
class Disassembly:
    def __init__(self,
                 snapshot,
                 ctl_parser,
                 config=None,
                 final=False,
                 defb_size=8,
                 defb_mod=1,
                 zfill=False,
                 defm_width=66,
                 asm_hex=False,
                 asm_lower=False):
        ctl_parser.apply_asm_data_directives(snapshot)
        self.disassembler = Disassembler(snapshot, defb_size, defb_mod, zfill,
                                         defm_width, asm_hex, asm_lower)
        self.ctl_parser = ctl_parser
        if asm_hex:
            if asm_lower:
                self.address_fmt = '{0:04x}'
            else:
                self.address_fmt = '{0:04X}'
        else:
            self.address_fmt = '{0}'
        self.entry_map = {}
        self.config = config or {}
        self.build(final)

    def build(self, final=False):
        self.instructions = {}
        self.entries = []
        self._create_entries()
        if self.entries:
            self.org = self.entries[0].address
        else:
            self.org = None
        if final:
            self._calculate_references()

    def _create_entries(self):
        for block in self.ctl_parser.get_blocks():
            if block.start in self.entry_map:
                entry = self.entry_map[block.start]
                self.entries.append(entry)
                for instruction in entry.instructions:
                    self.instructions[instruction.address] = instruction
                continue
            title = block.title
            if not title:
                ctl = block.ctl
                if ctl != 'i' or block.description or block.registers or block.blocks[
                        0].header:
                    name = 'Title-' + ctl
                    title = format_template(self.config.get(name, ''),
                                            name,
                                            address=self._address_str(
                                                block.start))
            for sub_block in block.blocks:
                address = sub_block.start
                if sub_block.ctl in 'cBT':
                    base = sub_block.sublengths[0][1]
                    instructions = self.disassembler.disassemble(
                        sub_block.start, sub_block.end, base)
                elif sub_block.ctl in 'bgstuw':
                    sublengths = sub_block.sublengths
                    if sublengths[0][0]:
                        if sub_block.ctl == 's':
                            length = sublengths[0][0]
                        else:
                            length = sum([s[0] for s in sublengths])
                    else:
                        length = sub_block.end - sub_block.start
                    instructions = []
                    while address < sub_block.end:
                        end = min(address + length, sub_block.end)
                        if sub_block.ctl == 't':
                            instructions += self.disassembler.defm_range(
                                address, end, sublengths)
                        elif sub_block.ctl == 'w':
                            instructions += self.disassembler.defw_range(
                                address, end, sublengths)
                        elif sub_block.ctl == 's':
                            instructions.append(
                                self.disassembler.defs(address, end,
                                                       sublengths))
                        else:
                            instructions += self.disassembler.defb_range(
                                address, end, sublengths)
                        address += length
                else:
                    instructions = self.disassembler.ignore(
                        sub_block.start, sub_block.end)
                sub_block.instructions = instructions
                for instruction in instructions:
                    self.instructions[instruction.address] = instruction
                    instruction.asm_directives = sub_block.asm_directives.get(
                        instruction.address, ())

            sub_blocks = []
            i = 0
            while i < len(block.blocks):
                sub_block = block.blocks[i]
                i += 1
                sub_blocks.append(sub_block)
                if sub_block.multiline_comment is not None:
                    end, sub_block.comment = sub_block.multiline_comment
                    while i < len(
                            block.blocks) and block.blocks[i].start < end:
                        next_sub_block = block.blocks[i]
                        sub_block.instructions += next_sub_block.instructions
                        sub_block.end = next_sub_block.end
                        i += 1

            entry = Entry(block.header, title, block.description, block.ctl,
                          sub_blocks, block.registers, block.end_comment,
                          block.footer, block.asm_directives,
                          block.ignoreua_directives)
            self.entry_map[entry.address] = entry
            self.entries.append(entry)
        for i, entry in enumerate(self.entries[1:]):
            self.entries[i].next = entry

    def remove_entry(self, address):
        if address in self.entry_map:
            del self.entry_map[address]

    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 _address_str(self, address):
        return self.address_fmt.format(address)
Beispiel #3
0
class Disassembly:
    def __init__(self, snapshot, ctl_parser, final=False, defb_size=8, defb_mod=1, zfill=False, defm_width=66, asm_hex=False, asm_lower=False):
        self.disassembler = Disassembler(snapshot, defb_size, defb_mod, zfill, defm_width, asm_hex, asm_lower)
        self.ctl_parser = ctl_parser
        if asm_hex:
            if asm_lower:
                self.address_fmt = '{0:04x}'
            else:
                self.address_fmt = '{0:04X}'
        else:
            self.address_fmt = '{0}'
        self.entry_map = {}
        self.build(final)

    def build(self, final=False):
        self.instructions = {}
        self.entries = []
        self._create_entries()
        self.org = self.entries[0].address
        if final:
            self._calculate_references()

    def _create_entries(self):
        for block in self.ctl_parser.get_blocks():
            if block.start in self.entry_map:
                entry = self.entry_map[block.start]
                self.entries.append(entry)
                for instruction in entry.instructions:
                    self.instructions[instruction.address] = instruction
                continue
            title = block.title
            if block.ctl == 'c':
                title = title or 'Routine at {}'.format(self._address_str(block.start))
            elif block.ctl in 'bw':
                title = title or 'Data block at {}'.format(self._address_str(block.start))
            elif block.ctl == 't':
                title = title or 'Message at {}'.format(self._address_str(block.start))
            elif block.ctl == 'g':
                title = title or 'Game status buffer entry at {}'.format(self._address_str(block.start))
            elif block.ctl in 'us':
                title = title or 'Unused'
            elif block.ctl == 'i' and (block.description or block.registers or block.blocks[0].header):
                title = title or 'Ignored'
            for sub_block in block.blocks:
                address = sub_block.start
                if sub_block.ctl in 'cBT':
                    base = sub_block.sublengths[0][1]
                    instructions = self.disassembler.disassemble(sub_block.start, sub_block.end, base)
                elif sub_block.ctl in 'bgstuw':
                    sublengths = sub_block.sublengths
                    if sublengths[0][0]:
                        if sub_block.ctl == 's':
                            length = sublengths[0][0]
                        else:
                            length = sum([s[0] for s in sublengths])
                    else:
                        length = sub_block.end - sub_block.start
                    instructions = []
                    while address < sub_block.end:
                        end = min(address + length, sub_block.end)
                        if sub_block.ctl == 't':
                            instructions += self.disassembler.defm_range(address, end, sublengths)
                        elif sub_block.ctl == 'w':
                            instructions += self.disassembler.defw_range(address, end, sublengths)
                        elif sub_block.ctl == 's':
                            instructions.append(self.disassembler.defs(address, end, sublengths))
                        else:
                            instructions += self.disassembler.defb_range(address, end, sublengths)
                        address += length
                else:
                    instructions = self.disassembler.ignore(sub_block.start, sub_block.end)
                sub_block.instructions = instructions
                for instruction in instructions:
                    self.instructions[instruction.address] = instruction
                    instruction.asm_directives = sub_block.asm_directives.get(instruction.address, ())

            sub_blocks = []
            i = 0
            while i < len(block.blocks):
                sub_block = block.blocks[i]
                i += 1
                sub_blocks.append(sub_block)
                if sub_block.multiline_comment is not None:
                    end, sub_block.comment = sub_block.multiline_comment
                    while i < len(block.blocks) and block.blocks[i].start < end:
                        next_sub_block = block.blocks[i]
                        sub_block.instructions += next_sub_block.instructions
                        sub_block.end = next_sub_block.end
                        i += 1

            entry = Entry(title, block.description, block.ctl, sub_blocks,
                          block.registers, block.end_comment, block.asm_directives,
                          block.ignoreua_directives)
            self.entry_map[entry.address] = entry
            self.entries.append(entry)
        for i, entry in enumerate(self.entries[1:]):
            self.entries[i].next = entry

    def remove_entry(self, address):
        if address in self.entry_map:
            del self.entry_map[address]

    def contains_entry_asm_directive(self, asm_dir):
        for entry in self.entries:
            for directive, value in entry.asm_directives:
                if directive == asm_dir:
                    return True

    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 _address_str(self, address):
        return self.address_fmt.format(address)
Beispiel #4
0
class SftParser:
    def __init__(self, snapshot, sftfile, zfill=False, asm_hex=False, asm_lower=False):
        self.snapshot = snapshot
        self.disassembler = Disassembler(snapshot, zfill=zfill, asm_hex=asm_hex, asm_lower=asm_lower)
        self.sftfile = sftfile
        self.address_fmt = get_address_format(asm_hex, asm_lower)
        self.stack = []
        self.disassemble = True

    def _parse_instruction(self, line):
        ctl = line[0]
        lengths = []
        if line[1] == ';':
            inst_ctl = None
            start = None
            i = line.find(' ', 2)
            if i < 0:
                i = len(line.rstrip())
            comment_index = get_int_param(line[2:i])
        else:
            inst_ctl = line[1]
            if inst_ctl == 'I':
                i = find_unquoted(line.rstrip(), ' ', 2)
                address_end = j = find_unquoted(line, ';', 2, i)
            else:
                address_end = line.index(',', 2)
                i = find_unquoted(line.rstrip(), ' ', address_end + 1)
                j = find_unquoted(line, ';', address_end + 1, i)
            start = get_int_param(line[2:address_end])
            if j == i:
                comment_index = -1
            else:
                comment_index = get_int_param(line[j + 1:i])
            if j > address_end + 1:
                params = split_unquoted(line[address_end + 1:j], ',')
                lengths = parse_params(inst_ctl, params, 0)
            elif inst_ctl != 'I':
                raise ValueError

        comment = line[i:].strip()
        return ctl, inst_ctl, start, lengths, comment_index, comment

    def _parse_asm_directive(self, directive):
        if parse_asm_block_directive(directive, self.stack):
            self.disassemble = True
            for _p, i in self.stack:
                if i != '-':
                    self.disassemble = False
                    break

    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 _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 write_skool(self, min_address=0, max_address=65536):
        for line in self._parse_sft(min_address, max_address):
            write_line(str(line))
Beispiel #5
0
class Disassembly:
    def __init__(self, snapshot, ctl_parser, config=None, final=False, defb_size=8, defb_mod=1,
                 zfill=False, defm_width=66, asm_hex=False, asm_lower=False):
        ctl_parser.apply_asm_data_directives(snapshot)
        self.disassembler = Disassembler(snapshot, defb_size, defb_mod, zfill, defm_width, asm_hex, asm_lower)
        self.ctl_parser = ctl_parser
        if asm_hex:
            if asm_lower:
                self.address_fmt = '{0:04x}'
            else:
                self.address_fmt = '{0:04X}'
        else:
            self.address_fmt = '{0}'
        self.entry_map = {}
        self.config = config or {}
        self.build(final)

    def build(self, final=False):
        self.instructions = {}
        self.entries = []
        self._create_entries()
        if self.entries:
            self.org = self.entries[0].address
        else:
            self.org = None
        if final:
            self._calculate_references()

    def _create_entries(self):
        for block in self.ctl_parser.get_blocks():
            if block.start in self.entry_map:
                entry = self.entry_map[block.start]
                self.entries.append(entry)
                for instruction in entry.instructions:
                    self.instructions[instruction.address] = instruction
                continue
            title = block.title
            if not any(title):
                ctl = block.ctl
                if ctl != 'i' or block.description or block.registers or block.blocks[0].header:
                    name = 'Title-' + ctl
                    title = [format_template(self.config.get(name, ''), name, address=self._address_str(block.start))]
            for sub_block in block.blocks:
                address = sub_block.start
                if sub_block.ctl in 'cBT':
                    base = sub_block.sublengths[0][1]
                    instructions = self.disassembler.disassemble(sub_block.start, sub_block.end, base)
                elif sub_block.ctl in 'bgstuw':
                    sublengths = sub_block.sublengths
                    if sublengths[0][0]:
                        if sub_block.ctl == 's':
                            length = sublengths[0][0]
                        else:
                            length = sum([s[0] for s in sublengths])
                    else:
                        length = sub_block.end - sub_block.start
                    instructions = []
                    while address < sub_block.end:
                        end = min(address + length, sub_block.end)
                        if sub_block.ctl == 't':
                            instructions += self.disassembler.defm_range(address, end, sublengths)
                        elif sub_block.ctl == 'w':
                            instructions += self.disassembler.defw_range(address, end, sublengths)
                        elif sub_block.ctl == 's':
                            instructions += self.disassembler.defs(address, end, sublengths)
                        else:
                            instructions += self.disassembler.defb_range(address, end, sublengths)
                        address += length
                else:
                    instructions = self.disassembler.ignore(sub_block.start, sub_block.end)
                self._add_instructions(sub_block, instructions)

            sub_blocks = []
            i = 0
            while i < len(block.blocks):
                sub_block = block.blocks[i]
                i += 1
                sub_blocks.append(sub_block)
                if sub_block.multiline_comment is not None:
                    end, sub_block.comment = sub_block.multiline_comment
                    while i < len(block.blocks) and block.blocks[i].start < end:
                        next_sub_block = block.blocks[i]
                        sub_block.instructions += next_sub_block.instructions
                        sub_block.end = next_sub_block.end
                        i += 1

            entry = Entry(block.header, title, block.description, block.ctl, sub_blocks,
                          block.registers, block.end_comment, block.footer, block.asm_directives,
                          block.ignoreua_directives)
            self.entry_map[entry.address] = entry
            self.entries.append(entry)
        for i, entry in enumerate(self.entries[1:]):
            self.entries[i].next = entry

    def remove_entry(self, address):
        if address in self.entry_map:
            del self.entry_map[address]

    def _add_instructions(self, sub_block, instructions):
        sub_block.instructions = instructions
        for instruction in instructions:
            self.instructions[instruction.address] = instruction
            instruction.asm_directives = sub_block.asm_directives.get(instruction.address, ())
            instruction.label = None
            for asm_dir in instruction.asm_directives:
                if asm_dir.startswith(AD_LABEL + '='):
                    instruction.label = asm_dir[6:]
                    if instruction.label.startswith('*'):
                        instruction.ctl = '*'
                    break

    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 and (entry.ctl != 'u' or callee.entry == entry) and callee.label != '':
                            callee.add_referrer(entry)

    def _address_str(self, address):
        return self.address_fmt.format(address)