disassembler: Emit %start directive. This avoids a warning when re-assembling
[b43-tools.git] / disassembler / main.c
index e984480ebedce68de9ae477c2672021c65e59aa7..76295fd5eb2a836b91c1692827c7fb50405e8033 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *   Copyright (C) 2006  Michael Buesch <mb@bu3sch.de>
+ *   Copyright (C) 2006-2010  Michael Buesch <mb@bu3sch.de>
  *
  *   This program is free software; you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License version 2
  *   GNU General Public License for more details.
  */
 
+#include "main.h"
 #include "list.h"
 #include "util.h"
+#include "args.h"
 
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
-#define ARRAY_SIZE(x)  (sizeof(x)/sizeof(x[0]))
-
-/* The header that fwcutter puts in to every .fw file */
-struct fw_header {
-       /* Type of the firmware data */
-       uint8_t type;
-       /* Version number of the firmware data format */
-       uint8_t ver;
-       uint8_t __padding[2];
-       /* Size of the data. For ucode and PCM this is in bytes.
-        * For IV this is in number-of-ivs. */
-       uint32_t size;
-} __attribute__ ((__packed__));
-
 
 struct bin_instruction {
        unsigned int opcode;
@@ -64,6 +52,9 @@ struct statement {
 };
 
 struct disassembler_context {
+       /* The architecture of the input file. */
+       unsigned int arch;
+
        struct bin_instruction *code;
        size_t nr_insns;
 
@@ -71,7 +62,9 @@ struct disassembler_context {
 };
 
 
-static FILE *infile;
+FILE *infile;
+FILE *outfile;
+const char *infile_name;
 const char *outfile_name;
 
 
@@ -79,8 +72,8 @@ static const char * gen_raw_code(unsigned int operand)
 {
        char *ret;
 
-       ret = xmalloc(5);
-       snprintf(ret, 5, "@%03X", operand);
+       ret = xmalloc(6);
+       snprintf(ret, 6, "@%X", operand);
 
        return ret;
 }
@@ -89,8 +82,8 @@ static const char * disasm_mem_operand(unsigned int operand)
 {
        char *ret;
 
-       ret = xmalloc(8);
-       snprintf(ret, 8, "[0x%03X]", operand);
+       ret = xmalloc(9);
+       snprintf(ret, 9, "[0x%X]", operand);
 
        return ret;
 }
@@ -98,10 +91,23 @@ static const char * disasm_mem_operand(unsigned int operand)
 static const char * disasm_indirect_mem_operand(unsigned int operand)
 {
        char *ret;
+       unsigned int offset, reg;
 
+       switch (cmdargs.arch) {
+       case 5:
+               offset = (operand & 0x3F);
+               reg = ((operand >> 6) & 0x7);
+               break;
+       case 15:
+               offset = (operand & 0x7F);
+               reg = ((operand >> 7) & 0x7);
+               break;
+       default:
+               fprintf(stderr, "Internal error: disasm_indirect_mem_operand invalid arch\n");
+               exit(1);
+       }
        ret = xmalloc(12);
-       snprintf(ret, 12, "[0x%02X,off%u]",
-                (operand & 0x3F), ((operand >> 6) & 0x7));
+       snprintf(ret, 12, "[0x%02X,off%u]", offset, reg);
 
        return ret;
 }
@@ -109,14 +115,29 @@ static const char * disasm_indirect_mem_operand(unsigned int operand)
 static const char * disasm_imm_operand(unsigned int operand)
 {
        char *ret;
+       unsigned int signmask;
+       unsigned int mask;
 
-       operand &= ~0xC00;
+       switch (cmdargs.arch) {
+       case 5:
+               signmask = (1 << 9);
+               mask = 0x3FF;
+               break;
+       case 15:
+               signmask = (1 << 10);
+               mask = 0x7FF;
+               break;
+       default:
+               fprintf(stderr, "Internal error: disasm_imm_operand invalid arch\n");
+               exit(1);
+       }
+
+       operand &= mask;
 
        ret = xmalloc(7);
-       if (operand & (1 << 9))
-               snprintf(ret, 7, "0x%04X", (operand | 0xFC00));
-       else
-               snprintf(ret, 7, "0x%03X", operand);
+       if (operand & signmask)
+               operand = (operand | (~mask & 0xFFFF));
+       snprintf(ret, 7, "0x%X", operand);
 
        return ret;
 }
@@ -124,9 +145,22 @@ static const char * disasm_imm_operand(unsigned int operand)
 static const char * disasm_spr_operand(unsigned int operand)
 {
        char *ret;
+       unsigned int mask;
 
-       ret = xmalloc(7);
-       snprintf(ret, 7, "spr%03X", (operand & 0x1FF));
+       switch (cmdargs.arch) {
+       case 5:
+               mask = 0x1FF;
+               break;
+       case 15:
+               mask = 0x7FF;
+               break;
+       default:
+               fprintf(stderr, "Internal error: disasm_spr_operand invalid arch\n");
+               exit(1);
+       }
+
+       ret = xmalloc(8);
+       snprintf(ret, 8, "spr%X", (operand & mask));
 
        return ret;
 }
@@ -134,19 +168,22 @@ static const char * disasm_spr_operand(unsigned int operand)
 static const char * disasm_gpr_operand(unsigned int operand)
 {
        char *ret;
+       unsigned int mask;
 
-       ret = xmalloc(4);
-       snprintf(ret, 4, "r%u", (operand & 0x3F));
-
-       return ret;
-}
-
-static const char * disasm_offr_operand(unsigned int operand)
-{
-       char *ret;
+       switch (cmdargs.arch) {
+       case 5:
+               mask = 0x3F;
+               break;
+       case 15:
+               mask = 0x7F;
+               break;
+       default:
+               fprintf(stderr, "Internal error: disasm_gpr_operand invalid arch\n");
+               exit(1);
+       }
 
        ret = xmalloc(5);
-       snprintf(ret, 5, "off%u", (operand & 0x7));
+       snprintf(ret, 5, "r%u", (operand & mask));
 
        return ret;
 }
@@ -161,29 +198,60 @@ static void disasm_std_operand(struct statement *stmt,
        if (forceraw)
                goto raw;
 
-       if (!(operand & 0x800)) {
-               stmt->u.insn.operands[out_idx] = disasm_mem_operand(operand);
-               return;
-       } else if ((operand & 0xC00) == 0xC00) { 
-               stmt->u.insn.operands[out_idx] = disasm_imm_operand(operand);
-               return;
-       } else if ((operand & 0xFC0) == 0xBC0) {
-               stmt->u.insn.operands[out_idx] = disasm_gpr_operand(operand);
-               return;
-       } else if ((operand & 0xE00) == 0x800) {
-               stmt->u.insn.operands[out_idx] = disasm_spr_operand(operand);
-               return;
-       } else if ((operand & 0xFF8) == 0x860) {
-               stmt->u.insn.operands[out_idx] = disasm_offr_operand(operand);
-               return;
-       } else if ((operand & 0xE00) == 0xA00) {
-               stmt->u.insn.operands[out_idx] = disasm_indirect_mem_operand(operand);
-               return;
+       switch (cmdargs.arch) {
+       case 5:
+               if (!(operand & 0x800)) {
+                       stmt->u.insn.operands[out_idx] = disasm_mem_operand(operand);
+                       return;
+               } else if ((operand & 0xC00) == 0xC00) { 
+                       stmt->u.insn.operands[out_idx] = disasm_imm_operand(operand);
+                       return;
+               } else if ((operand & 0xFC0) == 0xBC0) {
+                       stmt->u.insn.operands[out_idx] = disasm_gpr_operand(operand);
+                       return;
+               } else if ((operand & 0xE00) == 0x800) {
+                       stmt->u.insn.operands[out_idx] = disasm_spr_operand(operand);
+                       return;
+               } else if ((operand & 0xE00) == 0xA00) {
+                       stmt->u.insn.operands[out_idx] = disasm_indirect_mem_operand(operand);
+                       return;
+               }
+               break;
+       case 15:
+               if (!(operand & 0x1000)) {
+                       stmt->u.insn.operands[out_idx] = disasm_mem_operand(operand);
+                       return;
+               } else if ((operand & 0x1800) == 0x1800) { 
+                       stmt->u.insn.operands[out_idx] = disasm_imm_operand(operand);
+                       return;
+               } else if ((operand & 0x1F80) == 0x1780) {
+                       stmt->u.insn.operands[out_idx] = disasm_gpr_operand(operand);
+                       return;
+               } else if ((operand & 0x1C00) == 0x1000) {
+                       stmt->u.insn.operands[out_idx] = disasm_spr_operand(operand);
+                       return;
+               } else if ((operand & 0x1C00) == 0x1400) {
+                       stmt->u.insn.operands[out_idx] = disasm_indirect_mem_operand(operand);
+                       return;
+               }
+               break;
+       default:
+               fprintf(stderr, "Internal error: disasm_std_operand invalid arch\n");
+               exit(1);
        }
 raw:
        stmt->u.insn.operands[out_idx] = gen_raw_code(operand);
 }
 
+static void disasm_opcode_raw(struct disassembler_context *ctx,
+                             struct statement *stmt)
+{
+       stmt->u.insn.name = gen_raw_code(stmt->u.insn.bin->opcode);
+       disasm_std_operand(stmt, 0, 0, 1);
+       disasm_std_operand(stmt, 1, 1, 1);
+       disasm_std_operand(stmt, 2, 2, 1);
+}
+
 static void disasm_constant_opcodes(struct disassembler_context *ctx,
                                    struct statement *stmt)
 {
@@ -393,12 +461,20 @@ static void disasm_constant_opcodes(struct disassembler_context *ctx,
        case 0x002: {
                char *str;
 
-               stmt->u.insn.name = "call";
-               stmt->u.insn.is_labelref = 1;
-               stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
-               str = xmalloc(4);
-               snprintf(str, 4, "lr%u", stmt->u.insn.bin->operands[0]);
-               stmt->u.insn.operands[0] = str;
+               switch (cmdargs.arch) {
+               case 5:
+                       stmt->u.insn.name = "call";
+                       stmt->u.insn.is_labelref = 1;
+                       stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
+                       str = xmalloc(4);
+                       snprintf(str, 4, "lr%u", stmt->u.insn.bin->operands[0]);
+                       stmt->u.insn.operands[0] = str;
+                       break;
+               case 15:
+                       //FIXME: This opcode is different on r15. Decode raw for now.
+                       disasm_opcode_raw(ctx, stmt);
+                       break;
+               }
                break;
        }
        case 0x003: {
@@ -414,10 +490,22 @@ static void disasm_constant_opcodes(struct disassembler_context *ctx,
                break;
        }
        case 0x1E0: {
-               unsigned int flags;
+               unsigned int flags, mask;
+
+               switch (cmdargs.arch) {
+               case 5:
+                       mask = 0x3FF;
+                       break;
+               case 15:
+                       mask = 0x7FF;
+                       break;
+               default:
+                       fprintf(stderr, "Internal error: TKIP invalid arch\n");
+                       exit(1);
+               }
 
                flags = stmt->u.insn.bin->operands[1];
-               switch (flags & ~0xC00) {
+               switch (flags & mask) {
                case 0x1:
                        stmt->u.insn.name = "tkiph";
                        break;
@@ -440,26 +528,36 @@ static void disasm_constant_opcodes(struct disassembler_context *ctx,
                break;
        }
        case 0x001: {
+               unsigned int mask;
+
                stmt->u.insn.name = "nap";
-               if (stmt->u.insn.bin->operands[0] != 0xBC0) {
-                       fprintf(stderr, "NAP: invalid first argument 0x%03X\n",
+               switch (cmdargs.arch) {
+               case 5:
+                       mask = 0xBC0;
+                       break;
+               case 15:
+                       mask = 0x1780;
+                       break;
+               default:
+                       fprintf(stderr, "Internal error: NAP invalid arch\n");
+                       exit(1);
+               }
+               if (stmt->u.insn.bin->operands[0] != mask) {
+                       fprintf(stderr, "NAP: invalid first argument 0x%04X\n",
                                stmt->u.insn.bin->operands[0]);
                }
-               if (stmt->u.insn.bin->operands[1] != 0xBC0) {
-                       fprintf(stderr, "NAP: invalid second argument 0x%03X\n",
+               if (stmt->u.insn.bin->operands[1] != mask) {
+                       fprintf(stderr, "NAP: invalid second argument 0x%04X\n",
                                stmt->u.insn.bin->operands[1]);
                }
-               if (stmt->u.insn.bin->operands[2] != 0x000) {
-                       fprintf(stderr, "NAP: invalid third argument 0x%03X\n",
+               if (stmt->u.insn.bin->operands[2] != 0) {
+                       fprintf(stderr, "NAP: invalid third argument 0x%04X\n",
                                stmt->u.insn.bin->operands[2]);
                }
                break;
        }
        default:
-               stmt->u.insn.name = gen_raw_code(bin->opcode);
-               disasm_std_operand(stmt, 0, 0, 1);
-               disasm_std_operand(stmt, 1, 1, 1);
-               disasm_std_operand(stmt, 2, 2, 1);
+               disasm_opcode_raw(ctx, stmt);
                break;
        }
 }
@@ -546,9 +644,13 @@ static void disasm_opcodes(struct disassembler_context *ctx)
                        snprintf(str, 5, "0x%02X", (bin->opcode & 0x0FF));
                        stmt->u.insn.operands[0] = str;
 
-                       disasm_std_operand(stmt, 0, 1, 0);
-                       disasm_std_operand(stmt, 1, 2, 0);
-                       stmt->u.insn.is_labelref = 3;
+                       /* We don't disassemble the first and second operand, as
+                        * that always is a dummy r0 operand.
+                        * disasm_std_operand(stmt, 0, 1, 0);
+                        * disasm_std_operand(stmt, 1, 2, 0);
+                        * stmt->u.insn.is_labelref = 3;
+                        */
+                       stmt->u.insn.is_labelref = 1;
                        stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
                        break;
                case 0x700:
@@ -558,9 +660,13 @@ static void disasm_opcodes(struct disassembler_context *ctx)
                        snprintf(str, 5, "0x%02X", (bin->opcode & 0x0FF));
                        stmt->u.insn.operands[0] = str;
 
-                       disasm_std_operand(stmt, 0, 1, 0);
-                       disasm_std_operand(stmt, 1, 2, 0);
-                       stmt->u.insn.is_labelref = 3;
+                       /* We don't disassemble the first and second operand, as
+                        * that always is a dummy r0 operand.
+                        * disasm_std_operand(stmt, 0, 1, 0);
+                        * disasm_std_operand(stmt, 1, 2, 0);
+                        * stmt->u.insn.is_labelref = 3;
+                        */
+                       stmt->u.insn.is_labelref = 1;
                        stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2];
                        break;
                default:
@@ -635,48 +741,52 @@ static void resolve_labels(struct disassembler_context *ctx)
 static void emit_asm(struct disassembler_context *ctx)
 {
        struct statement *stmt;
-       FILE *fd;
        int first, i;
+       int err;
+       unsigned int addr = 0;
 
-       fd = fopen(outfile_name, "w+");
-       if (!fd) {
-               fprintf(stderr, "Could not open output file \"%s\"\n",
-                       outfile_name);
+       err = open_output_file();
+       if (err)
                exit(1);
-       }
-       /* FIXME: We currently only support v5 architecture. */
-       fprintf(fd, "%%arch 5\n\n");
+
+       fprintf(outfile, "%%arch %u\n", ctx->arch);
+       fprintf(outfile, "%%start entry\n\n");
+       fprintf(outfile, "entry:\n");
        list_for_each_entry(stmt, &ctx->stmt_list, list) {
                switch (stmt->type) {
                case STMT_INSN:
-                       fprintf(fd, "\t%s", stmt->u.insn.name);
+                       if (cmdargs.print_addresses)
+                               fprintf(outfile, "/* %03X */", addr);
+                       fprintf(outfile, "\t%s", stmt->u.insn.name);
                        first = 1;
                        for (i = 0; i < ARRAY_SIZE(stmt->u.insn.operands); i++) {
                                if (stmt->u.insn.is_labelref == i) {
-                                       fprintf(fd, ",%s",
+                                       fprintf(outfile, ", %s",
                                                stmt->u.insn.labelref->u.label.name);
                                }
                                if (!stmt->u.insn.operands[i])
                                        continue;
                                if (first)
-                                       fprintf(fd, "\t");
+                                       fprintf(outfile, "\t");
                                if (!first)
-                                       fprintf(fd, ",");
+                                       fprintf(outfile, ", ");
                                first = 0;
-                               fprintf(fd, "%s",
+                               fprintf(outfile, "%s",
                                        stmt->u.insn.operands[i]);
                        }
-                       fprintf(fd, "\n");
+                       fprintf(outfile, "\n");
+                       addr++;
                        break;
                case STMT_LABEL:
-                       fprintf(fd, "%s:\n", stmt->u.label.name);
+                       fprintf(outfile, "%s:\n", stmt->u.label.name);
                        break;
                }
        }
-       fclose(fd);
+
+       close_output_file();
 }
 
-static void read_input(struct disassembler_context *ctx)
+static int read_input(struct disassembler_context *ctx)
 {
        size_t size = 0, pos = 0;
        size_t ret;
@@ -684,21 +794,32 @@ static void read_input(struct disassembler_context *ctx)
        unsigned char tmp[sizeof(uint64_t)];
        uint64_t codeword;
        struct fw_header hdr;
-
-       ret = fread(&hdr, 1, sizeof(hdr), infile);
-       if (!ret || ret != sizeof(hdr)) {
-               fprintf(stderr, "Corrupt input file (not fwcutter output)\n");
-               exit(1);
-       }       
-
-       if (hdr.ver != 1) {
-               fprintf(stderr, "Invalid fwcutter header version\n");
-               exit(1);
-       }
-
-       if (hdr.type != 'u') {
-               fprintf(stderr, "Not a microcode image\n");
-               exit(1);
+       int err;
+
+       err = open_input_file();
+       if (err)
+               goto error;
+
+       switch (cmdargs.informat) {
+       case FMT_RAW_LE32:
+       case FMT_RAW_BE32:
+               /* Nothing */
+               break;
+       case FMT_B43:
+               ret = fread(&hdr, 1, sizeof(hdr), infile);
+               if (ret != sizeof(hdr)) {
+                       fprintf(stderr, "Corrupt input file (not fwcutter output)\n");
+                       goto err_close;
+               }
+               if (hdr.type != FW_TYPE_UCODE) {
+                       fprintf(stderr, "Corrupt input file. Not a microcode image.\n");
+                       goto err_close;
+               }
+               if (hdr.ver != FW_HDR_VER) {
+                       fprintf(stderr, "Invalid input file header version.\n");
+                       goto err_close;
+               }
+               break;
        }
 
        while (1) {
@@ -711,65 +832,115 @@ static void read_input(struct disassembler_context *ctx)
                        break;
                if (ret != sizeof(uint64_t)) {
                        fprintf(stderr, "Corrupt input file (not 8 byte aligned)\n");
-                       exit(1);
+                       goto err_free_code;
                }
 
-               codeword = 0;
-               codeword |= ((uint64_t)tmp[0]) << 56;
-               codeword |= ((uint64_t)tmp[1]) << 48;
-               codeword |= ((uint64_t)tmp[2]) << 40;
-               codeword |= ((uint64_t)tmp[3]) << 32;
-               codeword |= ((uint64_t)tmp[4]) << 24;
-               codeword |= ((uint64_t)tmp[5]) << 16;
-               codeword |= ((uint64_t)tmp[6]) << 8;
-               codeword |= ((uint64_t)tmp[7]);
-
-               code[pos].opcode = (codeword >> 4) & 0xFFF;
-               code[pos].operands[0] = (codeword & 0xF) << 8;
-               code[pos].operands[0] |= (codeword >> 56) & 0xFF;
-               code[pos].operands[1] = (codeword >> 44) & 0xFFF;
-               code[pos].operands[2] = (codeword >> 32) & 0xFFF;
+               switch (cmdargs.informat) {
+               case FMT_B43:
+               case FMT_RAW_BE32:
+                       codeword = 0;
+                       codeword |= ((uint64_t)tmp[0]) << 56;
+                       codeword |= ((uint64_t)tmp[1]) << 48;
+                       codeword |= ((uint64_t)tmp[2]) << 40;
+                       codeword |= ((uint64_t)tmp[3]) << 32;
+                       codeword |= ((uint64_t)tmp[4]) << 24;
+                       codeword |= ((uint64_t)tmp[5]) << 16;
+                       codeword |= ((uint64_t)tmp[6]) << 8;
+                       codeword |= ((uint64_t)tmp[7]);
+                       codeword = ((codeword & (uint64_t)0xFFFFFFFF00000000ULL) >> 32) |
+                                  ((codeword & (uint64_t)0x00000000FFFFFFFFULL) << 32);
+                       break;
+               case FMT_RAW_LE32:
+                       codeword = 0;
+                       codeword |= ((uint64_t)tmp[7]) << 56;
+                       codeword |= ((uint64_t)tmp[6]) << 48;
+                       codeword |= ((uint64_t)tmp[5]) << 40;
+                       codeword |= ((uint64_t)tmp[4]) << 32;
+                       codeword |= ((uint64_t)tmp[3]) << 24;
+                       codeword |= ((uint64_t)tmp[2]) << 16;
+                       codeword |= ((uint64_t)tmp[1]) << 8;
+                       codeword |= ((uint64_t)tmp[0]);
+                       break;
+               }
+
+               switch (cmdargs.arch) {
+               case 5:
+                       if (codeword >> 48) {
+                               fprintf(stderr, "Instruction format error at 0x%X (upper not clear). "
+                                       "Wrong input format or architecture?\n", (unsigned int)pos);
+                               goto err_free_code;
+                       }
+                       code[pos].opcode = (codeword >> 36) & 0xFFF;
+                       code[pos].operands[2] = codeword & 0xFFF;
+                       code[pos].operands[1] = (codeword >> 12) & 0xFFF;
+                       code[pos].operands[0] = (codeword >> 24) & 0xFFF;
+                       break;
+               case 15:
+                       if (codeword >> 51) {
+                               fprintf(stderr, "Instruction format error at 0x%X (upper not clear). "
+                                       "Wrong input format or architecture?\n", (unsigned int)pos);
+                               goto err_free_code;
+                       }
+                       code[pos].opcode = (codeword >> 39) & 0xFFF;
+                       code[pos].operands[2] = codeword & 0x1FFF;
+                       code[pos].operands[1] = (codeword >> 13) & 0x1FFF;
+                       code[pos].operands[0] = (codeword >> 26) & 0x1FFF;
+                       break;
+               default:
+                       fprintf(stderr, "Internal error: read_input unknown arch %u\n",
+                               cmdargs.arch);
+                       goto err_free_code;
+               }
 
                pos++;
        }
 
        ctx->code = code;
        ctx->nr_insns = pos;
+
+       close_input_file();
+
+       return 0;
+
+err_free_code:
+       free(code);
+err_close:
+       close_input_file();
+error:
+       return -1;
 }
 
 static void disassemble(void)
 {
        struct disassembler_context ctx;
+       int err;
 
        memset(&ctx, 0, sizeof(ctx));
        INIT_LIST_HEAD(&ctx.stmt_list);
+       ctx.arch = cmdargs.arch;
 
-       read_input(&ctx);
+       err = read_input(&ctx);
+       if (err)
+               exit(1);
        disasm_opcodes(&ctx);
        resolve_labels(&ctx);
        emit_asm(&ctx);
 }
 
-static void usage(int argc, char **argv)
-{
-       fprintf(stderr, "Usage: %s output_file\n", argv[0]);
-}
-
-static void parse_args(int argc, char **argv)
-{
-       if (argc != 2) {
-               usage(argc, argv);
-               exit(1);
-       }
-       outfile_name = argv[1];
-}
-
 int main(int argc, char **argv)
 {
-       infile = stdin;
-       parse_args(argc, argv);
+       int err, res = 1;
+
+       err = parse_args(argc, argv);
+       if (err < 0)
+               goto out;
+       if (err > 0) {
+               res = 0;
+               goto out;
+       }
        disassemble();
-
+       res = 0;
+out:
        /* Lazyman simply leaks all allocated memory. */
-       return 0;
+       return res;
 }