b43-asm: Add an option to print the code sizes after assembling.
[b43-tools.git] / assembler / main.c
index 2c4749344305b606daa16e5452ce7e3f8a8d173f..255cabc88c74b6966d0c0a4262e374d2048cdf14 100644 (file)
@@ -49,6 +49,11 @@ struct code_output {
                OUT_LABEL,
        } type;
 
+       /* Set to true, if this is a jump instruction.
+        * This is only used when assembling RET to check
+        * whether the previous instruction was a jump or not. */
+       bool is_jump_insn;
+
        unsigned int opcode;
        struct out_operand operands[3];
 
@@ -289,7 +294,7 @@ static unsigned int generate_reg_operand(struct assembler_context *ctx,
        case SPR:
                /* format: 0b100. .... .... */
                val |= 0x800;
-               if (ctx->arch == 15) //FIXME is this ok?
+               if (ctx->arch == 15)
                        val <<= 1;
                if (reg->nr & ~0x1FF)
                        asm_error(ctx, "SPR-nr too big");
@@ -298,7 +303,7 @@ static unsigned int generate_reg_operand(struct assembler_context *ctx,
        case OFFR:
                /* format: 0b1000 0110 0rrr */
                val |= 0x860;
-               if (ctx->arch == 15) //FIXME is this ok?
+               if (ctx->arch == 15)
                        val <<= 1;
                if (reg->nr & ~0x7)
                        asm_error(ctx, "OFFR-nr too big");
@@ -379,9 +384,9 @@ static void generate_operand(struct assembler_context *ctx,
        }
 }
 
-static void do_assemble_insn(struct assembler_context *ctx,
-                            struct instruction *insn,
-                            unsigned int opcode)
+static struct code_output * do_assemble_insn(struct assembler_context *ctx,
+                                            struct instruction *insn,
+                                            unsigned int opcode)
 {
        int i;
        struct operlist *ol;
@@ -432,6 +437,8 @@ static void do_assemble_insn(struct assembler_context *ctx,
                               "lowlevel do_assemble_insn");
 
        list_add_tail(&out->list, &ctx->output);
+
+       return out;
 }
 
 static unsigned int merge_ext_into_opcode(struct assembler_context *ctx,
@@ -463,19 +470,34 @@ static unsigned int merge_external_jmp_into_opcode(struct assembler_context *ctx
                                                   unsigned int opbase,
                                                   struct instruction *insn)
 {
+       struct operand *fake;
+       struct registr *fake_reg;
+       struct operand *target;
        struct operlist *ol;
        unsigned int cond;
        unsigned int opcode;
 
        ol = insn->operands;
        opcode = opbase;
-       cond = ol->oper[0]->u.raw;
+       cond = ol->oper[0]->u.imm->imm;
        if (cond & ~0xFF)
                asm_error(ctx, "External jump condition value too big (> 0xFF)");
        opcode |= cond;
-       ol->oper[0] = ol->oper[1];
-       ol->oper[1] = ol->oper[2];
-       ol->oper[2] = ol->oper[3];
+       target = ol->oper[1];
+       memset(ol->oper, 0, sizeof(ol->oper));
+
+       /* This instruction has two fake r0 operands
+        * at position 0 and 1. */
+       fake = xmalloc(sizeof(*fake));
+       fake_reg = xmalloc(sizeof(*fake_reg));
+       fake->type = OPER_REG;
+       fake->u.reg = fake_reg;
+       fake_reg->type = GPR;
+       fake_reg->nr = 0;
+
+       ol->oper[0] = fake;
+       ol->oper[1] = fake;
+       ol->oper[2] = target;
 
        return opcode;
 }
@@ -549,19 +571,20 @@ static void emulate_jmp_insn(struct assembler_context *ctx,
 {
        struct instruction em_insn;
        struct operlist em_ol;
-       struct operand em_op;
-       struct immediate em_imm;
-
-       /* This is a pseudo-OP. We emulate it by JE */
-
-       em_insn.op = OP_JE;
-       em_imm.imm = 1;
-       em_op.type = OPER_IMM;
-       em_op.u.imm = &em_imm;
-       em_ol.oper[0] = &em_op;
-       em_ol.oper[1] = &em_op;
-       em_ol.oper[2] = insn->operands->oper[0];
+       struct immediate em_condition;
+       struct operand em_cond_op;
+
+       /* This is a pseudo-OP. We emulate it with
+        * JEXT 0x7F, target */
+
+       em_insn.op = OP_JEXT;
+       em_condition.imm = 0x7F; /* Ext cond: Always true */
+       em_cond_op.type = OPER_IMM;
+       em_cond_op.u.imm = &em_condition;
+       em_ol.oper[0] = &em_cond_op;
+       em_ol.oper[1] = insn->operands->oper[0]; /* Target */
        em_insn.operands = &em_ol;
+
        assemble_instruction(ctx, &em_insn); /* recurse */
 }
 
@@ -569,6 +592,7 @@ static void emulate_jand_insn(struct assembler_context *ctx,
                              struct instruction *insn,
                              int inverted)
 {
+       struct code_output *out;
        struct instruction em_insn;
        struct operlist em_ol;
        struct operand em_op_shift;
@@ -638,14 +662,16 @@ static void emulate_jand_insn(struct assembler_context *ctx,
 
        /* Do a normal JAND/JNAND instruction */
        if (inverted)
-               do_assemble_insn(ctx, insn, 0x040 | 0x1);
+               out = do_assemble_insn(ctx, insn, 0x040 | 0x1);
        else
-               do_assemble_insn(ctx, insn, 0x040);
+               out = do_assemble_insn(ctx, insn, 0x040);
+       out->is_jump_insn = 1;
 }
 
 static void assemble_instruction(struct assembler_context *ctx,
                                 struct instruction *insn)
 {
+       struct code_output *out;
        unsigned int opcode;
 
        switch (insn->op) {
@@ -721,61 +747,88 @@ static void assemble_instruction(struct assembler_context *ctx,
                emulate_jand_insn(ctx, insn, 1);
                return;
        case OP_JS:
-               do_assemble_insn(ctx, insn, 0x050);
+               out = do_assemble_insn(ctx, insn, 0x050);
+               out->is_jump_insn = 1;
                break;
        case OP_JNS:
-               do_assemble_insn(ctx, insn, 0x050 | 0x1);
+               out = do_assemble_insn(ctx, insn, 0x050 | 0x1);
+               out->is_jump_insn = 1;
                break;
        case OP_JE:
-               do_assemble_insn(ctx, insn, 0x0D0);
+               out = do_assemble_insn(ctx, insn, 0x0D0);
+               out->is_jump_insn = 1;
                break;
        case OP_JNE:
-               do_assemble_insn(ctx, insn, 0x0D0 | 0x1);
+               out = do_assemble_insn(ctx, insn, 0x0D0 | 0x1);
+               out->is_jump_insn = 1;
                break;
        case OP_JLS:
-               do_assemble_insn(ctx, insn, 0x0D2);
+               out = do_assemble_insn(ctx, insn, 0x0D2);
+               out->is_jump_insn = 1;
                break;
        case OP_JGES:
-               do_assemble_insn(ctx, insn, 0x0D2 | 0x1);
+               out = do_assemble_insn(ctx, insn, 0x0D2 | 0x1);
+               out->is_jump_insn = 1;
                break;
        case OP_JGS:
-               do_assemble_insn(ctx, insn, 0x0D4);
+               out = do_assemble_insn(ctx, insn, 0x0D4);
+               out->is_jump_insn = 1;
                break;
        case OP_JLES:
-               do_assemble_insn(ctx, insn, 0x0D4 | 0x1);
+               out = do_assemble_insn(ctx, insn, 0x0D4 | 0x1);
+               out->is_jump_insn = 1;
                break;
        case OP_JL:
-               do_assemble_insn(ctx, insn, 0x0DA);
+               out = do_assemble_insn(ctx, insn, 0x0DA);
+               out->is_jump_insn = 1;
                break;
        case OP_JGE:
-               do_assemble_insn(ctx, insn, 0x0DA | 0x1);
+               out = do_assemble_insn(ctx, insn, 0x0DA | 0x1);
+               out->is_jump_insn = 1;
                break;
        case OP_JG:
-               do_assemble_insn(ctx, insn, 0x0DC);
+               out = do_assemble_insn(ctx, insn, 0x0DC);
                break;
        case OP_JLE:
-               do_assemble_insn(ctx, insn, 0x0DC | 0x1);
+               out = do_assemble_insn(ctx, insn, 0x0DC | 0x1);
+               out->is_jump_insn = 1;
                break;
        case OP_JZX:
                opcode = merge_ext_into_opcode(ctx, 0x400, insn);
-               do_assemble_insn(ctx, insn, opcode);
+               out = do_assemble_insn(ctx, insn, opcode);
+               out->is_jump_insn = 1;
                break;
        case OP_JNZX:
                opcode = merge_ext_into_opcode(ctx, 0x500, insn);
-               do_assemble_insn(ctx, insn, opcode);
+               out = do_assemble_insn(ctx, insn, opcode);
+               out->is_jump_insn = 1;
                break;
        case OP_JEXT:
                opcode = merge_external_jmp_into_opcode(ctx, 0x700, insn);
-               do_assemble_insn(ctx, insn, opcode);
+               out = do_assemble_insn(ctx, insn, opcode);
+               out->is_jump_insn = 1;
                break;
        case OP_JNEXT:
                opcode = merge_external_jmp_into_opcode(ctx, 0x600, insn);
-               do_assemble_insn(ctx, insn, opcode);
+               out = do_assemble_insn(ctx, insn, opcode);
+               out->is_jump_insn = 1;
                break;
        case OP_CALL:
                do_assemble_insn(ctx, insn, 0x002);
                break;
        case OP_RET:
+               /* Get the previous instruction and check whether it
+                * is a jump instruction. */
+               list_for_each_entry_reverse(out, &ctx->output, list) {
+                       /* Search the last insn. */
+                       if (out->type == OUT_INSN) {
+                               if (out->is_jump_insn) {
+                                       asm_error(ctx, "RET instruction directly after "
+                                                 "jump instruction. The hardware won't like this.");
+                               }
+                               break;
+                       }
+               }
                do_assemble_insn(ctx, insn, 0x003);
                break;
        case OP_TKIPH:
@@ -947,8 +1000,16 @@ recalculate_addresses:
                                if (addr < 0)
                                        goto does_not_exist;
                                c->operands[i].u.operand = addr;
-                               if (i != 2) /* Is not a jump target */
-                                       c->operands[i].u.operand |= 0xC00; /* Make it be an immediate */
+                               if (i != 2) {
+                                       /* Is not a jump target.
+                                        * Make it be an immediate */
+                                       if (ctx->arch == 5)
+                                               c->operands[i].u.operand |= 0xC00;
+                                       else if (ctx->arch == 15)
+                                               c->operands[i].u.operand |= 0xC00 << 1;
+                                       else
+                                               asm_error(ctx, "Internal error: label res imm");
+                               }
                        }
                        break;
                case OUT_LABEL:
@@ -1064,6 +1125,12 @@ static void emit_code(struct assembler_context *ctx)
                        break;
                }
        }
+
+       if (arg_print_sizes) {
+               printf("%s:  text = %u instructions (%u bytes)\n",
+                      fn, insn_count, insn_count * sizeof(uint64_t));
+       }
+
        fclose(fd);
        free(fn);
 }
@@ -1098,8 +1165,12 @@ int main(int argc, char **argv)
        int err, res = 1;
 
        err = parse_args(argc, argv);
-       if (err)
+       if (err < 0)
+               goto out;
+       if (err > 0) {
+               res = 0;
                goto out;
+       }
        err = open_input_file();
        if (err)
                goto out;