Example #1
0
 def test_invalid_lines(self):
     ctl_specs = [
         ('  30000,1',        'blank directive with no containing block'),
         ('B 30745,15,5:X10', 'invalid integer'),
         ('T 30760,5,2:Y3',   'invalid integer'),
         ('W 30765,5,1:B4',   'invalid integer'),
         ('S 30770,10,T8:2',  'invalid integer'),
         ('C 40000,Q',        'invalid integer'),
         ('; @label:EDCBA=Z', 'invalid ASM directive address'),
         ('; @label=Z',       'invalid ASM directive declaration'),
         ('b 50000,20',       'extra parameters after address'),
         ('d 50020',          'invalid directive'),
         ('! 50030',          'invalid directive')
     ]
     ctls = [spec[0] for spec in ctl_specs]
     ctl_parser = CtlParser()
     ctlfile = self.write_text_file('\n'.join(ctls))
     ctl_parser.parse_ctl(ctlfile)
     exp_warnings = []
     for line_no, (ctl, error_msg) in enumerate(ctl_specs, 1):
         if error_msg:
             exp_warnings.append('WARNING: Ignoring line {} in {} ({}):'.format(line_no, ctlfile, error_msg))
     warnings = self.err.getvalue().split('\n')[:-1]
     self.assertEqual(exp_warnings, warnings[0::2])
     invalid_ctls = [spec[0] for spec in ctl_specs if spec[1]]
     self.assertEqual(invalid_ctls, warnings[1::2])
Example #2
0
    def test_word_formats(self):
        ctl = '\n'.join((
            'w 40000 Test word formats',
            '  40000,10 5 default',
            '  40010,b10 5 words in binary format',
            'W 40020,b10,6,d2,h2 3 binary, 1 decimal, 1 hex',
            'W 40030,b10,4:d4:h2 2 binary, 2 decimal, 1 hex, one line',
            '  40040,10,b2,4,h4 1 binary, 2 default, 2 hex',
            '  40050,10,b2:6:h2 1 binary, 3 default, 1 hex, one line',
        ))
        ctl_parser = CtlParser()
        ctlfile = self.write_text_file(ctl)
        ctl_parser.parse_ctl(ctlfile)

        exp_lengths = {
            40010: [(None, [(None, 'b')])],
            40020: [
                (6, [(6, 'b')]),
                (2, [(2, 'd')]),
                (2, [(2, 'h')])
            ],
            40030: [(10, [(4, 'b'), (4, 'd'), (2, 'h')])],
            40040: [
                (2, [(2, 'b')]),
                (4, None),
                (4, [(4, 'h')])
            ],
            40050: [(10, [(2, 'b'), (6, None), (2, 'h')])]
        }
        self.assertEqual(exp_lengths, ctl_parser.lengths)
Example #3
0
    def test_word_formats_hex(self):
        snapshot = [240] * 8
        ctl = '\n'.join((
            'w 00000',
            '  00000,8,b2,d2,h2,2',
            'i 00008'
        ))
        ctl_parser = CtlParser()
        ctl_parser.parse_ctl(self.write_text_file(ctl))
        disassembly = Disassembly(snapshot, ctl_parser, True, asm_hex=True)

        entries = disassembly.entries
        self.assertEqual(len(entries), 2)

        entry = entries[0]
        self.assertEqual(entry.address, 0)
        instructions = entry.instructions
        actual_instructions = [(i.address, i.operation) for i in instructions]
        exp_instructions = [
            (0, 'DEFW %1111000011110000'),
            (2, 'DEFW 61680'),
            (4, 'DEFW $F0F0'),
            (6, 'DEFW $F0F0')
        ]
        self.assertEqual(exp_instructions, actual_instructions)
Example #4
0
    def test_s_directives(self):
        snapshot = []
        ctl = '\n'.join((
            's 00000',
            '  00000,4',
            '  00004,b4',
            'S 00008,d4',
            '  00012,h8',
            '  00020,40,b10,10,h10',
            'S 00060,b40,10,d10,h10',
            '  00100,d40,b10,10,h10',
            '  00140,h60,b10,d10,40',
            'S 00200,768,b256,d256,h256',
            '  00968,56,16:b%10101010,40:h17',
            'i 01024'
        ))
        ctl_parser = CtlParser()
        ctl_parser.parse_ctl(self.write_text_file(ctl))
        disassembly = Disassembly(snapshot, ctl_parser, True)

        entries = disassembly.entries
        self.assertEqual(len(entries), 2)

        entry = entries[0]
        self.assertEqual(entry.address, 0)
        instructions = entry.instructions
        actual_instructions = [(i.address, i.operation) for i in instructions]
        exp_instructions = [
            (  0, 'DEFS 4'),
            (  4, 'DEFS %00000100'),
            (  8, 'DEFS 4'),
            ( 12, 'DEFS 8'),
            ( 20, 'DEFS %00001010'),
            ( 30, 'DEFS 10'),
            ( 40, 'DEFS $0A'),
            ( 50, 'DEFS $0A'),
            ( 60, 'DEFS %00001010'),
            ( 70, 'DEFS 10'),
            ( 80, 'DEFS $0A'),
            ( 90, 'DEFS $0A'),
            (100, 'DEFS %00001010'),
            (110, 'DEFS 10'),
            (120, 'DEFS $0A'),
            (130, 'DEFS $0A'),
            (140, 'DEFS %00001010'),
            (150, 'DEFS 10'),
            (160, 'DEFS $28'),
            (200, 'DEFS %0000000100000000'),
            (456, 'DEFS 256'),
            (712, 'DEFS $0100'),
            (968, 'DEFS 16,%10101010'),
            (984, 'DEFS 40,$11')
        ]
        self.assertEqual(exp_instructions, actual_instructions)
