dbd2b5f066aa1a1bbbeb701ec4247b5ffd0ecac8
[b43-tools.git] / disassembler / main.c
1 /*
2  *   Copyright (C) 2006  Michael Buesch <mb@bu3sch.de>
3  *
4  *   This program is free software; you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License version 2
6  *   as published by the Free Software Foundation.
7  *
8  *   This program is distributed in the hope that it will be useful,
9  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *   GNU General Public License for more details.
12  */
13
14 #include "main.h"
15 #include "list.h"
16 #include "util.h"
17 #include "args.h"
18
19 #include <stdio.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24
25 struct bin_instruction {
26         unsigned int opcode;
27         unsigned int operands[3];
28 };
29
30 struct statement {
31         enum {
32                 STMT_INSN,
33                 STMT_LABEL,
34         } type;
35
36         union {
37                 struct {
38                         struct bin_instruction *bin;
39                         const char *name;
40                         const char *operands[5];
41
42                         int is_labelref;
43                         unsigned int labeladdr;
44                         struct statement *labelref;
45                 } insn;
46                 struct {
47                         char *name;
48                 } label;
49         } u;
50
51         struct list_head list;
52 };
53
54 struct disassembler_context {
55         /* The architecture of the input file. */
56         unsigned int arch;
57
58         struct bin_instruction *code;
59         size_t nr_insns;
60
61         struct list_head stmt_list;
62 };
63
64
65 FILE *infile;
66 FILE *outfile;
67 const char *infile_name;
68 const char *outfile_name;
69
70
71 static const char * gen_raw_code(unsigned int operand)
72 {
73         char *ret;
74
75         ret = xmalloc(5);
76         snprintf(ret, 5, "@%03X", operand);
77
78         return ret;
79 }
80
81 static const char * disasm_mem_operand(unsigned int operand)
82 {
83         char *ret;
84
85         ret = xmalloc(8);
86         snprintf(ret, 8, "[0x%03X]", operand);
87
88         return ret;
89 }
90
91 static const char * disasm_indirect_mem_operand(unsigned int operand)
92 {
93         char *ret;
94
95         ret = xmalloc(12);
96         snprintf(ret, 12, "[0x%02X,off%u]",
97                  (operand & 0x3F), ((operand >> 6) & 0x7));
98
99         return ret;
100 }
101
102 static const char * disasm_imm_operand(unsigned int operand)
103 {
104         char *ret;
105
106         operand &= ~0xC00;
107
108         ret = xmalloc(7);
109         if (operand & (1 << 9))
110                 snprintf(ret, 7, "0x%04X", (operand | 0xFC00));
111         else
112                 snprintf(ret, 7, "0x%03X", operand);
113
114         return ret;
115 }
116
117 static const char * disasm_spr_operand(unsigned int operand)
118 {
119         char *ret;
120
121         ret = xmalloc(7);
122         snprintf(ret, 7, "spr%03X", (operand & 0x1FF));
123
124         return ret;
125 }
126
127 static const char * disasm_gpr_operand(unsigned int operand)
128 {
129         char *ret;
130
131         ret = xmalloc(4);
132         snprintf(ret, 4, "r%u", (operand & 0x3F));
133
134         return ret;
135 }
136
137 static const char * disasm_offr_operand(unsigned int operand)
138 {
139         char *ret;
140
141         ret = xmalloc(5);
142         snprintf(ret, 5, "off%u", (operand & 0x7));
143
144         return ret;
145 }
146
147 static void disasm_std_operand(struct statement *stmt,
148                                int oper_idx,
149                                int out_idx,
150                                int forceraw)
151 {
152         unsigned int operand = stmt->u.insn.bin->operands[oper_idx];
153
154         if (forceraw)
155                 goto raw;
156
157         if (!(operand & 0x800)) {
158                 stmt->u.insn.operands[out_idx] = disasm_mem_operand(operand);
159                 return;
160         } else if ((operand & 0xC00) == 0xC00) { 
161                 stmt->u.insn.operands[out_idx] = disasm_imm_operand(operand);
162                 return;
163         } else if ((operand & 0xFC0) == 0xBC0) {
164                 stmt->u.insn.operands[out_idx] = disasm_gpr_operand(operand);
165                 return;
166         } else if ((operand & 0xE00) == 0x800) {
167                 stmt->u.insn.operands[out_idx] = disasm_spr_operand(operand);
168                 return;
169         } else if ((operand & 0xFF8) == 0x860) {
170                 stmt->u.insn.operands[out_idx] = disasm_offr_operand(operand);
171                 return;
172         } else if ((operand & 0xE00) == 0xA00) {
173                 stmt->u.insn.operands[out_idx] = disasm_indirect_mem_operand(operand);
174                 return;
175         }
176 raw:
177         stmt->u.insn.operands[out_idx] = gen_raw_code(operand);
178 }
179
180 static void disasm_constant_opcodes(struct disassembler_context *ctx,
181                                     struct statement *stmt)
182 {
183         struct bin_instruction *bin = stmt->u.insn.bin;
184
185         switch (bin->opcode) {
186         case 0x1C0:
187                 stmt->u.insn.name = "add";
188                 disasm_std_operand(stmt, 0, 0, 0);
189                 disasm_std_operand(stmt, 1, 1, 0);
190                 disasm_std_operand(stmt, 2, 2, 0);
191                 break;
192         case 0x1C2:
193                 stmt->u.insn.name = "add.";
194                 disasm_std_operand(stmt, 0, 0, 0);
195                 disasm_std_operand(stmt, 1, 1, 0);
196                 disasm_std_operand(stmt, 2, 2, 0);
197                 break;
198         case 0x1C1:
199                 stmt->u.insn.name = "addc";
200                 disasm_std_operand(stmt, 0, 0, 0);
201                 disasm_std_operand(stmt, 1, 1, 0);
202                 disasm_std_operand(stmt, 2, 2, 0);
203                 break;
204         case 0x1C3:
205                 stmt->u.insn.name = "addc.";
206                 disasm_std_operand(stmt, 0, 0, 0);
207                 disasm_std_operand(stmt, 1, 1, 0);
208                 disasm_std_operand(stmt, 2, 2, 0);
209                 break;
210         case 0x1D0:
211                 stmt->u.insn.name = "sub";
212                 disasm_std_operand(stmt, 0, 0, 0);
213                 disasm_std_operand(stmt, 1, 1, 0);
214                 disasm_std_operand(stmt, 2, 2, 0);
215                 break;
216         case 0x1D2:
217                 stmt->u.insn.name = "sub.";
218                 disasm_std_operand(stmt, 0, 0, 0);
219                 disasm_std_operand(stmt, 1, 1, 0);
220                 disasm_std_operand(stmt, 2, 2, 0);
221                 break;
222         case 0x1D1:
223                 stmt->u.insn.name = "subc";
224                 disasm_std_operand(stmt, 0, 0, 0);
225                 disasm_std_operand(stmt, 1, 1, 0);
226                 disasm_std_operand(stmt, 2, 2, 0);
227                 break;
228         case 0x1D3:
229                 stmt->u.insn.name = "subc.";
230                 disasm_std_operand(stmt, 0, 0, 0);
231                 disasm_std_operand(stmt, 1, 1, 0);
232                 disasm_std_operand(stmt, 2, 2, 0);
233                 break;
234         case 0x130:
235                 stmt->u.insn.name = "sra";
236                 disasm_std_operand(stmt, 0, 0, 0);
237                 disasm_std_operand(stmt, 1, 1, 0);
238                 disasm_std_operand(stmt, 2, 2, 0);
239                 break;
240         case 0x160:
241                 stmt->u.insn.name = "or";
242                 disasm_std_operand(stmt, 0, 0, 0);
243                 disasm_std_operand(stmt, 1, 1, 0);
244                 disasm_std_operand(stmt, 2, 2, 0);
245                 break;
246         case 0x140:
247                 stmt->u.insn.name = "and";
248                 disasm_std_operand(stmt, 0, 0, 0);
249                 disasm_std_operand(stmt, 1, 1, 0);
250                 disasm_std_operand(stmt, 2, 2, 0);
251                 break;
252         case 0x170:
253                 stmt->u.insn.name = "xor";
254                 disasm_std_operand(stmt, 0, 0, 0);
255                 disasm_std_operand(stmt, 1, 1, 0);
256                 disasm_std_operand(stmt, 2, 2, 0);
257                 break;
258         case 0x120:
259                 stmt->u.insn.name = "sr";
260                 disasm_std_operand(stmt, 0, 0, 0);
261                 disasm_std_operand(stmt, 1, 1, 0);
262                 disasm_std_operand(stmt, 2, 2, 0);
263                 break;
264         case 0x110:
265                 stmt->u.insn.name = "sl";
266                 disasm_std_operand(stmt, 0, 0, 0);
267                 disasm_std_operand(stmt, 1, 1, 0);
268                 disasm_std_operand(stmt, 2, 2, 0);
269                 break;
270         case 0x1A0:
271                 stmt->u.insn.name = "rl";
272                 disasm_std_operand(stmt, 0, 0, 0);
273                 disasm_std_operand(stmt, 1, 1, 0);
274                 disasm_std_operand(stmt, 2, 2, 0);
275                 break;
276         case 0x1B0:
277                 stmt->u.insn.name = "rr";
278                 disasm_std_operand(stmt, 0, 0, 0);
279                 disasm_std_operand(stmt, 1, 1, 0);
280                 disasm_std_operand(stmt, 2, 2, 0);
281                 break;
282         case 0x150:
283                 stmt->u.insn.name = "nand";
284                 disasm_std_operand(stmt, 0, 0, 0);
285                 disasm_std_operand(stmt, 1, 1, 0);
286                 disasm_std_operand(stmt, 2, 2, 0);
287                 break;
288         case 0x040:
289                 stmt->u.insn.name = "jand";
290                 stmt->u.insn.is_labelref = 2;
291                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
292                 disasm_std_operand(stmt, 0, 0, 0);
293                 disasm_std_operand(stmt, 1, 1, 0);
294                 break;
295         case (0x040 | 0x1):
296                 stmt->u.insn.name = "jnand";
297                 stmt->u.insn.is_labelref = 2;
298                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
299                 disasm_std_operand(stmt, 0, 0, 0);
300                 disasm_std_operand(stmt, 1, 1, 0);
301                 break;
302         case 0x050:
303                 stmt->u.insn.name = "js";
304                 stmt->u.insn.is_labelref = 2;
305                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
306                 disasm_std_operand(stmt, 0, 0, 0);
307                 disasm_std_operand(stmt, 1, 1, 0);
308                 break;
309         case (0x050 | 0x1):
310                 stmt->u.insn.name = "jns";
311                 stmt->u.insn.is_labelref = 2;
312                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
313                 disasm_std_operand(stmt, 0, 0, 0);
314                 disasm_std_operand(stmt, 1, 1, 0);
315                 break;
316         case 0x0D0:
317                 stmt->u.insn.name = "je";
318                 stmt->u.insn.is_labelref = 2;
319                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
320                 disasm_std_operand(stmt, 0, 0, 0);
321                 disasm_std_operand(stmt, 1, 1, 0);
322                 break;
323         case (0x0D0 | 0x1):
324                 stmt->u.insn.name = "jne";
325                 stmt->u.insn.is_labelref = 2;
326                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
327                 disasm_std_operand(stmt, 0, 0, 0);
328                 disasm_std_operand(stmt, 1, 1, 0);
329                 break;
330         case 0x0D2:
331                 stmt->u.insn.name = "jls";
332                 stmt->u.insn.is_labelref = 2;
333                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
334                 disasm_std_operand(stmt, 0, 0, 0);
335                 disasm_std_operand(stmt, 1, 1, 0);
336                 break;
337         case (0x0D2 | 0x1):
338                 stmt->u.insn.name = "jges";
339                 stmt->u.insn.is_labelref = 2;
340                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
341                 disasm_std_operand(stmt, 0, 0, 0);
342                 disasm_std_operand(stmt, 1, 1, 0);
343                 break;
344         case 0x0D4:
345                 stmt->u.insn.name = "jgs";
346                 stmt->u.insn.is_labelref = 2;
347                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
348                 disasm_std_operand(stmt, 0, 0, 0);
349                 disasm_std_operand(stmt, 1, 1, 0);
350                 break;
351         case (0x0D4 | 0x1):
352                 stmt->u.insn.name = "jles";
353                 stmt->u.insn.is_labelref = 2;
354                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
355                 disasm_std_operand(stmt, 0, 0, 0);
356                 disasm_std_operand(stmt, 1, 1, 0);
357                 break;
358         case 0x0DA:
359                 stmt->u.insn.name = "jl";
360                 stmt->u.insn.is_labelref = 2;
361                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
362                 disasm_std_operand(stmt, 0, 0, 0);
363                 disasm_std_operand(stmt, 1, 1, 0);
364                 break;
365         case (0x0DA | 0x1):
366                 stmt->u.insn.name = "jge";
367                 stmt->u.insn.is_labelref = 2;
368                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
369                 disasm_std_operand(stmt, 0, 0, 0);
370                 disasm_std_operand(stmt, 1, 1, 0);
371                 break;
372         case 0x0DC:
373                 stmt->u.insn.name = "jg";
374                 stmt->u.insn.is_labelref = 2;
375                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
376                 disasm_std_operand(stmt, 0, 0, 0);
377                 disasm_std_operand(stmt, 1, 1, 0);
378                 break;
379         case (0x0DC | 0x1):
380                 stmt->u.insn.name = "jle";
381                 stmt->u.insn.is_labelref = 2;
382                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
383                 disasm_std_operand(stmt, 0, 0, 0);
384                 disasm_std_operand(stmt, 1, 1, 0);
385                 break;
386         case 0x002: {
387                 char *str;
388
389                 stmt->u.insn.name = "call";
390                 stmt->u.insn.is_labelref = 1;
391                 stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
392                 str = xmalloc(4);
393                 snprintf(str, 4, "lr%u", stmt->u.insn.bin->operands[0]);
394                 stmt->u.insn.operands[0] = str;
395                 break;
396         }
397         case 0x003: {
398                 char *str;
399
400                 stmt->u.insn.name = "ret";
401                 str = xmalloc(4);
402                 snprintf(str, 4, "lr%u", stmt->u.insn.bin->operands[0]);
403                 stmt->u.insn.operands[0] = str;
404                 str = xmalloc(4);
405                 snprintf(str, 4, "lr%u", stmt->u.insn.bin->operands[2]);
406                 stmt->u.insn.operands[2] = str;
407                 break;
408         }
409         case 0x1E0: {
410                 unsigned int flags;
411
412                 flags = stmt->u.insn.bin->operands[1];
413                 switch (flags & ~0xC00) {
414                 case 0x1:
415                         stmt->u.insn.name = "tkiph";
416                         break;
417                 case (0x1 | 0x2):
418                         stmt->u.insn.name = "tkiphs";
419                         break;
420                 case 0x0:
421                         stmt->u.insn.name = "tkipl";
422                         break;
423                 case (0x0 | 0x2):
424                         stmt->u.insn.name = "tkipls";
425                         break;
426                 default:
427                         fprintf(stderr, "Invalid TKIP flags %X\n",
428                                 flags);
429                         exit(1);
430                 }
431                 disasm_std_operand(stmt, 0, 0, 0);
432                 disasm_std_operand(stmt, 2, 2, 0);
433                 break;
434         }
435         case 0x001: {
436                 stmt->u.insn.name = "nap";
437                 if (stmt->u.insn.bin->operands[0] != 0xBC0) {
438                         fprintf(stderr, "NAP: invalid first argument 0x%03X\n",
439                                 stmt->u.insn.bin->operands[0]);
440                 }
441                 if (stmt->u.insn.bin->operands[1] != 0xBC0) {
442                         fprintf(stderr, "NAP: invalid second argument 0x%03X\n",
443                                 stmt->u.insn.bin->operands[1]);
444                 }
445                 if (stmt->u.insn.bin->operands[2] != 0x000) {
446                         fprintf(stderr, "NAP: invalid third argument 0x%03X\n",
447                                 stmt->u.insn.bin->operands[2]);
448                 }
449                 break;
450         }
451         default:
452                 stmt->u.insn.name = gen_raw_code(bin->opcode);
453                 disasm_std_operand(stmt, 0, 0, 1);
454                 disasm_std_operand(stmt, 1, 1, 1);
455                 disasm_std_operand(stmt, 2, 2, 1);
456                 break;
457         }
458 }
459
460 static void disasm_opcodes(struct disassembler_context *ctx)
461 {
462         struct bin_instruction *bin;
463         size_t i;
464         struct statement *stmt;
465         char *str;
466
467         for (i = 0; i < ctx->nr_insns; i++) {
468                 bin = &(ctx->code[i]);
469
470                 stmt = xmalloc(sizeof(struct statement));
471                 stmt->type = STMT_INSN;
472                 INIT_LIST_HEAD(&stmt->list);
473                 stmt->u.insn.bin = bin;
474                 stmt->u.insn.is_labelref = -1;
475
476                 switch (bin->opcode & 0xF00) {
477                 case 0x200:
478                         stmt->u.insn.name = "srx";
479
480                         str = xmalloc(3);
481                         snprintf(str, 3, "%d", (bin->opcode & 0x0F0) >> 4);
482                         stmt->u.insn.operands[0] = str;
483                         str = xmalloc(3);
484                         snprintf(str, 3, "%d", (bin->opcode & 0x00F));
485                         stmt->u.insn.operands[1] = str;
486
487                         disasm_std_operand(stmt, 0, 2, 0);
488                         disasm_std_operand(stmt, 1, 3, 0);
489                         disasm_std_operand(stmt, 2, 4, 0);
490                         break;
491                 case 0x300:
492                         stmt->u.insn.name = "orx";
493
494                         str = xmalloc(3);
495                         snprintf(str, 3, "%d", (bin->opcode & 0x0F0) >> 4);
496                         stmt->u.insn.operands[0] = str;
497                         str = xmalloc(3);
498                         snprintf(str, 3, "%d", (bin->opcode & 0x00F));
499                         stmt->u.insn.operands[1] = str;
500
501                         disasm_std_operand(stmt, 0, 2, 0);
502                         disasm_std_operand(stmt, 1, 3, 0);
503                         disasm_std_operand(stmt, 2, 4, 0);
504                         break;
505                 case 0x400:
506                         stmt->u.insn.name = "jzx";
507
508                         str = xmalloc(3);
509                         snprintf(str, 3, "%d", (bin->opcode & 0x0F0) >> 4);
510                         stmt->u.insn.operands[0] = str;
511                         str = xmalloc(3);
512                         snprintf(str, 3, "%d", (bin->opcode & 0x00F));
513                         stmt->u.insn.operands[1] = str;
514
515                         disasm_std_operand(stmt, 0, 2, 0);
516                         disasm_std_operand(stmt, 1, 3, 0);
517                         stmt->u.insn.is_labelref = 4;
518                         stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
519                         break;
520                 case 0x500:
521                         stmt->u.insn.name = "jnzx";
522
523                         str = xmalloc(3);
524                         snprintf(str, 3, "%d", (bin->opcode & 0x0F0) >> 4);
525                         stmt->u.insn.operands[0] = str;
526                         str = xmalloc(3);
527                         snprintf(str, 3, "%d", (bin->opcode & 0x00F));
528                         stmt->u.insn.operands[1] = str;
529
530                         disasm_std_operand(stmt, 0, 2, 0);
531                         disasm_std_operand(stmt, 1, 3, 0);
532                         stmt->u.insn.is_labelref = 4;
533                         stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
534                         break;
535                 case 0x600:
536                         stmt->u.insn.name = "jnext";
537
538                         str = xmalloc(5);
539                         snprintf(str, 5, "0x%02X", (bin->opcode & 0x0FF));
540                         stmt->u.insn.operands[0] = str;
541
542                         /* We don't disassemble the first and second operand, as
543                          * that always is a dummy r0 operand.
544                          * disasm_std_operand(stmt, 0, 1, 0);
545                          * disasm_std_operand(stmt, 1, 2, 0);
546                          * stmt->u.insn.is_labelref = 3;
547                          */
548                         stmt->u.insn.is_labelref = 1;
549                         stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
550                         break;
551                 case 0x700:
552                         stmt->u.insn.name = "jext";
553
554                         str = xmalloc(5);
555                         snprintf(str, 5, "0x%02X", (bin->opcode & 0x0FF));
556                         stmt->u.insn.operands[0] = str;
557
558                         /* We don't disassemble the first and second operand, as
559                          * that always is a dummy r0 operand.
560                          * disasm_std_operand(stmt, 0, 1, 0);
561                          * disasm_std_operand(stmt, 1, 2, 0);
562                          * stmt->u.insn.is_labelref = 3;
563                          */
564                         stmt->u.insn.is_labelref = 1;
565                         stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
566                         break;
567                 default:
568                         disasm_constant_opcodes(ctx, stmt);
569                         break;
570                 }
571
572                 list_add_tail(&stmt->list, &ctx->stmt_list);
573         }
574 }
575
576 static struct statement * get_label_at(struct disassembler_context *ctx,
577                                        unsigned int addr)
578 {
579         unsigned int addrcnt = 0;
580         struct statement *stmt, *ret, *prev;
581
582         list_for_each_entry(stmt, &ctx->stmt_list, list) {
583                 if (stmt->type != STMT_INSN)
584                         continue;
585                 if (addrcnt == addr) {
586                         prev = list_entry(stmt->list.prev, struct statement, list);
587                         if (prev->type == STMT_LABEL)
588                                 return prev;
589                         ret = xmalloc(sizeof(struct statement));
590                         INIT_LIST_HEAD(&ret->list);
591                         ret->type = STMT_LABEL;
592                         list_add(&ret->list, &prev->list);
593
594                         return ret;
595                 }
596                 addrcnt++;
597         }
598
599         return NULL;
600 }
601
602 static void resolve_labels(struct disassembler_context *ctx)
603 {
604         struct statement *stmt;
605         struct statement *label;
606         struct statement *n;
607         unsigned int labeladdr;
608         unsigned int namecnt = 0;
609
610         /* Resolve label references */
611         list_for_each_entry_safe(stmt, n, &ctx->stmt_list, list) {
612                 if (stmt->type != STMT_INSN)
613                         continue;
614                 if (stmt->u.insn.is_labelref == -1)
615                         continue;
616                 labeladdr = stmt->u.insn.labeladdr;
617                 label = get_label_at(ctx, labeladdr);
618                 if (!label) {
619                         fprintf(stderr, "Labeladdress %X out of bounds\n",
620                                 labeladdr);
621                         exit(1);
622                 }
623                 stmt->u.insn.labelref = label;
624         }
625
626         /* Name the labels */
627         list_for_each_entry(stmt, &ctx->stmt_list, list) {
628                 if (stmt->type != STMT_LABEL)
629                         continue;
630                 stmt->u.label.name = xmalloc(20);
631                 snprintf(stmt->u.label.name, 20, "L%u", namecnt);
632                 namecnt++;
633         }
634 }
635
636 static void emit_asm(struct disassembler_context *ctx)
637 {
638         struct statement *stmt;
639         int first, i;
640         int err;
641         unsigned int addr = 0;
642
643         err = open_output_file();
644         if (err)
645                 exit(1);
646
647         fprintf(outfile, "%%arch %u\n\n", ctx->arch);
648         list_for_each_entry(stmt, &ctx->stmt_list, list) {
649                 switch (stmt->type) {
650                 case STMT_INSN:
651                         if (cmdargs.print_addresses)
652                                 fprintf(outfile, "/* %03X */", addr);
653                         fprintf(outfile, "\t%s", stmt->u.insn.name);
654                         first = 1;
655                         for (i = 0; i < ARRAY_SIZE(stmt->u.insn.operands); i++) {
656                                 if (stmt->u.insn.is_labelref == i) {
657                                         fprintf(outfile, ", %s",
658                                                 stmt->u.insn.labelref->u.label.name);
659                                 }
660                                 if (!stmt->u.insn.operands[i])
661                                         continue;
662                                 if (first)
663                                         fprintf(outfile, "\t");
664                                 if (!first)
665                                         fprintf(outfile, ", ");
666                                 first = 0;
667                                 fprintf(outfile, "%s",
668                                         stmt->u.insn.operands[i]);
669                         }
670                         fprintf(outfile, "\n");
671                         addr++;
672                         break;
673                 case STMT_LABEL:
674                         fprintf(outfile, "%s:\n", stmt->u.label.name);
675                         break;
676                 }
677         }
678
679         close_output_file();
680 }
681
682 static int read_input(struct disassembler_context *ctx)
683 {
684         size_t size = 0, pos = 0;
685         size_t ret;
686         struct bin_instruction *code = NULL;
687         unsigned char tmp[sizeof(uint64_t)];
688         uint64_t codeword;
689         struct fw_header hdr;
690         int err;
691
692         err = open_input_file();
693         if (err)
694                 goto error;
695
696         if (!cmdargs.no_header) {
697                 ret = fread(&hdr, 1, sizeof(hdr), infile);
698                 if (ret != sizeof(hdr)) {
699                         fprintf(stderr, "Corrupt input file (not fwcutter output)\n");
700                         goto err_close;
701                 }
702                 if (hdr.type != FW_TYPE_UCODE) {
703                         fprintf(stderr, "Corrupt input file. Not a microcode image.\n");
704                         goto err_close;
705                 }
706                 if (hdr.ver != FW_HDR_VER) {
707                         fprintf(stderr, "Invalid input file header version.\n");
708                         goto err_close;
709                 }
710         }
711
712         while (1) {
713                 if (pos >= size) {
714                         size += 512;
715                         code = xrealloc(code, size * sizeof(struct bin_instruction));
716                 }
717                 ret = fread(tmp, 1, sizeof(uint64_t), infile);
718                 if (!ret)
719                         break;
720                 if (ret != sizeof(uint64_t)) {
721                         fprintf(stderr, "Corrupt input file (not 8 byte aligned)\n");
722                         goto err_free_code;
723                 }
724
725                 codeword = 0;
726                 codeword |= ((uint64_t)tmp[0]) << 56;
727                 codeword |= ((uint64_t)tmp[1]) << 48;
728                 codeword |= ((uint64_t)tmp[2]) << 40;
729                 codeword |= ((uint64_t)tmp[3]) << 32;
730                 codeword |= ((uint64_t)tmp[4]) << 24;
731                 codeword |= ((uint64_t)tmp[5]) << 16;
732                 codeword |= ((uint64_t)tmp[6]) << 8;
733                 codeword |= ((uint64_t)tmp[7]);
734
735                 code[pos].opcode = (codeword >> 4) & 0xFFF;
736                 code[pos].operands[0] = (codeword & 0xF) << 8;
737                 code[pos].operands[0] |= (codeword >> 56) & 0xFF;
738                 code[pos].operands[1] = (codeword >> 44) & 0xFFF;
739                 code[pos].operands[2] = (codeword >> 32) & 0xFFF;
740
741                 pos++;
742         }
743
744         ctx->code = code;
745         ctx->nr_insns = pos;
746
747         close_input_file();
748
749         return 0;
750
751 err_free_code:
752         free(code);
753 err_close:
754         close_input_file();
755 error:
756         return -1;
757 }
758
759 static void disassemble(void)
760 {
761         struct disassembler_context ctx;
762         int err;
763
764         memset(&ctx, 0, sizeof(ctx));
765         INIT_LIST_HEAD(&ctx.stmt_list);
766         ctx.arch = cmdargs.arch;
767
768         err = read_input(&ctx);
769         if (err)
770                 exit(1);
771         disasm_opcodes(&ctx);
772         resolve_labels(&ctx);
773         emit_asm(&ctx);
774 }
775
776 int main(int argc, char **argv)
777 {
778         int err, res = 1;
779
780         err = parse_args(argc, argv);
781         if (err < 0)
782                 goto out;
783         if (err > 0) {
784                 res = 0;
785                 goto out;
786         }
787         disassemble();
788         res = 0;
789 out:
790         /* Lazyman simply leaks all allocated memory. */
791         return res;
792 }