1 /*
2  * libtilemdb - Utilities for debugging Z80 assembly programs
3  *
4  * Copyright (C) 2010 Benjamin Moody
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdarg.h>
29 #include "tilemdb.h"
30 
31 typedef struct _TilemDisasmSymbol {
32 	char* name;
33 	dword value;
34 } TilemDisasmSymbol;
35 
36 typedef struct _TilemDisasmSymTable {
37 	int nsyms;
38 	int nsyms_a;
39 	TilemDisasmSymbol* syms;
40 } TilemDisasmSymTable;
41 
42 struct _TilemDisasm {
43 	TilemDisasmSymTable labels;
44 	TilemDisasmSymTable romcalls;
45 	TilemDisasmSymTable flags;
46 	TilemDisasmSymTable macros;
47 };
48 
tilem_disasm_new()49 TilemDisasm* tilem_disasm_new()
50 {
51 	TilemDisasm* dasm = tilem_new0(TilemDisasm, 1);
52 	dasm->labels.syms = NULL;
53 	dasm->romcalls.syms = NULL;
54 	dasm->flags.syms = NULL;
55 	dasm->macros.syms = NULL;
56 	return dasm;
57 }
58 
tilem_disasm_sym_table_free(TilemDisasmSymTable * stab)59 static void tilem_disasm_sym_table_free(TilemDisasmSymTable* stab)
60 {
61 	int i;
62 
63 	for (i = 0; i < stab->nsyms; i++)
64 		tilem_free(stab->syms[i].name);
65 	tilem_free(stab->syms);
66 }
67 
tilem_disasm_free(TilemDisasm * dasm)68 void tilem_disasm_free(TilemDisasm* dasm)
69 {
70 	if (!dasm)
71 		return;
72 
73 	tilem_disasm_sym_table_free(&dasm->labels);
74 	tilem_disasm_sym_table_free(&dasm->romcalls);
75 	tilem_disasm_sym_table_free(&dasm->flags);
76 	tilem_disasm_sym_table_free(&dasm->macros);
77 	tilem_free(dasm);
78 }
79 
80 /* Find symbol in the given table, if any */
find_symbol(const TilemDisasmSymTable * stab,dword value)81 static TilemDisasmSymbol* find_symbol(const TilemDisasmSymTable* stab,
82 				      dword value)
83 {
84 	int start, end, i;
85 
86 	start = 0;
87 	end = stab->nsyms;
88 	while (start < end) {
89 		i = (start + end) / 2;
90 		if (stab->syms[i].value == value)
91 			return &stab->syms[i];
92 		else if (stab->syms[i].value <= value)
93 			start = i + 1;
94 		else
95 			end = i;
96 	}
97 	return NULL;
98 }
99 
100 /* Find previous symbol in the given table, if any */
find_prev_symbol(const TilemDisasmSymTable * stab,dword value)101 static TilemDisasmSymbol* find_prev_symbol(const TilemDisasmSymTable* stab,
102 					   dword value)
103 {
104 	int start, end, i;
105 
106 	start = 0;
107 	end = stab->nsyms;
108 	while (start < end) {
109 		i = (start + end) / 2;
110 		if (stab->syms[i].value <= value)
111 			start = i + 1;
112 		else
113 			end = i;
114 	}
115 	if (start > 0)
116 		return &stab->syms[start - 1];
117 	else
118 		return NULL;
119 }
120 
121 /* Find symbol with given name */
find_symbol_by_name(const TilemDisasmSymTable * stab,const char * name)122 static TilemDisasmSymbol* find_symbol_by_name(const TilemDisasmSymTable* stab,
123 					      const char* name)
124 {
125 	int i;
126 	for (i = 0; i < stab->nsyms; i++)
127 		if (!strcmp(stab->syms[i].name, name))
128 			return &stab->syms[i];
129 	return NULL;
130 }
131 
132 /* Find a given symbol in the table, or create a new one */
add_symbol(TilemDisasmSymTable * stab,dword value)133 static TilemDisasmSymbol* add_symbol(TilemDisasmSymTable* stab,
134 				     dword value)
135 {
136 	int start, end, i;
137 	TilemDisasmSymbol* syms;
138 
139 	start = 0;
140 	end = stab->nsyms;
141 
142 	while (start < end) {
143 		i = (start + end) / 2;
144 		if (stab->syms[i].value == value)
145 			return &stab->syms[i];
146 		else if (stab->syms[i].value < value)
147 			start = i + 1;
148 		else
149 			end = i;
150 	}
151 
152 	/* insert new label into the array */
153 	if (stab->nsyms < stab->nsyms_a) {
154 		if (start < stab->nsyms)
155 			memmove(&stab->syms[start + 1], &stab->syms[start],
156 				((stab->nsyms - start)
157 				 * sizeof(TilemDisasmSymbol)));
158 	}
159 	else {
160 		stab->nsyms_a = (stab->nsyms + 1) * 2;
161 		syms = tilem_new(TilemDisasmSymbol, stab->nsyms_a);
162 		if (start > 0)
163 			memcpy(syms, stab->syms,
164 			       start * sizeof(TilemDisasmSymbol));
165 		if (start < stab->nsyms)
166 			memcpy(syms + start + 1, stab->syms + start,
167 			       ((stab->nsyms - start)
168 				* sizeof(TilemDisasmSymbol)));
169 		tilem_free(stab->syms);
170 		stab->syms = syms;
171 	}
172 
173 	stab->nsyms++;
174 
175 	stab->syms[start].value = value;
176 	stab->syms[start].name = NULL;
177 	return &stab->syms[start];
178 }
179 
180 /* Remove a symbol from the table */
del_symbol(TilemDisasmSymTable * stab,TilemDisasmSymbol * sym)181 static void del_symbol(TilemDisasmSymTable* stab,
182 		       TilemDisasmSymbol* sym)
183 {
184 	int n = sym - stab->syms;
185 
186 	tilem_free(sym->name);
187 
188 	if (n < stab->nsyms - 1) {
189 		memmove(sym, sym + 1,
190 			(stab->nsyms - n - 1) * sizeof(TilemDisasmSymbol));
191 	}
192 
193 	stab->nsyms--;
194 }
195 
set_symbol(TilemDisasmSymTable * stab,const char * name,dword value)196 static void set_symbol(TilemDisasmSymTable* stab,
197 		       const char* name, dword value)
198 {
199 	TilemDisasmSymbol* sym;
200 
201 	if ((sym = find_symbol_by_name(stab, name))) {
202 		if (sym->value == value)
203 			return;
204 		else
205 			del_symbol(stab, sym);
206 	}
207 
208 	sym = add_symbol(stab, value);
209 	tilem_free(sym->name);
210 	sym->name = tilem_new_atomic(char, strlen(name) + 1);
211 	strcpy(sym->name, name);
212 }
213 
skipws(char * p)214 static char* skipws(char* p)
215 {
216 	while (*p == ' ' || *p == '\t')
217 		p++;
218 	return p;
219 }
220 
skipwc(char * p)221 static char* skipwc(char* p)
222 {
223 	while ((unsigned char) *p > ' ')
224 		p++;
225 	return p;
226 }
227 
parse_sym_value(const char * text,dword * value)228 static int parse_sym_value(const char* text, dword* value)
229 {
230 	char* p;
231 	dword x;
232 
233 	if (text[0] >= '0' && text[0] <= '7' && text[1] == ',') {
234 		x = strtol(text + 2, &p, 16);
235 		*value = 0x1000 + (x << 4) + (text[0] - '0');
236 	}
237 	else {
238 		*value = strtol(text, &p, 16);
239 	}
240 
241 	return (p != text && *p == 0);
242 }
243 
parse_sym_line(TilemDisasmSymTable * stab,char * line)244 static int parse_sym_line(TilemDisasmSymTable* stab, char* line)
245 {
246 	char *w1end, *w2start, *w2end, *name;
247 	dword value;
248 
249 	if (line[0] == '#' || line[0] == ';')
250 		return 1;
251 
252 	w1end = skipwc(line);
253 	w2start = skipws(w1end);
254 	w2end = skipwc(w2start);
255 
256 	if (w1end == line || w2start == w1end || w2end == w2start)
257 		return 1;
258 	if (*w2end)
259 		return 1;
260 
261 	*w1end = *w2end = 0;
262 
263 	if (*line >= '0' && *line <= '9') {
264 		name = w2start;
265 		if (!parse_sym_value(line, &value))
266 			return 1;
267 	}
268 	else {
269 		name = line;
270 		if (!parse_sym_value(w2start, &value))
271 			return 1;
272 	}
273 
274 	set_symbol(stab, name, value);
275 	return 0;
276 }
277 
tilem_disasm_read_symbol_file(TilemDisasm * dasm,FILE * symfile)278 int tilem_disasm_read_symbol_file(TilemDisasm* dasm, FILE* symfile)
279 {
280 	char buf[1024];
281 	char* p;
282 	TilemDisasmSymTable* curtbl;
283 	int status = 1;
284 
285 	curtbl = &dasm->labels;
286 
287 	while (fgets(buf, sizeof(buf), symfile)) {
288 		p = buf + strlen(buf);
289 		while (p != buf && (p[-1] == '\n' || p[-1] == '\r'))
290 			p--;
291 		*p = 0;
292 
293 		if (!strcmp(buf, "[labels]"))
294 			curtbl = &dasm->labels;
295 		else if (!strcmp(buf, "[romcalls]"))
296 			curtbl = &dasm->romcalls;
297 		else if (!strcmp(buf, "[flags]"))
298 			curtbl = &dasm->flags;
299 		else if (!strcmp(buf, "[macros]"))
300 			curtbl = &dasm->macros;
301 		else if (!parse_sym_line(curtbl, buf))
302 			status = 0;
303 	}
304 
305 	return status;
306 }
307 
tilem_disasm_set_label(TilemDisasm * dasm,const char * name,dword value)308 void tilem_disasm_set_label(TilemDisasm* dasm, const char* name,
309 			     dword value)
310 {
311 	set_symbol(&dasm->labels, name, value);
312 }
313 
tilem_disasm_get_label(const TilemDisasm * dasm,const char * name,dword * value)314 int tilem_disasm_get_label(const TilemDisasm* dasm, const char* name,
315 			   dword* value)
316 {
317 	TilemDisasmSymbol* sym = find_symbol_by_name(&dasm->labels, name);
318 
319 	if (!sym)
320 		return 0;
321 	else if (value)
322 		*value = sym->value;
323 	return 1;
324 }
325 
tilem_disasm_get_label_at_address(const TilemDisasm * dasm,dword addr)326 const char* tilem_disasm_get_label_at_address(const TilemDisasm* dasm,
327 					      dword addr)
328 {
329 	TilemDisasmSymbol* sym = find_symbol(&dasm->labels, addr);
330 
331 	if (sym)
332 		return sym->name;
333 	else
334 		return NULL;
335 }
336 
337 typedef struct _TilemDisasmInstruction {
338 	int length;
339 	const char* pattern;
340 } TilemDisasmInstruction;
341 
342 static const TilemDisasmInstruction insts_main[256] = {
343 	{1,"NOP"},       {3,"LD~BC,%w"},  {1,"LD~(BC),A"},   {1,"INC~BC"},
344 	{1,"INC~B"},     {1,"DEC~B"},     {2,"LD~B,%b"},     {1,"RLCA"},
345 	{1,"EX~AF,AF'"}, {1,"ADD~HL,BC"}, {1,"LD~A,(BC)"},   {1,"DEC~BC"},
346 	{1,"INC~C"},     {1,"DEC~C"},     {2,"LD~C,%b"},     {1,"RRCA"},
347 	{2,"DJNZ~%r"},   {3,"LD~DE,%w"},  {1,"LD~(DE),A"},   {1,"INC~DE"},
348 	{1,"INC~D"},     {1,"DEC~D"},     {2,"LD~D,%b"},     {1,"RLA"},
349 	{2,"JR~%r"},     {1,"ADD~HL,DE"}, {1,"LD~A,(DE)"},   {1,"DEC~DE"},
350 	{1,"INC~E"},     {1,"DEC~E"},     {2,"LD~E,%b"},     {1,"RRA"},
351 	{2,"JR~NZ,%r"},  {3,"LD~HL,%w"},  {3,"LD~(%a),HL"},  {1,"INC~HL"},
352 	{1,"INC~H"},     {1,"DEC~H"},     {2,"LD~H,%b"},     {1,"DAA"},
353 	{2,"JR~Z,%r"},   {1,"ADD~HL,HL"}, {3,"LD~HL,(%a)"},  {1,"DEC~HL"},
354 	{1,"INC~L"},     {1,"DEC~L"},     {2,"LD~L,%b"},     {1,"CPL"},
355 	{2,"JR~NC,%r"},  {3,"LD~SP,%w"},  {3,"LD~(%a),A"},   {1,"INC~SP"},
356 	{1,"INC~(HL)"},  {1,"DEC~(HL)"},  {2,"LD~(HL),%b"},  {1,"SCF"},
357 	{2,"JR~C,%r"},   {1,"ADD~HL,SP"}, {3,"LD~A,(%a)"},   {1,"DEC~SP"},
358 	{1,"INC~A"},     {1,"DEC~A"},     {2,"LD~A,%b"},     {1,"CCF"},
359 
360 	{1,"LD~B,B"},    {1,"LD~B,C"},    {1,"LD~B,D"},    {1,"LD~B,E"},
361 	{1,"LD~B,H"},    {1,"LD~B,L"},    {1,"LD~B,(HL)"}, {1,"LD~B,A"},
362 	{1,"LD~C,B"},    {1,"LD~C,C"},    {1,"LD~C,D"},    {1,"LD~C,E"},
363 	{1,"LD~C,H"},    {1,"LD~C,L"},    {1,"LD~C,(HL)"}, {1,"LD~C,A"},
364 	{1,"LD~D,B"},    {1,"LD~D,C"},    {1,"LD~D,D"},    {1,"LD~D,E"},
365 	{1,"LD~D,H"},    {1,"LD~D,L"},    {1,"LD~D,(HL)"}, {1,"LD~D,A"},
366 	{1,"LD~E,B"},    {1,"LD~E,C"},    {1,"LD~E,D"},    {1,"LD~E,E"},
367 	{1,"LD~E,H"},    {1,"LD~E,L"},    {1,"LD~E,(HL)"}, {1,"LD~E,A"},
368 	{1,"LD~H,B"},    {1,"LD~H,C"},    {1,"LD~H,D"},    {1,"LD~H,E"},
369 	{1,"LD~H,H"},    {1,"LD~H,L"},    {1,"LD~H,(HL)"}, {1,"LD~H,A"},
370 	{1,"LD~L,B"},    {1,"LD~L,C"},    {1,"LD~L,D"},    {1,"LD~L,E"},
371 	{1,"LD~L,H"},    {1,"LD~L,L"},    {1,"LD~L,(HL)"}, {1,"LD~L,A"},
372 	{1,"LD~(HL),B"}, {1,"LD~(HL),C"}, {1,"LD~(HL),D"}, {1,"LD~(HL),E"},
373 	{1,"LD~(HL),H"}, {1,"LD~(HL),L"}, {1,"HALT"},      {1,"LD~(HL),A"},
374 	{1,"LD~A,B"},    {1,"LD~A,C"},    {1,"LD~A,D"},    {1,"LD~A,E"},
375 	{1,"LD~A,H"},    {1,"LD~A,L"},    {1,"LD~A,(HL)"}, {1,"LD~A,A"},
376 
377 	{1,"ADD~A,B"}, {1,"ADD~A,C"}, {1,"ADD~A,D"},    {1,"ADD~A,E"},
378 	{1,"ADD~A,H"}, {1,"ADD~A,L"}, {1,"ADD~A,(HL)"}, {1,"ADD~A,A"},
379 	{1,"ADC~A,B"}, {1,"ADC~A,C"}, {1,"ADC~A,D"},    {1,"ADC~A,E"},
380 	{1,"ADC~A,H"}, {1,"ADC~A,L"}, {1,"ADC~A,(HL)"}, {1,"ADC~A,A"},
381 	{1,"SUB~B"},   {1,"SUB~C"},   {1,"SUB~D"},      {1,"SUB~E"},
382 	{1,"SUB~H"},   {1,"SUB~L"},   {1,"SUB~(HL)"},   {1,"SUB~A"},
383 	{1,"SBC~A,B"}, {1,"SBC~A,C"}, {1,"SBC~A,D"},    {1,"SBC~A,E"},
384 	{1,"SBC~A,H"}, {1,"SBC~A,L"}, {1,"SBC~A,(HL)"}, {1,"SBC~A,A"},
385 	{1,"AND~B"},   {1,"AND~C"},   {1,"AND~D"},      {1,"AND~E"},
386 	{1,"AND~H"},   {1,"AND~L"},   {1,"AND~(HL)"},   {1,"AND~A"},
387 	{1,"XOR~B"},   {1,"XOR~C"},   {1,"XOR~D"},      {1,"XOR~E"},
388 	{1,"XOR~H"},   {1,"XOR~L"},   {1,"XOR~(HL)"},   {1,"XOR~A"},
389 	{1,"OR~B"},    {1,"OR~C"},    {1,"OR~D"},       {1,"OR~E"},
390 	{1,"OR~H"},    {1,"OR~L"},    {1,"OR~(HL)"},    {1,"OR~A"},
391 	{1,"CP~B"},    {1,"CP~C"},    {1,"CP~D"},       {1,"CP~E"},
392 	{1,"CP~H"},    {1,"CP~L"},    {1,"CP~(HL)"},    {1,"CP~A"},
393 
394 	{1,"RET~NZ"},      {1,"POP~BC"},   {3,"JP~NZ,%j"},  {3,"JP~%j"},
395 	{3,"CALL~NZ,%j"},  {1,"PUSH~BC"},  {2,"ADD~A,%b"},  {1,"RST~%z"},
396 	{1,"RET~Z"},       {1,"RET"},      {3,"JP~Z,%j"},   {1,0},
397 	{3,"CALL~Z,%j"},   {3,"CALL~%j"},  {2,"ADC~A,%b"},  {1,"RST~%z"},
398 	{1,"RET~NC"},      {1,"POP~DE"},   {3,"JP~NC,%j"},  {2,"OUT~(%b),A"},
399 	{3,"CALL~NC,%j"},  {1,"PUSH~DE"},  {2,"SUB~%b"},    {1,"RST~%z"},
400 	{1,"RET~C"},       {1,"EXX"},      {3,"JP~C,%j"},   {2,"IN~A,(%b)"},
401 	{3,"CALL~C,%j"},   {1,0},          {2,"SBC~A,%b"},  {1,"RST~%z"},
402 	{1,"RET~PO"},      {1,"POP~HL"},   {3,"JP~PO,%j"},  {1,"EX~(SP),HL"},
403 	{3,"CALL~PO,%j"},  {1,"PUSH~HL"},  {2,"AND~%b"},    {1,"RST~%z"},
404 	{1,"RET~PE"},      {1,"JP~(HL)"},  {3,"JP~PE,%j"},  {1,"EX~DE,HL"},
405 	{3,"CALL~PE,%j"},  {1,0},          {2,"XOR~%b"},    {1,"RST~%z"},
406 	{1,"RET~P"},       {1,"POP~AF"},   {3,"JP~P,%j"},   {1,"DI"},
407 	{3,"CALL~P,%j"},   {1,"PUSH~AF"},  {2,"OR~%b"},     {1,"RST~%z"},
408 	{1,"RET~M"},       {1,"LD~SP,HL"}, {3,"JP~M,%j"},   {1,"EI"},
409 	{3,"CALL~M,%j"},   {1,0},          {2,"CP~%b"},     {1,"RST~%z"}};
410 
411 static const TilemDisasmInstruction insts_ddfd[256] = {
412 	{1,0},            {1,0},            {1,0},              {1,0},
413 	{1,0},            {1,0},            {1,0},              {1,0},
414 	{1,0},            {2,"ADD~%i,BC"},  {1,0},              {1,0},
415 	{1,0},            {1,0},            {1,0},              {1,0},
416 	{1,0},            {1,0},            {1,0},              {1,0},
417 	{1,0},            {1,0},            {1,0},              {1,0},
418 	{1,0},            {2,"ADD~%i,DE"},  {1,0},              {1,0},
419 	{1,0},            {1,0},            {1,0},              {1,0},
420 	{1,0},            {4,"LD~%i,%w"},   {4,"LD~(%a),%i"},   {2,"INC~%i"},
421 	{2,"INC~%iH"},    {2,"DEC~%iH"},    {3,"LD~%iH,%b"},    {1,0},
422 	{1,0},            {2,"ADD~%i,%i"},  {4,"LD~%i,(%a)"},   {2,"DEC~%i"},
423 	{2,"INC~%iL"},    {2,"DEC~%iL"},    {3,"LD~%iL,%b"},    {1,0},
424 	{1,0},            {1,0},            {1,0},              {1,0},
425 	{3,"INC~(%i%s)"}, {3,"DEC~(%i%s)"}, {4,"LD~(%i%s),%b"}, {1,0},
426 	{1,0},            {2,"ADD~%i,SP"},  {1,0},              {1,0},
427 	{1,0},            {1,0},            {1,0},              {1,0},
428 
429 	{1,0},             {1,0},             {1,0},             {1,0},
430 	{2,"LD~B,%iH"},    {2,"LD~B,%iL"},    {3,"LD~B,(%i%s)"}, {1,0},
431 	{1,0},             {1,0},             {1,0},             {1,0},
432 	{2,"LD~C,%iH"},    {2,"LD~C,%iL"},    {3,"LD~C,(%i%s)"}, {1,0},
433 	{1,0},             {1,0},             {1,0},             {1,0},
434 	{2,"LD~D,%iH"},    {2,"LD~D,%iL"},    {3,"LD~D,(%i%s)"}, {1,0},
435 	{1,0},             {1,0},             {1,0},             {1,0},
436 	{2,"LD~E,%iH"},    {2,"LD~E,%iL"},    {3,"LD~E,(%i%s)"}, {1,0},
437 	{2,"LD~%iH,B"},    {2,"LD~%iH,C"},    {2,"LD~%iH,D"},    {2,"LD~%iH,E"},
438 	{2,"LD~%iH,%iH"},  {2,"LD~%iH,%iL"},  {3,"LD~H,(%i%s)"}, {2,"LD~%iH,A"},
439 	{2,"LD~%iL,B"},    {2,"LD~%iL,C"},    {2,"LD~%iL,D"},    {2,"LD~%iL,E"},
440 	{2,"LD~%iL,%iH"},  {2,"LD~%iL,%iL"},  {3,"LD~L,(%i%s)"}, {2,"LD~%iL,A"},
441 	{3,"LD~(%i%s),B"}, {3,"LD~(%i%s),C"}, {3,"LD~(%i%s),D"}, {3,"LD~(%i%s),E"},
442 	{3,"LD~(%i%s),H"}, {3,"LD~(%i%s),L"}, {1,0},             {3,"LD~(%i%s),A"},
443 	{1,0},             {1,0},             {1,0},             {1,0},
444 	{2,"LD~A,%iH"},    {2,"LD~A,%iL"},    {3,"LD~A,(%i%s)"}, {1,0},
445 
446 	{1,0},           {1,0},           {1,0},              {1,0},
447 	{2,"ADD~A,%iH"}, {2,"ADD~A,%iL"}, {3,"ADD~A,(%i%s)"}, {1,0},
448 	{1,0},           {1,0},           {1,0},              {1,0},
449 	{2,"ADC~A,%iH"}, {2,"ADC~A,%iL"}, {3,"ADC~A,(%i%s)"}, {1,0},
450 	{1,0},           {1,0},           {1,0},              {1,0},
451 	{2,"SUB~%iH"},   {2,"SUB~%iL"},   {3,"SUB~(%i%s)"},   {1,0},
452 	{1,0},           {1,0},           {1,0},              {1,0},
453 	{2,"SBC~A,%iH"}, {2,"SBC~A,%iL"}, {3,"SBC~A,(%i%s)"}, {1,0},
454 	{1,0},           {1,0},           {1,0},              {1,0},
455 	{2,"AND~%iH"},   {2,"AND~%iL"},   {3,"AND~(%i%s)"},   {1,0},
456 	{1,0},           {1,0},           {1,0},              {1,0},
457 	{2,"XOR~%iH"},   {2,"XOR~%iL"},   {3,"XOR~(%i%s)"},   {1,0},
458 	{1,0},           {1,0},           {1,0},              {1,0},
459 	{2,"OR~%iH"},    {2,"OR~%iL"},    {3,"OR~(%i%s)"},    {1,0},
460 	{1,0},           {1,0},           {1,0},              {1,0},
461 	{2,"CP~%iH"},    {2,"CP~%iL"},    {3,"CP~(%i%s)"},    {1,0},
462 
463 	{1,0}, {1,0},          {1,0}, {1,0},
464 	{1,0}, {1,0},          {1,0}, {1,0},
465 	{1,0}, {1,0},          {1,0}, {1,0},
466 	{1,0}, {1,0},          {1,0}, {1,0},
467 	{1,0}, {1,0},          {1,0}, {1,0},
468 	{1,0}, {1,0},          {1,0}, {1,0},
469 	{1,0}, {1,0},          {1,0}, {1,0},
470 	{1,0}, {1,0},          {1,0}, {1,0},
471 	{1,0}, {2,"POP~%i"},   {1,0}, {2,"EX~(SP),%i"},
472 	{1,0}, {2,"PUSH~%i"},  {1,0}, {1,0},
473 	{1,0}, {2,"JP~(%i)"},  {1,0}, {1,0},
474 	{1,0}, {1,0},          {1,0}, {1,0},
475 	{1,0}, {1,0},          {1,0}, {1,0},
476 	{1,0}, {1,0},          {1,0}, {1,0},
477 	{1,0}, {2,"LD~SP,%i"}, {1,0}, {1,0},
478 	{1,0}, {1,0},          {1,0}, {1,0}};
479 
480 static const TilemDisasmInstruction insts_cb[256] = {
481 	{2,"RLC~B"},  {2,"RLC~C"},  {2,"RLC~D"},     {2,"RLC~E"},
482 	{2,"RLC~H"},  {2,"RLC~L"},  {2,"RLC~(HL)"},  {2,"RLC~A"},
483 	{2,"RRC~B"},  {2,"RRC~C"},  {2,"RRC~D"},     {2,"RRC~E"},
484 	{2,"RRC~H"},  {2,"RRC~L"},  {2,"RRC~(HL)"},  {2,"RRC~A"},
485 	{2,"RL~B"},   {2,"RL~C"},   {2,"RL~D"},      {2,"RL~E"},
486 	{2,"RL~H"},   {2,"RL~L"},   {2,"RL~(HL)"},   {2,"RL~A"},
487 	{2,"RR~B"},   {2,"RR~C"},   {2,"RR~D"},      {2,"RR~E"},
488 	{2,"RR~H"},   {2,"RR~L"},   {2,"RR~(HL)"},   {2,"RR~A"},
489 	{2,"SLA~B"},  {2,"SLA~C"},  {2,"SLA~D"},     {2,"SLA~E"},
490 	{2,"SLA~H"},  {2,"SLA~L"},  {2,"SLA~(HL)"},  {2,"SLA~A"},
491 	{2,"SRA~B"},  {2,"SRA~C"},  {2,"SRA~D"},     {2,"SRA~E"},
492 	{2,"SRA~H"},  {2,"SRA~L"},  {2,"SRA~(HL)"},  {2,"SRA~A"},
493 	{2,"SLIA~B"}, {2,"SLIA~C"}, {2,"SLIA~D"},    {2,"SLIA~E"},
494 	{2,"SLIA~H"}, {2,"SLIA~L"}, {2,"SLIA~(HL)"}, {2,"SLIA~A"},
495 	{2,"SRL~B"},  {2,"SRL~C"},  {2,"SRL~D"},     {2,"SRL~E"},
496 	{2,"SRL~H"},  {2,"SRL~L"},  {2,"SRL~(HL)"},  {2,"SRL~A"},
497 
498 	{2,"BIT~0,B"}, {2,"BIT~0,C"}, {2,"BIT~0,D"},    {2,"BIT~0,E"},
499 	{2,"BIT~0,H"}, {2,"BIT~0,L"}, {2,"BIT~0,(HL)"}, {2,"BIT~0,A"},
500 	{2,"BIT~1,B"}, {2,"BIT~1,C"}, {2,"BIT~1,D"},    {2,"BIT~1,E"},
501 	{2,"BIT~1,H"}, {2,"BIT~1,L"}, {2,"BIT~1,(HL)"}, {2,"BIT~1,A"},
502 	{2,"BIT~2,B"}, {2,"BIT~2,C"}, {2,"BIT~2,D"},    {2,"BIT~2,E"},
503 	{2,"BIT~2,H"}, {2,"BIT~2,L"}, {2,"BIT~2,(HL)"}, {2,"BIT~2,A"},
504 	{2,"BIT~3,B"}, {2,"BIT~3,C"}, {2,"BIT~3,D"},    {2,"BIT~3,E"},
505 	{2,"BIT~3,H"}, {2,"BIT~3,L"}, {2,"BIT~3,(HL)"}, {2,"BIT~3,A"},
506 	{2,"BIT~4,B"}, {2,"BIT~4,C"}, {2,"BIT~4,D"},    {2,"BIT~4,E"},
507 	{2,"BIT~4,H"}, {2,"BIT~4,L"}, {2,"BIT~4,(HL)"}, {2,"BIT~4,A"},
508 	{2,"BIT~5,B"}, {2,"BIT~5,C"}, {2,"BIT~5,D"},    {2,"BIT~5,E"},
509 	{2,"BIT~5,H"}, {2,"BIT~5,L"}, {2,"BIT~5,(HL)"}, {2,"BIT~5,A"},
510 	{2,"BIT~6,B"}, {2,"BIT~6,C"}, {2,"BIT~6,D"},    {2,"BIT~6,E"},
511 	{2,"BIT~6,H"}, {2,"BIT~6,L"}, {2,"BIT~6,(HL)"}, {2,"BIT~6,A"},
512 	{2,"BIT~7,B"}, {2,"BIT~7,C"}, {2,"BIT~7,D"},    {2,"BIT~7,E"},
513 	{2,"BIT~7,H"}, {2,"BIT~7,L"}, {2,"BIT~7,(HL)"}, {2,"BIT~7,A"},
514 
515 	{2,"RES~0,B"}, {2,"RES~0,C"}, {2,"RES~0,D"},    {2,"RES~0,E"},
516 	{2,"RES~0,H"}, {2,"RES~0,L"}, {2,"RES~0,(HL)"}, {2,"RES~0,A"},
517 	{2,"RES~1,B"}, {2,"RES~1,C"}, {2,"RES~1,D"},    {2,"RES~1,E"},
518 	{2,"RES~1,H"}, {2,"RES~1,L"}, {2,"RES~1,(HL)"}, {2,"RES~1,A"},
519 	{2,"RES~2,B"}, {2,"RES~2,C"}, {2,"RES~2,D"},    {2,"RES~2,E"},
520 	{2,"RES~2,H"}, {2,"RES~2,L"}, {2,"RES~2,(HL)"}, {2,"RES~2,A"},
521 	{2,"RES~3,B"}, {2,"RES~3,C"}, {2,"RES~3,D"},    {2,"RES~3,E"},
522 	{2,"RES~3,H"}, {2,"RES~3,L"}, {2,"RES~3,(HL)"}, {2,"RES~3,A"},
523 	{2,"RES~4,B"}, {2,"RES~4,C"}, {2,"RES~4,D"},    {2,"RES~4,E"},
524 	{2,"RES~4,H"}, {2,"RES~4,L"}, {2,"RES~4,(HL)"}, {2,"RES~4,A"},
525 	{2,"RES~5,B"}, {2,"RES~5,C"}, {2,"RES~5,D"},    {2,"RES~5,E"},
526 	{2,"RES~5,H"}, {2,"RES~5,L"}, {2,"RES~5,(HL)"}, {2,"RES~5,A"},
527 	{2,"RES~6,B"}, {2,"RES~6,C"}, {2,"RES~6,D"},    {2,"RES~6,E"},
528 	{2,"RES~6,H"}, {2,"RES~6,L"}, {2,"RES~6,(HL)"}, {2,"RES~6,A"},
529 	{2,"RES~7,B"}, {2,"RES~7,C"}, {2,"RES~7,D"},    {2,"RES~7,E"},
530 	{2,"RES~7,H"}, {2,"RES~7,L"}, {2,"RES~7,(HL)"}, {2,"RES~7,A"},
531 
532 	{2,"SET~0,B"}, {2,"SET~0,C"}, {2,"SET~0,D"},    {2,"SET~0,E"},
533 	{2,"SET~0,H"}, {2,"SET~0,L"}, {2,"SET~0,(HL)"}, {2,"SET~0,A"},
534 	{2,"SET~1,B"}, {2,"SET~1,C"}, {2,"SET~1,D"},    {2,"SET~1,E"},
535 	{2,"SET~1,H"}, {2,"SET~1,L"}, {2,"SET~1,(HL)"}, {2,"SET~1,A"},
536 	{2,"SET~2,B"}, {2,"SET~2,C"}, {2,"SET~2,D"},    {2,"SET~2,E"},
537 	{2,"SET~2,H"}, {2,"SET~2,L"}, {2,"SET~2,(HL)"}, {2,"SET~2,A"},
538 	{2,"SET~3,B"}, {2,"SET~3,C"}, {2,"SET~3,D"},    {2,"SET~3,E"},
539 	{2,"SET~3,H"}, {2,"SET~3,L"}, {2,"SET~3,(HL)"}, {2,"SET~3,A"},
540 	{2,"SET~4,B"}, {2,"SET~4,C"}, {2,"SET~4,D"},    {2,"SET~4,E"},
541 	{2,"SET~4,H"}, {2,"SET~4,L"}, {2,"SET~4,(HL)"}, {2,"SET~4,A"},
542 	{2,"SET~5,B"}, {2,"SET~5,C"}, {2,"SET~5,D"},    {2,"SET~5,E"},
543 	{2,"SET~5,H"}, {2,"SET~5,L"}, {2,"SET~5,(HL)"}, {2,"SET~5,A"},
544 	{2,"SET~6,B"}, {2,"SET~6,C"}, {2,"SET~6,D"},    {2,"SET~6,E"},
545 	{2,"SET~6,H"}, {2,"SET~6,L"}, {2,"SET~6,(HL)"}, {2,"SET~6,A"},
546 	{2,"SET~7,B"}, {2,"SET~7,C"}, {2,"SET~7,D"},    {2,"SET~7,E"},
547 	{2,"SET~7,H"}, {2,"SET~7,L"}, {2,"SET~7,(HL)"}, {2,"SET~7,A"}};
548 
549 static const TilemDisasmInstruction insts_ddfdcb[256] = {
550 	{4,"RLC~B,(%i%s)"},  {4,"RLC~C,(%i%s)"},  {4,"RLC~D,(%i%s)"},  {4,"RLC~E,(%i%s)"},
551 	{4,"RLC~H,(%i%s)"},  {4,"RLC~L,(%i%s)"},  {4,"RLC~(%i%s)"},    {4,"RLC~A,(%i%s)"},
552 	{4,"RRC~B,(%i%s)"},  {4,"RRC~C,(%i%s)"},  {4,"RRC~D,(%i%s)"},  {4,"RRC~E,(%i%s)"},
553 	{4,"RRC~H,(%i%s)"},  {4,"RRC~L,(%i%s)"},  {4,"RRC~(%i%s)"},    {4,"RRC~A,(%i%s)"},
554 	{4,"RL~B,(%i%s)"},   {4,"RL~C,(%i%s)"},   {4,"RL~D,(%i%s)"},   {4,"RL~E,(%i%s)"},
555 	{4,"RL~H,(%i%s)"},   {4,"RL~L,(%i%s)"},   {4,"RL~(%i%s)"},     {4,"RL~A,(%i%s)"},
556 	{4,"RR~B,(%i%s)"},   {4,"RR~C,(%i%s)"},   {4,"RR~D,(%i%s)"},   {4,"RR~E,(%i%s)"},
557 	{4,"RR~H,(%i%s)"},   {4,"RR~L,(%i%s)"},   {4,"RR~(%i%s)"},     {4,"RR~A,(%i%s)"},
558 	{4,"SLA~B,(%i%s)"},  {4,"SLA~C,(%i%s)"},  {4,"SLA~D,(%i%s)"},  {4,"SLA~E,(%i%s)"},
559 	{4,"SLA~H,(%i%s)"},  {4,"SLA~L,(%i%s)"},  {4,"SLA~(%i%s)"},    {4,"SLA~A,(%i%s)"},
560 	{4,"SRA~B,(%i%s)"},  {4,"SRA~C,(%i%s)"},  {4,"SRA~D,(%i%s)"},  {4,"SRA~E,(%i%s)"},
561 	{4,"SRA~H,(%i%s)"},  {4,"SRA~L,(%i%s)"},  {4,"SRA~(%i%s)"},    {4,"SRA~A,(%i%s)"},
562 	{4,"SLIA~B,(%i%s)"}, {4,"SLIA~C,(%i%s)"}, {4,"SLIA~D,(%i%s)"}, {4,"SLIA~E,(%i%s)"},
563 	{4,"SLIA~H,(%i%s)"}, {4,"SLIA~L,(%i%s)"}, {4,"SLIA~(%i%s)"},   {4,"SLIA~A,(%i%s)"},
564 	{4,"SRL~B,(%i%s)"},  {4,"SRL~C,(%i%s)"},  {4,"SRL~D,(%i%s)"},  {4,"SRL~E,(%i%s)"},
565 	{4,"SRL~H,(%i%s)"},  {4,"SRL~L,(%i%s)"},  {4,"SRL~(%i%s)"},    {4,"SRL~A,(%i%s)"},
566 
567 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"},
568 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"},  {4,"BIT~%f*"},
569 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"},
570 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"},  {4,"BIT~%f*"},
571 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"},
572 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"},  {4,"BIT~%f*"},
573 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"},
574 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"},  {4,"BIT~%f*"},
575 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"},
576 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"},  {4,"BIT~%f*"},
577 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"},
578 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"},  {4,"BIT~%f*"},
579 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"},
580 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"},  {4,"BIT~%f*"},
581 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"},
582 	{4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"},  {4,"BIT~%f*"},
583 
584 	{4,"RES~0,B,(%i%s)"}, {4,"RES~0,C,(%i%s)"}, {4,"RES~0,D,(%i%s)"}, {4,"RES~0,E,(%i%s)"},
585 	{4,"RES~0,H,(%i%s)"}, {4,"RES~0,L,(%i%s)"}, {4,"RES~%f"},         {4,"RES~0,A,(%i%s)"},
586 	{4,"RES~1,B,(%i%s)"}, {4,"RES~1,C,(%i%s)"}, {4,"RES~1,D,(%i%s)"}, {4,"RES~1,E,(%i%s)"},
587 	{4,"RES~1,H,(%i%s)"}, {4,"RES~1,L,(%i%s)"}, {4,"RES~%f"},         {4,"RES~1,A,(%i%s)"},
588 	{4,"RES~2,B,(%i%s)"}, {4,"RES~2,C,(%i%s)"}, {4,"RES~2,D,(%i%s)"}, {4,"RES~2,E,(%i%s)"},
589 	{4,"RES~2,H,(%i%s)"}, {4,"RES~2,L,(%i%s)"}, {4,"RES~%f"},         {4,"RES~2,A,(%i%s)"},
590 	{4,"RES~3,B,(%i%s)"}, {4,"RES~3,C,(%i%s)"}, {4,"RES~3,D,(%i%s)"}, {4,"RES~3,E,(%i%s)"},
591 	{4,"RES~3,H,(%i%s)"}, {4,"RES~3,L,(%i%s)"}, {4,"RES~%f"},         {4,"RES~3,A,(%i%s)"},
592 	{4,"RES~4,B,(%i%s)"}, {4,"RES~4,C,(%i%s)"}, {4,"RES~4,D,(%i%s)"}, {4,"RES~4,E,(%i%s)"},
593 	{4,"RES~4,H,(%i%s)"}, {4,"RES~4,L,(%i%s)"}, {4,"RES~%f"},         {4,"RES~4,A,(%i%s)"},
594 	{4,"RES~5,B,(%i%s)"}, {4,"RES~5,C,(%i%s)"}, {4,"RES~5,D,(%i%s)"}, {4,"RES~5,E,(%i%s)"},
595 	{4,"RES~5,H,(%i%s)"}, {4,"RES~5,L,(%i%s)"}, {4,"RES~%f"},         {4,"RES~5,A,(%i%s)"},
596 	{4,"RES~6,B,(%i%s)"}, {4,"RES~6,C,(%i%s)"}, {4,"RES~6,D,(%i%s)"}, {4,"RES~6,E,(%i%s)"},
597 	{4,"RES~6,H,(%i%s)"}, {4,"RES~6,L,(%i%s)"}, {4,"RES~%f"},         {4,"RES~6,A,(%i%s)"},
598 	{4,"RES~7,B,(%i%s)"}, {4,"RES~7,C,(%i%s)"}, {4,"RES~7,D,(%i%s)"}, {4,"RES~7,E,(%i%s)"},
599 	{4,"RES~7,H,(%i%s)"}, {4,"RES~7,L,(%i%s)"}, {4,"RES~%f"},         {4,"RES~7,A,(%i%s)"},
600 
601 	{4,"SET~0,B,(%i%s)"}, {4,"SET~0,C,(%i%s)"}, {4,"SET~0,D,(%i%s)"}, {4,"SET~0,E,(%i%s)"},
602 	{4,"SET~0,H,(%i%s)"}, {4,"SET~0,L,(%i%s)"}, {4,"SET~%f"},         {4,"SET~0,A,(%i%s)"},
603 	{4,"SET~1,B,(%i%s)"}, {4,"SET~1,C,(%i%s)"}, {4,"SET~1,D,(%i%s)"}, {4,"SET~1,E,(%i%s)"},
604 	{4,"SET~1,H,(%i%s)"}, {4,"SET~1,L,(%i%s)"}, {4,"SET~%f"},         {4,"SET~1,A,(%i%s)"},
605 	{4,"SET~2,B,(%i%s)"}, {4,"SET~2,C,(%i%s)"}, {4,"SET~2,D,(%i%s)"}, {4,"SET~2,E,(%i%s)"},
606 	{4,"SET~2,H,(%i%s)"}, {4,"SET~2,L,(%i%s)"}, {4,"SET~%f"},         {4,"SET~2,A,(%i%s)"},
607 	{4,"SET~3,B,(%i%s)"}, {4,"SET~3,C,(%i%s)"}, {4,"SET~3,D,(%i%s)"}, {4,"SET~3,E,(%i%s)"},
608 	{4,"SET~3,H,(%i%s)"}, {4,"SET~3,L,(%i%s)"}, {4,"SET~%f"},         {4,"SET~3,A,(%i%s)"},
609 	{4,"SET~4,B,(%i%s)"}, {4,"SET~4,C,(%i%s)"}, {4,"SET~4,D,(%i%s)"}, {4,"SET~4,E,(%i%s)"},
610 	{4,"SET~4,H,(%i%s)"}, {4,"SET~4,L,(%i%s)"}, {4,"SET~%f"},         {4,"SET~4,A,(%i%s)"},
611 	{4,"SET~5,B,(%i%s)"}, {4,"SET~5,C,(%i%s)"}, {4,"SET~5,D,(%i%s)"}, {4,"SET~5,E,(%i%s)"},
612 	{4,"SET~5,H,(%i%s)"}, {4,"SET~5,L,(%i%s)"}, {4,"SET~%f"},         {4,"SET~5,A,(%i%s)"},
613 	{4,"SET~6,B,(%i%s)"}, {4,"SET~6,C,(%i%s)"}, {4,"SET~6,D,(%i%s)"}, {4,"SET~6,E,(%i%s)"},
614 	{4,"SET~6,H,(%i%s)"}, {4,"SET~6,L,(%i%s)"}, {4,"SET~%f"},         {4,"SET~6,A,(%i%s)"},
615 	{4,"SET~7,B,(%i%s)"}, {4,"SET~7,C,(%i%s)"}, {4,"SET~7,D,(%i%s)"}, {4,"SET~7,E,(%i%s)"},
616 	{4,"SET~7,H,(%i%s)"}, {4,"SET~7,L,(%i%s)"}, {4,"SET~%f"},         {4,"SET~7,A,(%i%s)"}};
617 
618 static const TilemDisasmInstruction insts_ed[256] = {
619 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
620 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
621 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
622 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
623 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
624 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
625 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
626 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
627 
628 	{2,"IN~B,(C)"}, {2,"OUT~(C),B"}, {2,"SBC~HL,BC"}, {4,"LD~(%a),BC"},
629 	{2,"NEG"},      {2,"RETN"},      {2,"IM~0"},      {2,"LD~I,A"},
630 	{2,"IN~C,(C)"}, {2,"OUT~(C),C"}, {2,"ADC~HL,BC"}, {4,"LD~BC,(%a)"},
631 	{2,"NEG*"},     {2,"RETI"},      {2,"IM~0*"},     {2,"LD~R,A"},
632 	{2,"IN~D,(C)"}, {2,"OUT~(C),D"}, {2,"SBC~HL,DE"}, {4,"LD~(%a),DE"},
633 	{2,"NEG*"},     {2,"RETN*"},     {2,"IM~1"},      {2,"LD~A,I"},
634 	{2,"IN~E,(C)"}, {2,"OUT~(C),E"}, {2,"ADC~HL,DE"}, {4,"LD~DE,(%a)"},
635 	{2,"NEG*"},     {2,"RETN*"},     {2,"IM~2"},      {2,"LD~A,R"},
636 	{2,"IN~H,(C)"}, {2,"OUT~(C),H"}, {2,"SBC~HL,HL"}, {4,"LD~(%a),HL*"},
637 	{2,"NEG*"},     {2,"RETN*"},     {2,"IM~0*"},     {2,"RRD"},
638 	{2,"IN~L,(C)"}, {2,"OUT~(C),L"}, {2,"ADC~HL,HL"}, {4,"LD~HL,(%a)*"},
639 	{2,"NEG*"},     {2,"RETN*"},     {2,"IM~0*"},     {2,"RLD"},
640 	{2,"IN~(C)"},   {2,"OUT~(C),0"}, {2,"SBC~HL,SP"}, {4,"LD~(%a),SP"},
641 	{2,"NEG*"},     {2,"RETN*"},     {2,"IM~1*"},     {2,0},
642 	{2,"IN~A,(C)"}, {2,"OUT~(C),A"}, {2,"ADC~HL,SP"}, {4,"LD~SP,(%a)"},
643 	{2,"NEG*"},     {2,"RETN*"},     {2,"IM~2*"},     {2,0},
644 
645 	{2,0},      {2,0},      {2,0},      {2,0},
646 	{2,0},      {2,0},      {2,0},      {2,0},
647 	{2,0},      {2,0},      {2,0},      {2,0},
648 	{2,0},      {2,0},      {2,0},      {2,0},
649 	{2,0},      {2,0},      {2,0},      {2,0},
650 	{2,0},      {2,0},      {2,0},      {2,0},
651 	{2,0},      {2,0},      {2,0},      {2,0},
652 	{2,0},      {2,0},      {2,0},      {2,0},
653 	{2,"LDI"},  {2,"CPI"},  {2,"INI"},  {2,"OUTI"},
654 	{2,0},      {2,0},      {2,0},      {2,0},
655 	{2,"LDD"},  {2,"CPD"},  {2,"IND"},  {2,"OUTD"},
656 	{2,0},      {2,0},      {2,0},      {2,0},
657 	{2,"LDIR"}, {2,"CPIR"}, {2,"INIR"}, {2,"OTIR"},
658 	{2,0},      {2,0},      {2,0},      {2,0},
659 	{2,"LDDR"}, {2,"CPDR"}, {2,"INDR"}, {2,"OTDR"},
660 	{2,0},      {2,0},      {2,0},      {2,0},
661 
662 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
663 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
664 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
665 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
666 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
667 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
668 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0},
669 	{2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}};
670 
671 /* Count number of bytes of arguments for a given instruction/macro
672    pattern */
pattern_arg_size(const char * pattern)673 static int pattern_arg_size(const char* pattern)
674 {
675 	char* p;
676 	int count = 0, offs;
677 
678 	while (*pattern) {
679 		if (*pattern != '%')
680 			pattern++;
681 		else {
682 			pattern++;
683 
684 			if (*pattern >= '0' && *pattern <= '9') {
685 				offs = strtol(pattern, &p, 10);
686 				pattern = p;
687 			}
688 			else {
689 				offs = count;
690 			}
691 
692 			switch (*pattern) {
693 			case 0:
694 				pattern--;
695 				break;
696 
697 			case 'b':
698 			case 'C':
699 			case 'r':
700 			case 's':
701 				offs++;
702 				break;
703 
704 			case 'a':
705 			case 'c':
706 			case 'f':
707 			case 'j':
708 			case 'w':
709 				offs += 2;
710 				break;
711 			}
712 
713 			pattern++;
714 			if (offs > count)
715 				count = offs;
716 		}
717 	}
718 
719 	return count;
720 }
721 
get_instruction_info(const TilemDisasm * dasm,const byte * instr,int * length,int * argbase,const char ** pattern)722 static void get_instruction_info(const TilemDisasm* dasm,
723 				 const byte* instr,
724 				 int* length, int* argbase,
725 				 const char** pattern)
726 {
727 	const TilemDisasmSymbol* sym;
728 	const TilemDisasmInstruction* ii;
729 	dword mvalue;
730 	int i;
731 
732 	mvalue = 0;
733 	for (i = 0; i < 4; i++) {
734 		mvalue = (mvalue << 8) | instr[i];
735 		if ((sym = find_symbol(&dasm->macros, mvalue))) {
736 			*pattern = sym->name;
737 			*length = i + 1 + pattern_arg_size(sym->name);
738 			*argbase = i + 1;
739 			return;
740 		}
741 	}
742 
743 	if (instr[0] == 0xed) {
744 		ii = &insts_ed[instr[1]];
745 		*argbase = 2;
746 	}
747 	else if (instr[0] == 0xdd || instr[0] == 0xfd) {
748 		if (instr[1] == 0xcb) {
749 			ii = &insts_ddfdcb[instr[3]];
750 		}
751 		else {
752 			ii = &insts_ddfd[instr[1]];
753 		}
754 		*argbase = 2;
755 	}
756 	else if (instr[0] == 0xcb) {
757 		ii = &insts_cb[instr[1]];
758 		*argbase = 2;
759 	}
760 	else {
761 		ii = &insts_main[instr[0]];
762 		*argbase = 1;
763 	}
764 
765 	*length = ii->length;
766 
767 	if (ii->pattern) {
768 		*pattern = ii->pattern;
769 	}
770 	else {
771 		*argbase = 0;
772 		if (ii->length == 1)
773 			*pattern = "DB~%b";
774 		else
775 			*pattern = "DB~%b,%b";
776 	}
777 }
778 
779 static void TILEM_ATTR_PRINTF(3, 4)
printv(char ** buf,int * bsize,const char * fmt,...)780 printv(char** buf, int* bsize, const char* fmt, ...)
781 {
782 	va_list ap;
783 	int n;
784 
785 	if (*bsize == 0)
786 		return;
787 
788 	va_start(ap, fmt);
789 	n = vsnprintf(*buf, *bsize, fmt, ap);
790 	va_end(ap);
791 
792 	if (n >= *bsize) {
793 		*buf += *bsize - 1;
794 		**buf = 0;
795 		*bsize = 0;
796 	}
797 	else {
798 		*buf += n;
799 		**buf = 0;
800 		*bsize -= n;
801 	}
802 }
803 
print_byte(char ** buf,int * bsize,unsigned int b)804 static void print_byte(char** buf, int* bsize, unsigned int b)
805 {
806 	printv(buf, bsize, "$%02X", b);
807 }
808 
print_word(const TilemDisasm * dasm,char ** buf,int * bsize,dword w,int autonum,int autodiff)809 static void print_word(const TilemDisasm* dasm, char** buf, int* bsize,
810 		       dword w, int autonum, int autodiff)
811 {
812 	TilemDisasmSymbol* sym;
813 
814 	if (autonum && w < 0x100) {
815 		printv(buf, bsize, "$%04X", w);
816 		return;
817 	}
818 
819 	sym = find_prev_symbol(&dasm->labels, w);
820 
821 	if (sym && !strcmp(sym->name, "flags")) {
822 		w -= sym->value;
823 		sym = find_symbol(&dasm->flags, w);
824 		if (sym) {
825 			printv(buf, bsize, "flags + %s", sym->name);
826 		}
827 		else {
828 			printv(buf, bsize, "flags + $%02X", w);
829 		}
830 	}
831 	else if (sym && w == sym->value) {
832 		printv(buf, bsize, "%s", sym->name);
833 	}
834 	else if (sym && autodiff && w > 0x8000 && w - sym->value < 64) {
835 		printv(buf, bsize, "%s + %d", sym->name, w - sym->value);
836 	}
837 	else {
838 		printv(buf, bsize, "$%04X", w);
839 	}
840 }
841 
print_romcall(const TilemDisasm * dasm,char ** buf,int * bsize,dword w)842 static void print_romcall(const TilemDisasm* dasm, char** buf, int* bsize,
843 			  dword w)
844 {
845 	TilemDisasmSymbol* sym;
846 
847 	sym = find_symbol(&dasm->romcalls, w);
848 	if (sym) {
849 		printv(buf, bsize, "%s", sym->name);
850 	}
851 	else {
852 		printv(buf, bsize, "$%04X", w);
853 	}
854 }
855 
print_flag(const TilemDisasm * dasm,char ** buf,int * bsize,unsigned int bit,unsigned int offset,unsigned int prefix)856 static void print_flag(const TilemDisasm* dasm, char** buf, int* bsize,
857 		       unsigned int bit, unsigned int offset,
858 		       unsigned int prefix)
859 {
860 	TilemDisasmSymbol* sym;
861 	int i;
862 
863 	if (prefix == 0xfd) {
864 		sym = find_symbol(&dasm->flags, 0x1000 + (offset << 4) + bit);
865 		if (sym) {
866 			for (i = 0; sym->name[i]; i++) {
867 				printv(buf, bsize, "%c", sym->name[i]);
868 				if (sym->name[i] == ',')
869 					printv(buf, bsize, " (IY + ");
870 			}
871 			printv(buf, bsize, ")");
872 			return;
873 		}
874 
875 		sym = find_symbol(&dasm->flags, offset);
876 		if (sym) {
877 			printv(buf, bsize, "%d, (IY + %s)", bit, sym->name);
878 			return;
879 		}
880 	}
881 
882 	printv(buf, bsize, "%d, (%s", bit, (prefix == 0xfd ? "IY" : "IX"));
883 
884 	if (offset & 0x80) {
885 		printv(buf, bsize, " - $%02X", 0x100 - offset);
886 	}
887 	else if (offset) {
888 		printv(buf, bsize, " + $%02X", offset);
889 	}
890 
891 	printv(buf, bsize, ")");
892 }
893 
disassemble_pattern(const TilemDisasm * dasm,const char * pattern,const byte * ibuf,dword pc,int argbase,char ** buf,int * bsize)894 static void disassemble_pattern(const TilemDisasm* dasm, const char* pattern,
895 				const byte* ibuf, dword pc, int argbase,
896 				char** buf, int* bsize)
897 {
898 	int argidx, offs;
899 	char* p;
900 	dword w;
901 	TilemDisasmSymbol* sym;
902 
903 	argidx = argbase;
904 
905 	while (*bsize && *pattern) {
906 		if (*pattern == '~')
907 			printv(buf, bsize, "\t");
908 		else if (*pattern == ',') {
909 			printv(buf, bsize, ", ");
910 		}
911 		else if (*pattern != '%') {
912 			printv(buf, bsize, "%c", *pattern);
913 		}
914 		else {
915 			pattern++;
916 			if (*pattern >= '0' && *pattern <= '9') {
917 				offs = argbase + strtol(pattern, &p, 10);
918 				pattern = p;
919 			}
920 			else {
921 				offs = argidx;
922 			}
923 
924 			switch (*pattern) {
925 			case 0:
926 				pattern--;
927 				break;
928 
929 			case '%':
930 				printv(buf, bsize, "%%");
931 				break;
932 
933 			case 'a':
934 				/* %a: word value, always an address */
935 				w = ibuf[offs] | (ibuf[offs + 1] << 8);
936 				print_word(dasm, buf, bsize, w, 0, 1);
937 				offs += 2;
938 				break;
939 
940 			case 'b':
941 				/* %b: byte value */
942 				print_byte(buf, bsize, ibuf[offs]);
943 				offs++;
944 				break;
945 
946 			case 'c':
947 				/* %c: word value, always a ROM call number */
948 				w = ibuf[offs] | (ibuf[offs + 1] << 8);
949 				print_romcall(dasm, buf, bsize, w);
950 				offs += 2;
951 				break;
952 
953 			case 'C':
954 				/* %C: byte value, always a ROM call number */
955 				print_romcall(dasm, buf, bsize, ibuf[offs]);
956 				offs++;
957 				break;
958 
959 			case 'f':
960 				/* %f: flag value */
961 				print_flag(dasm, buf, bsize,
962 					   (ibuf[offs + 1] >> 3) & 7,
963 					   ibuf[offs], ibuf[0]);
964 				offs += 2;
965 				break;
966 
967 			case 'i':
968 				/* %i: IX or IY by instruction prefix */
969 				if (ibuf[0] == 0xdd)
970 					printv(buf, bsize, "IX");
971 				else
972 					printv(buf, bsize, "IY");
973 				break;
974 
975 			case 'j':
976 				/* %j: word value, always a jump address */
977 				w = ibuf[offs] | (ibuf[offs + 1] << 8);
978 				print_word(dasm, buf, bsize, w, 0, 0);
979 				offs += 2;
980 				break;
981 
982 			case 'r':
983 				/* %r: one-byte PC-relative value */
984 				if (ibuf[offs] & 0x80)
985 					w = pc + offs - 0xff + ibuf[offs];
986 				else
987 					w = pc + offs + 1 + ibuf[offs];
988 				print_word(dasm, buf, bsize, w, 0, 0);
989 				offs++;
990 				break;
991 
992 			case 's':
993 				/* %s: one-byte signed displacement */
994 				if (ibuf[0] == 0xfd
995 				    && (sym = find_symbol(&dasm->flags,
996 							  ibuf[offs]))) {
997 					printv(buf, bsize, " + %s", sym->name);
998 				}
999 				else if (ibuf[offs] & 0x80) {
1000 					printv(buf, bsize, " - ");
1001 					print_byte(buf, bsize,
1002 						   0x100 - ibuf[offs]);
1003 				}
1004 				else if (ibuf[offs]) {
1005 					printv(buf, bsize, " + ");
1006 					print_byte(buf, bsize, ibuf[offs]);
1007 				}
1008 				offs++;
1009 				break;
1010 
1011 			case 'w':
1012 				/* %w: word value */
1013 				w = ibuf[offs] | (ibuf[offs + 1] << 8);
1014 				print_word(dasm, buf, bsize, w, 1, 1);
1015 				offs += 2;
1016 				break;
1017 
1018 			case 'z':
1019 				/* %z: RST target address */
1020 				print_word(dasm, buf, bsize, ibuf[0] & 0x38,
1021 					   0, 0);
1022 				break;
1023 			}
1024 			if (offs > argidx)
1025 				argidx = offs;
1026 		}
1027 
1028 		pattern++;
1029 	}
1030 }
1031 
tilem_disasm_disassemble(const TilemDisasm * dasm,TilemCalc * calc,int phys,dword addr,dword * nextaddr,char * buffer,int bufsize)1032 void tilem_disasm_disassemble(const TilemDisasm* dasm, TilemCalc* calc,
1033 			      int phys, dword addr, dword* nextaddr,
1034 			      char* buffer, int bufsize)
1035 {
1036 	byte ibuf[64];
1037 	dword a, addr_l, max;
1038 	int length, argbase, i;
1039 	const char* pattern;
1040 
1041 	if (phys) {
1042 		max = calc->hw.romsize + calc->hw.ramsize;
1043 		for (i = 0; i < 4; i++) {
1044 			a = (addr + i) % max;
1045 			ibuf[i] = calc->mem[a];
1046 		}
1047 
1048 		addr_l = (*calc->hw.mem_ptol)(calc, addr);
1049 		if (addr_l == 0xffffffff)
1050 			addr_l = (addr & 0x3fff) | 0x4000;
1051 	}
1052 	else {
1053 		max = 0x10000;
1054 		for (i = 0; i < 4; i++) {
1055 			a = (addr + i) & 0xffff;
1056 			ibuf[i] = calc->mem[(*calc->hw.mem_ltop)(calc, a)];
1057 		}
1058 
1059 		addr_l = addr;
1060 	}
1061 
1062 	get_instruction_info(dasm, ibuf, &length, &argbase, &pattern);
1063 
1064 	if (phys) {
1065 		for (i = 0; i < length; i++) {
1066 			ibuf[i] = calc->mem[addr];
1067 			addr = (addr + 1) % max;
1068 		}
1069 	}
1070 	else {
1071 		for (i = 0; i < length; i++) {
1072 			ibuf[i] = calc->mem[(*calc->hw.mem_ltop)(calc, addr)];
1073 			addr = (addr + 1) & 0xffff;
1074 		}
1075 	}
1076 
1077 	if (nextaddr)
1078 		*nextaddr = addr;
1079 
1080 	if (buffer) {
1081 		disassemble_pattern(dasm, pattern, ibuf, addr_l, argbase,
1082 				    &buffer, &bufsize);
1083 	}
1084 }
1085