Example #5
0
    def test_byte_formats(self):
        snapshot = [42] * 75
        ctl = '\n'.join((
            'b 00000',
            '  00000,b5',
            '  00005,b15',
            '  00020,b5,2,d2,h1',
            'B 00025,b5,2:d2:h1',
            '  00030,h10,5:d3:b2',
            'B 00040,5,b1,h2',
            '  00045,5,h1,T4',
            '  00050,5,b2:T3',
            'T 00055,5,h2,3',
            'T 00060,5,2:d3',
            'T 00065,5,3,B1',
            'T 00070,5,B2:h3',
            'i 00075'
        ))
        ctl_parser = CtlParser()
        ctl_parser.parse_ctl(self.write_text_file(ctl))
        disassembly = Disassembly(snapshot, ctl_parser, True)

        entries = disassembly.entries
        self.assertEqual(len(entries), 2)

        entry = entries[0]
        self.assertEqual(entry.address, 0)
        instructions = entry.instructions
        actual_instructions = [(i.address, i.operation) for i in instructions]
        exp_instructions = [
            ( 0, 'DEFB %00101010,%00101010,%00101010,%00101010,%00101010'),
            ( 5, 'DEFB %00101010,%00101010,%00101010,%00101010,%00101010,%00101010,%00101010,%00101010'),
            (13, 'DEFB %00101010,%00101010,%00101010,%00101010,%00101010,%00101010,%00101010'),
            (20, 'DEFB %00101010,%00101010'),
            (22, 'DEFB 42,42'),
            (24, 'DEFB $2A'),
            (25, 'DEFB %00101010,%00101010,42,42,$2A'),
            (30, 'DEFB $2A,$2A,$2A,$2A,$2A,42,42,42,%00101010,%00101010'),
            (40, 'DEFB %00101010'),
            (41, 'DEFB $2A,$2A'),
            (43, 'DEFB $2A,$2A'),
            (45, 'DEFB $2A'),
            (46, 'DEFB "****"'),
            (50, 'DEFB %00101010,%00101010,"***"'),
            (55, 'DEFM $2A,$2A'),
            (57, 'DEFM "***"'),
            (60, 'DEFM "**",42,42,42'),
            (65, 'DEFM "***"'),
            (68, 'DEFM 42'),
            (69, 'DEFM 42'),
            (70, 'DEFM 42,42,$2A,$2A,$2A')
        ]
        self.assertEqual(actual_instructions, exp_instructions)
Example #6
0
    def test_s_directives(self):
        ctl = '\n'.join((
            's 50000 Test s/S directives',
            '  50000,10',
            '  50010,b10',
            '  50020,d10',
            '  50030,h10',
            'S 50040,b20,5,d5,h5',
            'S 50060,d20,b5,5,h5',
            'S 50080,h20,b5,d5,5',
            '  50100,20,b5,d5,5',
            '  50120,20,d20:b%10001000',
            '  50140,20,20:h$44',
            '  50160,12,10:h10,h2:2'
        ))
        ctl_parser = CtlParser()
        ctlfile = self.write_text_file(ctl)
        ctl_parser.parse_ctl(ctlfile)

        exp_lengths = {
            50010: [(None, [(None, 'b')])],
            50020: [(None, [(None, 'd')])],
            50030: [(None, [(None, 'h')])],
            50040: [
                (5, [(5, 'b')]),
                (5, [(5, 'd')]),
                (5, [(5, 'h')])
            ],
            50060: [
                (5, [(5, 'b')]),
                (5, [(5, 'd')]),
                (5, [(5, 'h')])
            ],
            50080: [
                (5, [(5, 'b')]),
                (5, [(5, 'd')]),
                (5, [(5, 'h')])
            ],
            50100: [
                (5, [(5, 'b')]),
                (5, [(5, 'd')]),
                (5, None)
            ],
            50120: [(20, [(20, 'd'), (136, 'b')])],
            50140: [(20, [(20, None), (68, 'h')])],
            50160: [
                (10, [(10, None), (10, 'h')]),
                (2, [(2, 'h'), (2, None)]),
            ]
        }
        self.assertEqual(exp_lengths, ctl_parser.lengths)
