def main(args): parser = argparse.ArgumentParser( usage='bin2tap.py [options] FILE [file.tap]', description="Convert a binary (raw memory) file or a SNA, SZX or Z80 snapshot into a TAP file. " "FILE may be a regular file, or '-' to read a binary file from standard input.", 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('-c', '--clear', dest='clear', metavar='N', type=int, help="Use a 'CLEAR N' command in the BASIC loader and leave the stack pointer alone") group.add_argument('-e', '--end', dest='end', metavar='ADDR', type=int, default=65536, help="Set the end address when reading a snapshot") group.add_argument('-o', '--org', dest='org', metavar='ORG', type=int, help="Set the origin address (default: 16384 for a snapshot, otherwise 65536 minus the length of FILE)") group.add_argument('-p', '--stack', dest='stack', metavar='STACK', type=int, help="Set the stack pointer (default: ORG)") group.add_argument('-s', '--start', dest='start', metavar='START', type=int, help="Set the start address to JP to (default: ORG)") group.add_argument('-S', '--screen', dest='screen', metavar='FILE', help="Add a loading screen to the TAP file; FILE may be a snapshot or a 6912-byte SCR file") group.add_argument('-t', '--tapfile', dest='tapfile', metavar='TAPFILE', help="Set the TAP filename") 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) infile = namespace.infile if unknown_args or infile is None: parser.exit(2, parser.format_help()) if infile.lower().endswith(('.sna', '.szx', '.z80')): org = namespace.org or 16384 if org >= namespace.end: raise SkoolKitError('End address must be greater than {}'.format(org)) ram = get_snapshot(infile)[org:namespace.end] else: ram = read_bin_file(infile, 49152) if len(ram) == 0: raise SkoolKitError('{} is empty'.format(infile)) org = namespace.org or 65536 - len(ram) clear = namespace.clear start = namespace.start or org stack = namespace.stack or org tapfile = namespace.outfile or namespace.tapfile if tapfile is None: if infile.lower().endswith(('.bin', '.sna', '.szx', '.z80')): prefix = infile[:-4] elif infile == '-': prefix = 'program' else: prefix = infile tapfile = prefix + ".tap" scr = namespace.screen if scr is not None: if scr.lower().endswith(('.sna', '.szx', '.z80')): scr = get_snapshot(scr)[16384:23296] else: scr = read_bin_file(scr, 6912) run(ram, clear, org, start, stack, tapfile, scr)
def run(infile, outfile, options): if options.binary or options.org is not None: snapshot = read_bin_file(infile, 49152) if options.org is None: org = 65536 - len(snapshot) else: org = options.org snapshot = [0] * org + list(snapshot) + [0] * (65536 - org - len(snapshot)) elif infile[-4:].lower() == '.scr': scr = read_bin_file(infile, 6912) snapshot = [0] * 65536 snapshot[16384:16384 + len(scr)] = scr elif infile[-4:].lower() in ('.sna', '.szx', '.z80'): snapshot = get_snapshot(infile) else: try: snapshot = BinWriter(infile, fix_mode=options.fix_mode).snapshot except SkoolKitError: raise except: raise SkoolKitError( 'Unable to parse {} as a skool file'.format(infile)) for spec in options.moves: move(snapshot, spec) for spec in options.pokes: poke(snapshot, spec) if options.macro is not None: match = re.match('(#?)(FONT|SCR|UDG|UDGARRAY)([^A-Z]|$)', options.macro) if match: macro = match.group(2) try: frame = MACROS[macro](snapshot, options.macro[match.end(2):]) except skoolmacro.MacroParsingError as e: raise SkoolKitError('Invalid #{} macro: {}'.format( macro, e.args[0])) else: raise SkoolKitError('Macro must be #FONT, #SCR, #UDG or #UDGARRAY') else: (x, y), (w, h) = options.origin, options.size frame = Frame(scr_udgs(snapshot, x, y, w, h), options.scale) if options.invert: for row in frame.udgs: for udg in row: if udg.attr & 128: udg.data = [b ^ 255 for b in udg.data] udg.attr &= 127 flip_udgs(frame.udgs, options.flip) rotate_udgs(frame.udgs, options.rotate) _write_image(frame, outfile, options.animated)
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 _test_z80(self, options, header, exp_header=None, ram=None, exp_ram=None, version=3, compress=False): if exp_header is None: exp_header = header if ram is None: ram = [0] * 49152 if exp_ram is None: exp_ram = ram infile = self.write_z80_file(header, ram, version, compress) outfile = '{}-out.z80'.format(infile[:-4]) self.tempfiles.append(outfile) output, error = self.run_snapmod('{} {} {}'.format( options, infile, outfile)) self.assertEqual([], output) self.assertEqual(error, '') z80_header = list(read_bin_file(outfile, len(exp_header))) self.assertEqual(exp_header, z80_header) z80_ram = get_snapshot(outfile)[16384:] self.assertEqual(exp_ram, z80_ram)
def run(infile, outfile, options): x, y = options.origin w = min(32 - x, options.size[0]) h = min(24 - y, options.size[1]) if infile[-4:].lower() == '.scr': scr = read_bin_file(infile, 6912) snapshot = [0] * 65536 snapshot[16384:16384 + len(scr)] = scr else: snapshot = get_snapshot(infile) for spec in options.pokes: poke(snapshot, spec) scrshot = _get_screenshot(snapshot[16384:23296], x, y, w, h) if options.invert: for row in scrshot: for udg in row: if udg.attr & 128: udg.data = [b^255 for b in udg.data] udg.attr &= 127 flip_udgs(scrshot, options.flip) rotate_udgs(scrshot, options.rotate) _write_image(scrshot, outfile, options.scale, options.animated)
def _read_z80(z80file): data = read_bin_file(z80file) if get_word(data, 6) > 0: header = data[:30] else: header_len = 32 + get_word(data, 30) header = data[:header_len] return list(header), get_snapshot(z80file)
def make_snapshot(fname, org=None, start=16384, end=65536, page=None): if fname[-4:].lower() in ('.sna', '.szx', '.z80'): return get_snapshot(fname, page), max(16384, start), end ram = read_bin_file(fname, 65536) if org is None: org = 65536 - len(ram) mem = [0] * 65536 mem[org:org + len(ram)] = ram return mem, max(org, start), min(end, org + len(ram))
def make_snapshot(fname, org, start=0, end=65536, page=None): snapshot_reader = get_snapshot_reader() if snapshot_reader.can_read(fname): return snapshot_reader.get_snapshot(fname, page), max(16384, start), end ram = read_bin_file(fname, 65536) if org is None: org = 65536 - len(ram) mem = [0] * 65536 mem[org:org + len(ram)] = ram return mem, max(org, start), min(end, org + len(ram))
def test_option_f_clobber_input_file(self): header = [0] * 30 header[6] = 1 # PC > 0 exp_header = header[:] exp_header[12] |= 34 # RAM block compressed, BORDER 1 ram = [0] * 49152 infile = self.write_z80_file(header, ram, 1) output, error = self.run_snapmod('-f -s border=1 {}'.format(infile)) self.assertEqual([], output) self.assertEqual(error, '') z80_header = list(read_bin_file(infile, len(exp_header))) self.assertEqual(exp_header, z80_header)
def test_option_force_clobber_output_file(self): header = [0] * 30 header[6] = 1 # PC > 0 exp_header = header[:] exp_header[12] |= 36 # RAM block compressed, BORDER 2 ram = [0] * 49152 infile = self.write_z80_file(header, ram, 1) outfile = self.write_bin_file(suffix='.z80') output, error = self.run_snapmod('--force -s border=2 {} {}'.format(infile, outfile)) self.assertEqual([], output) self.assertEqual(error, '') z80_header = list(read_bin_file(outfile, len(exp_header))) self.assertEqual(exp_header, z80_header)
def _test_z80(self, options, header, exp_header, ram=None, exp_ram=None, version=3, compress=False): if ram is None: ram = [0] * 49152 if exp_ram is None: exp_ram = ram infile = self.write_z80_file(header, ram, version, compress) outfile = '{}-out.z80'.format(infile[:-4]) self.tempfiles.append(outfile) output, error = self.run_snapmod('{} {} {}'.format(options, infile, outfile)) self.assertEqual(output, '') self.assertEqual(error, '') z80_header = list(read_bin_file(outfile, len(exp_header))) self.assertEqual(exp_header, z80_header) z80_ram = get_snapshot(outfile)[16384:] self.assertEqual(exp_ram, z80_ram)
def get_snapshot(fname, page=None): ext = fname[-4:].lower() if ext not in ('.sna', '.z80', '.szx'): raise SnapshotError("{0}: Unknown file type '{1}'".format(fname, ext[1:])) data = read_bin_file(fname) if ext == '.sna': ram = _read_sna(data, page) elif ext == '.z80': ram = _read_z80(data, page) elif ext == '.szx': ram = _read_szx(data, page) if len(ram) != 49152: raise SnapshotError("RAM size is {0}".format(len(ram))) mem = [0] * 16384 mem.extend(ram) return mem
def run(infile, outfile, options): ram = list(read_bin_file(infile, 49152)) org = options.org or 65536 - len(ram) ram = [0] * (org - 16384) + ram + [0] * (65536 - org - len(ram)) if options.start is None: start = org else: start = options.start if options.stack is None: stack = org else: stack = options.stack parent_dir = os.path.dirname(outfile) if parent_dir and not os.path.isdir(parent_dir): os.makedirs(parent_dir) with open(outfile, 'wb') as f: f.write(bytearray(_get_z80(ram, stack, start, options.border)))
def get_snapshot(fname, page=None): ext = fname[-4:].lower() if ext not in ('.sna', '.z80', '.szx'): raise SnapshotError("{0}: Unknown file type '{1}'".format( fname, ext[1:])) data = read_bin_file(fname) if ext == '.sna': ram = _read_sna(data, page) elif ext == '.z80': ram = _read_z80(data, page) elif ext == '.szx': ram = _read_szx(data, page) if len(ram) != 49152: raise SnapshotError("RAM size is {0}".format(len(ram))) mem = [0] * 16384 mem.extend(ram) return mem
def run(infile, outfile, options): ram = list(read_bin_file(infile, 49152)) org = options.org or 65536 - len(ram) ram = [0] * (org - 16384) + ram + [0] * (65536 - org - len(ram)) if options.start is None: start = org else: start = options.start if options.stack is None: stack = org else: stack = options.stack parent_dir = os.path.dirname(outfile) if parent_dir and not os.path.isdir(parent_dir): os.makedirs(parent_dir) registers = ('sp={}'.format(stack), 'pc={}'.format(start)) state = ('border={}'.format(options.border), ) write_z80v3(outfile, ram, registers, state)
def run(infile, outfile, options): ram = list(read_bin_file(infile, 49152)) org = options.org or 65536 - len(ram) snapshot = [0] * org + ram + [0] * (65536 - org - len(ram)) if options.start is None: start = org else: start = options.start if options.stack is None: stack = org else: stack = options.stack for spec in options.pokes: poke(snapshot, spec) parent_dir = os.path.dirname(outfile) if parent_dir and not os.path.isdir(parent_dir): os.makedirs(parent_dir) registers = ['sp={}'.format(stack), 'pc={}'.format(start)] + options.reg state = ['border={}'.format(options.border)] + options.state write_z80v3(outfile, snapshot[16384:], registers, state)
def run(snafile, options): # Read the snapshot file if snafile[-4:].lower() in ('.sna', '.szx', '.z80'): snapshot = get_snapshot(snafile, options.page) start = max(START, options.start) else: ram = read_bin_file(snafile) if options.org is None: org = 65536 - len(ram) else: org = options.org snapshot = [0] * org snapshot.extend(ram) start = max(org, options.start) end = min(options.end, len(snapshot)) # Pad out the end of the snapshot to avoid disassembly errors when an # instruction crosses the 64K boundary snapshot += [0] * (65539 - len(snapshot)) if options.sftfile: # Use a skool file template info('Using skool file template: {}'.format(options.sftfile)) writer = SftParser(snapshot, options.sftfile, options.zfill, options.asm_hex, options.asm_lower) writer.write_skool(options.start, options.end) return if options.genctlfile: # Generate a control file ctls = generate_ctls(snapshot, start, end, options.code_map) write_ctl(options.genctlfile, ctls, options.ctl_hex) ctl_parser = CtlParser(ctls) elif options.ctlfile: # Use a control file info('Using control file: {}'.format(options.ctlfile)) ctl_parser = CtlParser() ctl_parser.parse_ctl(options.ctlfile, options.start, options.end) else: ctl_parser = CtlParser({start: 'c', end: 'i'}) writer = SkoolWriter(snapshot, ctl_parser, options) writer.write_skool(options.write_refs, options.text)
def run(snafile, options, config): # Read the snapshot file if snafile[-4:].lower() in ('.sna', '.szx', '.z80'): snapshot = get_snapshot(snafile, options.page) start = max(START, options.start) else: ram = read_bin_file(snafile, 65536) if options.org is None: org = 65536 - len(ram) else: org = options.org snapshot = [0] * org snapshot.extend(ram) start = max(org, options.start) end = min(options.end, len(snapshot)) snapshot += [0] * (65536 - len(snapshot)) if options.sftfile: # Use a skool file template info('Using skool file template: {}'.format(options.sftfile)) writer = SftParser(snapshot, options.sftfile, options.zfill, options.base == 16, options.case == 1) writer.write_skool(options.start, options.end) return if options.genctlfile: # Generate a control file ctls = generate_ctls(snapshot, start, end, options.code_map) write_ctl(options.genctlfile, ctls, options.ctl_hex) ctl_parser = CtlParser(ctls) elif options.ctlfile: # Use a control file info('Using control file: {}'.format(options.ctlfile)) ctl_parser = CtlParser() ctl_parser.parse_ctl(options.ctlfile, options.start, options.end) else: ctl_parser = CtlParser({start: 'c', end: 'i'}) writer = SkoolWriter(snapshot, ctl_parser, options, config) writer.write_skool(options.write_refs, options.text)
def _analyse_sna(snafile): sna = read_bin_file(snafile, 147488) if len(sna) not in (49179, 131103, 147487): raise SkoolKitError('{}: not a SNA file'.format(snafile)) is128 = len(sna) > 49179 print('RAM: {}K'.format(128 if is128 else 48)) print('Interrupts: {}abled'.format('en' if sna[19] & 4 else 'dis')) print('Interrupt mode: {}'.format(sna[25])) print('Border: {}'.format(sna[26] & 7)) # Print register contents reg = Registers() reg.i = sna[0] reg.hl2 = get_word(sna, 1) reg.de2 = get_word(sna, 3) reg.bc2 = get_word(sna, 5) reg.f2 = sna[7] reg.a2 = sna[8] reg.hl = get_word(sna, 9) reg.de = get_word(sna, 11) reg.bc = get_word(sna, 13) reg.iy = get_word(sna, 15) reg.ix = get_word(sna, 17) reg.r = sna[20] reg.f = sna[21] reg.a = sna[22] reg.sp = get_word(sna, 23) if is128: reg.pc = get_word(sna, 49179) else: reg.pc = get_word(sna, reg.sp - 16357) print('Registers:') for line in reg.get_lines(): print(' ' + line) if is128: _print_ram_banks(sna)
def _analyse_szx(szxfile): szx = read_bin_file(szxfile) magic = _get_block_id(szx, 0) if magic != 'ZXST': raise SkoolKitError("{} is not an SZX file".format(szxfile)) print('Version: {}.{}'.format(szx[4], szx[5])) machine_id = szx[6] print('Machine: {}'.format(SZX_MACHINES.get(machine_id, 'Unknown'))) variables = {'chMachineId': machine_id} i = 8 while i < len(szx): block_id = _get_block_id(szx, i) block_size = get_dword(szx, i + 4) i += 8 print('{}: {} bytes'.format(block_id, block_size)) printer = SZX_BLOCK_PRINTERS.get(block_id) if printer: for line in printer(szx[i:i + block_size], variables): print(" " + line) i += block_size
def get_snapshot(fname, page=None): """ Read a snapshot file and produce a 65536-element list of byte values. :param fname: The snapshot filename. :param page: The page number to map to addresses 49152-65535 (C000-FFFF). This is relevant only when reading a 128K snapshot file. :return: A 65536-element list of byte values. """ if not can_read(fname): raise SnapshotError("{}: Unknown file type".format(fname)) data = read_bin_file(fname) ext = fname[-4:].lower() if ext == '.sna': ram = _read_sna(data, page) elif ext == '.z80': ram = _read_z80(data, page) elif ext == '.szx': ram = _read_szx(data, page) if len(ram) != 49152: raise SnapshotError("RAM size is {0}".format(len(ram))) mem = [0] * 16384 mem.extend(ram) return mem
def _analyse_z80(z80file): data = read_bin_file(z80file) # Extract header and RAM block(s) if get_word(data, 6) > 0: version = 1 header = data[:30] ram_blocks = data[30:] else: header_len = 32 + get_word(data, 30) header = data[:header_len] ram_blocks = data[header_len:] version = 2 if header_len == 55 else 3 # Print file name, version, machine, interrupt status, border, port $7FFD print('Z80 file: {}'.format(z80file)) print('Version: {}'.format(version)) bank = None if version == 1: machine = '48K Spectrum' block_dict = BLOCK_ADDRESSES_48K else: machine_dict = V2_MACHINES if version == 2 else V3_MACHINES machine_id = header[34] index = header[37] // 128 machine_spec = machine_dict.get( machine_id, MACHINES.get(machine_id, ('Unknown', 'Unknown', True))) machine = machine_spec[index] if machine_spec[2]: block_dict = BLOCK_ADDRESSES_48K else: block_dict = BLOCK_ADDRESSES_128K bank = header[35] & 7 print('Machine: {}'.format(machine)) print('Interrupts: {}abled'.format('en' if header[28] else 'dis')) print('Interrupt mode: {}'.format(header[29] & 3)) print('Border: {}'.format((header[12] // 2) & 7)) if bank is not None: print( 'Port $7FFD: {} - bank {} (block {}) paged into 49152-65535 C000-FFFF' .format(header[35], bank, bank + 3)) # Print register contents reg = Registers() reg.a = header[0] reg.f = header[1] reg.bc = get_word(header, 2) reg.hl = get_word(header, 4) if version == 1: reg.pc = get_word(header, 6) else: reg.pc = get_word(header, 32) reg.sp = get_word(header, 8) reg.i = header[10] reg.r = 128 * (header[12] & 1) + (header[11] & 127) reg.de = get_word(header, 13) reg.bc2 = get_word(header, 15) reg.de2 = get_word(header, 17) reg.hl2 = get_word(header, 19) reg.a2 = header[21] reg.f2 = header[22] reg.iy = get_word(header, 23) reg.ix = get_word(header, 25) print('Registers:') for line in reg.get_lines(): print(' ' + line) # Print RAM block info if version == 1: block_len = len(ram_blocks) prefix = '' if header[12] & 32 else 'un' print('48K RAM block (16384-65535 4000-FFFF): {} bytes ({}compressed)'. format(block_len, prefix)) else: i = 0 while i < len(ram_blocks): block_len = get_word(ram_blocks, i) page_num = ram_blocks[i + 2] addr_range = block_dict.get(page_num) if addr_range is None and page_num - 3 == bank: addr_range = '49152-65535 C000-FFFF' if addr_range: addr_range = ' ({})'.format(addr_range) else: addr_range = '' i += 3 if block_len == 65535: block_len = 16384 prefix = 'un' else: prefix = '' print('RAM block {}{}: {} bytes ({}compressed)'.format( page_num, addr_range, block_len, prefix)) i += block_len
def main(args): parser = argparse.ArgumentParser( usage='bin2tap.py [options] FILE [file.tap]', description= "Convert a binary (raw memory) file or a SNA, SZX or Z80 snapshot into a TAP file. " "FILE may be a regular file, or '-' to read a binary file from standard input.", 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( '-c', '--clear', dest='clear', metavar='N', type=integer, help= "Use a 'CLEAR N' command in the BASIC loader and leave the stack pointer alone." ) group.add_argument('-e', '--end', dest='end', metavar='ADDR', type=integer, default=65536, help="Set the end address when reading a snapshot.") group.add_argument( '-o', '--org', dest='org', metavar='ORG', type=integer, help= "Set the origin address (default: 16384 for a snapshot, otherwise 65536 minus the length of FILE)." ) group.add_argument('-p', '--stack', dest='stack', metavar='STACK', type=integer, help="Set the stack pointer (default: ORG).") group.add_argument('-s', '--start', dest='start', metavar='START', type=integer, help="Set the start address to JP to (default: ORG).") group.add_argument( '-S', '--screen', dest='screen', metavar='FILE', help= "Add a loading screen to the TAP file. FILE may be a snapshot or a 6912-byte SCR file." ) 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) infile = namespace.infile if unknown_args or infile is None: parser.exit(2, parser.format_help()) snapshot_reader = get_snapshot_reader() if snapshot_reader.can_read(infile): org = namespace.org or 16384 if org >= namespace.end: raise SkoolKitError( 'End address must be greater than {}'.format(org)) ram = snapshot_reader.get_snapshot(infile)[org:namespace.end] else: ram = read_bin_file(infile, 49152) if not ram: raise SkoolKitError('{} is empty'.format(infile)) org = namespace.org or 65536 - len(ram) clear = namespace.clear start = namespace.start or org stack = namespace.stack or org tapfile = namespace.outfile if tapfile is None: if infile.lower().endswith(('.bin', '.sna', '.szx', '.z80')): prefix = os.path.basename(infile)[:-4] elif infile == '-': prefix = 'program' else: prefix = os.path.basename(infile) tapfile = prefix + ".tap" scr = namespace.screen if scr is not None: if snapshot_reader.can_read(scr): scr = snapshot_reader.get_snapshot(scr)[16384:23296] else: scr = read_bin_file(scr, 6912) run(ram, clear, org, start, stack, tapfile, scr)
def test_read_bin_file(self): tempdir = self.make_directory() with self.assertRaises(IOError) as cm: read_bin_file(tempdir) self.assertEqual(cm.exception.errno, ERRNO)
def _analyse_z80(z80file): data = read_bin_file(z80file) # Extract header and RAM block(s) if get_word(data, 6) > 0: version = 1 header = data[:30] ram_blocks = data[30:] else: header_len = 32 + get_word(data, 30) header = data[:header_len] ram_blocks = data[header_len:] version = 2 if header_len == 55 else 3 # Print file name, version, machine, interrupt status, border, port $7FFD print('Z80 file: {}'.format(z80file)) print('Version: {}'.format(version)) bank = None if version == 1: machine = '48K Spectrum' block_dict = BLOCK_ADDRESSES_48K else: machine_dict = V2_MACHINES if version == 2 else V3_MACHINES machine_id = header[34] index = header[37] // 128 machine_spec = machine_dict.get(machine_id, MACHINES.get(machine_id, ('Unknown', 'Unknown', True))) machine = machine_spec[index] if machine_spec[2]: block_dict = BLOCK_ADDRESSES_48K else: block_dict = BLOCK_ADDRESSES_128K bank = header[35] & 7 print('Machine: {}'.format(machine)) print('Interrupts: {}abled'.format('en' if header[28] else 'dis')) print('Interrupt mode: {}'.format(header[29] & 3)) print('Border: {}'.format((header[12] // 2) & 7)) if bank is not None: print('Port $7FFD: {} - bank {} (block {}) paged into 49152-65535 C000-FFFF'.format(header[35], bank, bank + 3)) # Print register contents reg = Registers() reg.a = header[0] reg.f = header[1] reg.bc = get_word(header, 2) reg.hl = get_word(header, 4) if version == 1: reg.pc = get_word(header, 6) else: reg.pc = get_word(header, 32) reg.sp = get_word(header, 8) reg.i = header[10] reg.r = 128 * (header[12] & 1) + (header[11] & 127) reg.de = get_word(header, 13) reg.bc2 = get_word(header, 15) reg.de2 = get_word(header, 17) reg.hl2 = get_word(header, 19) reg.a2 = header[21] reg.f2 = header[22] reg.iy = get_word(header, 23) reg.ix = get_word(header, 25) print('Registers:') for line in reg.get_lines(): print(' ' + line) # Print RAM block info if version == 1: block_len = len(ram_blocks) prefix = '' if header[12] & 32 else 'un' print('48K RAM block (16384-65535 4000-FFFF): {} bytes ({}compressed)'.format(block_len, prefix)) else: i = 0 while i < len(ram_blocks): block_len = get_word(ram_blocks, i) page_num = ram_blocks[i + 2] addr_range = block_dict.get(page_num) if addr_range is None and page_num - 3 == bank: addr_range = '49152-65535 C000-FFFF' if addr_range: addr_range = ' ({})'.format(addr_range) else: addr_range = '' i += 3 if block_len == 65535: block_len = 16384 prefix = 'un' else: prefix = '' print('RAM block {}{}: {} bytes ({}compressed)'.format(page_num, addr_range, block_len, prefix)) i += block_len