Mercurial > repos > blastem
comparison jagdis.c @ 1093:4987fddd42a0
Initial stab at jaguar disassemler
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 30 Oct 2016 19:42:48 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
1092:f338c950fcef | 1093:4987fddd42a0 |
---|---|
1 /* | |
2 Copyright 2013 Michael Pavone | |
3 This file is part of BlastEm. | |
4 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. | |
5 */ | |
6 #include <stdio.h> | |
7 #include <stdlib.h> | |
8 #include <string.h> | |
9 #include <stdarg.h> | |
10 #include <ctype.h> | |
11 #include "tern.h" | |
12 #include "util.h" | |
13 #include "jagcpu.h" | |
14 | |
15 uint8_t visited[(16*1024*1024)/16]; | |
16 uint16_t label[(16*1024*1024)/8]; | |
17 | |
18 void fatal_error(char *format, ...) | |
19 { | |
20 va_list args; | |
21 va_start(args, format); | |
22 vfprintf(stderr, format, args); | |
23 va_end(args); | |
24 exit(1); | |
25 } | |
26 | |
27 | |
28 void visit(uint32_t address) | |
29 { | |
30 address &= 0xFFFFFF; | |
31 visited[address/16] |= 1 << ((address / 2) % 8); | |
32 } | |
33 | |
34 void reference(uint32_t address) | |
35 { | |
36 address &= 0xFFFFFF; | |
37 //printf("referenced: %X\n", address); | |
38 label[address/16] |= 1 << (address % 16); | |
39 } | |
40 | |
41 uint8_t is_visited(uint32_t address) | |
42 { | |
43 address &= 0xFFFFFF; | |
44 return visited[address/16] & (1 << ((address / 2) % 8)); | |
45 } | |
46 | |
47 uint16_t is_label(uint32_t address) | |
48 { | |
49 address &= 0xFFFFFF; | |
50 return label[address/16] & (1 << (address % 16)); | |
51 } | |
52 | |
53 typedef struct { | |
54 uint32_t num_labels; | |
55 uint32_t storage; | |
56 char *labels[]; | |
57 } label_names; | |
58 | |
59 tern_node * add_label(tern_node * head, char * name, uint32_t address) | |
60 { | |
61 char key[MAX_INT_KEY_SIZE]; | |
62 address &= 0xFFFFFF; | |
63 reference(address); | |
64 tern_int_key(address, key); | |
65 label_names * names = tern_find_ptr(head, key); | |
66 if (names) | |
67 { | |
68 if (names->num_labels == names->storage) | |
69 { | |
70 names->storage = names->storage + (names->storage >> 1); | |
71 names = realloc(names, sizeof(label_names) + names->storage * sizeof(char *)); | |
72 } | |
73 } else { | |
74 names = malloc(sizeof(label_names) + 4 * sizeof(char *)); | |
75 names->num_labels = 0; | |
76 names->storage = 4; | |
77 head = tern_insert_ptr(head, key, names); | |
78 } | |
79 names->labels[names->num_labels++] = strdup(name); | |
80 return head; | |
81 } | |
82 | |
83 typedef struct deferred { | |
84 uint32_t address; | |
85 struct deferred *next; | |
86 } deferred; | |
87 | |
88 deferred * defer(uint32_t address, deferred * next) | |
89 { | |
90 if (is_visited(address) || address & 1) { | |
91 return next; | |
92 } | |
93 //printf("deferring %X\n", address); | |
94 deferred * d = malloc(sizeof(deferred)); | |
95 d->address = address; | |
96 d->next = next; | |
97 return d; | |
98 } | |
99 | |
100 void check_reference(uint16_t inst, uint32_t address, uint8_t is_gpu) | |
101 { | |
102 if (jag_opcode(inst, is_gpu) == JAG_JR) { | |
103 reference(jag_jr_dest(inst, address)); | |
104 } | |
105 } | |
106 | |
107 char * strip_ws(char * text) | |
108 { | |
109 while (*text && (!isprint(*text) || isblank(*text))) | |
110 { | |
111 text++; | |
112 } | |
113 char * ret = text; | |
114 text = ret + strlen(ret) - 1; | |
115 while (text > ret && (!isprint(*text) || isblank(*text))) | |
116 { | |
117 *text = 0; | |
118 text--; | |
119 } | |
120 return ret; | |
121 } | |
122 | |
123 int main(int argc, char ** argv) | |
124 { | |
125 long filesize; | |
126 unsigned short *filebuf; | |
127 char disbuf[1024]; | |
128 unsigned short * cur; | |
129 deferred *def = NULL, *tmpd; | |
130 uint32_t start = 0xFFFFFFFF; | |
131 | |
132 uint8_t labels = 0, addr = 0, only = 0, vos = 0, reset = 0; | |
133 tern_node * named_labels = NULL; | |
134 | |
135 uint32_t address_off = 0xFFFFFFFF, address_end; | |
136 uint8_t is_gpu = 1; | |
137 for(uint8_t opt = 2; opt < argc; ++opt) { | |
138 if (argv[opt][0] == '-') { | |
139 FILE * address_log; | |
140 switch (argv[opt][1]) | |
141 { | |
142 case 'l': | |
143 labels = 1; | |
144 break; | |
145 case 'a': | |
146 addr = 1; | |
147 break; | |
148 case 'o': | |
149 only = 1; | |
150 break; | |
151 case 'r': | |
152 reset = 1; | |
153 break; | |
154 case 's': | |
155 opt++; | |
156 if (opt >= argc) { | |
157 fputs("-s must be followed by an offset\n", stderr); | |
158 exit(1); | |
159 } | |
160 address_off = strtol(argv[opt], NULL, 0); | |
161 break; | |
162 case 'p': | |
163 opt++; | |
164 if (opt >= argc) { | |
165 fputs("-p must be followed by a starting PC value\n", stderr); | |
166 exit(1); | |
167 } | |
168 start = strtol(argv[opt], NULL, 0); | |
169 break; | |
170 case 'd': | |
171 is_gpu = 0; | |
172 break; | |
173 case 'f': | |
174 opt++; | |
175 if (opt >= argc) { | |
176 fputs("-f must be followed by a filename\n", stderr); | |
177 exit(1); | |
178 } | |
179 address_log = fopen(argv[opt], "r"); | |
180 if (!address_log) { | |
181 fprintf(stderr, "Failed to open %s for reading\n", argv[opt]); | |
182 exit(1); | |
183 } | |
184 while (fgets(disbuf, sizeof(disbuf), address_log)) { | |
185 if (disbuf[0]) { | |
186 char *end; | |
187 uint32_t address = strtol(disbuf, &end, 16); | |
188 if (address) { | |
189 def = defer(address, def); | |
190 reference(address); | |
191 if (*end == '=') { | |
192 named_labels = add_label(named_labels, strip_ws(end+1), address); | |
193 } | |
194 } | |
195 } | |
196 } | |
197 } | |
198 } else { | |
199 char *end; | |
200 uint32_t address = strtol(argv[opt], &end, 16); | |
201 def = defer(address, def); | |
202 reference(address); | |
203 if (*end == '=') { | |
204 named_labels = add_label(named_labels, end+1, address); | |
205 } | |
206 } | |
207 } | |
208 if (address_off == 0xFFFFFFFF) { | |
209 address_off = is_gpu ? 0xF03000 : 0xF1B000; | |
210 } | |
211 if (start == 0xFFFFFFFF) { | |
212 start = address_off; | |
213 } | |
214 FILE * f = fopen(argv[1], "rb"); | |
215 fseek(f, 0, SEEK_END); | |
216 filesize = ftell(f); | |
217 fseek(f, 0, SEEK_SET); | |
218 | |
219 char int_key[MAX_INT_KEY_SIZE]; | |
220 address_end = address_off + filesize; | |
221 filebuf = malloc(filesize); | |
222 if (fread(filebuf, 2, filesize/2, f) != filesize/2) | |
223 { | |
224 fprintf(stderr, "Failure while reading file %s\n", argv[1]); | |
225 } | |
226 fclose(f); | |
227 for(cur = filebuf; cur - filebuf < (filesize/2); ++cur) | |
228 { | |
229 *cur = (*cur >> 8) | (*cur << 8); | |
230 } | |
231 named_labels = add_label(named_labels, "start", start); | |
232 if (!def || !only) { | |
233 def = defer(start, def); | |
234 } | |
235 | |
236 uint16_t *encoded, *next; | |
237 uint32_t size, tmp_addr; | |
238 uint32_t address; | |
239 while(def) { | |
240 do { | |
241 encoded = NULL; | |
242 address = def->address; | |
243 if (!is_visited(address)) { | |
244 encoded = filebuf + (address - address_off)/2; | |
245 } | |
246 tmpd = def; | |
247 def = def->next; | |
248 free(tmpd); | |
249 } while(def && encoded == NULL); | |
250 if (!encoded) { | |
251 break; | |
252 } | |
253 for(;;) { | |
254 if (address > address_end || address < address_off) { | |
255 break; | |
256 } | |
257 visit(address); | |
258 uint16_t inst = *encoded; | |
259 uint32_t inst_address = address; | |
260 check_reference(inst, address, is_gpu); | |
261 uint16_t opcode = jag_opcode(inst, is_gpu); | |
262 if (opcode == JAG_MOVEI) { | |
263 address += 6; | |
264 encoded += 3; | |
265 } else { | |
266 address += 2; | |
267 encoded++; | |
268 } | |
269 | |
270 | |
271 if (opcode == JAG_JR || opcode == JAG_JUMP) { | |
272 if (!(jag_reg2(inst) & 0xF)) { | |
273 //unconditional jump | |
274 if (opcode == JAG_JR) { | |
275 address = jag_jr_dest(inst, inst_address); | |
276 reference(address); | |
277 if (is_visited(address)) { | |
278 break; | |
279 } | |
280 } else { | |
281 break; | |
282 } | |
283 } else if (opcode == JAG_JR) { | |
284 uint32_t dest = jag_jr_dest(inst, inst_address); | |
285 reference(dest); | |
286 def = defer(dest, def); | |
287 } | |
288 } | |
289 } | |
290 } | |
291 if (labels) { | |
292 for (address = 0; address < address_off; address++) { | |
293 if (is_label(address)) { | |
294 printf("ADR_%X equ $%X\n", address, address); | |
295 } | |
296 } | |
297 for (address = filesize; address < (16*1024*1024); address++) { | |
298 char key[MAX_INT_KEY_SIZE]; | |
299 tern_int_key(address, key); | |
300 label_names *names = tern_find_ptr(named_labels, key); | |
301 if (names) { | |
302 for (int i = 0; i < names->num_labels; i++) | |
303 { | |
304 printf("%s equ $%X\n", names->labels[i], address); | |
305 } | |
306 } else if (is_label(address)) { | |
307 printf("ADR_%X equ $%X\n", address, address); | |
308 } | |
309 } | |
310 puts(""); | |
311 } | |
312 for (address = address_off; address < address_end; address+=2) { | |
313 if (is_visited(address)) { | |
314 encoded = filebuf + (address-address_off)/2; | |
315 jag_cpu_disasm(&encoded, address, disbuf, is_gpu, labels); | |
316 if (labels) { | |
317 char keybuf[MAX_INT_KEY_SIZE]; | |
318 label_names * names = tern_find_ptr(named_labels, tern_int_key(address, keybuf)); | |
319 if (names) | |
320 { | |
321 for (int i = 0; i < names->num_labels; i++) | |
322 { | |
323 printf("%s:\n", names->labels[i]); | |
324 } | |
325 } else if (is_label(address)) { | |
326 printf("ADR_%X:\n", address); | |
327 } | |
328 if (addr) { | |
329 printf("\t%s\t;%X\n", disbuf, address); | |
330 } else { | |
331 printf("\t%s\n", disbuf); | |
332 } | |
333 } else { | |
334 printf("%X: %s\n", address, disbuf); | |
335 } | |
336 } | |
337 } | |
338 return 0; | |
339 } |