Example #7
0
    def test_comments(self):
        ctl = '\n'.join((
            '# This is a comment',
            'b 32768',
            '% This is also a comment',
            'w 32769',
            '; This is a comment too'
        ))
        ctl_parser = CtlParser()
        ctlfile = self.write_text_file(ctl)
        ctl_parser.parse_ctl(ctlfile)

        self.assertEqual(self.err.getvalue(), '')
        self.assertEqual({32768: 'b', 32769: 'w'}, ctl_parser.ctls)
Example #8
0
    def test_word_formats(self):
        snapshot = [170, 53] * 32
        ctl = '\n'.join((
            'w 00000',
            '  00000,4',
            '  00004,b4',
            '  00008,d4',
            'W 00012,h4',
            'W 00016,b8,2,d2,h4',
            '  00024,d8,b4:2:h2',
            '  00032,h8,b2:d4:2',
            '  00040,8,b2,4,h2',
            'W 00048,8,b2:2:h4',
            '  00056,8,4',
            'i 00064'
        ))
        ctl_parser = CtlParser()
        ctl_parser.parse_ctl(self.write_text_file(ctl))
        disassembly = Disassembly(snapshot, ctl_parser, True)

        entries = disassembly.entries
        self.assertEqual(len(entries), 2)

        entry = entries[0]
        self.assertEqual(entry.address, 0)
        instructions = entry.instructions
        actual_instructions = [(i.address, i.operation) for i in instructions]
        exp_instructions = [
            ( 0, 'DEFW 13738'),
            ( 2, 'DEFW 13738'),
            ( 4, 'DEFW %0011010110101010'),
            ( 6, 'DEFW %0011010110101010'),
            ( 8, 'DEFW 13738'),
            (10, 'DEFW 13738'),
            (12, 'DEFW $35AA'),
            (14, 'DEFW $35AA'),
            (16, 'DEFW %0011010110101010'),
            (18, 'DEFW 13738'),
            (20, 'DEFW $35AA,$35AA'),
            (24, 'DEFW %0011010110101010,%0011010110101010,13738,$35AA'),
            (32, 'DEFW %0011010110101010,13738,13738,$35AA'),
            (40, 'DEFW %0011010110101010'),
            (42, 'DEFW 13738,13738'),
            (46, 'DEFW $35AA'),
            (48, 'DEFW %0011010110101010,13738,$35AA,$35AA'),
            (56, 'DEFW 13738,13738'),
            (60, 'DEFW 13738,13738')
        ]
        self.assertEqual(actual_instructions, exp_instructions)
Example #9
0
    def test_byte_formats(self):
        ctl = '\n'.join((
            'b 40000 Test byte formats',
            '  40000,b10 10 bytes in binary format',
            'B 40010,b10,5,d3,h2 5 binary, 3 decimal, 2 hex',
            'B 40020,b10,2:d3:h5 2 binary, 3 decimal, 5 hex, one line',
            '  40030,10,b6,3,h1 6 binary, 3 default, 1 hex',
            '  40040,10,b5:2:h3 5 binary, 2 default, 3 hex, one line',
            '  40050,10,1,T9 1 default, 9 text',
            '  40060,10,h4:T6 4 hex, 6 text, one line',
            'T 40070,10,3,b7 3 text, 7 binary',
            'T 40080,10,2:h8 2 text, 8 hex, one line',
            'T 40090,10,5,B5 5 text, 5 default'
        ))
        ctl_parser = CtlParser()
        ctlfile = self.write_text_file(ctl)
        ctl_parser.parse_ctl(ctlfile)

        exp_lengths = {
            40000: [(None, [(None, 'b')])],
            40010: [
                (5, [(5, 'b')]),
                (3, [(3, 'd')]),
                (2, [(2, 'h')])
            ],
            40020: [(10, [(2, 'b'), (3, 'd'), (5, 'h')])],
            40030: [
                (6, [(6, 'b')]),
                (3, None),
                (1, [(1, 'h')])
            ],
            40040: [(10, [(5, 'b'), (2, None), (3, 'h')])],
            40050: [
                (1, None),
                (9, [(9, 'T')])
            ],
            40060: [(10, [(4, 'h'), (6, 'T')])],
            40070: [
                (3, None),
                (7, [(7, 'b')])
            ],
            40080: [(10, [(2, None), (8, 'h')])],
            40090: [
                (5, None),
                (5, [(5, 'B')])
            ],
        }
        self.assertEqual(exp_lengths, ctl_parser.lengths)
