main
gentests.py
1#!/usr/bin/env python
2
3def 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
12class 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 dirname(self):
20 return self.inst.name + '_' + self.inst.size
21 def name(self):
22 return str(self.inst).replace('.', '_').replace('#', '_').replace(',', '_').replace(' ', '_').replace('(', '[').replace(')', ']')
23
24 def write_rom_test(self, outfile):
25 outfile.write('\tdc.l $0, start\n')
26 needdivzero = self.inst.name.startswith('div')
27 needchk = self.inst.name.startswith('chk')
28 for i in xrange(0x8, 0x100, 0x4):
29 if needdivzero and i == 0x14:
30 outfile.write('\tdc.l div_zero_handler\n')
31 elif needchk and i == 0x18:
32 outfile.write('\tdc.l chk_handler\n')
33 else:
34 outfile.write('\tdc.l empty_handler\n')
35 outfile.write('\tdc.b "SEGA"\nempty_handler:\n\trte\n')
36 if needdivzero:
37 outfile.write('div_zero_handler:\n')
38 div_zero_count = self.get_dreg()
39 outfile.write('\taddq #1, ' + str(div_zero_count) + '\n')
40 outfile.write('\trte\n')
41 if needchk:
42 outfile.write('chk_handler:\n')
43 chk_count = self.get_dreg()
44 outfile.write('\taddq #1, ' + str(chk_count) + '\n')
45 outfile.write('\trte\n')
46 outfile.write('start:\n\tmove #0, CCR\n')
47 if needdivzero:
48 outfile.write('\tmoveq #0, ' + str(div_zero_count) + '\n')
49 already = {}
50 self.inst.write_init(outfile, already)
51 if 'label' in already:
52 outfile.write('lbl_' + str(already['label']) + ':\n')
53 outfile.write('\t'+str(self.inst)+'\n')
54 outfile.write('\t'+self.inst.save_result(self.get_dreg(), True) + '\n')
55 save_ccr = self.get_dreg()
56 outfile.write('\tmove SR, ' + str(save_ccr) + '\n')
57 outfile.write('\tmove #$1F, CCR\n')
58 self.inst.invalidate_dest(already)
59 self.inst.write_init(outfile, already)
60 if 'label' in already:
61 outfile.write('lbl_' + str(already['label']) + ':\n')
62 outfile.write('\t'+str(self.inst)+'\n')
63 outfile.write('\t'+self.inst.save_result(self.get_dreg(), False) + '\n')
64 outfile.write('\treset\nforever:\n\tbra.s forever\n')
65
66 def consume_dreg(self, num):
67 self.avail_dregs.discard(num)
68
69 def consume_areg(self, num):
70 self.avail_aregs.discard(num)
71
72 def get_dreg(self):
73 return Register('d', self.avail_dregs.pop())
74
75class Dummy(object):
76 def __str__(self):
77 return ''
78 def write_init(self, outfile, size, already):
79 pass
80 def consume_regs(self, program):
81 pass
82
83dummy_op = Dummy()
84
85class Register(object):
86 def __init__(self, kind, num):
87 self.kind = kind
88 self.num = num
89
90 def __str__(self):
91 if self.kind == 'd' or self.kind == 'a':
92 return self.kind + str(self.num)
93 return self.kind
94
95 def write_init(self, outfile, size, already):
96 if not str(self) in already:
97 minv,maxv = get_size_range(size)
98 val = randint(minv,maxv)
99 already[str(self)] = val
100 outfile.write('\tmove.'+size+' #'+str(val)+', ' + str(self) + '\n')
101
102 def consume_regs(self, program):
103 if self.kind == 'd':
104 program.consume_dreg(self.num)
105 elif self.kind == 'a':
106 program.consume_areg(self.num)
107
108def valid_ram_address(address, size='b'):
109 return address >= 0xE00000 and address <= 0xFFFFFFFC and (address & 0xE00000) == 0xE00000 and (size == 'b' or not address & 1)
110
111def random_ram_address(mina=0xE00000, maxa=0xFFFFFFFC):
112 return randint(mina/2, maxa/2)*2 | 0xE00000
113
114class Indexed(object):
115 def __init__(self, base, index, index_size, disp):
116 self.base = base
117 self.index = index
118 self.index_size = index_size
119 self.disp = disp
120
121 def write_init(self, outfile, size, already):
122 if self.base.kind == 'pc':
123 if str(self.index) in already:
124 index = already[str(self.index)]
125 if self.index_size == 'w':
126 index = index & 0xFFFF
127 #sign extend index
128 if index & 0x8000:
129 index -= 65536
130 if index > -1024:
131 index = already[str(self.index)] = 2 * randint(-16384, -512)
132 outfile.write('\tmove.l #' + str(index) + ', ' + str(self.index) + '\n')
133 else:
134 index = already[str(self.index)] = 2 * randint(-16384, -512)
135 outfile.write('\tmove.l #' + str(index) + ', ' + str(self.index) + '\n')
136 num = already.get('label', 0)+1
137 already['label'] = num
138 if (already[str(self.index)] + self.disp) & 1:
139 self.disp += 1
140 address = 'lbl_' + str(num) + ' + 2 + ' + str(self.disp) + ' + ' + str(index)
141 else:
142 if self.base == self.index:
143 if str(self.base) in already:
144 if not valid_ram_address(already[str(self.base)]*2):
145 del already[str(self.base)]
146 self.write_init(outfile, size, already)
147 return
148 else:
149 base = index = already[str(self.base)]
150 else:
151 base = index = already[str(self.base)] = random_ram_address()/2
152 outfile.write('\tmove.l #' + str(base) + ', ' + str(self.base) + '\n')
153 else:
154 if str(self.base) in already:
155 if not valid_ram_address(already[str(self.base)]):
156 del already[str(self.base)]
157 self.write_init(outfile, size, already)
158 return
159 else:
160 base = already[str(self.base)]
161 else:
162 base = already[str(self.base)] = random_ram_address()
163 outfile.write('\tmove.l #' + str(base) + ', ' + str(self.base) + '\n')
164 if str(self.index) in already:
165 index = already[str(self.index)]
166 if self.index_size == 'w':
167 index = index & 0xFFFF
168 #sign extend index
169 if index & 0x8000:
170 index -= 65536
171 if not valid_ram_address(base + index):
172 index = already[str(self.index)] = randint(-64, 63)
173 outfile.write('\tmove.l #' + str(index) + ', ' + str(self.index) + '\n')
174 else:
175 index = already[str(self.index)] = randint(-64, 63)
176 outfile.write('\tmove.l #' + str(index) + ', ' + str(self.index) + '\n')
177 address = base + index + self.disp
178 if (address & 0xFFFFFF) < 0xE00000:
179 if (address & 0xFFFFFF) < 128:
180 self.disp -= (address & 0xFFFFFF)
181 else:
182 self.disp += 0xE00000-(address & 0xFFFFFF)
183 if self.disp > 127:
184 self.disp = 127
185 elif self.disp < -128:
186 self.disp = -128
187 address = base + index + self.disp
188 elif (address & 0xFFFFFF) > 0xFFFFFC:
189 self.disp -= (address & 0xFFFFFF) - 0xFFFFFC
190 if self.disp > 127:
191 self.disp = 127
192 elif self.disp < -128:
193 self.disp = -128
194 address = base + index + self.disp
195 if size != 'b' and address & 1:
196 self.disp = self.disp ^ 1
197 address = base + index + self.disp
198 minv,maxv = get_size_range(size)
199 outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n')
200
201 def __str__(self):
202 return '(' + str(self.disp) + ', ' + str(self.base) + ', ' + str(self.index) + '.' + self.index_size + ')'
203
204 def consume_regs(self, program):
205 self.base.consume_regs(program)
206 self.index.consume_regs(program)
207
208class Displacement(object):
209 def __init__(self, base, disp):
210 self.base = base
211 if disp & 1:
212 disp += 1
213 self.disp = disp
214
215 def write_init(self, outfile, size, already):
216 if self.base.kind == 'pc':
217 num = already.get('label', 0)+1
218 already['label'] = num
219 address = 'lbl_' + str(num) + ' + 2 + ' + str(self.disp)
220 else:
221 if str(self.base) in already:
222 if not valid_ram_address(already[str(self.base)]):
223 del already[str(self.base)]
224 self.write_init(outfile, size, already)
225 return
226 else:
227 base = already[str(self.base)]
228 else:
229 base = already[str(self.base)] = random_ram_address()
230 outfile.write('\tmove.l #' + str(base) + ', ' + str(self.base) + '\n')
231 address = base + self.disp
232 if (address & 0xFFFFFF) < 0xE00000:
233 if (address & 0xFFFFFF) < 0x10000:
234 self.disp -= (address & 0xFFFFFF)
235 else:
236 self.disp += 0xE00000-(address & 0xFFFFFF)
237 address = base + self.disp
238 elif (address & 0xFFFFFF) > 0xFFFFFC:
239 self.disp -= (address & 0xFFFFFF) - 0xFFFFFC
240 address = base + self.disp
241 if size != 'b' and address & 1:
242 self.disp = self.disp ^ 1
243 address = base + self.disp
244 minv,maxv = get_size_range(size)
245 outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n')
246
247 def __str__(self):
248 return '(' + str(self.disp) + ', ' + str(self.base) + ')'
249
250 def consume_regs(self, program):
251 self.base.consume_regs(program)
252
253class Indirect(object):
254 def __init__(self, reg):
255 self.reg = reg
256
257 def __str__(self):
258 return '(' + str(self.reg) + ')'
259
260 def write_init(self, outfile, size, already):
261 if str(self.reg) in already:
262 if not valid_ram_address(already[str(self.reg)], size):
263 del already[str(self.reg)]
264 self.write_init(outfile, size, already)
265 return
266 else:
267 address = already[str(self.reg)]
268 else:
269 address = random_ram_address()
270 if size != 'b':
271 address = address & 0xFFFFFFFE
272 outfile.write('\tmove.l #' + str(address) + ', ' + str(self.reg) + '\n')
273 already[str(self.reg)] = address
274 minv,maxv = get_size_range(size)
275 outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n')
276
277 def consume_regs(self, program):
278 self.reg.consume_regs(program)
279
280class Increment(object):
281 def __init__(self, reg):
282 self.reg = reg
283
284 def __str__(self):
285 return '(' + str(self.reg) + ')+'
286
287 def write_init(self, outfile, size, already):
288 if str(self.reg) in already:
289 if not valid_ram_address(already[str(self.reg)], size):
290 del already[str(self.reg)]
291 self.write_init(outfile, size, already)
292 return
293 else:
294 address = already[str(self.reg)]
295 else:
296 address = random_ram_address()
297 if size != 'b':
298 address = address & 0xFFFFFFFE
299 outfile.write('\tmove.l #' + str(address) + ', ' + str(self.reg) + '\n')
300 already[str(self.reg)] = address
301 minv,maxv = get_size_range(size)
302 outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n')
303
304 def consume_regs(self, program):
305 self.reg.consume_regs(program)
306
307class Decrement(object):
308 def __init__(self, reg):
309 self.reg = reg
310
311 def __str__(self):
312 return '-(' + str(self.reg) + ')'
313
314 def write_init(self, outfile, size, already):
315 if str(self.reg) in already:
316 if not valid_ram_address(already[str(self.reg)]- 4 if size == 'l' else 2 if size == 'w' else 1, size):
317 del already[str(self.reg)]
318 self.write_init(outfile, size, already)
319 return
320 else:
321 address = already[str(self.reg)]
322 else:
323 address = random_ram_address(mina=0xE00004)
324 if size != 'b':
325 address = address & 0xFFFFFFFE
326 outfile.write('\tmove.l #' + str(address) + ', ' + str(self.reg) + '\n')
327 already[str(self.reg)] = address
328 minv,maxv = get_size_range(size)
329 outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', (' + str(address) + ').l\n')
330
331 def consume_regs(self, program):
332 self.reg.consume_regs(program)
333
334class Absolute(object):
335 def __init__(self, address, size):
336 self.address = address
337 self.size = size
338
339 def __str__(self):
340 return '(' + str(self.address) + ').' + self.size
341
342 def write_init(self, outfile, size, already):
343 minv,maxv = get_size_range(size)
344 outfile.write('\tmove.' + size + ' #' + str(randint(minv, maxv)) + ', '+str(self)+'\n')
345
346 def consume_regs(self, program):
347 pass
348
349class Immediate(object):
350 def __init__(self, value):
351 self.value = value
352
353 def __str__(self):
354 return '#' + str(self.value)
355
356 def write_init(self, outfile, size, already):
357 pass
358
359 def consume_regs(self, program):
360 pass
361
362all_dregs = [Register('d', i) for i in range(0, 8)]
363all_aregs = [Register('a', i) for i in range(0, 8)]
364all_indirect = [Indirect(reg) for reg in all_aregs]
365all_predec = [Decrement(reg) for reg in all_aregs]
366all_postinc = [Increment(reg) for reg in all_aregs]
367from random import randint
368def all_indexed():
369 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')]
370
371def all_disp():
372 return [Displacement(base, randint(-32768, 32767)) for base in all_aregs]
373
374def rand_pc_disp():
375 return [Displacement(Register('pc', 0), randint(-32768, -1024)) for x in xrange(0, 8)]
376
377def all_pc_indexed():
378 return [Indexed(Register('pc', 0), index, index_size, randint(-128, 127)) for index in all_dregs + all_aregs for index_size in ('w','l')]
379
380def rand_abs_short():
381 return [Absolute(random_ram_address(0xFFFF8000), 'w') for x in xrange(0, 8)]
382
383def rand_abs_long():
384 return [Absolute(random_ram_address(), 'l') for x in xrange(0, 8)]
385
386def get_size_range(size):
387 if size == 'b':
388 return (-128, 127)
389 elif size == 'w':
390 return (-32768, 32767)
391 else:
392 return (-2147483648, 2147483647)
393
394def rand_immediate(size):
395 minv,maxv = get_size_range(size)
396
397 return [Immediate(randint(minv, maxv)) for x in xrange(0,8)]
398
399def get_variations(mode, size):
400 mapping = {
401 'd':all_dregs,
402 'a':all_aregs,
403 '(a)':all_indirect,
404 '-(a)':all_predec,
405 '(a)+':all_postinc,
406 '(n,a)':all_disp,
407 '(n,a,x)':all_indexed,
408 '(n,pc)':rand_pc_disp,
409 '(n,pc,x)':all_pc_indexed,
410 '(n).w':rand_abs_short,
411 '(n).l':rand_abs_long
412 }
413 if mode in mapping:
414 ret = mapping[mode]
415 if type(ret) != list:
416 ret = ret()
417 return ret
418 elif mode == '#n':
419 return rand_immediate(size)
420 elif mode.startswith('#(') and mode.endswith(')'):
421 inner = mode[2:-1]
422 start,sep,end = inner.rpartition('-')
423 start,end = int(start),int(end)
424 if end-start > 16:
425 return [Immediate(randint(start, end)) for x in range(0,8)]
426 else:
427 return [Immediate(num) for num in range(start, end+1)]
428 else:
429 print "Don't know what to do with source type", mode
430 return None
431
432class Inst2Op(object):
433 def __init__(self, name, size, src, dst):
434 self.name = name
435 self.size = size
436 self.src = src
437 self.dst = dst
438
439 def __str__(self):
440 return self.name + '.' + self.size + ' ' + str(self.src) + ', ' + str(self.dst)
441
442 def write_init(self, outfile, already):
443 self.src.write_init(outfile, self.size, already)
444 self.dst.write_init(outfile, self.size, already)
445
446 def invalidate_dest(self, already):
447 if type(self.dst) == Register:
448 del already[str(self.dst)]
449
450 def save_result(self, reg, always):
451 if always or type(self.dst) != Register:
452 if type(self.dst) == Decrement:
453 src = Increment(self.dst.reg)
454 elif type(self.dst) == Increment:
455 src = Decrement(self.dst.reg)
456 else:
457 src = self.dst
458 return 'move.' + self.size + ' ' + str(src) + ', ' + str(reg)
459 else:
460 return ''
461
462 def consume_regs(self, program):
463 self.src.consume_regs(program)
464 self.dst.consume_regs(program)
465
466class Inst1Op(Inst2Op):
467 def __init__(self, name, size, dst):
468 super(Inst1Op, self).__init__(name, size, dummy_op, dst)
469
470 def __str__(self):
471 return self.name + '.' + self.size + ' ' + str(self.dst)
472
473class Entry(object):
474 def __init__(self, line):
475 fields = split_fields(line)
476 self.name = fields[0]
477 sizes = fields[1]
478 sources = fields[2].split(';')
479 if len(fields) > 3:
480 dests = fields[3].split(';')
481 else:
482 dests = None
483 combos = []
484 for size in sizes:
485 for source in sources:
486 if size != 'b' or source != 'a':
487 if dests:
488 for dest in dests:
489 if size != 'b' or dest != 'a':
490 combos.append((size, source, dest))
491 else:
492 combos.append((size, None, source))
493 self.cases = combos
494
495 def programs(self):
496 res = []
497 for (size, src, dst) in self.cases:
498 dests = get_variations(dst, size)
499 if src:
500 sources = get_variations(src, size)
501 for source in sources:
502 for dest in dests:
503 res.append(Program(Inst2Op(self.name, size, source, dest)))
504 else:
505 for dest in dests:
506 res.append(Program(Inst1Op(self.name, size, dest)))
507 return res
508
509def process_entries(f):
510 entries = []
511 for line in f:
512 if not line.startswith('Name') and not line.startswith('#') and len(line.strip()) > 0:
513 entries.append(Entry(line))
514 return entries
515
516from os import path, mkdir
517def main(args):
518 entries = process_entries(open('testcases.txt'))
519 for entry in entries:
520 programs = entry.programs()
521 for program in programs:
522 dname = program.dirname()
523 if not path.exists('generated_tests/' + dname):
524 mkdir('generated_tests/' + dname)
525 f = open('generated_tests/' + dname + '/' + program.name() + '.s68', 'w')
526 program.write_rom_test(f)
527 f.close()
528
529if __name__ == '__main__':
530 import sys
531 main(sys.argv)
532