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