dasm: Move some definitions around.
[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                         disasm_std_operand(stmt, 0, 1, 0);
543                         disasm_std_operand(stmt, 1, 2, 0);
544                         stmt->u.insn.is_labelref = 3;
545                         stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
546                         break;
547                 case 0x700:
548                         stmt->u.insn.name = "jext";
549
550                         str = xmalloc(5);
551                         snprintf(str, 5, "0x%02X", (bin->opcode & 0x0FF));
552                         stmt->u.insn.operands[0] = str;
553
554                         disasm_std_operand(stmt, 0, 1, 0);
555                         disasm_std_operand(stmt, 1, 2, 0);
556                         stmt->u.insn.is_labelref = 3;
557                         stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
558                         break;
559                 default:
560                         disasm_constant_opcodes(ctx, stmt);
561                         break;
562                 }
563
564                 list_add_tail(&stmt->list, &ctx->stmt_list);
565         }
566 }
567
568 static struct statement * get_label_at(struct disassembler_context *ctx,
569                                        unsigned int addr)
570 {
571         unsigned int addrcnt = 0;
572         struct statement *stmt, *ret, *prev;
573
574         list_for_each_entry(stmt, &ctx->stmt_list, list) {
575                 if (stmt->type != STMT_INSN)
576                         continue;
577                 if (addrcnt == addr) {
578                         prev = list_entry(stmt->list.prev, struct statement, list);
579                         if (prev->type == STMT_LABEL)
580                                 return prev;
581                         ret = xmalloc(sizeof(struct statement));
582                         INIT_LIST_HEAD(&ret->list);
583                         ret->type = STMT_LABEL;
584                         list_add(&ret->list, &prev->list);
585
586                         return ret;
587                 }
588                 addrcnt++;
589         }
590
591         return NULL;
592 }
593
594 static void resolve_labels(struct disassembler_context *ctx)
595 {
596         struct statement *stmt;
597         struct statement *label;
598         struct statement *n;
599         unsigned int labeladdr;
600         unsigned int namecnt = 0;
601
602         /* Resolve label references */
603         list_for_each_entry_safe(stmt, n, &ctx->stmt_list, list) {
604                 if (stmt->type != STMT_INSN)
605                         continue;
606                 if (stmt->u.insn.is_labelref == -1)
607                         continue;
608                 labeladdr = stmt->u.insn.labeladdr;
609                 label = get_label_at(ctx, labeladdr);
610                 if (!label) {
611                         fprintf(stderr, "Labeladdress %X out of bounds\n",
612                                 labeladdr);
613                         exit(1);
614                 }
615                 stmt->u.insn.labelref = label;
616         }
617
618         /* Name the labels */
619         list_for_each_entry(stmt, &ctx->stmt_list, list) {
620                 if (stmt->type != STMT_LABEL)
621                         continue;
622                 stmt->u.label.name = xmalloc(20);
623                 snprintf(stmt->u.label.name, 20, "L%u", namecnt);
624                 namecnt++;
625         }
626 }
627
628 static void emit_asm(struct disassembler_context *ctx)
629 {
630         struct statement *stmt;
631         int first, i;
632         int err;
633
634         err = open_output_file();
635         if (err)
636                 exit(1);
637
638         fprintf(outfile, "%%arch %u\n\n", ctx->arch);
639         list_for_each_entry(stmt, &ctx->stmt_list, list) {
640                 switch (stmt->type) {
641                 case STMT_INSN:
642                         fprintf(outfile, "\t%s", stmt->u.insn.name);
643                         first = 1;
644                         for (i = 0; i < ARRAY_SIZE(stmt->u.insn.operands); i++) {
645                                 if (stmt->u.insn.is_labelref == i) {
646                                         fprintf(outfile, ",%s",
647                                                 stmt->u.insn.labelref->u.label.name);
648                                 }
649                                 if (!stmt->u.insn.operands[i])
650                                         continue;
651                                 if (first)
652                                         fprintf(outfile, "\t");
653                                 if (!first)
654                                         fprintf(outfile, ",");
655                                 first = 0;
656                                 fprintf(outfile, "%s",
657                                         stmt->u.insn.operands[i]);
658                         }
659                         fprintf(outfile, "\n");
660                         break;
661                 case STMT_LABEL:
662                         fprintf(outfile, "%s:\n", stmt->u.label.name);
663                         break;
664                 }
665         }
666
667         close_output_file();
668 }
669
670 static int read_input(struct disassembler_context *ctx)
671 {
672         size_t size = 0, pos = 0;
673         size_t ret;
674         struct bin_instruction *code = NULL;
675         unsigned char tmp[sizeof(uint64_t)];
676         uint64_t codeword;
677         struct fw_header hdr;
678         int err;
679
680         err = open_input_file();
681         if (err)
682                 goto error;
683
684         ret = fread(&hdr, 1, sizeof(hdr), infile);
685         if (ret != sizeof(hdr)) {
686                 fprintf(stderr, "Corrupt input file (not fwcutter output)\n");
687                 goto err_close;
688         }
689         if (hdr.type != FW_TYPE_UCODE) {
690                 fprintf(stderr, "Corrupt input file. Not a microcode image.\n");
691                 goto err_close;
692         }
693         if (hdr.ver != FW_HDR_VER) {
694                 fprintf(stderr, "Invalid input file header version.\n");
695                 goto err_close;
696         }
697
698         while (1) {
699                 if (pos >= size) {
700                         size += 512;
701                         code = xrealloc(code, size * sizeof(struct bin_instruction));
702                 }
703                 ret = fread(tmp, 1, sizeof(uint64_t), infile);
704                 if (!ret)
705                         break;
706                 if (ret != sizeof(uint64_t)) {
707                         fprintf(stderr, "Corrupt input file (not 8 byte aligned)\n");
708                         goto err_free_code;
709                 }
710
711                 codeword = 0;
712                 codeword |= ((uint64_t)tmp[0]) << 56;
713                 codeword |= ((uint64_t)tmp[1]) << 48;
714                 codeword |= ((uint64_t)tmp[2]) << 40;
715                 codeword |= ((uint64_t)tmp[3]) << 32;
716                 codeword |= ((uint64_t)tmp[4]) << 24;
717                 codeword |= ((uint64_t)tmp[5]) << 16;
718                 codeword |= ((uint64_t)tmp[6]) << 8;
719                 codeword |= ((uint64_t)tmp[7]);
720
721                 code[pos].opcode = (codeword >> 4) & 0xFFF;
722                 code[pos].operands[0] = (codeword & 0xF) << 8;
723                 code[pos].operands[0] |= (codeword >> 56) & 0xFF;
724                 code[pos].operands[1] = (codeword >> 44) & 0xFFF;
725                 code[pos].operands[2] = (codeword >> 32) & 0xFFF;
726
727                 pos++;
728         }
729
730         ctx->code = code;
731         ctx->nr_insns = pos;
732
733         close_input_file();
734
735         return 0;
736
737 err_free_code:
738         free(code);
739 err_close:
740         close_input_file();
741 error:
742         return -1;
743 }
744
745 static void disassemble(void)
746 {
747         struct disassembler_context ctx;
748         int err;
749
750         memset(&ctx, 0, sizeof(ctx));
751         INIT_LIST_HEAD(&ctx.stmt_list);
752         ctx.arch = cmdargs.arch;
753
754         err = read_input(&ctx);
755         if (err)
756                 exit(1);
757         disasm_opcodes(&ctx);
758         resolve_labels(&ctx);
759         emit_asm(&ctx);
760 }
761
762 int main(int argc, char **argv)
763 {
764         int err, res = 1;
765
766         err = parse_args(argc, argv);
767         if (err < 0)
768                 goto out;
769         if (err > 0) {
770                 res = 0;
771                 goto out;
772         }
773         disassemble();
774         res = 0;
775 out:
776         /* Lazyman simply leaks all allocated memory. */
777         return res;
778 }