summaryrefslogtreecommitdiff
path: root/gentests.py
diff options
context:
space:
mode:
authorMike Pavone <pavone@retrodev.com>2013-04-19 09:29:37 -0700
committerMike Pavone <pavone@retrodev.com>2013-04-19 09:29:37 -0700
commit1a6e3d076d0f73ad1ab1cc5b808a772d371021df (patch)
tree37ef9d213dd363673dc8d5dbe1ae0acd082ee848 /gentests.py
parent9d263ae4d5ae12022177b4dda9120c88b20604df (diff)
Add test generator, builder and runner
Diffstat (limited to 'gentests.py')
-rwxr-xr-xgentests.py444
1 files changed, 444 insertions, 0 deletions
diff --git a/gentests.py b/gentests.py
new file mode 100755
index 0000000..f314835
--- /dev/null
+++ b/gentests.py
@@ -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)
+