Example #10
0
 def test_invalid_lines(self):
     ctl_specs = [
         ('  30000,1',           'blank directive with no containing block'),
         ('B 30745,15,5:X10',    'invalid integer'),
         ('T 30760,5,2:Y3',      'invalid integer'),
         ('W 30765,5,1:B4',      'invalid integer'),
         ('S 30770,10,T8:2',     'invalid integer'),
         ('B 30780,10,h,5',      'invalid integer'),
         ('C 40000,Q',           'invalid integer'),
         ('@ FEDCB label=Z',     'invalid ASM directive address'),
         ('@ 49152',             'invalid ASM directive declaration'),
         ('b 50000,20',          'extra parameters after address'),
         ('c 50000,20',          'extra parameters after address'),
         ('g 50000,20',          'extra parameters after address'),
         ('i 50000,20',          'extra parameters after address'),
         ('s 50000,20',          'extra parameters after address'),
         ('t 50000,20',          'extra parameters after address'),
         ('u 50000,20',          'extra parameters after address'),
         ('w 50000,20',          'extra parameters after address'),
         ('D 50000,20 Desc.',    'extra parameters after address'),
         ('E 50000,20 End.',     'extra parameters after address'),
         ('N 50000,20 Note.',    'extra parameters after address'),
         ('R 50000,20 A 10',     'extra parameters after address'),
         ('b b50010',            'invalid address'),
         ('d 50020',             'invalid directive'),
         ('! 50030',             'invalid directive'),
         ('@ 50000 ignoreua:g',  "invalid @ignoreua directive suffix: 'g'"),
         ('L 51000',             'loop length not specified'),
         ('L 51000,10',          'loop count not specified')
     ]
     ctls = [spec[0] for spec in ctl_specs]
     ctl_parser = CtlParser()
     ctlfile = self.write_text_file('\n'.join(ctls))
     ctl_parser.parse_ctl(ctlfile)
     exp_warnings = []
     for line_no, (ctl, error_msg) in enumerate(ctl_specs, 1):
         if error_msg:
             exp_warnings.append('WARNING: Ignoring line {} in {} ({}):'.format(line_no, ctlfile, error_msg))
     warnings = self.err.getvalue().split('\n')[:-1]
     self.assertEqual(exp_warnings, warnings[0::2])
     invalid_ctls = [spec[0] for spec in ctl_specs if spec[1]]
     self.assertEqual(invalid_ctls, warnings[1::2])
Example #11
0
    def test_byte_formats_hex(self):
        snapshot = [85] * 4
        ctl = '\n'.join((
            'b 00000',
            '  00000,4,b1:d1:h1:1',
            'i 00004'
        ))
        ctl_parser = CtlParser()
        ctl_parser.parse_ctl(self.write_text_file(ctl))
        disassembly = Disassembly(snapshot, ctl_parser, True, asm_hex=True)

        entries = disassembly.entries
        self.assertEqual(len(entries), 2)

        entry = entries[0]
        self.assertEqual(entry.address, 0)
        instructions = entry.instructions
        self.assertEqual(len(instructions), 1)
        defb = instructions[0]
        self.assertEqual(defb.operation, 'DEFB %01010101,85,$55,$55')
Example #12
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)
Example #13
0
def get_ctl_parser(ctls, infile, start, end, def_start, def_end):
    if infile[-4:].lower() in ('.bin', '.sna', '.szx', '.z80'):
        prefix = infile[:-4]
    else:
        prefix = infile
    if not ctls:
        ctls.extend(sorted(glob.glob(prefix + '*.ctl')))
    ctlfiles = []
    if ctls and '0' not in ctls:
        # Use control file(s)
        for ctl in ctls:
            if os.path.isdir(ctl):
                ctlfiles.extend(sorted(glob.glob(os.path.join(ctl, '*.ctl'))))
            else:
                ctlfiles.append(ctl)
    if ctlfiles:
        if len(ctlfiles) > 1:
            suffix = 's'
        else:
            suffix = ''
        info('Using control file{}: {}'.format(suffix, ', '.join(ctlfiles)))
        ctl_parser = CtlParser()
        ctl_parser.parse_ctls(ctlfiles, start, end)
    else:
        ctl_parser = CtlParser({def_start: 'c', def_end: 'i'})
    return ctl_parser
Example #14
0
def run(snafile, options, config):
    snapshot, start, end = make_snapshot(snafile, options.org, options.start, options.end, options.page)

    if options.sftfile:
        # Use a skool file template
        info('Using skool file template: {}'.format(options.sftfile))
        writer = SftParser(snapshot, options.sftfile, config['DefbZfill'], options.base == 16, options.case == 1)
        writer.write_skool(options.start, options.end)
        return

    if options.ctlfiles:
        # Use control file(s)
        if len(options.ctlfiles) > 1:
            suffix = 's'
        else:
            suffix = ''
        info('Using control file{}: {}'.format(suffix, ', '.join(options.ctlfiles)))
        ctl_parser = CtlParser()
        ctl_parser.parse_ctls(options.ctlfiles, options.start, options.end)
    else:
        ctl_parser = CtlParser({start: 'c', end: 'i'})
    writer = SkoolWriter(snapshot, ctl_parser, options, config)
    writer.write_skool(config['ListRefs'], config['Text'])
