view gentests.py @ 380:1c8d74f2ab0b

Make the PSG and YM2612 use the master clock internal with an increment based on clock divider so that they stay perflectly in sync. Run both the PSG and YM2612 whenver one of them needs to be run.
author Mike Pavone <pavone@retrodev.com>
date Mon, 03 Jun 2013 21:43:38 -0700
parents 8db584faac4b
children bfbb8613efb4
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 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)
				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.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 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

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)