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