Example #15
0
    def test_s_directives_hex(self):
        snapshot = []
        ctl = '\n'.join((
            's 00000',
            '  00000,14,d2:b1,h2:128,h10:2',
            'i 00014'
        ))
        ctl_parser = CtlParser()
        ctl_parser.parse_ctl(self.write_text_file(ctl))
        disassembly = Disassembly(snapshot, ctl_parser, True, asm_hex=True)

        entries = disassembly.entries
        self.assertEqual(len(entries), 2)

        entry = entries[0]
        self.assertEqual(entry.address, 0)
        instructions = entry.instructions
        actual_instructions = [(i.address, i.operation) for i in instructions]
        exp_instructions = [
            (0, 'DEFS 2,%00000001'),
            (2, 'DEFS 2,$80'),
            (4, 'DEFS $0A,2')
        ]
        self.assertEqual(exp_instructions, actual_instructions)
Example #16
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)
Example #17
0
def run(snafile, options, config):
    snapshot, start, end = make_snapshot(snafile, options.org, options.start, options.end, options.page)

    if options.ctlfiles:
        # Use control file(s)
        if len(options.ctlfiles) > 1:
            suffix = 's'
        else:
            suffix = ''
        info('Using control file{}: {}'.format(suffix, ', '.join(options.ctlfiles)))
        ctl_parser = CtlParser()
        ctl_parser.parse_ctls(options.ctlfiles, options.start, options.end)
    else:
        ctl_parser = CtlParser({start: 'c', end: 'i'})
    writer = SkoolWriter(snapshot, ctl_parser, options, config)
    writer.write_skool(config['ListRefs'], config['Text'])
Example #18
0
 def _get_writer(self, snapshot, ctl, defb_size=8, defb_mod=1, zfill=False, defm_width=66, asm_hex=False, asm_lower=False):
     ctl_parser = CtlParser()
     ctl_parser.parse_ctl(self.write_text_file(ctl))
     return SkoolWriter(snapshot, ctl_parser, defb_size, defb_mod, zfill, defm_width, asm_hex, asm_lower)
Example #19
0
    def test_parse_ctl(self):
        ctl_parser = CtlParser()
        ctlfile = self.write_text_file(CTL)
        ctl_parser.parse_ctl(ctlfile)

        exp_ctls = {
            30000: 'b',
            30100: 'c',
            30200: 'g',
            30300: 'i',
            30400: 't',
            30500: 'u',
            30600: 'w',
            30700: 's'
        }
        self.assertEqual(exp_ctls, ctl_parser.ctls)

        exp_subctls = {
            30002: 't',
            30012: 'w',
            30020: 'c',
            30027: None,
            30050: 'b',
            30055: None,
            30100: None,
            30200: 'b',
            30210: None,
            30450: 't',
            30457: None,
            30500: 'b',
            30502: 'b',
            30505: None,
            30510: 'b',
            30522: None,
            30530: 'b',
            30550: None,
            30560: 'b',
            30581: None,
            30620: 's',
            30627: None,
            30720: 'b',
            30730: 't',
            30745: None
        }
        self.assertEqual(exp_subctls, ctl_parser.subctls)

        exp_titles = {
            30000: 'Data at 30000',
            30100: 'Routine at 30100',
            30200: 'Game status buffer entry at 30200',
            30300: 'Ignored block at 30300',
            30400: 'Message at 30400',
            30500: 'Unused block at 30500',
            30600: 'Words at 30600',
            30700: 'Zeroes at 30700'
        }
        self.assertEqual(exp_titles, ctl_parser.titles)

        exp_instruction_comments = {
            30002: 'Message in the data block',
            30012: None,
            30020: None,
            30050: 'Complex DEFB with a blank directive',
            30200: "Blank directive in a 'g' block",
            30450: 'Complex DEFM with a blank directive',
            30500: "Blank directive in a 'u' block",
            30502: None,
            30510: None,
            30530: None,
            30560: None,
            30620: None,
            30720: None,
            30730: None
        }
        self.assertEqual(exp_instruction_comments, ctl_parser.instruction_comments)

        exp_comments = {
            30100: ['Description of routine at 30100']
        }
        self.assertEqual(exp_comments, ctl_parser.comments)

        exp_registers = {
            30100: [['A', 'Some value'], ['BC', 'Some other value']]
        }
        self.assertEqual(exp_registers, ctl_parser.registers)

        exp_end_comments = {
            30100: ['First paragraph of the end comment for the routine at 30100',
                    'Second paragraph of the end comment for the routine at 30100']
        }
        self.assertEqual(exp_end_comments, ctl_parser.end_comments)

        exp_lengths = {
            30050: [(5, [(3, None), (2, 'T')])],
            30200: [(1, None)],
            30450: [(7, [(4, None), (3, 'B')])],
            30510: [(3, None)],
            30530: list(zip([2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 3], [None] * 11)),
            30560: list(zip([6, 5, 4, 3, 2, 1], [None] * 6)),
            30720: [
                (1, None),
                (5, [(3, 'T'), (2, None)]),
                (2, [(1, None), (1, 'T')]),
                (2, [(1, None), (1, 'T')])
            ],
            30730: [(15, [(10, None), (5, 'B')])]
        }
        self.assertEqual(exp_lengths, ctl_parser.lengths)

        exp_multiline_comments = {
            30012: (30026, 'This comment covers the following two sub-blocks')
        }
        self.assertEqual(exp_multiline_comments, ctl_parser.multiline_comments)

        exp_entry_asm_directives = {
            30000: [('start', None)]
        }
        self.assertEqual(exp_entry_asm_directives, ctl_parser.entry_asm_directives)

        exp_instruction_asm_directives = {
            30101: [('label', 'LOOP')]
        }
        self.assertEqual(exp_instruction_asm_directives, ctl_parser.instruction_asm_directives)
