Esempio n. 1
0
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)
Esempio n. 2
0
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)
Esempio n. 3
0
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
Esempio n. 4
0
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
Esempio n. 5
0
 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)
Esempio n. 6
0
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)
Esempio n. 7
0
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)
Esempio n. 8
0
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)
Esempio n. 9
0
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))
Esempio n. 10
0
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))
Esempio n. 11
0
 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)
Esempio n. 12
0
 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)
Esempio n. 13
0
 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)
Esempio n. 14
0
 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)
Esempio n. 15
0
 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)
Esempio n. 16
0
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
Esempio n. 17
0
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)))
Esempio n. 18
0
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
Esempio n. 19
0
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)
Esempio n. 20
0
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)
Esempio n. 21
0
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)
Esempio n. 22
0
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)
Esempio n. 23
0
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)
Esempio n. 24
0
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)
Esempio n. 25
0
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
Esempio n. 26
0
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
Esempio n. 27
0
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
Esempio n. 28
0
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
Esempio n. 29
0
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)
Esempio n. 30
0
 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)
Esempio n. 31
0
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
Esempio n. 32
0
 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)