1# ScummVM - Graphic Adventure Engine 2# 3# ScummVM is the legal property of its developers, whose names 4# are too numerous to list here. Please refer to the COPYRIGHT 5# file distributed with this source distribution. 6# 7# This program is free software; you can redistribute it and/or 8# modify it under the terms of the GNU General Public License 9# as published by the Free Software Foundation; either version 2 10# of the License, or (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program; if not, write to the Free Software 19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20# 21 22import op, traceback, re, proc 23from copy import copy 24proc_module = proc 25 26class CrossJump(Exception): 27 pass 28 29def parse_bin(s): 30 b = s.group(1) 31 v = hex(int(b, 2)) 32 #print "BINARY: %s -> %s" %(b, v) 33 return v 34 35class cpp: 36 def __init__(self, context, namespace, skip_first = 0, blacklist = [], skip_output = [], skip_dispatch_call = False, skip_addr_constants = False, header_omit_blacklisted = False, function_name_remapping = { }): 37 self.namespace = namespace 38 fname = namespace.lower() + ".cpp" 39 header = namespace.lower() + ".h" 40 banner = """/* PLEASE DO NOT MODIFY THIS FILE. ALL CHANGES WILL BE LOST! LOOK FOR README FOR DETAILS */ 41 42/* ScummVM - Graphic Adventure Engine 43 * 44 * ScummVM is the legal property of its developers, whose names 45 * are too numerous to list here. Please refer to the COPYRIGHT 46 * file distributed with this source distribution. 47 * 48 * This program is free software; you can redistribute it and/or 49 * modify it under the terms of the GNU General Public License 50 * as published by the Free Software Foundation; either version 2 51 * of the License, or (at your option) any later version. 52 * 53 * This program is distributed in the hope that it will be useful, 54 * but WITHOUT ANY WARRANTY; without even the implied warranty of 55 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 56 * GNU General Public License for more details. 57 * 58 * You should have received a copy of the GNU General Public License 59 * along with this program; if not, write to the Free Software 60 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 61 * 62 */ 63""" 64 self.fd = open(fname, "wt") 65 self.hd = open(header, "wt") 66 hid = "TASMRECOVER_%s_STUBS_H__" %namespace.upper() 67 self.hd.write("""#ifndef %s 68#define %s 69 70%s""" %(hid, hid, banner)) 71 self.context = context 72 self.data_seg = context.binary_data 73 self.procs = context.proc_list 74 self.skip_first = skip_first 75 self.proc_queue = [] 76 self.proc_done = [] 77 self.blacklist = blacklist 78 self.failed = list(blacklist) 79 self.skip_output = skip_output 80 self.skip_dispatch_call = skip_dispatch_call 81 self.skip_addr_constants = skip_addr_constants 82 self.header_omit_blacklisted = header_omit_blacklisted 83 self.function_name_remapping = function_name_remapping 84 self.translated = [] 85 self.proc_addr = [] 86 self.used_data_offsets = set() 87 self.methods = [] 88 self.fd.write("""%s 89#include \"%s\" 90 91namespace %s { 92""" %(banner, header, namespace)) 93 94 def expand_cb(self, match): 95 name = match.group(0).lower() 96 if len(name) == 2 and \ 97 ((name[0] in ['a', 'b', 'c', 'd'] and name[1] in ['h', 'x', 'l']) or name in ['si', 'di', 'es', 'ds', 'cs']): 98 return "%s" %name 99 100 if self.indirection == -1: 101 try: 102 offset,p,p = self.context.get_offset(name) 103 except: 104 pass 105 else: 106 print "OFFSET = %d" %offset 107 self.indirection = 0 108 self.used_data_offsets.add((name,offset)) 109 return "offset_%s" % (name,) 110 111 g = self.context.get_global(name) 112 if isinstance(g, op.const): 113 value = self.expand_equ(g.value) 114 print "equ: %s -> %s" %(name, value) 115 elif isinstance(g, proc.proc): 116 if self.indirection != -1: 117 raise Exception("invalid proc label usage") 118 value = str(g.offset) 119 self.indirection = 0 120 else: 121 size = g.size 122 if size == 0: 123 raise Exception("invalid var '%s' size %u" %(name, size)) 124 if self.indirection == 0: 125 value = "data.%s(k%s)" %("byte" if size == 1 else "word", name.capitalize()) 126 elif self.indirection == -1: 127 value = "%s" %g.offset 128 self.indirection = 0 129 else: 130 raise Exception("invalid indirection %d" %self.indirection) 131 return value 132 133 def get_size(self, expr): 134 #print 'get_size("%s")' %expr 135 try: 136 v = self.context.parse_int(expr) 137 return 1 if v < 256 else 2 138 except: 139 pass 140 141 if re.match(r'byte\s+ptr\s', expr) is not None: 142 return 1 143 144 if re.match(r'word\s+ptr\s', expr) is not None: 145 return 2 146 147 if len(expr) == 2 and expr[0] in ['a', 'b', 'c', 'd'] and expr[1] in ['h', 'l']: 148 return 1 149 if expr in ['ax', 'bx', 'cx', 'dx', 'si', 'di', 'sp', 'bp', 'ds', 'cs', 'es', 'fs']: 150 return 2 151 152 m = re.match(r'[a-zA-Z_]\w*', expr) 153 if m is not None: 154 name = m.group(0) 155 try: 156 g = self.context.get_global(name) 157 return g.size 158 except: 159 pass 160 161 return 0 162 163 def expand_equ_cb(self, match): 164 name = match.group(0).lower() 165 g = self.context.get_global(name) 166 if isinstance(g, op.const): 167 return g.value 168 return str(g.offset) 169 170 def expand_equ(self, expr): 171 n = 1 172 while n > 0: 173 expr, n = re.subn(r'\b[a-zA-Z_][a-zA-Z0-9_]+\b', self.expand_equ_cb, expr) 174 expr = re.sub(r'\b([0-9][a-fA-F0-9]*)h', '0x\\1', expr) 175 return "(%s)" %expr 176 177 def expand(self, expr, def_size = 0): 178 #print "EXPAND \"%s\"" %expr 179 size = self.get_size(expr) if def_size == 0 else def_size 180 indirection = 0 181 seg = None 182 reg = True 183 184 m = re.match(r'seg\s+(.*?)$', expr) 185 if m is not None: 186 return "data" 187 188 match_id = True 189 m = re.match(r'offset\s+(.*?)$', expr) 190 if m is not None: 191 indirection -= 1 192 expr = m.group(1).strip() 193 194 m = re.match(r'byte\s+ptr\s+(.*?)$', expr) 195 if m is not None: 196 expr = m.group(1).strip() 197 198 m = re.match(r'word\s+ptr\s+(.*?)$', expr) 199 if m is not None: 200 expr = m.group(1).strip() 201 202 m = re.match(r'\[(.*)\]$', expr) 203 if m is not None: 204 indirection += 1 205 expr = m.group(1).strip() 206 207 m = re.match(r'(\w{2,2}):(.*)$', expr) 208 if m is not None: 209 seg_prefix = m.group(1) 210 expr = m.group(2).strip() 211 print "SEGMENT %s, remains: %s" %(seg_prefix, expr) 212 else: 213 seg_prefix = "ds" 214 215 m = re.match(r'(([abcd][xhl])|si|di|bp|sp)([\+-].*)?$', expr) 216 if m is not None: 217 reg = m.group(1) 218 plus = m.group(3) 219 if plus is not None: 220 plus = self.expand(plus) 221 else: 222 plus = "" 223 match_id = False 224 #print "COMMON_REG: ", reg, plus 225 expr = "%s%s" %(reg, plus) 226 227 expr = re.sub(r'\b([0-9][a-fA-F0-9]*)h', '0x\\1', expr) 228 expr = re.sub(r'\b([0-1]+)b', parse_bin, expr) 229 expr = re.sub(r'"(.)"', '\'\\1\'', expr) 230 if match_id: 231 #print "BEFORE: %d" %indirection 232 self.indirection = indirection 233 expr = re.sub(r'\b[a-zA-Z_][a-zA-Z0-9_]+\b', self.expand_cb, expr) 234 indirection = self.indirection 235 #print "AFTER: %d" %indirection 236 237 if indirection == 1: 238 if size == 1: 239 expr = "%s.byte(%s)" %(seg_prefix, expr) 240 elif size == 2: 241 expr = "%s.word(%s)" %(seg_prefix, expr) 242 else: 243 expr = "@invalid size 0" 244 elif indirection == 0: 245 pass 246 elif indirection == -1: 247 expr = "&%s" %expr 248 else: 249 raise Exception("invalid indirection %d" %indirection) 250 return expr 251 252 def mangle_label(self, name): 253 name = name.lower() 254 return re.sub(r'\$', '_tmp', name) 255 256 def resolve_label(self, name): 257 name = name.lower() 258 if not name in self.proc.labels: 259 try: 260 offset, proc, pos = self.context.get_offset(name) 261 except: 262 print "no label %s, trying procedure" %name 263 proc = self.context.get_global(name) 264 pos = 0 265 if not isinstance(proc, proc_module.proc): 266 raise CrossJump("cross-procedure jump to non label and non procedure %s" %(name)) 267 self.proc.labels.add(name) 268 for i in xrange(0, len(self.unbounded)): 269 u = self.unbounded[i] 270 if u[1] == proc: 271 if pos < u[2]: 272 self.unbounded[i] = (name, proc, pos) 273 return self.mangle_label(name) 274 self.unbounded.append((name, proc, pos)) 275 276 return self.mangle_label(name) 277 278 def jump_to_label(self, name): 279 jump_proc = False 280 if name in self.blacklist: 281 jump_proc = True 282 283 if self.context.has_global(name) : 284 g = self.context.get_global(name) 285 if isinstance(g, proc_module.proc): 286 jump_proc = True 287 288 if jump_proc: 289 if name in self.function_name_remapping: 290 return "{ %s(); return; }" %self.function_name_remapping[name] 291 else: 292 return "{ %s(); return; }" %name 293 else: 294 # TODO: name or self.resolve_label(name) or self.mangle_label(name)?? 295 if name in self.proc.retlabels: 296 return "return /* (%s) */" % (name) 297 return "goto %s" %self.resolve_label(name) 298 299 def _label(self, name): 300 self.body += "%s:\n" %self.mangle_label(name) 301 302 def schedule(self, name): 303 name = name.lower() 304 if name in self.proc_queue or name in self.proc_done or name in self.failed: 305 return 306 print "+scheduling function %s..." %name 307 self.proc_queue.append(name) 308 309 def _call(self, name): 310 name = name.lower() 311 if name == 'ax': 312 self.body += "\t__dispatch_call(%s);\n" %self.expand('ax', 2) 313 return 314 if name in self.function_name_remapping: 315 self.body += "\t%s();\n" %self.function_name_remapping[name] 316 else: 317 self.body += "\t%s();\n" %name 318 self.schedule(name) 319 320 def _ret(self): 321 self.body += "\treturn;\n" 322 323 def parse2(self, dst, src): 324 dst_size, src_size = self.get_size(dst), self.get_size(src) 325 if dst_size == 0: 326 if src_size == 0: 327 raise Exception("both sizes are 0") 328 dst_size = src_size 329 if src_size == 0: 330 src_size = dst_size 331 332 dst = self.expand(dst, dst_size) 333 src = self.expand(src, src_size) 334 return dst, src 335 336 def _mov(self, dst, src): 337 self.body += "\t%s = %s;\n" %self.parse2(dst, src) 338 339 def _add(self, dst, src): 340 self.body += "\t_add(%s, %s);\n" %self.parse2(dst, src) 341 342 def _sub(self, dst, src): 343 self.body += "\t_sub(%s, %s);\n" %self.parse2(dst, src) 344 345 def _and(self, dst, src): 346 self.body += "\t_and(%s, %s);\n" %self.parse2(dst, src) 347 348 def _or(self, dst, src): 349 self.body += "\t_or(%s, %s);\n" %self.parse2(dst, src) 350 351 def _xor(self, dst, src): 352 self.body += "\t_xor(%s, %s);\n" %self.parse2(dst, src) 353 354 def _neg(self, dst): 355 dst = self.expand(dst) 356 self.body += "\t_neg(%s);\n" %(dst) 357 358 def _cbw(self): 359 self.body += "\tax.cbw();\n" 360 361 def _shr(self, dst, src): 362 self.body += "\t_shr(%s, %s);\n" %self.parse2(dst, src) 363 364 def _shl(self, dst, src): 365 self.body += "\t_shl(%s, %s);\n" %self.parse2(dst, src) 366 367 #def _sar(self, dst, src): 368 # self.body += "\t_sar(%s%s);\n" %self.parse2(dst, src) 369 370 #def _sal(self, dst, src): 371 # self.body += "\t_sal(%s, %s);\n" %self.parse2(dst, src) 372 373 #def _rcl(self, dst, src): 374 # self.body += "\t_rcl(%s, %s);\n" %self.parse2(dst, src) 375 376 #def _rcr(self, dst, src): 377 # self.body += "\t_rcr(%s, %s);\n" %self.parse2(dst, src) 378 379 def _mul(self, src): 380 src = self.expand(src) 381 self.body += "\t_mul(%s);\n" %(src) 382 383 def _div(self, src): 384 src = self.expand(src) 385 self.body += "\t_div(%s);\n" %(src) 386 387 def _inc(self, dst): 388 dst = self.expand(dst) 389 self.body += "\t_inc(%s);\n" %(dst) 390 391 def _dec(self, dst): 392 dst = self.expand(dst) 393 self.body += "\t_dec(%s);\n" %(dst) 394 395 def _cmp(self, a, b): 396 self.body += "\t_cmp(%s, %s);\n" %self.parse2(a, b) 397 398 def _test(self, a, b): 399 self.body += "\t_test(%s, %s);\n" %self.parse2(a, b) 400 401 def _js(self, label): 402 self.body += "\tif (flags.s())\n\t\t%s;\n" %(self.jump_to_label(label)) 403 404 def _jns(self, label): 405 self.body += "\tif (!flags.s())\n\t\t%s;\n" %(self.jump_to_label(label)) 406 407 def _jz(self, label): 408 self.body += "\tif (flags.z())\n\t\t%s;\n" %(self.jump_to_label(label)) 409 410 def _jnz(self, label): 411 self.body += "\tif (!flags.z())\n\t\t%s;\n" %(self.jump_to_label(label)) 412 413 def _jl(self, label): 414 self.body += "\tif (flags.l())\n\t\t%s;\n" %(self.jump_to_label(label)) 415 416 def _jg(self, label): 417 self.body += "\tif (!flags.le())\n\t\t%s;\n" %(self.jump_to_label(label)) 418 419 def _jle(self, label): 420 self.body += "\tif (flags.le())\n\t\t%s;\n" %(self.jump_to_label(label)) 421 422 def _jge(self, label): 423 self.body += "\tif (!flags.l())\n\t\t%s;\n" %(self.jump_to_label(label)) 424 425 def _jc(self, label): 426 self.body += "\tif (flags.c())\n\t\t%s;\n" %(self.jump_to_label(label)) 427 428 def _jnc(self, label): 429 self.body += "\tif (!flags.c())\n\t\t%s;\n" %(self.jump_to_label(label)) 430 431 def _xchg(self, dst, src): 432 self.body += "\t_xchg(%s, %s);\n" %self.parse2(dst, src) 433 434 def _jmp(self, label): 435 self.body += "\t%s;\n" %(self.jump_to_label(label)) 436 437 def _loop(self, label): 438 self.body += "\tif (--cx)\n\t\t%s;\n" %self.jump_to_label(label) 439 440 def _push(self, regs): 441 p = str(); 442 for r in regs: 443 r = self.expand(r) 444 p += "\tpush(%s);\n" %(r) 445 self.body += p 446 447 def _pop(self, regs): 448 p = str(); 449 for r in regs: 450 self.temps_count -= 1 451 i = self.temps_count 452 r = self.expand(r) 453 p += "\t%s = pop();\n" %r 454 self.body += p 455 456 def _rep(self): 457 self.body += "\twhile(cx--)\n\t" 458 459 def _lodsb(self): 460 self.body += "\t_lodsb();\n" 461 462 def _lodsw(self): 463 self.body += "\t_lodsw();\n" 464 465 def _stosb(self, n, clear_cx): 466 self.body += "\t_stosb(%s%s);\n" %("" if n == 1 else n, ", true" if clear_cx else "") 467 468 def _stosw(self, n, clear_cx): 469 self.body += "\t_stosw(%s%s);\n" %("" if n == 1 else n, ", true" if clear_cx else "") 470 471 def _movsb(self, n, clear_cx): 472 self.body += "\t_movsb(%s%s);\n" %("" if n == 1 else n, ", true" if clear_cx else "") 473 474 def _movsw(self, n, clear_cx): 475 self.body += "\t_movsw(%s%s);\n" %("" if n == 1 else n, ", true" if clear_cx else "") 476 477 def _stc(self): 478 self.body += "\tflags._c = true;\n " 479 480 def _clc(self): 481 self.body += "\tflags._c = false;\n " 482 483 def __proc(self, name, def_skip = 0): 484 try: 485 skip = def_skip 486 self.temps_count = 0 487 self.temps_max = 0 488 if self.context.has_global(name): 489 self.proc = self.context.get_global(name) 490 else: 491 print "No procedure named %s, trying label" %name 492 off, src_proc, skip = self.context.get_offset(name) 493 494 self.proc = proc_module.proc(name) 495 self.proc.stmts = copy(src_proc.stmts) 496 self.proc.labels = copy(src_proc.labels) 497 self.proc.retlabels = copy(src_proc.retlabels) 498 #for p in xrange(skip, len(self.proc.stmts)): 499 # s = self.proc.stmts[p] 500 # if isinstance(s, op.basejmp): 501 # o, p, s = self.context.get_offset(s.label) 502 # if p == src_proc and s < skip: 503 # skip = s 504 505 506 self.proc_addr.append((name, self.proc.offset)) 507 self.body = str() 508 if name in self.function_name_remapping: 509 self.body += "void %sContext::%s() {\n\tSTACK_CHECK;\n" %(self.namespace, self.function_name_remapping[name]); 510 else: 511 self.body += "void %sContext::%s() {\n\tSTACK_CHECK;\n" %(self.namespace, name); 512 self.proc.optimize() 513 self.unbounded = [] 514 self.proc.visit(self, skip) 515 516 #adding remaining labels: 517 for i in xrange(0, len(self.unbounded)): 518 u = self.unbounded[i] 519 print "UNBOUNDED: ", u 520 proc = u[1] 521 for p in xrange(u[2], len(proc.stmts)): 522 s = proc.stmts[p] 523 if isinstance(s, op.basejmp): 524 self.resolve_label(s.label) 525 526 #adding statements 527 #BIG FIXME: this is quite ugly to handle code analysis from the code generation. rewrite me! 528 for label, proc, offset in self.unbounded: 529 self.body += "\treturn;\n" #we need to return before calling code from the other proc 530 self.body += "/*continuing to unbounded code: %s from %s:%d-%d*/\n" %(label, proc.name, offset, len(proc.stmts)) 531 start = len(self.proc.stmts) 532 self.proc.add_label(label) 533 for s in proc.stmts[offset:]: 534 if isinstance(s, op.label): 535 self.proc.labels.add(s.name) 536 self.proc.stmts.append(s) 537 self.proc.add("ret") 538 print "skipping %d instructions, todo: %d" %(start, len(self.proc.stmts) - start) 539 print "re-optimizing..." 540 self.proc.optimize(keep_labels=[label]) 541 self.proc.visit(self, start) 542 self.body += "}\n"; 543 if name not in self.skip_output: 544 self.translated.insert(0, self.body) 545 self.proc = None 546 if self.temps_count > 0: 547 raise Exception("temps count == %d at the exit of proc" %self.temps_count); 548 return True 549 except (CrossJump, op.Unsupported) as e: 550 print "%s: ERROR: %s" %(name, e) 551 self.failed.append(name) 552 except: 553 raise 554 555 def get_type(self, width): 556 return "uint%d_t" %(width * 8) 557 558 def write_stubs(self, fname, procs): 559 fd = open(fname, "wt") 560 fd.write("namespace %s {\n" %self.namespace) 561 for p in procs: 562 if p in self.function_name_remapping: 563 fd.write("void %sContext::%s() {\n\t::error(\"%s\");\n}\n\n" %(self.namespace, self.function_name_remapping[p], self.function_name_remapping[p])) 564 else: 565 fd.write("void %sContext::%s() {\n\t::error(\"%s\");\n}\n\n" %(self.namespace, p, p)) 566 fd.write("} // End of namespace %s\n" %self.namespace) 567 fd.close() 568 569 570 def generate(self, start): 571 #print self.prologue() 572 #print context 573 self.proc_queue.append(start) 574 while len(self.proc_queue): 575 name = self.proc_queue.pop() 576 if name in self.failed or name in self.proc_done: 577 continue 578 if len(self.proc_queue) == 0 and len(self.procs) > 0: 579 print "queue's empty, adding remaining procs:" 580 for p in self.procs: 581 self.schedule(p) 582 self.procs = [] 583 print "continuing on %s" %name 584 self.proc_done.append(name) 585 self.__proc(name) 586 self.methods.append(name) 587 self.write_stubs("_stubs.cpp", self.failed) 588 self.methods += self.failed 589 done, failed = len(self.proc_done), len(self.failed) 590 591 self.fd.write("\n") 592 self.fd.write("\n".join(self.translated)) 593 self.fd.write("\n") 594 print "%d ok, %d failed of %d, %.02g%% translated" %(done, failed, done + failed, 100.0 * done / (done + failed)) 595 print "\n".join(self.failed) 596 data_bin = self.data_seg 597 data_impl = "\n\tstatic const uint8 src[] = {\n\t\t" 598 n = 0 599 comment = str() 600 for v in data_bin: 601 data_impl += "0x%02x, " %v 602 n += 1 603 604 comment += chr(v) if (v >= 0x20 and v < 0x7f and v != ord('\\')) else "." 605 if (n & 0xf) == 0: 606 data_impl += "\n\t\t//0x%04x: %s\n\t\t" %(n - 16, comment) 607 comment = str() 608 elif (n & 0x3) == 0: 609 comment += " " 610 data_impl += "};\n\tds.assign(src, src + sizeof(src));\n" 611 612 self.hd.write( 613"""\n#include "dreamweb/runtime.h" 614 615#include "dreamweb/structs.h" 616#include "dreamweb/dreambase.h" 617 618namespace %s { 619 620""" 621%(self.namespace)) 622 623 if self.skip_addr_constants == False: 624 for name,addr in self.proc_addr: 625 self.hd.write("static const uint16 addr_%s = 0x%04x;\n" %(name, addr)) 626 627 628 for name,addr in self.used_data_offsets: 629 self.hd.write("static const uint16 offset_%s = 0x%04x;\n" %(name, addr)) 630 631 offsets = [] 632 for k, v in self.context.get_globals().items(): 633 if isinstance(v, op.var): 634 offsets.append((k.capitalize(), v.offset)) 635 elif isinstance(v, op.const): 636 offsets.append((k.capitalize(), self.expand_equ(v.value))) #fixme: try to save all constants here 637 638 offsets = sorted(offsets, key=lambda t: t[1]) 639 for o in offsets: 640 self.hd.write("static const uint16 k%s = %s;\n" %o) 641 self.hd.write("\n") 642 643 self.hd.write( 644""" 645class %sContext : public DreamBase, public Context { 646public: 647 DreamGenContext(DreamWeb::DreamWebEngine *en) : DreamBase(en), Context(this) {} 648 649 void __start(); 650""" 651%(self.namespace)) 652 if self.skip_dispatch_call == False: 653 self.hd.write( 654""" void __dispatch_call(uint16 addr); 655""") 656 657 658 for p in set(self.methods): 659 if p in self.blacklist: 660 if self.header_omit_blacklisted == False: 661 self.hd.write("\t//void %s();\n" %p) 662 else: 663 if p in self.function_name_remapping: 664 self.hd.write("\tvoid %s();\n" %self.function_name_remapping[p]) 665 else: 666 self.hd.write("\tvoid %s();\n" %p) 667 668 self.hd.write("};\n\n} // End of namespace DreamGen\n\n#endif\n") 669 self.hd.close() 670 671 self.fd.write("void %sContext::__start() { %s\t%s(); \n}\n" %(self.namespace, data_impl, start)) 672 673 if self.skip_dispatch_call == False: 674 self.fd.write("\nvoid %sContext::__dispatch_call(uint16 addr) {\n\tswitch(addr) {\n" %self.namespace) 675 self.proc_addr.sort(cmp = lambda x, y: x[1] - y[1]) 676 for name,addr in self.proc_addr: 677 self.fd.write("\t\tcase addr_%s: %s(); break;\n" %(name, name)) 678 self.fd.write("\t\tdefault: ::error(\"invalid call to %04x dispatched\", (uint16)ax);") 679 self.fd.write("\n\t}\n}") 680 681 self.fd.write("\n} // End of namespace DreamGen\n") 682 self.fd.close() 683