def _write_z80(ram, options, fname): parent_dir = os.path.dirname(fname) if parent_dir and not os.path.isdir(parent_dir): os.makedirs(parent_dir) write_line('Writing {0}'.format(fname)) with open(fname, 'wb') as f: f.write(bytearray(_get_z80(ram, options)))
def write_entry(self, entry): for directive in entry.asm_directives: self._write_entry_asm_directive(entry, directive) address = self.addr_str(entry.address) self._write_entry_ignoreua_directive(entry, TITLE) if BLOCKS in self.elements: if BLOCK_TITLES in self.elements: write_line('{0} {1} {2}'.format(entry.ctl, address, entry.description).rstrip()) else: write_line('{0} {1}'.format(entry.ctl, address)) self._write_entry_ignoreua_directive(entry, DESCRIPTION) if BLOCK_DESC in self.elements: for p in entry.details: write_line('D {0} {1}'.format(address, p)) self._write_entry_ignoreua_directive(entry, REGISTERS) if REGISTERS in self.elements: for reg in entry.registers: if reg.prefix: name = '{}:{}'.format(reg.prefix, reg.name) else: name = reg.name write_line('R {} {} {}'.format(address, name, reg.contents).rstrip()) self.write_body(entry) self._write_entry_ignoreua_directive(entry, END) if BLOCK_COMMENTS in self.elements: for p in entry.end_comment: write_line('E {0} {1}'.format(address, p))
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 _write_body(self, entry, wrote_desc, write_refs, show_text): op_width = max((OP_WIDTH, entry.width())) line_width = op_width + 8 first_block = True for block in entry.blocks: ignoreua_m = block.has_ignoreua_directive(block.start, MID_BLOCK) begun_header = False if not first_block and entry.ctl == 'c' and write_refs > -1: referrers = block.instructions[0].referrers if referrers and (write_refs == 1 or not block.header): if ignoreua_m: self.write_asm_directive(AD_IGNOREUA) self.write_referrers(EREFS_PREFIX, referrers) begun_header = True if block.header: if first_block: if not wrote_desc: self._write_empty_paragraph() if not entry.registers: self._write_empty_paragraph() self.write_comment('') if begun_header: self._write_paragraph_separator() elif ignoreua_m: self.write_asm_directive(AD_IGNOREUA) self.write_paragraphs(block.header) comment_width = max(self.comment_width - line_width, MIN_INSTRUCTION_COMMENT_WIDTH) comment_lines = self._format_block_comment(block, comment_width) self._write_instructions(entry, block, op_width, comment_lines, write_refs, show_text) indent = ' ' * line_width for j in range(len(block.instructions), len(comment_lines)): write_line('{}; {}'.format(indent, comment_lines[j])) first_block = False
def main(args): parser = SkoolKitArgumentParser( usage='\n tap2sna.py [options] INPUT snapshot.z80\n tap2sna.py @FILE', description="Convert a TAP or TZX file (which may be inside a zip archive) into a Z80 snapshot. " "INPUT may be the full URL to a remote zip archive or TAP/TZX file, or the path to a local file. " "Arguments may be read from FILE instead of (or as well as) being given on the command line.", fromfile_prefix_chars='@', add_help=False ) parser.add_argument('args', help=argparse.SUPPRESS, nargs='*') group = parser.add_argument_group('Options') group.add_argument('-d', '--output-dir', dest='output_dir', metavar='DIR', help="Write the snapshot file in this directory.") group.add_argument('-f', '--force', action='store_true', help="Overwrite an existing snapshot.") group.add_argument('-p', '--stack', dest='stack', metavar='STACK', type=integer, help="Set the stack pointer.") group.add_argument('--ram', dest='ram_ops', metavar='OPERATION', action='append', default=[], help="Perform a load, move or poke operation on the memory snapshot being built. " "Do '--ram help' for more information. This option may be used multiple times.") group.add_argument('--reg', dest='reg', metavar='name=value', action='append', default=[], help="Set the value of a register. Do '--reg help' for more information. " "This option may be used multiple times.") group.add_argument('-s', '--start', dest='start', metavar='START', type=integer, help="Set the start address to JP to.") group.add_argument('--state', dest='state', metavar='name=value', action='append', default=[], help="Set a hardware state attribute. Do '--state help' for more information. " "This option may be used multiple times.") group.add_argument('-u', '--user-agent', dest='user_agent', metavar='AGENT', default='', help="Set the User-Agent header.") group.add_argument('-V', '--version', action='version', version='SkoolKit {}'.format(VERSION), help='Show SkoolKit version number and exit.') namespace, unknown_args = parser.parse_known_args(args) if 'help' in namespace.ram_ops: _print_ram_help() return if 'help' in namespace.reg: print_reg_help() return if 'help' in namespace.state: print_state_help() return if unknown_args or len(namespace.args) != 2: parser.exit(2, parser.format_help()) url, z80 = namespace.args if namespace.output_dir: z80 = os.path.join(namespace.output_dir, z80) if namespace.stack is not None: namespace.reg.append('sp={}'.format(namespace.stack)) if namespace.start is not None: namespace.reg.append('pc={}'.format(namespace.start)) if namespace.force or not os.path.isfile(z80): try: make_z80(url, namespace, z80) except Exception as e: raise SkoolKitError("Error while getting snapshot {}: {}".format(os.path.basename(z80), e.args[0] if e.args else e)) else: write_line('{0}: file already exists; use -f to overwrite'.format(z80))
def write_skool(self, write_refs, text): if not self.disassembly.contains_entry_asm_directive(AD_START): self.write_asm_directive(AD_START) if not self.disassembly.contains_entry_asm_directive(AD_ORG): self.write_asm_directive(AD_ORG, self.address_str(self.disassembly.org, False)) for entry_index, entry in enumerate(self.disassembly.entries): if entry_index: write_line('') self._write_entry(entry, write_refs, text)
def write_body(self, entry): if entry.ctl in 'gu': entry_ctl = 'b' else: entry_ctl = entry.ctl first_instruction = entry.instructions[0] if entry_ctl == 'i' and not first_instruction.operation: # Don't write any sub-blocks for an empty 'i' entry return # Split the entry into sections separated by mid-block comments sections = [] for instruction in entry.instructions: mbc = instruction.mid_block_comment if mbc or not sections: sections.append((mbc, [instruction])) else: sections[-1][1].append(instruction) for k, (mbc, instructions) in enumerate(sections): if BLOCK_COMMENTS in self.elements and mbc: first_instruction = instructions[0] if first_instruction.ignoremrcua and self.write_asm_dirs: self._write_asm_directive('{}:{}'.format(AD_IGNOREUA, MID_BLOCK), first_instruction.address) address_str = self.addr_str(first_instruction.address) for paragraph in mbc: write_line('N {} {}'.format(address_str, paragraph)) if SUBBLOCKS in self.elements: sub_blocks = self.get_sub_blocks(instructions) for j, (ctl, instructions) in enumerate(sub_blocks): has_bases = False for instruction in instructions: self._write_instruction_asm_directives(instruction) if instruction.bases: has_bases = True first_instruction = instructions[0] if ctl != 'M' or COMMENTS in self.elements: if ctl == 'M': offset = first_instruction.comment.rowspan + 1 if j + offset < len(sub_blocks): length = sub_blocks[j + offset][1][0].address - first_instruction.address elif k + 1 < len(sections): length = sections[k + 1][1][0].address - first_instruction.address else: length = '' else: length = None comment_text = '' comment = first_instruction.comment if comment and COMMENTS in self.elements: comment_text = comment.text if comment.rowspan > 1 and not comment_text.replace('.', ''): comment_text = '.' + comment_text if comment_text or ctl != entry_ctl or ctl != 'c' or has_bases: self.write_sub_block(ctl, entry_ctl, comment_text, instructions, length)
def _write_blocks(self, blocks, address, footer=False): if NON_ENTRY_BLOCKS in self.elements: prefix = '> ' + address if footer: prefix += ',1' for index, block in enumerate(blocks): if index: write_line(prefix) for line in block: write_line('{} {}'.format(prefix, line))
def clock(operation, prefix, *args, **kwargs): if verbose: if show_timings: write('{0} '.format(prefix)) go = time.time() else: write_line(prefix) result = operation(*args, **kwargs) if verbose and show_timings: notify('({0:0.2f}s)'.format(time.time() - go)) return result
def write_comment(self, text, paragraphs=False): if isinstance(text, str): lines = [text] elif len(text) == 1: lines = self.wrap(text[0]) else: lines = self._trim_lines(text[:]) for line in lines: if line: write_line('; ' + line) elif paragraphs: self._write_paragraph_separator() else: write_line(';')
def _write_entry(self, entry, write_refs, show_text): if entry.header: for line in entry.header: write_line(line) write_line('') self.write_asm_directives(*entry.asm_directives) if entry.has_ignoreua_directive(TITLE): self.write_asm_directives(AD_IGNOREUA) if entry.ctl == 'i' and entry.blocks[-1].end >= 65536 and not entry.has_title and all([b.ctl == 'i' for b in entry.blocks]): return for block in entry.bad_blocks: addr1 = self.address_str(block.instructions[-1].address, False) addr2 = self.address_str(block.end, False) warn('Instruction at {} overlaps the following instruction at {}'.format(addr1, addr2)) if entry.has_title: self.write_comment(entry.title) wrote_desc = self._write_entry_description(entry, write_refs) wrote_desc = self._write_registers(entry, wrote_desc) else: wrote_desc = False self._write_body(entry, wrote_desc, write_refs, show_text and entry.ctl != 't') if entry.has_ignoreua_directive(END): self.write_asm_directives(AD_IGNOREUA) self.write_paragraphs(entry.end_comment) if entry.footer: write_line('') for line in entry.footer: write_line(line)
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 main(args): parser = argparse.ArgumentParser( usage='snapmod.py [options] in.z80 [out.z80]', description="Modify a 48K Z80 snapshot.", add_help=False ) parser.add_argument('infile', help=argparse.SUPPRESS, nargs='?') parser.add_argument('outfile', help=argparse.SUPPRESS, nargs='?') group = parser.add_argument_group('Options') group.add_argument('-f', '--force', dest='force', action='store_true', help="Overwrite an existing snapshot.") group.add_argument('-m', '--move', dest='moves', metavar='src,size,dest', action='append', default=[], help='Move a block of bytes of the given size from src to dest. This option may be used multiple times.') group.add_argument('-p', '--poke', dest='pokes', metavar='a[-b[-c]],[^+]v', action='append', default=[], help="POKE N,v for N in {a, a+c, a+2c..., b}. " "Prefix 'v' with '^' to perform an XOR operation, or '+' to perform an ADD operation. " "This option may be used multiple times.") group.add_argument('-r', '--reg', dest='reg', metavar='name=value', action='append', default=[], help="Set the value of a register. Do '--reg help' for more information. This option may be used multiple times.") group.add_argument('-s', '--state', dest='state', metavar='name=value', action='append', default=[], help="Set a hardware state attribute. Do '--state help' for more information. This option may be used multiple times.") group.add_argument('-V', '--version', action='version', version='SkoolKit {}'.format(VERSION), help='Show SkoolKit version number and exit.') namespace, unknown_args = parser.parse_known_args(args) if 'help' in namespace.reg: print_reg_help('r') return if 'help' in namespace.state: print_state_help('s') return infile = namespace.infile outfile = namespace.outfile if unknown_args or infile is None: parser.exit(2, parser.format_help()) if not infile.lower().endswith('.z80'): raise SkoolKitError('Unrecognised input snapshot type') if outfile is None: outfile = infile if namespace.force or not os.path.isfile(outfile): run(infile, namespace, outfile) else: write_line('{}: file already exists; use -f to overwrite'.format(outfile))
def _write_lines(self, lines, ctl=None, address=None, grouped=False): if ctl: write_line('{} {}'.format(ctl, address)) if grouped: for index, group in enumerate(lines): for line_no, line in enumerate(group): if line_no and index < len(lines) - 1: write_line((': ' + line).rstrip()) else: write_line(('. ' + line).rstrip()) else: for line in lines: write_line(('. ' + line).rstrip())
def _write_registers(self, entry, wrote_desc): registers = [] for spec in entry.registers: if len(spec) == 1: reg, desc = spec[0].partition(' ')[::2] if reg: registers.append((reg, desc)) elif self._trim_lines(spec): registers.append(('', spec)) entry.registers = registers if registers: max_indent = max(r[0].find(':') for r in registers) if not wrote_desc: self._write_empty_paragraph() wrote_desc = True self.write_comment('') if entry.has_ignoreua_directive(REGISTERS): self.write_asm_directives(AD_IGNOREUA) for reg, desc in registers: if reg: reg = reg.rjust(max_indent + len(reg) - reg.find(':')) desc_indent = len(reg) + 1 desc_lines = wrap(desc, max(self.comment_width - desc_indent, MIN_COMMENT_WIDTH)) or [''] desc_prefix = '.'.ljust(desc_indent) write_line('; {} {}'.format(reg, desc_lines[0]).rstrip()) for line in desc_lines[1:]: write_line('; {}{}'.format(desc_prefix, line).rstrip()) else: for line in desc: write_line('; {}'.format(line).rstrip()) return wrote_desc
def _write_instructions(self, entry, block, op_width, comment_lines, write_refs, show_text): index = 0 for instruction in block.instructions: ctl = instruction.ctl or ' ' address = instruction.address operation = instruction.operation if block.comment: comment = comment_lines[index] elif show_text and entry.ctl != 't': comment = self.to_ascii(instruction.bytes) else: comment = '' if index > 0 and entry.ctl == 'c' and ctl == '*' and write_refs > -1: self.write_referrers(EREFS_PREFIX, instruction.referrers) self.write_asm_directives(*instruction.asm_directives) if block.has_ignoreua_directive(instruction.address, INSTRUCTION): self.write_asm_directives(AD_IGNOREUA) if entry.ctl == 'c' or comment: write_line(('{}{} {} ; {}'.format(ctl, self.address_str(address), operation.ljust(op_width), comment)).rstrip()) else: write_line(('{}{} {}'.format(ctl, self.address_str(address), operation)).rstrip()) index += 1
def show_search_dirs(): write(SEARCH_DIRS_MSG) prefix = '- ' write_line(prefix + 'The directory that contains the skool or ref file named on the command line') for search_dir in SEARCH_DIRS: if not search_dir: search_dir = 'The current working directory' elif not os.path.split(search_dir)[0]: search_dir = os.path.join('.', search_dir) else: search_dir = os.path.normpath(search_dir) write_line(prefix + search_dir) write_line(prefix + 'Any other directories specified by the -S/--search option') sys.exit(0)
def _write_registers(self, entry): self.write_comment('') if entry.has_ignoreua_directive(REGISTERS): self.write_asm_directive(AD_IGNOREUA) max_indent = max([reg.find(':') for reg, desc in entry.registers]) for reg, desc in entry.registers: reg = reg.rjust(max_indent + len(reg) - reg.find(':')) if desc: desc_indent = len(reg) + 1 desc_lines = wrap(desc, max(self.comment_width - desc_indent, MIN_COMMENT_WIDTH)) write_line('; {} {}'.format(reg, desc_lines[0])) desc_prefix = '.'.ljust(desc_indent) for line in desc_lines[1:]: write_line('; {}{}'.format(desc_prefix, line)) else: write_line('; {}'.format(reg))
def _write_instructions(self, entry, block, op_width, write_refs): for index, instruction in enumerate(block.instructions): ctl = instruction.ctl or ' ' address = instruction.address operation = instruction.operation comment = instruction.comment.pop(0) if index > 0 and entry.ctl == 'c' and ctl == '*' and write_refs: self.write_referrers(instruction.referrers) self.write_asm_directives(*instruction.asm_directives) if block.has_ignoreua_directive(instruction.address, INSTRUCTION): self.write_asm_directives(AD_IGNOREUA) if entry.ctl in self.config['Semicolons'] or comment is not None: write_line(('{}{} {:{}} ; {}'.format(ctl, self.address_str(address), operation, op_width, comment or '')).rstrip()) else: write_line(('{}{} {}'.format(ctl, self.address_str(address), operation)).rstrip()) for comment in instruction.comment: write_line(' {:{}} ; {}'.format('', op_width, comment).rstrip())
def write_entry(self, entry): address = self.addr_str(entry.address) self._write_blocks(entry.header, address) for directive in entry.asm_directives: self._write_asm_directive(directive, entry.address) self._write_entry_ignoreua_directive(entry, TITLE) if BLOCKS in self.elements: if BLOCK_TITLES in self.elements and not self.keep_lines: write_line('{} {} {}'.format(entry.ctl, address, entry.title).rstrip()) else: write_line('{0} {1}'.format(entry.ctl, address)) if self.keep_lines: self._write_lines(entry.title) self._write_entry_ignoreua_directive(entry, DESCRIPTION) if entry.description and BLOCK_DESC in self.elements: self._write_block_comments(entry.description, 'D', address) self._write_entry_ignoreua_directive(entry, REGISTERS) if entry.registers and REGISTERS in self.elements: if self.keep_lines: self._write_lines(entry.registers[0].contents, 'R', address) else: for reg in entry.registers: if reg.prefix: name = '{}:{}'.format(reg.prefix, reg.name) else: name = reg.name write_line('R {} {} {}'.format(address, name.join(reg.delimiters), reg.contents).rstrip()) self.write_body(entry) self._write_entry_ignoreua_directive(entry, END) if entry.end_comment and BLOCK_COMMENTS in self.elements: self._write_block_comments(entry.end_comment, 'E', address) self._write_blocks(entry.footer, address, True)
def write_entry(self, entry): address = self.addr_str(entry.address) self._write_blocks(entry.header, address) for directive in entry.asm_directives: self._write_entry_asm_directive(entry, directive) self._write_entry_ignoreua_directive(entry, TITLE) if BLOCKS in self.elements: if BLOCK_TITLES in self.elements and not self.keep_lines: write_line('{} {} {}'.format(entry.ctl, address, entry.title).rstrip()) else: write_line('{0} {1}'.format(entry.ctl, address)) if self.keep_lines: self._write_lines(entry.title) self._write_entry_ignoreua_directive(entry, DESCRIPTION) if entry.description and BLOCK_DESC in self.elements: self._write_block_comments(entry.description, 'D', address) self._write_entry_ignoreua_directive(entry, REGISTERS) if entry.registers and REGISTERS in self.elements: if self.keep_lines: self._write_lines(entry.registers[0].contents, 'R', address) else: for reg in entry.registers: if reg.prefix: name = '{}:{}'.format(reg.prefix, reg.name) else: name = reg.name write_line('R {} {} {}'.format(address, name, reg.contents).rstrip()) self.write_body(entry) self._write_entry_ignoreua_directive(entry, END) if entry.end_comment and BLOCK_COMMENTS in self.elements: self._write_block_comments(entry.end_comment, 'E', address) self._write_blocks(entry.footer, address, True)
def _write_instructions(self, entry, block, op_width, write_refs): for index, instruction in enumerate(block.instructions): ctl = instruction.ctl or ' ' address = instruction.address operation = instruction.operation comment = instruction.comment.pop(0) if index > 0 and entry.ctl == 'c' and ctl == '*' and write_refs: self.write_referrers(instruction.referrers) self.write_asm_directives(*instruction.asm_directives) self.write_asm_directives( block.get_ignoreua_directive(INSTRUCTION, instruction.address)) if entry.ctl in self.config['Semicolons'] or comment is not None: write_line(('{}{} {:{}} ; {}'.format(ctl, self.address_str(address), operation, op_width, comment or '')).rstrip()) else: write_line(('{}{} {}'.format(ctl, self.address_str(address), operation)).rstrip()) for comment in instruction.comment: write_line(' {:{}} ; {}'.format('', op_width, comment).rstrip())
def _write_asm_directive(self, directive, address): write_line('@ {} {}'.format(self.addr_str(address), directive))
def write(self): for entry in self.parser.memory_map: self.write_entry(entry) if self.parser.end_address < 65536: write_line('i {}'.format(self.addr_str(self.parser.end_address)))
def write_asm_directives(self, *directives): for directive in directives: write_line('@' + directive)
def write_comment(self, text): if text: for line in self.wrap(text): write_line('; {0}'.format(line)) else: write_line(';')
def write_skool(self, write_refs, text): for entry_index, entry in enumerate(self.disassembly.entries): if entry_index: write_line('') self._write_entry(entry, write_refs, text)
def _write_block_comments(self, comments, ctl, address): if self.keep_lines: self._write_lines(comments, ctl, address) else: for p in comments: write_line('{} {} {}'.format(ctl, address, p))
def _write_asm_directive(self, directive, address): if self.write_asm_dirs: write_line('@ {} {}'.format(self.addr_str(address), directive))
def _write_z80(ram, options, fname): parent_dir = os.path.dirname(fname) if parent_dir and not os.path.isdir(parent_dir): os.makedirs(parent_dir) write_line('Writing {0}'.format(fname)) write_z80v3(fname, ram, options.reg, options.state)
def main(args): parser = SkoolKitArgumentParser( usage='\n tap2sna.py [options] INPUT snapshot.z80\n tap2sna.py @FILE', description= "Convert a TAP or TZX file (which may be inside a zip archive) into a Z80 snapshot. " "INPUT may be the full URL to a remote zip archive or TAP/TZX file, or the path to a local file. " "Arguments may be read from FILE instead of (or as well as) being given on the command line.", fromfile_prefix_chars='@', add_help=False) parser.add_argument('args', help=argparse.SUPPRESS, nargs='*') group = parser.add_argument_group('Options') group.add_argument('-d', '--output-dir', dest='output_dir', metavar='DIR', help="Write the snapshot file in this directory.") group.add_argument('-f', '--force', action='store_true', help="Overwrite an existing snapshot.") group.add_argument('-p', '--stack', dest='stack', metavar='STACK', type=integer, help="Set the stack pointer.") group.add_argument( '--ram', dest='ram_ops', metavar='OPERATION', action='append', default=[], help= "Perform a load, move or poke operation on the memory snapshot being built. " "Do '--ram help' for more information. This option may be used multiple times." ) group.add_argument( '--reg', dest='reg', metavar='name=value', action='append', default=[], help= "Set the value of a register. Do '--reg help' for more information. " "This option may be used multiple times.") group.add_argument('-s', '--start', dest='start', metavar='START', type=integer, help="Set the start address to JP to.") group.add_argument( '--state', dest='state', metavar='name=value', action='append', default=[], help= "Set a hardware state attribute. Do '--state help' for more information. " "This option may be used multiple times.") group.add_argument('-V', '--version', action='version', version='SkoolKit {}'.format(VERSION), help='Show SkoolKit version number and exit.') namespace, unknown_args = parser.parse_known_args(args) if 'help' in namespace.ram_ops: _print_ram_help() return if 'help' in namespace.reg: print_reg_help() return if 'help' in namespace.state: print_state_help() return if unknown_args or len(namespace.args) != 2: parser.exit(2, parser.format_help()) url, z80 = namespace.args if namespace.output_dir: z80 = os.path.join(namespace.output_dir, z80) if namespace.stack is not None: namespace.reg.append('sp={}'.format(namespace.stack)) if namespace.start is not None: namespace.reg.append('pc={}'.format(namespace.start)) if namespace.force or not os.path.isfile(z80): try: make_z80(url, namespace, z80) except Exception as e: raise SkoolKitError("Error while getting snapshot {0}: {1}".format( os.path.basename(z80), e.args[0])) else: write_line('{0}: file already exists; use -f to overwrite'.format(z80))
def write(self, min_address=0, max_address=65536): for line in self._parse_skool(min_address, max_address): write_line(str(line))
def notify(notice): if verbose: write_line(notice)
def write_body(self, entry): if entry.ctl in 'gu': entry_ctl = 'b' else: entry_ctl = entry.ctl first_instruction = entry.instructions[0] if entry_ctl == 'i' and not first_instruction.operation: # Don't write any sub-blocks for an empty 'i' entry return # Split the entry into sections separated by mid-block comments sections = [] for instruction in entry.instructions: mbc = instruction.mid_block_comment if mbc or not sections: sections.append((mbc, [instruction])) else: sections[-1][1].append(instruction) for k, (mbc, instructions) in enumerate(sections): if BLOCK_COMMENTS in self.elements and mbc: first_instruction = instructions[0] if first_instruction.ignoremrcua and self.write_asm_dirs: self._write_asm_directive( '{}:{}'.format(AD_IGNOREUA, MID_BLOCK), first_instruction.address) address_str = self.addr_str(first_instruction.address) for paragraph in mbc: write_line('N {} {}'.format(address_str, paragraph)) if SUBBLOCKS in self.elements: sub_blocks = self.get_sub_blocks(instructions) for j, (ctl, instructions) in enumerate(sub_blocks): has_bases = False for instruction in instructions: self._write_instruction_asm_directives(instruction) if instruction.bases: has_bases = True first_instruction = instructions[0] if ctl != 'M' or COMMENTS in self.elements: if ctl == 'M': offset = first_instruction.comment.rowspan + 1 if j + offset < len(sub_blocks): length = sub_blocks[j + offset][1][ 0].address - first_instruction.address elif k + 1 < len(sections): length = sections[k + 1][1][ 0].address - first_instruction.address else: length = '' else: length = None comment_text = '' comment = first_instruction.comment if comment and COMMENTS in self.elements: comment_text = comment.text if comment.rowspan > 1 and not comment_text.replace( '.', ''): comment_text = '.' + comment_text if comment_text or ctl != entry_ctl or ctl != 'c' or has_bases: self.write_sub_block(ctl, entry_ctl, comment_text, instructions, length)
def write_skool(self, min_address=0, max_address=65536): for line in self._parse_sft(min_address, max_address): write_line(str(line))
def write_asm_directive(self, directive, value=None): if value is None: suffix = '' else: suffix = '={0}'.format(value) write_line('@{}{}'.format(directive, suffix))
def main(args): parser = argparse.ArgumentParser( usage='snapmod.py [options] in.z80 [out.z80]', description="Modify a 48K Z80 snapshot.", add_help=False) parser.add_argument('infile', help=argparse.SUPPRESS, nargs='?') parser.add_argument('outfile', help=argparse.SUPPRESS, nargs='?') group = parser.add_argument_group('Options') group.add_argument('-f', '--force', dest='force', action='store_true', help="Overwrite an existing snapshot.") group.add_argument( '-m', '--move', dest='moves', metavar='src,size,dest', action='append', default=[], help= 'Move a block of bytes of the given size from src to dest. This option may be used multiple times.' ) group.add_argument( '-p', '--poke', dest='pokes', metavar='a[-b[-c]],[^+]v', action='append', default=[], help="POKE N,v for N in {a, a+c, a+2c..., b}. " "Prefix 'v' with '^' to perform an XOR operation, or '+' to perform an ADD operation. " "This option may be used multiple times.") group.add_argument( '-r', '--reg', dest='reg', metavar='name=value', action='append', default=[], help= "Set the value of a register. Do '--reg help' for more information. This option may be used multiple times." ) group.add_argument( '-s', '--state', dest='state', metavar='name=value', action='append', default=[], help= "Set a hardware state attribute. Do '--state help' for more information. This option may be used multiple times." ) group.add_argument('-V', '--version', action='version', version='SkoolKit {}'.format(VERSION), help='Show SkoolKit version number and exit.') namespace, unknown_args = parser.parse_known_args(args) if 'help' in namespace.reg: _print_reg_help() return if 'help' in namespace.state: _print_state_help() return infile = namespace.infile outfile = namespace.outfile if unknown_args or infile is None: parser.exit(2, parser.format_help()) if not infile.lower().endswith('.z80'): raise SkoolKitError('Unrecognised input snapshot type') if outfile is None: outfile = infile if namespace.force or not os.path.isfile(outfile): run(infile, namespace, outfile) else: write_line( '{}: file already exists; use -f to overwrite'.format(outfile))