view gentests.py @ 995:2bc27415565b

Fix some stuff with interrupt timing. The change in adjust_int_cycle gets Overdrive working again (vint was not being preferred over hint in some cases). One of the changes seems to have broken Fatal Rewind again, but no other regressions that I can see.
author Michael Pavone <pavone@retrodev.com>
date Sat, 30 Apr 2016 08:37:55 -0700
parents 097c172839d4
children 188a60def81f
line wrap: on
line source

#!/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 dirname(self):
		return self.inst.name + '_' + self.inst.size
	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')
		needdivzero = self.inst.name.startswith('div')
		needchk = self.inst.name.startswith('chk')
		for i in xrange(0x8, 0x100, 0x4):
			if needdivzero and i == 0x14:
				outfile.write('\tdc.l div_zero_handler\n')
			elif needchk and i == 0x18:
				outfile.write('\tdc.l chk_handler\n')
			else:
				outfile.write('\tdc.l empty_handler\n')
		outfile.write('\tdc.b "SEGA"\nempty_handler:\n\trte\n')
		if needdivzero:
			outfile.write('div_zero_handler:\n')
			div_zero_count = self.get_dreg()
			outfile.write('\taddq #1, ' + str(div_zero_count) + '\n')
			outfile.write('\trte\n')
		if needchk:
			outfile.write('chk_handler:\n')
			chk_count = self.get_dreg()
			outfile.write('\taddq #1, ' + str(chk_count) + '\n')
			outfile.write('\trte\n')
		outfile.write('start:\n\tmove #0, CCR\n')
		if needdivzero:
			outfile.write('\tmoveq #0, ' + str(div_zero_count) + '\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 Dummy(object):
	def __str__(self):
		return ''
	def write_init(self, outfile, size, already):
		pass
	def consume_regs(self, program):
		pass

dummy_op = Dummy()

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 self.base == self.index:
				if str(self.base) in already:
					if not valid_ram_address(already[str(self.base)]*2):
						del already[str(self.base)]
						self.write_init(outfile, size, already)
						return
					else:
						base = index = already[str(self.base)]
				else:
					base = index = already[str(self.base)] = random_ram_address()/2
					outfile.write('\tmove.l #' + str(base) + ', ' + str(self.base) + '\n')
			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)
				if self.disp > 127:
					self.disp = 127
				elif self.disp < -128:
					self.disp = -128
				address = base + index + self.disp
			elif (address & 0xFFFFFF) > 0xFFFFFC:
				self.disp -= (address & 0xFFFFFF) - 0xFFFFFC
				if self.disp > 127:
					self.disp = 127
				elif self.disp < -128:
					self.disp = -128
				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.rpartition('-')
		start,end = int(start),int(end)
		if end-start > 16:
			return [Immediate(randint(start, end)) for x in range(0,8)]
		else:
			return [Immediate(num) for num in range(start, end+1)]
	else:
		print "Don't know what to do with source type", mode
		return None
		
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:
			if type(self.dst) == Decrement:
				src = Increment(self.dst.reg)
			elif type(self.dst) == Increment:
				src = Decrement(self.dst.reg)
			else:
				src = self.dst
			return 'move.' + self.size + ' ' + str(src) + ', ' + str(reg)
		else:
			return ''
	
	def consume_regs(self, program):
		self.src.consume_regs(program)
		self.dst.consume_regs(program)

class Inst1Op(Inst2Op):
	def __init__(self, name, size, dst):
		super(Inst1Op, self).__init__(name, size, dummy_op, dst)
	
	def __str__(self):
		return self.name + '.' + self.size + ' ' + str(self.dst)

class Entry(object):
	def __init__(self, line):
		fields = split_fields(line)
		self.name = fields[0]
		sizes = fields[1]
		sources = fields[2].split(';')
		if len(fields) > 3:
			dests = fields[3].split(';')
		else:
			dests = None
		combos = []
		for size in sizes:
			for source in sources:
				if size != 'b' or source != 'a':
					if dests:
						for dest in dests:
							if size != 'b' or dest != 'a':
								combos.append((size, source, dest))
					else:
						combos.append((size, None, source))
		self.cases = combos
		
	def programs(self):
		res = []
		for (size, src, dst) in self.cases:
			dests = get_variations(dst, size)
			if src:
				sources = get_variations(src, size)
				for source in sources:
					for dest in dests:
						res.append(Program(Inst2Op(self.name, size, source, dest)))
			else:
				for dest in dests:
					res.append(Program(Inst1Op(self.name, size, 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

from os import path, mkdir
def main(args):
	entries = process_entries(open('testcases.txt'))
	for entry in entries:
		programs = entry.programs()
		for program in programs:
			dname = program.dirname()
			if not path.exists('generated_tests/' + dname):
				mkdir('generated_tests/' + dname)
			f = open('generated_tests/' + dname + '/' + program.name() + '.s68', 'w')
			program.write_rom_test(f)
			f.close()
	
if __name__ == '__main__':
	import sys
	main(sys.argv)