Mercurial > repos > blastem
comparison cpu_dsl.py @ 1749:e4fe5a450d05
Added option to CPU DSL to produce a threaded interpreter using computed goto
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 08 Feb 2019 23:09:58 -0800 |
parents | 48a43dff4dc0 |
children | 01236179fc71 |
comparison
equal
deleted
inserted
replaced
1748:48a43dff4dc0 | 1749:e4fe5a450d05 |
---|---|
128 for var in self.locals: | 128 for var in self.locals: |
129 output.append('\n\tuint{sz}_t {name};'.format(sz=self.locals[var], name=var)) | 129 output.append('\n\tuint{sz}_t {name};'.format(sz=self.locals[var], name=var)) |
130 self.newLocals = [] | 130 self.newLocals = [] |
131 fieldVals,_ = self.getFieldVals(value) | 131 fieldVals,_ = self.getFieldVals(value) |
132 self.processOps(prog, fieldVals, output, otype, self.implementation) | 132 self.processOps(prog, fieldVals, output, otype, self.implementation) |
133 begin = '\nvoid ' + self.generateName(value) + '(' + prog.context_type + ' *context)\n{' | 133 |
134 if prog.dispatch == 'call': | |
135 begin = '\nvoid ' + self.generateName(value) + '(' + prog.context_type + ' *context)\n{' | |
136 elif prog.dispatch == 'goto': | |
137 begin = '\n' + self.generateName(value) + ': {' | |
138 else: | |
139 raise Exception('Unsupported dispatch type ' + prog.dispatch) | |
134 if prog.needFlagCoalesce: | 140 if prog.needFlagCoalesce: |
135 begin += prog.flags.coalesceFlags(prog, otype) | 141 begin += prog.flags.coalesceFlags(prog, otype) |
136 if prog.needFlagDisperse: | 142 if prog.needFlagDisperse: |
137 output.append(prog.flags.disperseFlags(prog, otype)) | 143 output.append(prog.flags.disperseFlags(prog, otype)) |
138 for var in self.newLocals: | 144 for var in self.newLocals: |
139 begin += '\n\tuint{sz}_t {name};'.format(sz=self.locals[var], name=var) | 145 begin += '\n\tuint{sz}_t {name};'.format(sz=self.locals[var], name=var) |
140 prog.popScope() | 146 prog.popScope() |
147 if prog.dispatch == 'goto': | |
148 output += prog.nextInstruction(otype) | |
141 return begin + ''.join(output) + '\n}' | 149 return begin + ''.join(output) + '\n}' |
142 | 150 |
143 def __str__(self): | 151 def __str__(self): |
144 pieces = [self.name + ' ' + hex(self.value) + ' ' + str(self.fields)] | 152 pieces = [self.name + ' ' + hex(self.value) + ' ' + str(self.fields)] |
145 for name in self.locals: | 153 for name in self.locals: |
325 def _dispatchCImpl(prog, params): | 333 def _dispatchCImpl(prog, params): |
326 if len(params) == 1: | 334 if len(params) == 1: |
327 table = 'main' | 335 table = 'main' |
328 else: | 336 else: |
329 table = params[1] | 337 table = params[1] |
330 return '\n\timpl_{tbl}[{op}](context);'.format(tbl = table, op = params[0]) | 338 if prog.dispatch == 'call': |
339 return '\n\timpl_{tbl}[{op}](context);'.format(tbl = table, op = params[0]) | |
340 elif prog.dispatch == 'goto': | |
341 return '\n\tgoto *impl_{tbl}[{op}];'.format(tbl = table, op = params[0]) | |
342 else: | |
343 raise Exception('Unsupported dispatch type ' + prog.dispatch) | |
331 | 344 |
332 def _updateFlagsCImpl(prog, params, rawParams): | 345 def _updateFlagsCImpl(prog, params, rawParams): |
333 autoUpdate, explicit = prog.flags.parseFlagUpdate(params[0]) | 346 autoUpdate, explicit = prog.flags.parseFlagUpdate(params[0]) |
334 output = [] | 347 output = [] |
335 parity = None | 348 parity = None |
1313 hFile.write('\n' + decl) | 1326 hFile.write('\n' + decl) |
1314 hFile.write('\n#endif //{0}_'.format(macro)) | 1327 hFile.write('\n#endif //{0}_'.format(macro)) |
1315 hFile.write('\n') | 1328 hFile.write('\n') |
1316 hFile.close() | 1329 hFile.close() |
1317 | 1330 |
1318 def _buildTable(self, otype, table, body): | 1331 def _buildTable(self, otype, table, body, lateBody): |
1319 pieces = [] | 1332 pieces = [] |
1320 opmap = [None] * (1 << self.opsize) | 1333 opmap = [None] * (1 << self.opsize) |
1321 bodymap = {} | 1334 bodymap = {} |
1322 if table in self.instructions: | 1335 if table in self.instructions: |
1323 instructions = self.instructions[table] | 1336 instructions = self.instructions[table] |
1331 self.needFlagDisperse = False | 1344 self.needFlagDisperse = False |
1332 self.lastOp = None | 1345 self.lastOp = None |
1333 opmap[val] = inst.generateName(val) | 1346 opmap[val] = inst.generateName(val) |
1334 bodymap[val] = inst.generateBody(val, self, otype) | 1347 bodymap[val] = inst.generateBody(val, self, otype) |
1335 | 1348 |
1336 pieces.append('\nstatic impl_fun impl_{name}[{sz}] = {{'.format(name = table, sz=len(opmap))) | 1349 if self.dispatch == 'call': |
1337 for inst in range(0, len(opmap)): | 1350 pieces.append('\nstatic impl_fun impl_{name}[{sz}] = {{'.format(name = table, sz=len(opmap))) |
1338 op = opmap[inst] | 1351 for inst in range(0, len(opmap)): |
1339 if op is None: | 1352 op = opmap[inst] |
1340 pieces.append('\n\tunimplemented,') | 1353 if op is None: |
1341 else: | 1354 pieces.append('\n\tunimplemented,') |
1342 pieces.append('\n\t' + op + ',') | 1355 else: |
1343 body.append(bodymap[inst]) | 1356 pieces.append('\n\t' + op + ',') |
1344 pieces.append('\n};') | 1357 body.append(bodymap[inst]) |
1358 pieces.append('\n};') | |
1359 elif self.dispatch == 'goto': | |
1360 body.append('\n\tstatic void *impl_{name}[{sz}] = {{'.format(name = table, sz=len(opmap))) | |
1361 for inst in range(0, len(opmap)): | |
1362 op = opmap[inst] | |
1363 if op is None: | |
1364 body.append('\n\t\t&&unimplemented,') | |
1365 else: | |
1366 body.append('\n\t\t&&' + op + ',') | |
1367 lateBody.append(bodymap[inst]) | |
1368 body.append('\n\t};') | |
1369 else: | |
1370 raise Exception("unimplmeneted dispatch type " + self.dispatch) | |
1345 body.extend(pieces) | 1371 body.extend(pieces) |
1372 | |
1373 def nextInstruction(self, otype): | |
1374 output = [] | |
1375 if self.dispatch == 'goto': | |
1376 output.append('\n\tif (context->cycles >= target_cycle) { return; }') | |
1377 self.meta = {} | |
1378 self.temp = {} | |
1379 self.subroutines[self.body].inline(self, [], output, otype, None) | |
1380 return output | |
1346 | 1381 |
1347 def build(self, otype): | 1382 def build(self, otype): |
1348 body = [] | 1383 body = [] |
1349 pieces = [] | 1384 pieces = [] |
1350 for include in self.includes: | 1385 for include in self.includes: |
1351 body.append('#include "{0}"\n'.format(include)) | 1386 body.append('#include "{0}"\n'.format(include)) |
1352 body.append('\nstatic void unimplemented({pre}context *context)'.format(pre = self.prefix)) | 1387 if self.dispatch == 'call': |
1353 body.append('\n{') | 1388 body.append('\nstatic void unimplemented({pre}context *context)'.format(pre = self.prefix)) |
1354 body.append('\n\tfatal_error("Unimplemented instruction\\n");') | 1389 body.append('\n{') |
1355 body.append('\n}\n') | 1390 body.append('\n\tfatal_error("Unimplemented instruction\\n");') |
1356 body.append('\ntypedef void (*impl_fun)({pre}context *context);'.format(pre=self.prefix)) | 1391 body.append('\n}\n') |
1392 body.append('\ntypedef void (*impl_fun)({pre}context *context);'.format(pre=self.prefix)) | |
1393 for table in self.extra_tables: | |
1394 body.append('\nstatic impl_fun impl_{name}[{sz}];'.format(name = table, sz=(1 << self.opsize))) | |
1395 body.append('\nstatic impl_fun impl_main[{sz}];'.format(sz=(1 << self.opsize))) | |
1396 elif self.dispatch == 'goto': | |
1397 body.append('\nvoid {pre}execute({type} *context, uint32_t target_cycle)'.format(pre = self.prefix, type = self.context_type)) | |
1398 body.append('\n{') | |
1399 | |
1357 for table in self.extra_tables: | 1400 for table in self.extra_tables: |
1358 body.append('\nstatic impl_fun impl_{name}[{sz}];'.format(name = table, sz=(1 << self.opsize))) | 1401 self._buildTable(otype, table, body, pieces) |
1359 body.append('\nstatic impl_fun impl_main[{sz}];'.format(sz=(1 << self.opsize))) | 1402 self._buildTable(otype, 'main', body, pieces) |
1360 for table in self.extra_tables: | 1403 if self.dispatch == 'call' and self.body in self.subroutines: |
1361 self._buildTable(otype, table, body) | |
1362 self._buildTable(otype, 'main', body) | |
1363 if self.body in self.subroutines: | |
1364 pieces.append('\nvoid {pre}execute({type} *context, uint32_t target_cycle)'.format(pre = self.prefix, type = self.context_type)) | 1404 pieces.append('\nvoid {pre}execute({type} *context, uint32_t target_cycle)'.format(pre = self.prefix, type = self.context_type)) |
1365 pieces.append('\n{') | 1405 pieces.append('\n{') |
1366 pieces.append('\n\twhile (context->cycles < target_cycle)') | 1406 pieces.append('\n\twhile (context->cycles < target_cycle)') |
1367 pieces.append('\n\t{') | 1407 pieces.append('\n\t{') |
1368 self.meta = {} | 1408 self.meta = {} |
1369 self.temp = {} | 1409 self.temp = {} |
1370 self.subroutines[self.body].inline(self, [], pieces, otype, None) | 1410 self.subroutines[self.body].inline(self, [], pieces, otype, None) |
1371 pieces.append('\n\t}') | 1411 pieces.append('\n\t}') |
1412 pieces.append('\n}') | |
1413 elif self.dispatch == 'goto': | |
1414 body += self.nextInstruction(otype) | |
1415 pieces.append('\nunimplemented:') | |
1416 pieces.append('\n\tfatal_error("Unimplemented instruction\\n");') | |
1372 pieces.append('\n}') | 1417 pieces.append('\n}') |
1373 return ''.join(body) + ''.join(pieces) | 1418 return ''.join(body) + ''.join(pieces) |
1374 | 1419 |
1375 def checkBool(self, name): | 1420 def checkBool(self, name): |
1376 if not name in self.booleans: | 1421 if not name in self.booleans: |
1487 return ret | 1532 return ret |
1488 | 1533 |
1489 def getRootScope(self): | 1534 def getRootScope(self): |
1490 return self.scopes[0] | 1535 return self.scopes[0] |
1491 | 1536 |
1492 def parse(f): | 1537 def parse(args): |
1538 f = args.source | |
1493 instructions = {} | 1539 instructions = {} |
1494 subroutines = {} | 1540 subroutines = {} |
1495 registers = None | 1541 registers = None |
1496 flags = None | 1542 flags = None |
1497 declares = [] | 1543 declares = [] |
1575 subroutines[cur_object.name] = cur_object | 1621 subroutines[cur_object.name] = cur_object |
1576 if errors: | 1622 if errors: |
1577 print(errors) | 1623 print(errors) |
1578 else: | 1624 else: |
1579 p = Program(registers, instructions, subroutines, info, flags) | 1625 p = Program(registers, instructions, subroutines, info, flags) |
1626 p.dispatch = args.dispatch | |
1580 p.declares = declares | 1627 p.declares = declares |
1581 p.booleans['dynarec'] = False | 1628 p.booleans['dynarec'] = False |
1582 p.booleans['interp'] = True | 1629 p.booleans['interp'] = True |
1630 if args.define: | |
1631 for define in args.define: | |
1632 name,sep,val = define.partition('=') | |
1633 name = name.strip() | |
1634 val = val.strip() | |
1635 if sep: | |
1636 p.booleans[name] = bool(val) | |
1637 else: | |
1638 p.booleans[name] = True | |
1583 | 1639 |
1584 if 'header' in info: | 1640 if 'header' in info: |
1585 print('#include "{0}"'.format(info['header'][0])) | 1641 print('#include "{0}"'.format(info['header'][0])) |
1586 p.writeHeader('c', info['header'][0]) | 1642 p.writeHeader('c', info['header'][0]) |
1587 print('#include "util.h"') | 1643 print('#include "util.h"') |
1588 print('#include <stdlib.h>') | 1644 print('#include <stdlib.h>') |
1589 print(p.build('c')) | 1645 print(p.build('c')) |
1590 | 1646 |
1591 def main(argv): | 1647 def main(argv): |
1592 f = open(argv[1]) | 1648 from argparse import ArgumentParser, FileType |
1593 parse(f) | 1649 argParser = ArgumentParser(description='CPU emulator DSL compiler') |
1650 argParser.add_argument('source', type=FileType('r')) | |
1651 argParser.add_argument('-D', '--define', action='append') | |
1652 argParser.add_argument('-d', '--dispatch', choices=('call', 'switch', 'goto'), default='call') | |
1653 parse(argParser.parse_args(argv[1:])) | |
1594 | 1654 |
1595 if __name__ == '__main__': | 1655 if __name__ == '__main__': |
1596 from sys import argv | 1656 from sys import argv |
1597 main(argv) | 1657 main(argv) |