Example #20
0
def _generate_ctls_without_code_map(snapshot, start, end):
    ctls = {start: 'c', end: 'i'}

    # Look for potential 'RET', 'JR d' and 'JP nn' instructions and assume that
    # they end a block (after which another block follows); note that we don't
    # bother examining the final byte because no block can follow it
    for address in range(start, end - 1):
        b = snapshot[address]
        if b == 201:
            ctls[address + 1] = 'c'
        elif b == 195 and address < end - 3:
            ctls[address + 3] = 'c'
        elif b == 24 and address < end - 2:
            ctls[address + 2] = 'c'

    ctl_parser = CtlParser(ctls)
    disassembly = Disassembly(snapshot, ctl_parser)

    # Scan the disassembly for pairs of adjacent blocks that overlap, and join
    # such pairs
    while True:
        done = True
        for entry in disassembly.entries[:-1]:
            if entry.bad_blocks:
                del ctls[entry.next.address]
                disassembly.remove_entry(entry.address)
                disassembly.remove_entry(entry.next.address)
                done = False
        if done:
            break
        disassembly.build()

    # Scan the disassembly for blocks that don't end in a 'RET', 'JP nn' or
    # 'JR d' instruction, and join them to the next block
    changed = False
    for entry in disassembly.entries[:-1]:
        last_instr = entry.instructions[-1].operation
        if last_instr != 'RET' and not (last_instr[:2] in ('JP', 'JR')
                                        and last_instr[3:].isdigit()):
            next_address = entry.next.address
            if next_address < end:
                del ctls[entry.next.address]
                disassembly.remove_entry(entry.address)
                disassembly.remove_entry(entry.next.address)
                changed = True
    if changed:
        disassembly.build()

    # Scan the disassembly for pairs of adjacent blocks where the start address
    # of the second block is JRed or JPed to from the first block, and join
    # such pairs
    while True:
        done = True
        for entry in disassembly.entries[:-1]:
            for instruction in entry.instructions:
                operation = instruction.operation
                if operation[:2] in ('JR', 'JP') and operation[-5:] == str(
                        entry.next.address):
                    del ctls[entry.next.address]
                    disassembly.remove_entry(entry.address)
                    disassembly.remove_entry(entry.next.address)
                    done = False
                    break
        if done:
            break
        disassembly.build()

    # Mark any NOP sequences at the beginning of a block as a separate zero
    # block
    for entry in disassembly.entries:
        if entry.instructions[0].operation != 'NOP':
            continue
        for instruction in entry.instructions[1:]:
            if instruction.operation != 'NOP':
                break
        ctls[entry.address] = 's'
        if entry.instructions[-1].operation != 'NOP':
            ctls[instruction.address] = 'c'

    # See which blocks marked as code look like text or data
    _analyse_blocks(disassembly, ctls)

    return ctls
