comparison gentests.py @ 214:9126c33cc33c

Add test generator, builder and runner
author Mike Pavone <pavone@retrodev.com>
date Fri, 19 Apr 2013 09:29:37 -0700
parents
children acd29e2664c6
comparison
equal deleted inserted replaced
213:4d4559b04c59 214:9126c33cc33c
1 #!/usr/bin/env python
2
3 def split_fields(line):
4 parts = []
5 while line:
6 field,_,line = line.partition('\t')
7 parts.append(field.strip())
8 while line.startswith('\t'):
9 line = line[1:]
10 return parts
11
12 class Program(object):
13 def __init__(self, instruction):
14 self.avail_dregs = {0,1,2,3,4,5,6,7}
15 self.avail_aregs = {0,1,2,3,4,5,6,7}
16 instruction.consume_regs(self)
17 self.inst = instruction
18
19 def name(self):
20 return str(self.inst).replace('.', '_').replace('#', '_').replace(',', '_').replace(' ', '_').replace('(', '[').replace(')', ']')
21
22 def write_rom_test(self, outfile):
23 outfile.write('\tdc.l $0, start\n')
24 for i in xrange(0x8, 0x100, 0x4):
25 outfile.write('\tdc.l empty_handler\n')
26 outfile.write('\tdc.b "SEGA"\nempty_handler:\n\trte\nstart:\n')
27 outfile.write('\tmove #0, CCR\n')
28 already = {}
29 self.inst.write_init(outfile, already)
30 if 'label' in already:
31 outfile.write('lbl_' + str(already['label']) + ':\n')
32 outfile.write('\t'+str(self.inst)+'\n')
33 outfile.write('\t'+self.inst.save_result(self.get_dreg(), True) + '\n')
34 save_ccr = self.get_dreg()
35 outfile.write('\tmove SR, ' + str(save_ccr) + '\n')
36 outfile.write('\tmove #$1F, CCR\n')
37 self.inst.invalidate_dest(already)
38 self.inst.write_init(outfile, already)
39 if 'label' in already:
40 outfile.write('lbl_' + str(already['label']) + ':\n')
41 outfile.write('\t'+str(self.inst)+'\n')
42 outfile.write('\t'+self.inst.save_result(self.get_dreg(), False) + '\n')
43 outfile.write('\treset\n')
44
45 def consume_dreg(self, num):
46 self.avail_dregs.discard(num)
47
48 def consume_areg(self, num):
49 self.avail_aregs.discard(num)
50
51 def get_dreg(self):
52 return Register('d', self.avail_dregs.pop())
53
54 class Register(object):
55 def __init__(self, kind, num):
56 self.kind = kind
57 self.num = num
58
59 def __str__(self):
60 if self.kind == 'd' or self.kind == 'a':
61 return self.kind + str(self.num)
62 return self.kind
63
64 def write_init(self, outfile, size, already):
65 if not str(self) in already:
66 minv,maxv = get_size_range(size)
67 val = randint(minv,maxv)
68 already[str(self)] = val
69 outfile.write('\tmove.'+size+' #'+str(val)+', ' + str(self) + '\n')
70
71 def consume_regs(self, program):
72 if self.kind == 'd':
73 program.consume_dreg(self.num)
74 elif self.kind == 'a':
75 program.consume_areg(self.num)
76
77 def valid_ram_address(address, size='b'):
78 return address >= 0xE00000 and address <= 0xFFFFFFFC and (address & 0xE00000) == 0xE00000 and (size == 'b' or not address & 1)
79
80 def random_ram_address(mina=0xE00000, maxa=0xFFFFFFFC):
81 return randint(mina, maxa) | 0xE00000
82
83 class Indexed(object):
84 def __init__(self, base, index, index_size, disp):
85 self.base = base
86 self.index = index
87 self.index_size = index_size
88 self.disp = disp
89
90 def write_init(self, outfile, size, already):
91 if self.base.kind == 'pc':
92 if str(self.index) in already:
93 index = already[str(self.index)]
94 if self.index_size == 'w':
95 index = index & 0xFFFF
96 #sign extend index
97 if index & 0x8000:
98 index -= 65536
99 if index > -1024:
100 index = already[str(self.index)] = randint(-32768, -1024)
101 outfile.write('\tmove.l #' + str(index) + ', ' + str(self.index) + '\n')
102 else:
103 index = already[str(self.index)] = randint(-32768, -1024)
104 outfile.write('\tmove.l #' + str(index) + ', ' + str(self.index) + '\n')
105 num = already.get('label', 0)+1
106 already['label'] = num
107 address = 'lbl_' + str(num) + ' + 2 + ' + str(self.disp) + ' + ' + str(index)
108 else:
109 if str(self.base) in already:
110 if not valid_ram_address(already[str(self.base)]):
111 del already[str(self.base)]
112 self.write_init(outfile, size, already)
113 return
114 else:
115 base = already[str(self.base)]
116 else:
117 base = already[str(self.base)] = random_ram_address()
118 outfile.write('\tmove.l #' + str(base) + ', ' + str(self.base) + '\n')
119 if str(self.index) in already:
120 index = already[str(self.index)]
121 if self.index_size == 'w':
122 index = index & 0xFFFF
123 #sign extend index
124 if index & 0x8000:
125 index -= 65536
126 if not valid_ram_address(base + index):
127 index = already[str(self.index)] = randint(-64, 63)
128 outfile.write('\tmove.l #' + str(index) + ', ' + str(self.index) + '\n')
129 else:
130 index = already[str(self.index)] = randint(-64, 63)
131 outfile.write('\tmove.l #' + str(index) + ', ' + str(self.index) + '\n')
132 address = base + index + self.disp
133 if (address & 0xFFFFFF) < 0xE00000:
134 if (address & 0xFFFFFF) < 128:
135 self.disp -= (address & 0xFFFFFF)
136 else:
137 self.disp += 0xE00000-(address & 0xFFFFFF)
138 address = base + index + self.disp
139 elif (address & 0xFFFFFF) > 0xFFFFFC:
140 self.disp -= (address & 0xFFFFFF) - 0xFFFFFC
141 address = base + index + self.disp
142 if size != 'b' and address & 1:
143 self.disp = self.disp ^ 1
144 address = base + index + self.disp
145 minv,maxv = get_size_range(size)
146 outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n')
147
148 def __str__(self):
149 return '(' + str(self.disp) + ', ' + str(self.base) + ', ' + str(self.index) + '.' + self.index_size + ')'
150
151 def consume_regs(self, program):
152 self.base.consume_regs(program)
153 self.index.consume_regs(program)
154
155 class Displacement(object):
156 def __init__(self, base, disp):
157 self.base = base
158 self.disp = disp
159
160 def write_init(self, outfile, size, already):
161 if self.base.kind == 'pc':
162 num = already.get('label', 0)+1
163 already['label'] = num
164 address = 'lbl_' + str(num) + ' + 2 + ' + str(self.disp)
165 else:
166 if str(self.base) in already:
167 if not valid_ram_address(already[str(self.base)]):
168 del already[str(self.base)]
169 self.write_init(outfile, size, already)
170 return
171 else:
172 base = already[str(self.base)]
173 else:
174 base = already[str(self.base)] = random_ram_address()
175 outfile.write('\tmove.l #' + str(base) + ', ' + str(self.base) + '\n')
176 address = base + self.disp
177 if (address & 0xFFFFFF) < 0xE00000:
178 if (address & 0xFFFFFF) < 0x10000:
179 self.disp -= (address & 0xFFFFFF)
180 else:
181 self.disp += 0xE00000-(address & 0xFFFFFF)
182 address = base + self.disp
183 elif (address & 0xFFFFFF) > 0xFFFFFC:
184 self.disp -= (address & 0xFFFFFF) - 0xFFFFFC
185 address = base + self.disp
186 if size != 'b' and address & 1:
187 self.disp = self.disp ^ 1
188 address = base + self.disp
189 minv,maxv = get_size_range(size)
190 outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n')
191
192 def __str__(self):
193 return '(' + str(self.disp) + ', ' + str(self.base) + ')'
194
195 def consume_regs(self, program):
196 self.base.consume_regs(program)
197
198 class Indirect(object):
199 def __init__(self, reg):
200 self.reg = reg
201
202 def __str__(self):
203 return '(' + str(self.reg) + ')'
204
205 def write_init(self, outfile, size, already):
206 if str(self.reg) in already:
207 if not valid_ram_address(already[str(self.reg)], size):
208 del already[str(self.reg)]
209 self.write_init(outfile, size, already)
210 return
211 else:
212 address = already[str(self.reg)]
213 else:
214 address = random_ram_address()
215 if size != 'b':
216 address = address & 0xFFFFFFFE
217 outfile.write('\tmove.l #' + str(address) + ', ' + str(self.reg) + '\n')
218 already[str(self.reg)] = address
219 minv,maxv = get_size_range(size)
220 outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n')
221
222 def consume_regs(self, program):
223 self.reg.consume_regs(program)
224
225 class Increment(object):
226 def __init__(self, reg):
227 self.reg = reg
228
229 def __str__(self):
230 return '(' + str(self.reg) + ')+'
231
232 def write_init(self, outfile, size, already):
233 if str(self.reg) in already:
234 if not valid_ram_address(already[str(self.reg)], size):
235 del already[str(self.reg)]
236 self.write_init(outfile, size, already)
237 return
238 else:
239 address = already[str(self.reg)]
240 else:
241 address = random_ram_address()
242 if size != 'b':
243 address = address & 0xFFFFFFFE
244 outfile.write('\tmove.l #' + str(address) + ', ' + str(self.reg) + '\n')
245 already[str(self.reg)] = address
246 minv,maxv = get_size_range(size)
247 outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n')
248
249 def consume_regs(self, program):
250 self.reg.consume_regs(program)
251
252 class Decrement(object):
253 def __init__(self, reg):
254 self.reg = reg
255
256 def __str__(self):
257 return '-(' + str(self.reg) + ')'
258
259 def write_init(self, outfile, size, already):
260 if str(self.reg) in already:
261 if not valid_ram_address(already[str(self.reg)]- 4 if size == 'l' else 2 if size == 'w' else 1, size):
262 del already[str(self.reg)]
263 self.write_init(outfile, size, already)
264 return
265 else:
266 address = already[str(self.reg)]
267 else:
268 address = random_ram_address(mina=0xE00004)
269 if size != 'b':
270 address = address & 0xFFFFFFFE
271 outfile.write('\tmove.l #' + str(address) + ', ' + str(self.reg) + '\n')
272 already[str(self.reg)] = address
273 minv,maxv = get_size_range(size)
274 outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n')
275
276 def consume_regs(self, program):
277 self.reg.consume_regs(program)
278
279 class Absolute(object):
280 def __init__(self, address, size):
281 self.address = address
282 self.size = size
283
284 def __str__(self):
285 return '(' + str(self.address) + ').' + self.size
286
287 def write_init(self, outfile, size, already):
288 minv,maxv = get_size_range(size)
289 outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', '+str(self)+'\n')
290
291 def consume_regs(self, program):
292 pass
293
294 class Immediate(object):
295 def __init__(self, value):
296 self.value = value
297
298 def __str__(self):
299 return '#' + str(self.value)
300
301 def write_init(self, outfile, size, already):
302 pass
303
304 def consume_regs(self, program):
305 pass
306
307 all_dregs = [Register('d', i) for i in range(0, 8)]
308 all_aregs = [Register('a', i) for i in range(0, 8)]
309 all_indirect = [Indirect(reg) for reg in all_aregs]
310 all_predec = [Decrement(reg) for reg in all_aregs]
311 all_postinc = [Increment(reg) for reg in all_aregs]
312 from random import randint
313 def all_indexed():
314 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')]
315
316 def all_disp():
317 return [Displacement(base, randint(-32768, 32767)) for base in all_aregs]
318
319 def rand_pc_disp():
320 return [Displacement(Register('pc', 0), randint(-32768, -1024)) for x in xrange(0, 8)]
321
322 def all_pc_indexed():
323 return [Indexed(Register('pc', 0), index, index_size, randint(-128, 127)) for index in all_dregs + all_aregs for index_size in ('w','l')]
324
325 def rand_abs_short():
326 return [Absolute(0xFFFF8000 + randint(0, 32767), 'w') for x in xrange(0, 8)]
327
328 def rand_abs_long():
329 return [Absolute(0xFF0000 + randint(0, 65535), 'l') for x in xrange(0, 8)]
330
331 def get_size_range(size):
332 if size == 'b':
333 return (-128, 127)
334 elif size == 'w':
335 return (-32768, 32767)
336 else:
337 return (-2147483648, 2147483647)
338
339 def rand_immediate(size):
340 minv,maxv = get_size_range(size)
341
342 return [Immediate(randint(minv, maxv)) for x in xrange(0,8)]
343
344 def get_variations(mode, size):
345 mapping = {
346 'd':all_dregs,
347 'a':all_aregs,
348 '(a)':all_indirect,
349 '-(a)':all_predec,
350 '(a)+':all_postinc,
351 '(n,a)':all_disp,
352 '(n,a,x)':all_indexed,
353 '(n,pc)':rand_pc_disp,
354 '(n,pc,x)':all_pc_indexed,
355 '(n).w':rand_abs_short,
356 '(n).l':rand_abs_long
357 }
358 if mode in mapping:
359 ret = mapping[mode]
360 if type(ret) != list:
361 ret = ret()
362 return ret
363 elif mode == '#n':
364 return rand_immediate(size)
365 elif mode.startswith('#(') and mode.endswith(')'):
366 inner = mode[2:-1]
367 start,sep,end = inner.partition('-')
368 return [Immediate(num) for num in range(int(start), int(end))]
369
370 class Inst2Op(object):
371 def __init__(self, name, size, src, dst):
372 self.name = name
373 self.size = size
374 self.src = src
375 self.dst = dst
376
377 def __str__(self):
378 return self.name + '.' + self.size + ' ' + str(self.src) + ', ' + str(self.dst)
379
380 def write_init(self, outfile, already):
381 self.src.write_init(outfile, self.size, already)
382 self.dst.write_init(outfile, self.size, already)
383
384 def invalidate_dest(self, already):
385 if type(self.dst) == Register:
386 del already[str(self.dst)]
387
388 def save_result(self, reg, always):
389 if always or type(self.dst) != Register:
390 return 'move.' + self.size + ' ' + str(self.dst) + ', ' + str(reg)
391 else:
392 return ''
393
394 def consume_regs(self, program):
395 self.src.consume_regs(program)
396 self.dst.consume_regs(program)
397
398 class Entry(object):
399 def __init__(self, line):
400 fields = split_fields(line)
401 self.name = fields[0]
402 sizes = fields[1]
403 sources = fields[2].split(';')
404 dests = fields[3].split(';')
405 combos = []
406 for size in sizes:
407 for source in sources:
408 if size != 'b' or source != 'a':
409 for dest in dests:
410 if size != 'b' or dest != 'a':
411 combos.append((size, source, dest))
412 self.cases = combos
413
414 def programs(self):
415 res = []
416 for (size, src, dst) in self.cases:
417 sources = get_variations(src, size)
418 dests = get_variations(dst, size)
419 for source in sources:
420 for dest in dests:
421 res.append(Program(Inst2Op(self.name, size, source, dest)))
422 return res
423
424 def process_entries(f):
425 entries = []
426 for line in f:
427 if not line.startswith('Name') and not line.startswith('#') and len(line.strip()) > 0:
428 entries.append(Entry(line))
429 return entries
430
431
432 def main(args):
433 entries = process_entries(open('testcases.txt'))
434 for entry in entries:
435 programs = entry.programs()
436 for program in programs:
437 f = open('generated_tests/' + program.name() + '.s68', 'w')
438 program.write_rom_test(f)
439 f.close()
440
441 if __name__ == '__main__':
442 import sys
443 main(sys.argv)
444