# HG changeset patch # User Mike Pavone # Date 1366388977 25200 # Node ID 9126c33cc33cf39cc8d10eefcff3f54d72186df2 # Parent 4d4559b04c59f3346930af4e041df7a706bcc7c9 Add test generator, builder and runner diff -r 4d4559b04c59 -r 9126c33cc33c comparetests.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/comparetests.py Fri Apr 19 09:29:37 2013 -0700 @@ -0,0 +1,32 @@ +#!/usr/bin/env python +from glob import glob +import subprocess +from sys import exit + +for path in glob('generated_tests/*.bin'): + try: + b = subprocess.check_output(['./blastem', path]) + try: + m = subprocess.check_output(['musashi/mustrans', path]) + _,_,b = b.partition('\n') + if b != m: + print '-----------------------------' + print 'Mismatch in ' + path + print 'blastem output:' + print b + print 'musashi output:' + print m + print '-----------------------------' + else: + print path, 'passed' + except subprocess.CalledProcessError as e: + print '-----------------------------' + print 'musashi exited with code', e.returncode, 'for test', path + print 'blastem output:' + print b + print '-----------------------------' + except subprocess.CalledProcessError as e: + print '-----------------------------' + print 'blastem exited with code', e.returncode, 'for test', path + print '-----------------------------' + diff -r 4d4559b04c59 -r 9126c33cc33c gentests.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gentests.py Fri Apr 19 09:29:37 2013 -0700 @@ -0,0 +1,444 @@ +#!/usr/bin/env python + +def split_fields(line): + parts = [] + while line: + field,_,line = line.partition('\t') + parts.append(field.strip()) + while line.startswith('\t'): + line = line[1:] + return parts + +class Program(object): + def __init__(self, instruction): + self.avail_dregs = {0,1,2,3,4,5,6,7} + self.avail_aregs = {0,1,2,3,4,5,6,7} + instruction.consume_regs(self) + self.inst = instruction + + def name(self): + return str(self.inst).replace('.', '_').replace('#', '_').replace(',', '_').replace(' ', '_').replace('(', '[').replace(')', ']') + + def write_rom_test(self, outfile): + outfile.write('\tdc.l $0, start\n') + for i in xrange(0x8, 0x100, 0x4): + outfile.write('\tdc.l empty_handler\n') + outfile.write('\tdc.b "SEGA"\nempty_handler:\n\trte\nstart:\n') + outfile.write('\tmove #0, CCR\n') + already = {} + self.inst.write_init(outfile, already) + if 'label' in already: + outfile.write('lbl_' + str(already['label']) + ':\n') + outfile.write('\t'+str(self.inst)+'\n') + outfile.write('\t'+self.inst.save_result(self.get_dreg(), True) + '\n') + save_ccr = self.get_dreg() + outfile.write('\tmove SR, ' + str(save_ccr) + '\n') + outfile.write('\tmove #$1F, CCR\n') + self.inst.invalidate_dest(already) + self.inst.write_init(outfile, already) + if 'label' in already: + outfile.write('lbl_' + str(already['label']) + ':\n') + outfile.write('\t'+str(self.inst)+'\n') + outfile.write('\t'+self.inst.save_result(self.get_dreg(), False) + '\n') + outfile.write('\treset\n') + + def consume_dreg(self, num): + self.avail_dregs.discard(num) + + def consume_areg(self, num): + self.avail_aregs.discard(num) + + def get_dreg(self): + return Register('d', self.avail_dregs.pop()) + +class Register(object): + def __init__(self, kind, num): + self.kind = kind + self.num = num + + def __str__(self): + if self.kind == 'd' or self.kind == 'a': + return self.kind + str(self.num) + return self.kind + + def write_init(self, outfile, size, already): + if not str(self) in already: + minv,maxv = get_size_range(size) + val = randint(minv,maxv) + already[str(self)] = val + outfile.write('\tmove.'+size+' #'+str(val)+', ' + str(self) + '\n') + + def consume_regs(self, program): + if self.kind == 'd': + program.consume_dreg(self.num) + elif self.kind == 'a': + program.consume_areg(self.num) + +def valid_ram_address(address, size='b'): + return address >= 0xE00000 and address <= 0xFFFFFFFC and (address & 0xE00000) == 0xE00000 and (size == 'b' or not address & 1) + +def random_ram_address(mina=0xE00000, maxa=0xFFFFFFFC): + return randint(mina, maxa) | 0xE00000 + +class Indexed(object): + def __init__(self, base, index, index_size, disp): + self.base = base + self.index = index + self.index_size = index_size + self.disp = disp + + def write_init(self, outfile, size, already): + if self.base.kind == 'pc': + if str(self.index) in already: + index = already[str(self.index)] + if self.index_size == 'w': + index = index & 0xFFFF + #sign extend index + if index & 0x8000: + index -= 65536 + if index > -1024: + index = already[str(self.index)] = randint(-32768, -1024) + outfile.write('\tmove.l #' + str(index) + ', ' + str(self.index) + '\n') + else: + index = already[str(self.index)] = randint(-32768, -1024) + outfile.write('\tmove.l #' + str(index) + ', ' + str(self.index) + '\n') + num = already.get('label', 0)+1 + already['label'] = num + address = 'lbl_' + str(num) + ' + 2 + ' + str(self.disp) + ' + ' + str(index) + else: + if str(self.base) in already: + if not valid_ram_address(already[str(self.base)]): + del already[str(self.base)] + self.write_init(outfile, size, already) + return + else: + base = already[str(self.base)] + else: + base = already[str(self.base)] = random_ram_address() + outfile.write('\tmove.l #' + str(base) + ', ' + str(self.base) + '\n') + if str(self.index) in already: + index = already[str(self.index)] + if self.index_size == 'w': + index = index & 0xFFFF + #sign extend index + if index & 0x8000: + index -= 65536 + if not valid_ram_address(base + index): + index = already[str(self.index)] = randint(-64, 63) + outfile.write('\tmove.l #' + str(index) + ', ' + str(self.index) + '\n') + else: + index = already[str(self.index)] = randint(-64, 63) + outfile.write('\tmove.l #' + str(index) + ', ' + str(self.index) + '\n') + address = base + index + self.disp + if (address & 0xFFFFFF) < 0xE00000: + if (address & 0xFFFFFF) < 128: + self.disp -= (address & 0xFFFFFF) + else: + self.disp += 0xE00000-(address & 0xFFFFFF) + address = base + index + self.disp + elif (address & 0xFFFFFF) > 0xFFFFFC: + self.disp -= (address & 0xFFFFFF) - 0xFFFFFC + address = base + index + self.disp + if size != 'b' and address & 1: + self.disp = self.disp ^ 1 + address = base + index + self.disp + minv,maxv = get_size_range(size) + outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n') + + def __str__(self): + return '(' + str(self.disp) + ', ' + str(self.base) + ', ' + str(self.index) + '.' + self.index_size + ')' + + def consume_regs(self, program): + self.base.consume_regs(program) + self.index.consume_regs(program) + +class Displacement(object): + def __init__(self, base, disp): + self.base = base + self.disp = disp + + def write_init(self, outfile, size, already): + if self.base.kind == 'pc': + num = already.get('label', 0)+1 + already['label'] = num + address = 'lbl_' + str(num) + ' + 2 + ' + str(self.disp) + else: + if str(self.base) in already: + if not valid_ram_address(already[str(self.base)]): + del already[str(self.base)] + self.write_init(outfile, size, already) + return + else: + base = already[str(self.base)] + else: + base = already[str(self.base)] = random_ram_address() + outfile.write('\tmove.l #' + str(base) + ', ' + str(self.base) + '\n') + address = base + self.disp + if (address & 0xFFFFFF) < 0xE00000: + if (address & 0xFFFFFF) < 0x10000: + self.disp -= (address & 0xFFFFFF) + else: + self.disp += 0xE00000-(address & 0xFFFFFF) + address = base + self.disp + elif (address & 0xFFFFFF) > 0xFFFFFC: + self.disp -= (address & 0xFFFFFF) - 0xFFFFFC + address = base + self.disp + if size != 'b' and address & 1: + self.disp = self.disp ^ 1 + address = base + self.disp + minv,maxv = get_size_range(size) + outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n') + + def __str__(self): + return '(' + str(self.disp) + ', ' + str(self.base) + ')' + + def consume_regs(self, program): + self.base.consume_regs(program) + +class Indirect(object): + def __init__(self, reg): + self.reg = reg + + def __str__(self): + return '(' + str(self.reg) + ')' + + def write_init(self, outfile, size, already): + if str(self.reg) in already: + if not valid_ram_address(already[str(self.reg)], size): + del already[str(self.reg)] + self.write_init(outfile, size, already) + return + else: + address = already[str(self.reg)] + else: + address = random_ram_address() + if size != 'b': + address = address & 0xFFFFFFFE + outfile.write('\tmove.l #' + str(address) + ', ' + str(self.reg) + '\n') + already[str(self.reg)] = address + minv,maxv = get_size_range(size) + outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n') + + def consume_regs(self, program): + self.reg.consume_regs(program) + +class Increment(object): + def __init__(self, reg): + self.reg = reg + + def __str__(self): + return '(' + str(self.reg) + ')+' + + def write_init(self, outfile, size, already): + if str(self.reg) in already: + if not valid_ram_address(already[str(self.reg)], size): + del already[str(self.reg)] + self.write_init(outfile, size, already) + return + else: + address = already[str(self.reg)] + else: + address = random_ram_address() + if size != 'b': + address = address & 0xFFFFFFFE + outfile.write('\tmove.l #' + str(address) + ', ' + str(self.reg) + '\n') + already[str(self.reg)] = address + minv,maxv = get_size_range(size) + outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n') + + def consume_regs(self, program): + self.reg.consume_regs(program) + +class Decrement(object): + def __init__(self, reg): + self.reg = reg + + def __str__(self): + return '-(' + str(self.reg) + ')' + + def write_init(self, outfile, size, already): + if str(self.reg) in already: + if not valid_ram_address(already[str(self.reg)]- 4 if size == 'l' else 2 if size == 'w' else 1, size): + del already[str(self.reg)] + self.write_init(outfile, size, already) + return + else: + address = already[str(self.reg)] + else: + address = random_ram_address(mina=0xE00004) + if size != 'b': + address = address & 0xFFFFFFFE + outfile.write('\tmove.l #' + str(address) + ', ' + str(self.reg) + '\n') + already[str(self.reg)] = address + minv,maxv = get_size_range(size) + outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n') + + def consume_regs(self, program): + self.reg.consume_regs(program) + +class Absolute(object): + def __init__(self, address, size): + self.address = address + self.size = size + + def __str__(self): + return '(' + str(self.address) + ').' + self.size + + def write_init(self, outfile, size, already): + minv,maxv = get_size_range(size) + outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', '+str(self)+'\n') + + def consume_regs(self, program): + pass + +class Immediate(object): + def __init__(self, value): + self.value = value + + def __str__(self): + return '#' + str(self.value) + + def write_init(self, outfile, size, already): + pass + + def consume_regs(self, program): + pass + +all_dregs = [Register('d', i) for i in range(0, 8)] +all_aregs = [Register('a', i) for i in range(0, 8)] +all_indirect = [Indirect(reg) for reg in all_aregs] +all_predec = [Decrement(reg) for reg in all_aregs] +all_postinc = [Increment(reg) for reg in all_aregs] +from random import randint +def all_indexed(): + return [Indexed(base, index, index_size, randint(-128, 127)) for base in all_aregs for index in all_dregs + all_aregs for index_size in ('w','l')] + +def all_disp(): + return [Displacement(base, randint(-32768, 32767)) for base in all_aregs] + +def rand_pc_disp(): + return [Displacement(Register('pc', 0), randint(-32768, -1024)) for x in xrange(0, 8)] + +def all_pc_indexed(): + return [Indexed(Register('pc', 0), index, index_size, randint(-128, 127)) for index in all_dregs + all_aregs for index_size in ('w','l')] + +def rand_abs_short(): + return [Absolute(0xFFFF8000 + randint(0, 32767), 'w') for x in xrange(0, 8)] + +def rand_abs_long(): + return [Absolute(0xFF0000 + randint(0, 65535), 'l') for x in xrange(0, 8)] + +def get_size_range(size): + if size == 'b': + return (-128, 127) + elif size == 'w': + return (-32768, 32767) + else: + return (-2147483648, 2147483647) + +def rand_immediate(size): + minv,maxv = get_size_range(size) + + return [Immediate(randint(minv, maxv)) for x in xrange(0,8)] + +def get_variations(mode, size): + mapping = { + 'd':all_dregs, + 'a':all_aregs, + '(a)':all_indirect, + '-(a)':all_predec, + '(a)+':all_postinc, + '(n,a)':all_disp, + '(n,a,x)':all_indexed, + '(n,pc)':rand_pc_disp, + '(n,pc,x)':all_pc_indexed, + '(n).w':rand_abs_short, + '(n).l':rand_abs_long + } + if mode in mapping: + ret = mapping[mode] + if type(ret) != list: + ret = ret() + return ret + elif mode == '#n': + return rand_immediate(size) + elif mode.startswith('#(') and mode.endswith(')'): + inner = mode[2:-1] + start,sep,end = inner.partition('-') + return [Immediate(num) for num in range(int(start), int(end))] + +class Inst2Op(object): + def __init__(self, name, size, src, dst): + self.name = name + self.size = size + self.src = src + self.dst = dst + + def __str__(self): + return self.name + '.' + self.size + ' ' + str(self.src) + ', ' + str(self.dst) + + def write_init(self, outfile, already): + self.src.write_init(outfile, self.size, already) + self.dst.write_init(outfile, self.size, already) + + def invalidate_dest(self, already): + if type(self.dst) == Register: + del already[str(self.dst)] + + def save_result(self, reg, always): + if always or type(self.dst) != Register: + return 'move.' + self.size + ' ' + str(self.dst) + ', ' + str(reg) + else: + return '' + + def consume_regs(self, program): + self.src.consume_regs(program) + self.dst.consume_regs(program) + +class Entry(object): + def __init__(self, line): + fields = split_fields(line) + self.name = fields[0] + sizes = fields[1] + sources = fields[2].split(';') + dests = fields[3].split(';') + combos = [] + for size in sizes: + for source in sources: + if size != 'b' or source != 'a': + for dest in dests: + if size != 'b' or dest != 'a': + combos.append((size, source, dest)) + self.cases = combos + + def programs(self): + res = [] + for (size, src, dst) in self.cases: + sources = get_variations(src, size) + dests = get_variations(dst, size) + for source in sources: + for dest in dests: + res.append(Program(Inst2Op(self.name, size, source, dest))) + return res + +def process_entries(f): + entries = [] + for line in f: + if not line.startswith('Name') and not line.startswith('#') and len(line.strip()) > 0: + entries.append(Entry(line)) + return entries + + +def main(args): + entries = process_entries(open('testcases.txt')) + for entry in entries: + programs = entry.programs() + for program in programs: + f = open('generated_tests/' + program.name() + '.s68', 'w') + program.write_rom_test(f) + f.close() + +if __name__ == '__main__': + import sys + main(sys.argv) + diff -r 4d4559b04c59 -r 9126c33cc33c maketests.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/maketests.py Fri Apr 19 09:29:37 2013 -0700 @@ -0,0 +1,22 @@ +#!/usr/bin/env python +from glob import glob +import subprocess +from sys import exit + +sources = set() +for path in glob('generated_tests/*.s68'): + sources.add(path) + +bins = set() +for path in glob('generated_tests/*.bin'): + bins.add(path) + +for path in sources: + binpath = path.replace('.s68', '.bin') + if not binpath in bins: + print binpath + res = subprocess.call(['vasmm68k_mot', '-Fbin', '-m68000', '-no-opt', '-spaces', '-o', binpath, path]) + if res != 0: + print 'vasmm68k_mot returned non-zero status code', res + exit(1) +