Example #21
0
    def test_disassembly(self):
        ctl_parser = CtlParser()
        ctl_parser.parse_ctl(self.write_text_file(DISASSEMBLY_CTL))
        disassembly = Disassembly(DISASSEMBLY_SNAPSHOT, ctl_parser, True)

        entries = disassembly.entries
        self.assertEqual(len(entries), 16)

        # Entry #1 (32768)
        entry = entries[0]
        self.assertEqual(entry.address, 32768)
        self.assertEqual(entry.title, 'Routine at 32768')
        self.assertEqual(entry.description, ['Routine description paragraph 1.', 'Routine description paragraph 2.'])
        self.assertEqual(entry.ctl, 'c')
        self.assertEqual(entry.registers, [['A', 'Some value'], ['B', 'Some other value']])
        self.assertEqual(entry.end_comment, ['Routine end comment.'])
        self.assertEqual(entry.start, True)
        self.assertEqual(entry.end, True)
        self.assertEqual(entry.writer, 'skoolkit.game.GameAsmWriter')
        self.assertEqual(entry.org, '32768')

        blocks = entry.blocks
        self.assertEqual(len(blocks), 1)
        block = blocks[0]
        self.assertEqual(block.header, None)
        self.assertEqual(block.comment, 'This is an instruction-level comment that spans two instructions')
        self.assertEqual(block.instructions, entry.instructions)
        self.assertEqual(block.end, 32770)

        instructions = entry.instructions
        self.assertEqual(len(instructions), 2)
        instruction = instructions[0]
        self.assertEqual(instruction.address, 32768)
        self.assertEqual(instruction.operation, 'XOR A')
        self.assertEqual(instruction.bytes, [175])
        self.assertEqual(instruction.referrers, [entries[13]])
        self.assertEqual(instruction.entry, entry)
        self.assertEqual(instruction.ctl, 'c')
        self.assertEqual(instruction.comment, None)
        self.assertEqual(instruction.asm_directives, [('label', 'START')])

        # Entry #2 (32770)
        entry = entries[1]
        self.assertEqual(entry.address, 32770)
        self.assertEqual(entry.title, 'Message at {0}'.format(entry.address))
        instructions = entry.instructions
        self.assertEqual(len(instructions), 2)
        self.assertEqual(instructions[0].operation, 'DEFM "Hi"')
        self.assertEqual(instructions[1].operation, 'DEFM "Lo"')

        # Entry #3 (32774)
        entry = entries[2]
        self.assertEqual(entry.address, 32774)
        self.assertEqual(entry.title, 'Yo')

        # Entry #4 (32776)
        entry = entries[3]
        self.assertEqual(entry.address, 32776)
        self.assertEqual(entry.title, 'Data block at {0}'.format(entry.address))
        instructions = entry.instructions
        self.assertEqual(len(instructions), 4)
        self.assertEqual(instructions[0].operation, 'DEFB 0,0,0')
        self.assertEqual(instructions[3].operation, 'DEFB 0,0,0')

        # Entry #5 (32788)
        entry = entries[4]
        self.assertEqual(entry.address, 32788)
        self.assertEqual(entry.title, 'Important byte')

        # Entry #6 (32789)
        entry = entries[5]
        self.assertEqual(entry.address, 32789)
        self.assertEqual(entry.title, 'Data block at {0}'.format(entry.address))
        instructions = entry.instructions
        self.assertEqual(len(instructions), 1)
        self.assertEqual(instructions[0].operation, 'DEFW 0,0')

        # Entry #7 (32793)
        entry = entries[6]
        self.assertEqual(entry.address, 32793)
        self.assertEqual(entry.title, 'Important word')

        # Entry #8 (32795)
        entry = entries[7]
        self.assertEqual(entry.address, 32795)
        self.assertEqual(entry.title, 'Game status buffer entry at {0}'.format(entry.address))

        # Entry #9 (32796)
        entry = entries[8]
        self.assertEqual(entry.address, 32796)
        self.assertEqual(entry.title, 'Important game status buffer byte')

        # Entry #10 (32797)
        entry = entries[9]
        self.assertEqual(entry.address, 32797)
        self.assertEqual(entry.title, 'Unused')

        # Entry #11 (32798)
        entry = entries[10]
        self.assertEqual(entry.address, 32798)
        self.assertEqual(entry.title, 'Unimportant unused byte')

        # Entry #12 (32799)
        entry = entries[11]
        self.assertEqual(entry.address, 32799)
        self.assertEqual(entry.title, 'Unused')
        instructions = entry.instructions
        self.assertEqual(len(instructions), 2)
        self.assertEqual(instructions[0].operation, 'DEFS 5')
        self.assertEqual(instructions[1].operation, 'DEFS 5')

        # Entry #13 (32809)
        entry = entries[12]
        self.assertEqual(entry.address, 32809)
        self.assertEqual(entry.title, 'Block of zeroes')
        instructions = entry.instructions
        self.assertEqual(len(instructions), 3)
        self.assertEqual(instructions[0].operation, 'DEFS 3')
        self.assertEqual(instructions[1].operation, 'NOP')
        self.assertEqual(instructions[2].operation, 'NOP')
        blocks = entry.blocks
        self.assertEqual(len(blocks), 1)
        block = blocks[0]
        self.assertEqual(block.comment, 'A DEFS followed by two NOPs')

        # Entry #14 (32814)
        entry = entries[13]
        self.assertEqual(entry.address, 32814)
        self.assertEqual(entry.title, 'Refers to the routine at 32768')

        # Entry #15 (32817)
        entry = entries[14]
        self.assertEqual(entry.address, 32817)
        instructions = entry.instructions
        self.assertEqual(len(instructions), 6)
        self.assertEqual(instructions[0].operation, 'DEFM "Hi",1,2,3')
        self.assertEqual(instructions[1].operation, 'DEFB 4,5,"Lo",6')
        self.assertEqual(instructions[2].operation, 'DEFM "ab",1')
        self.assertEqual(instructions[3].operation, 'DEFM "cd",2')
        self.assertEqual(instructions[4].operation, 'DEFM "e",3')
        self.assertEqual(instructions[5].operation, 'DEFM "f",4')

        # Entry #16 (32837)
        entry = entries[15]
        self.assertEqual(entry.address, 32837)
Example #22
0
def _generate_ctls_with_code_map(snapshot, start, end, config, code_map):
    # (1) Use the code map to create an initial set of 'c' ctls, and mark all
    #     unexecuted blocks as 'U' (unknown)
    # (2) Where a 'c' block doesn't end with a RET/JP/JR, extend it up to the
    #     next RET/JP/JR in the following 'U' blocks, or up to the next 'c'
    #     block
    # (3) Mark entry points in 'U' blocks that are CALLed or JPed to from 'c'
    #     blocks with 'c'
    # (4) Split 'c' blocks on RET/JP/JR
    # (5) Scan the disassembly for pairs of adjacent blocks where the start
    #     address of the second block is JRed or JPed to from the first block,
    #     and join such pairs
    # (6) Examine the remaining 'U' blocks for text
    # (7) Mark data blocks of all zeroes with 's'

    # (1) Mark all executed blocks as 'c' and unexecuted blocks as 'U'
    # (unknown)
    ctls = {start: 'U', end: 'i'}
    for address, length in _get_code_blocks(snapshot, start, end, code_map):
        ctls[address] = 'c'
        if address + length < end:
            ctls[address + length] = 'U'

    # (2) Where a 'c' block doesn't end with a RET/JP/JR, extend it up to the
    # next RET/JP/JR in the following 'U' blocks, or up to the next 'c' block
    while 1:
        done = True
        for ctl, b_start, b_end in _get_blocks(ctls):
            if ctl == 'c':
                last_op_id = list(decode(snapshot, b_start, b_end))[-1][3]
                if last_op_id == END:
                    continue
                if _find_terminal_instruction(snapshot, ctls, b_end,
                                              end) < end:
                    done = False
                    break
        if done:
            break

    # (3) Mark entry points in 'U' blocks that are CALLed or JPed to from 'c'
    # blocks with 'c'
    ctl_parser = CtlParser(ctls)
    disassembly = Disassembly(snapshot, ctl_parser)
    while 1:
        disassembly.build(True)
        done = True
        for entry in disassembly.entries:
            if entry.ctl == 'U':
                for instruction in entry.instructions:
                    for referrer in instruction.referrers:
                        if ctls[referrer] == 'c':
                            ctls[instruction.address] = 'c'
                            if entry.next:
                                e_end = entry.next.address
                            else:
                                e_end = 65536
                            _find_terminal_instruction(snapshot, ctls,
                                                       instruction.address,
                                                       e_end, entry.ctl)
                            disassembly.remove_entry(entry.address)
                            done = False
                            break
                    if not done:
                        break
                if not done:
                    break
        if done:
            break

    # (4) Split 'c' blocks on RET/JP/JR
    for ctl, b_address, b_end in _get_blocks(ctls):
        if ctl == 'c':
            next_address = _find_terminal_instruction(snapshot, ctls,
                                                      b_address, b_end, 'c')
            if next_address < b_end:
                disassembly.remove_entry(b_address)
                while next_address < b_end:
                    next_address = _find_terminal_instruction(
                        snapshot, ctls, next_address, b_end, 'c')

    # (5) Scan the disassembly for pairs of adjacent blocks where the start
    # address of the second block is JRed or JPed to from the first block, and
    # join such pairs
    while 1:
        disassembly.build()
        done = True
        for entry in disassembly.entries[:-1]:
            if entry.ctl == 'c':
                for instruction in entry.instructions:
                    operation = instruction.operation
                    if operation[:2] in ('JR', 'JP') and operation[-5:] == str(
                            entry.next.address):
                        del ctls[entry.next.address]
                        disassembly.remove_entry(entry.address)
                        disassembly.remove_entry(entry.next.address)
                        done = False
                        break
        if done:
            break

    # (6) Examine the 'U' blocks for text/data
    for ctl, b_start, b_end in _get_blocks(ctls):
        if ctl == 'U':
            ctls[b_start] = 'b'
            for t_start, t_end in _get_text_blocks(snapshot, b_start, b_end,
                                                   config):
                ctls[t_start] = 't'
                if t_end < b_end:
                    ctls[t_end] = 'b'

    # (7) Mark data blocks of all zeroes with 's'
    for ctl, b_start, b_end in _get_blocks(ctls):
        if ctl == 'b' and sum(snapshot[b_start:b_end]) == 0:
            ctls[b_start] = 's'

    return ctls
Example #23
0
 def _get_ctl_parser(self, ctl, min_address=0, max_address=65536):
     ctl_parser = CtlParser()
     ctlfile = self.write_text_file(ctl)
     ctl_parser.parse_ctl(ctlfile, min_address, max_address)
     return ctl_parser