12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295 |
- /*
- * Copyright (C) 2006-2010 Michael Buesch <m@bues.ch>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include "main.h"
- #include "list.h"
- #include "util.h"
- #include "parser.h"
- #include "args.h"
- #include "initvals.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- extern int yyparse(void);
- extern int yydebug;
- struct file infile;
- const char *infile_name;
- const char *outfile_name;
- struct out_operand {
- enum {
- OUTOPER_NORMAL,
- OUTOPER_LABELREF,
- } type;
- union {
- unsigned int operand; /* For NORMAL */
- struct label *label; /* For LABELREF */
- } u;
- };
- struct code_output {
- enum {
- OUT_INSN,
- 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];
- /* The absolute address of this instruction.
- * Only used in resolve_labels(). */
- unsigned int address;
- const char *labelname; /* only for OUT_LABEL */
- /* Set to 1, if this is the %start instruction. */
- int is_start_insn;
- struct list_head list;
- };
- struct assembler_context {
- /* The architecture version (802.11 core revision) */
- unsigned int arch;
- struct label *start_label;
- /* Tracking stuff */
- struct statement *cur_stmt;
- struct list_head output;
- };
- #define for_each_statement(ctx, s) \
- list_for_each_entry(s, &infile.sl, list) { \
- ctx->cur_stmt = s;
- #define for_each_statement_end(ctx, s) \
- } do { ctx->cur_stmt = NULL; } while (0)
- #define _msg_helper(type, stmt, msg, x...) do { \
- fprintf(stderr, "Assembler " type); \
- if (stmt) { \
- fprintf(stderr, " (file \"%s\", line %u)", \
- stmt->info.file, \
- stmt->info.lineno); \
- } \
- fprintf(stderr, ":\n " msg "\n" ,##x); \
- } while (0)
- #define asm_error(ctx, msg, x...) do { \
- _msg_helper("ERROR", (ctx)->cur_stmt, msg ,##x); \
- exit(1); \
- } while (0)
- #define asm_warn(ctx, msg, x...) \
- _msg_helper("warning", (ctx)->cur_stmt, msg ,##x)
- #define asm_info(ctx, msg, x...) \
- _msg_helper("info", (ctx)->cur_stmt, msg ,##x)
- static void eval_directives(struct assembler_context *ctx)
- {
- struct statement *s;
- struct asmdir *ad;
- struct label *l;
- int have_start_label = 0;
- int have_arch = 0;
- unsigned int arch_fallback = 0;
- for_each_statement(ctx, s) {
- if (s->type == STMT_ASMDIR) {
- ad = s->u.asmdir;
- switch (ad->type) {
- case ADIR_ARCH:
- if (have_arch)
- asm_error(ctx, "Multiple %%arch definitions");
- ctx->arch = ad->u.arch;
- if (ctx->arch > 5 && ctx->arch < 15)
- arch_fallback = 5;
- if (ctx->arch > 15)
- arch_fallback = 15;
- if (arch_fallback) {
- asm_warn(ctx, "Using %%arch %d is incorrect. "
- "The wireless core revision %d uses the "
- "firmware architecture %d. So use %%arch %d",
- ctx->arch, ctx->arch, arch_fallback, arch_fallback);
- ctx->arch = arch_fallback;
- }
- if (ctx->arch != 5 && ctx->arch != 15) {
- asm_error(ctx, "Architecture version %u unsupported",
- ctx->arch);
- }
- have_arch = 1;
- break;
- case ADIR_START:
- if (have_start_label)
- asm_error(ctx, "Multiple %%start definitions");
- ctx->start_label = ad->u.start;
- have_start_label = 1;
- break;
- default:
- asm_error(ctx, "Unknown ASM directive");
- }
- }
- } for_each_statement_end(ctx, s);
- if (!have_arch)
- asm_error(ctx, "No %%arch defined");
- if (!have_start_label)
- asm_info(ctx, "Using start address 0");
- }
- static bool is_possible_imm(unsigned int imm)
- {
- unsigned int mask;
- /* Immediates are only possible up to 16bit (wordsize). */
- mask = ~0;
- mask <<= 16;
- if (imm & (1 << 15)) {
- if ((imm & mask) != mask &&
- (imm & mask) != 0)
- return 0;
- } else {
- if ((imm & mask) != 0)
- return 0;
- }
- return 1;
- }
- static unsigned int immediate_nr_bits(struct assembler_context *ctx)
- {
- switch (ctx->arch) {
- case 5:
- return 10; /* 10 bits */
- case 15:
- return 11; /* 11 bits */
- }
- asm_error(ctx, "Internal error: immediate_nr_bits unknown arch\n");
- }
- static bool is_valid_imm(struct assembler_context *ctx,
- unsigned int imm)
- {
- unsigned int mask;
- unsigned int immediate_size;
- /* This function checks if the immediate value is representable
- * as a native immediate operand.
- *
- * For v5 architecture the immediate can be 10bit long.
- * For v15 architecture the immediate can be 11bit long.
- *
- * The value is sign-extended, so we allow values
- * of 0xFFFA, for example.
- */
- if (!is_possible_imm(imm))
- return 0;
- imm &= 0xFFFF;
- immediate_size = immediate_nr_bits(ctx);
- /* First create a mask with all possible bits for
- * an immediate value unset. */
- mask = (~0 << immediate_size) & 0xFFFF;
- /* Is the sign bit of the immediate set? */
- if (imm & (1 << (immediate_size - 1))) {
- /* Yes, so all bits above that must also
- * be set, otherwise we can't represent this
- * value in an operand. */
- if ((imm & mask) != mask)
- return 0;
- } else {
- /* All bits above the immediate's size must
- * be unset. */
- if (imm & mask)
- return 0;
- }
- return 1;
- }
- /* This checks if the value is nonzero and a power of two. */
- static bool is_power_of_two(unsigned int value)
- {
- return (value && ((value & (value - 1)) == 0));
- }
- /* This checks if all bits set in the mask are contiguous.
- * Zero is also considered a contiguous mask. */
- static bool is_contiguous_bitmask(unsigned int mask)
- {
- unsigned int low_zeros_mask;
- bool is_contiguous;
- if (mask == 0)
- return 1;
- /* Turn the lowest zeros of the mask into a bitmask.
- * Example: 0b00011000 -> 0b00000111 */
- low_zeros_mask = (mask - 1) & ~mask;
- /* Adding the low_zeros_mask to the original mask
- * basically is a bitwise OR operation.
- * If the original mask was contiguous, we end up with a
- * contiguous bitmask from bit 0 to the highest bit
- * set in the original mask. Adding 1 will result in a single
- * bit set, which is a power of two. */
- is_contiguous = is_power_of_two(mask + low_zeros_mask + 1);
- return is_contiguous;
- }
- static unsigned int generate_imm_operand(struct assembler_context *ctx,
- const struct immediate *imm)
- {
- unsigned int val, tmp;
- unsigned int mask;
- val = 0xC00;
- if (ctx->arch == 15)
- val <<= 1;
- tmp = imm->imm;
- if (!is_valid_imm(ctx, tmp)) {
- asm_warn(ctx, "IMMEDIATE 0x%X (%d) too long "
- "(> %u bits + sign). Did you intend to "
- "use implicit sign extension?",
- tmp, (int)tmp, immediate_nr_bits(ctx) - 1);
- }
- if (ctx->arch == 15)
- tmp &= 0x7FF;
- else
- tmp &= 0x3FF;
- val |= tmp;
- return val;
- }
- static unsigned int generate_reg_operand(struct assembler_context *ctx,
- const struct registr *reg)
- {
- unsigned int val = 0;
- switch (reg->type) {
- case GPR:
- val |= 0xBC0;
- if (ctx->arch == 15)
- val <<= 1;
- if (reg->nr & ~0x3F) /* REVISIT: 128 regs for v15 arch possible? Probably not... */
- asm_error(ctx, "GPR-nr too big");
- val |= reg->nr;
- break;
- case SPR:
- val |= 0x800;
- if (ctx->arch == 15)
- val <<= 1;
- if (reg->nr & ~0x1FF)
- asm_error(ctx, "SPR-nr too big");
- val |= reg->nr;
- break;
- case OFFR:
- val |= 0x860;
- if (ctx->arch == 15)
- val <<= 1;
- if (reg->nr & ~0x7)
- asm_error(ctx, "OFFR-nr too big");
- val |= reg->nr;
- break;
- default:
- asm_error(ctx, "generate_reg_operand() regtype");
- }
- return val;
- }
- static unsigned int generate_mem_operand(struct assembler_context *ctx,
- const struct memory *mem)
- {
- unsigned int val = 0, off, reg, off_mask, reg_shift;
- switch (mem->type) {
- case MEM_DIRECT:
- off = mem->offset;
- switch (ctx->arch) {
- case 5:
- if (off & ~0x7FF) {
- asm_warn(ctx, "DIRECT memoffset 0x%X too long (> 11 bits)", off);
- off &= 0x7FF;
- }
- break;
- case 15:
- if (off & ~0xFFF) {
- asm_warn(ctx, "DIRECT memoffset 0x%X too long (> 12 bits)", off);
- off &= 0xFFF;
- }
- break;
- default:
- asm_error(ctx, "Internal error: generate_mem_operand invalid arch");
- }
- val |= off;
- break;
- case MEM_INDIRECT:
- switch (ctx->arch) {
- case 5:
- val = 0xA00;
- off_mask = 0x3F;
- reg_shift = 6;
- break;
- case 15:
- val = 0x1400;
- off_mask = 0x7F;
- reg_shift = 7;
- break;
- default:
- asm_error(ctx, "Internal error: MEM_INDIRECT invalid arch\n");
- }
- off = mem->offset;
- reg = mem->offr_nr;
- if (off & ~off_mask) {
- asm_warn(ctx, "INDIRECT memoffset 0x%X too long (> %u bits)",
- off, reg_shift);
- off &= off_mask;
- }
- if (reg > 6) {
- /* Assembler bug. The parser shouldn't pass this value. */
- asm_error(ctx, "OFFR-nr too big");
- }
- if (reg == 6) {
- asm_warn(ctx, "Using offset register 6. This register is broken "
- "on certain devices. Use off0 to off5 only.");
- }
- val |= off;
- val |= (reg << reg_shift);
- break;
- default:
- asm_error(ctx, "generate_mem_operand() memtype");
- }
- return val;
- }
- static void generate_operand(struct assembler_context *ctx,
- const struct operand *oper,
- struct out_operand *out)
- {
- out->type = OUTOPER_NORMAL;
- switch (oper->type) {
- case OPER_IMM:
- out->u.operand = generate_imm_operand(ctx, oper->u.imm);
- break;
- case OPER_REG:
- out->u.operand = generate_reg_operand(ctx, oper->u.reg);
- break;
- case OPER_MEM:
- out->u.operand = generate_mem_operand(ctx, oper->u.mem);
- break;
- case OPER_LABEL:
- out->type = OUTOPER_LABELREF;
- out->u.label = oper->u.label;
- break;
- case OPER_ADDR:
- out->u.operand = oper->u.addr->addr;
- break;
- case OPER_RAW:
- out->u.operand = oper->u.raw;
- break;
- default:
- asm_error(ctx, "generate_operand() operstate");
- }
- }
- static struct code_output * do_assemble_insn(struct assembler_context *ctx,
- struct instruction *insn,
- unsigned int opcode)
- {
- unsigned int i;
- struct operlist *ol;
- int nr_oper = 0;
- uint64_t code = 0;
- struct code_output *out;
- struct label *labelref = NULL;
- struct operand *oper;
- int have_spr_operand = 0;
- int have_mem_operand = 0;
- out = xmalloc(sizeof(*out));
- INIT_LIST_HEAD(&out->list);
- out->opcode = opcode;
- ol = insn->operands;
- if (ARRAY_SIZE(out->operands) > ARRAY_SIZE(ol->oper))
- asm_error(ctx, "Internal operand array confusion");
- for (i = 0; i < ARRAY_SIZE(out->operands); i++) {
- oper = ol->oper[i];
- if (!oper)
- continue;
- /* If this is an INPUT operand (first or second), we must
- * make sure that not both are accessing SPR or MEMORY.
- * The device only supports one SPR or MEMORY operand in
- * the input operands. */
- if ((i == 0) || (i == 1)) {
- if ((oper->type == OPER_REG) &&
- (oper->u.reg->type == SPR)) {
- if (have_spr_operand)
- asm_error(ctx, "Multiple SPR input operands in one instruction");
- have_spr_operand = 1;
- }
- if (oper->type == OPER_MEM) {
- if (have_mem_operand)
- asm_error(ctx, "Multiple MEMORY input operands in on instruction");
- have_mem_operand = 1;
- }
- }
- generate_operand(ctx, oper, &out->operands[i]);
- nr_oper++;
- }
- if (nr_oper != 3)
- asm_error(ctx, "Internal error: nr_oper at "
- "lowlevel do_assemble_insn");
- list_add_tail(&out->list, &ctx->output);
- return out;
- }
- static void do_assemble_ret(struct assembler_context *ctx,
- struct instruction *insn,
- unsigned int opcode)
- {
- struct code_output *out;
- /* 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_warn(ctx, "RET instruction directly after "
- "jump instruction. The hardware won't like this.");
- }
- break;
- }
- }
- do_assemble_insn(ctx, insn, opcode);
- }
- static unsigned int merge_ext_into_opcode(struct assembler_context *ctx,
- unsigned int opbase,
- struct instruction *insn)
- {
- struct operlist *ol;
- unsigned int opcode;
- unsigned int mask, shift;
- ol = insn->operands;
- opcode = opbase;
- mask = ol->oper[0]->u.raw;
- if (mask & ~0xF)
- asm_error(ctx, "opcode MASK extension too big (> 0xF)");
- shift = ol->oper[1]->u.raw;
- if (shift & ~0xF)
- asm_error(ctx, "opcode SHIFT extension too big (> 0xF)");
- opcode |= (mask << 4);
- opcode |= shift;
- ol->oper[0] = ol->oper[2];
- ol->oper[1] = ol->oper[3];
- ol->oper[2] = ol->oper[4];
- return opcode;
- }
- 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.imm->imm;
- if (cond & ~0xFF)
- asm_error(ctx, "External jump condition value too big (> 0xFF)");
- opcode |= cond;
- 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;
- }
- static void assemble_instruction(struct assembler_context *ctx,
- struct instruction *insn);
- static void emulate_mov_insn(struct assembler_context *ctx,
- struct instruction *insn)
- {
- struct instruction em_insn;
- struct operlist em_ol;
- struct operand em_op_shift;
- struct operand em_op_mask;
- struct operand em_op_x;
- struct operand em_op_y;
- struct immediate em_imm_x;
- struct immediate em_imm_y;
- struct operand *in, *out;
- unsigned int tmp;
- /* This is a pseudo-OP. We emulate it by OR or ORX */
- in = insn->operands->oper[0];
- out = insn->operands->oper[1];
- em_insn.op = OP_OR;
- em_ol.oper[0] = in;
- em_imm_x.imm = 0;
- em_op_x.type = OPER_IMM;
- em_op_x.u.imm = &em_imm_x;
- em_ol.oper[1] = &em_op_x;
- em_ol.oper[2] = out;
- if (in->type == OPER_IMM) {
- tmp = in->u.imm->imm;
- if (!is_possible_imm(tmp))
- asm_error(ctx, "MOV operand 0x%X > 16bit", tmp);
- if (!is_valid_imm(ctx, tmp)) {
- /* Immediate too big for plain OR */
- em_insn.op = OP_ORX;
- em_op_mask.type = OPER_RAW;
- em_op_mask.u.raw = 0x7;
- em_op_shift.type = OPER_RAW;
- em_op_shift.u.raw = 0x8;
- em_imm_x.imm = (tmp & 0xFF00) >> 8;
- em_op_x.type = OPER_IMM;
- em_op_x.u.imm = &em_imm_x;
- em_imm_y.imm = (tmp & 0x00FF);
- em_op_y.type = OPER_IMM;
- em_op_y.u.imm = &em_imm_y;
- em_ol.oper[0] = &em_op_mask;
- em_ol.oper[1] = &em_op_shift;
- em_ol.oper[2] = &em_op_x;
- em_ol.oper[3] = &em_op_y;
- em_ol.oper[4] = out;
- }
- }
- em_insn.operands = &em_ol;
- assemble_instruction(ctx, &em_insn); /* recurse */
- }
- static void emulate_jmp_insn(struct assembler_context *ctx,
- struct instruction *insn)
- {
- struct instruction em_insn;
- struct operlist em_ol;
- 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 */
- }
- 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;
- struct operand em_op_mask;
- struct operand em_op_y;
- struct immediate em_imm;
- struct operand *oper0, *oper1, *oper2;
- struct operand *imm_oper = NULL;
- unsigned int tmp;
- int first_bit, last_bit;
- oper0 = insn->operands->oper[0];
- oper1 = insn->operands->oper[1];
- oper2 = insn->operands->oper[2];
- if (oper0->type == OPER_IMM)
- imm_oper = oper0;
- if (oper1->type == OPER_IMM)
- imm_oper = oper1;
- if (oper0->type == OPER_IMM && oper1->type == OPER_IMM)
- imm_oper = NULL;
- if (imm_oper) {
- /* We have a single immediate operand.
- * Check if it's representable by a normal JAND insn.
- */
- tmp = imm_oper->u.imm->imm;
- if (!is_valid_imm(ctx, tmp)) {
- /* Nope, this must be emulated by JZX/JNZX */
- if (!is_contiguous_bitmask(tmp)) {
- asm_error(ctx, "Long bitmask 0x%X is not contiguous",
- tmp);
- }
- first_bit = ffs(tmp);
- last_bit = ffs(~(tmp >> (first_bit - 1))) - 1 + first_bit - 1;
- if (inverted)
- em_insn.op = OP_JZX;
- else
- em_insn.op = OP_JNZX;
- em_op_shift.type = OPER_RAW;
- em_op_shift.u.raw = first_bit - 1;
- em_op_mask.type = OPER_RAW;
- em_op_mask.u.raw = last_bit - first_bit;
- em_imm.imm = 0;
- em_op_y.type = OPER_IMM;
- em_op_y.u.imm = &em_imm;
- em_ol.oper[0] = &em_op_mask;
- em_ol.oper[1] = &em_op_shift;
- if (oper0->type != OPER_IMM)
- em_ol.oper[2] = oper0;
- else
- em_ol.oper[2] = oper1;
- em_ol.oper[3] = &em_op_y;
- em_ol.oper[4] = oper2;
- em_insn.operands = &em_ol;
- assemble_instruction(ctx, &em_insn); /* recurse */
- return;
- }
- }
- /* Do a normal JAND/JNAND instruction */
- if (inverted)
- out = do_assemble_insn(ctx, insn, 0x040 | 0x1);
- else
- 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) {
- case OP_MUL:
- do_assemble_insn(ctx, insn, 0x101);
- break;
- case OP_ADD:
- do_assemble_insn(ctx, insn, 0x1C0);
- break;
- case OP_ADDSC:
- do_assemble_insn(ctx, insn, 0x1C2);
- break;
- case OP_ADDC:
- do_assemble_insn(ctx, insn, 0x1C1);
- break;
- case OP_ADDSCC:
- do_assemble_insn(ctx, insn, 0x1C3);
- break;
- case OP_SUB:
- do_assemble_insn(ctx, insn, 0x1D0);
- break;
- case OP_SUBSC:
- do_assemble_insn(ctx, insn, 0x1D2);
- break;
- case OP_SUBC:
- do_assemble_insn(ctx, insn, 0x1D1);
- break;
- case OP_SUBSCC:
- do_assemble_insn(ctx, insn, 0x1D3);
- break;
- case OP_SRA:
- do_assemble_insn(ctx, insn, 0x130);
- break;
- case OP_OR:
- do_assemble_insn(ctx, insn, 0x160);
- break;
- case OP_AND:
- do_assemble_insn(ctx, insn, 0x140);
- break;
- case OP_XOR:
- do_assemble_insn(ctx, insn, 0x170);
- break;
- case OP_SR:
- do_assemble_insn(ctx, insn, 0x120);
- break;
- case OP_SRX:
- opcode = merge_ext_into_opcode(ctx, 0x200, insn);
- do_assemble_insn(ctx, insn, opcode);
- break;
- case OP_SL:
- do_assemble_insn(ctx, insn, 0x110);
- break;
- case OP_RL:
- do_assemble_insn(ctx, insn, 0x1A0);
- break;
- case OP_RR:
- do_assemble_insn(ctx, insn, 0x1B0);
- break;
- case OP_NAND:
- do_assemble_insn(ctx, insn, 0x150);
- break;
- case OP_ORX:
- opcode = merge_ext_into_opcode(ctx, 0x300, insn);
- do_assemble_insn(ctx, insn, opcode);
- break;
- case OP_MOV:
- emulate_mov_insn(ctx, insn);
- return;
- case OP_JMP:
- emulate_jmp_insn(ctx, insn);
- return;
- case OP_JAND:
- emulate_jand_insn(ctx, insn, 0);
- return;
- case OP_JNAND:
- emulate_jand_insn(ctx, insn, 1);
- return;
- case OP_JS:
- out = do_assemble_insn(ctx, insn, 0x050);
- out->is_jump_insn = 1;
- break;
- case OP_JNS:
- out = do_assemble_insn(ctx, insn, 0x050 | 0x1);
- out->is_jump_insn = 1;
- break;
- case OP_JE:
- out = do_assemble_insn(ctx, insn, 0x0D0);
- out->is_jump_insn = 1;
- break;
- case OP_JNE:
- out = do_assemble_insn(ctx, insn, 0x0D0 | 0x1);
- out->is_jump_insn = 1;
- break;
- case OP_JLS:
- out = do_assemble_insn(ctx, insn, 0x0D2);
- out->is_jump_insn = 1;
- break;
- case OP_JGES:
- out = do_assemble_insn(ctx, insn, 0x0D2 | 0x1);
- out->is_jump_insn = 1;
- break;
- case OP_JGS:
- out = do_assemble_insn(ctx, insn, 0x0D4);
- out->is_jump_insn = 1;
- break;
- case OP_JLES:
- out = do_assemble_insn(ctx, insn, 0x0D4 | 0x1);
- out->is_jump_insn = 1;
- break;
- case OP_JL:
- out = do_assemble_insn(ctx, insn, 0x0DA);
- out->is_jump_insn = 1;
- break;
- case OP_JGE:
- out = do_assemble_insn(ctx, insn, 0x0DA | 0x1);
- out->is_jump_insn = 1;
- break;
- case OP_JG:
- out = do_assemble_insn(ctx, insn, 0x0DC);
- break;
- case OP_JLE:
- out = do_assemble_insn(ctx, insn, 0x0DC | 0x1);
- out->is_jump_insn = 1;
- break;
- case OP_JDN:
- out = do_assemble_insn(ctx, insn, 0x0D6);
- out->is_jump_insn = 1;
- break;
- case OP_JDPZ:
- out = do_assemble_insn(ctx, insn, 0x0D6 | 0x1);
- out->is_jump_insn = 1;
- break;
- case OP_JDP:
- out = do_assemble_insn(ctx, insn, 0x0D8);
- out->is_jump_insn = 1;
- break;
- case OP_JDNZ:
- out = do_assemble_insn(ctx, insn, 0x0D8 | 0x1);
- out->is_jump_insn = 1;
- break;
- case OP_JZX:
- opcode = merge_ext_into_opcode(ctx, 0x400, insn);
- out = do_assemble_insn(ctx, insn, opcode);
- out->is_jump_insn = 1;
- break;
- case OP_JNZX:
- opcode = merge_ext_into_opcode(ctx, 0x500, insn);
- 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);
- 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);
- out = do_assemble_insn(ctx, insn, opcode);
- out->is_jump_insn = 1;
- break;
- case OP_CALL:
- if (ctx->arch != 5)
- asm_error(ctx, "'call' instruction is only supported on arch 5");
- do_assemble_insn(ctx, insn, 0x002);
- break;
- case OP_CALLS:
- if (ctx->arch != 15)
- asm_error(ctx, "'calls' instruction is only supported on arch 15");
- do_assemble_insn(ctx, insn, 0x004);
- break;
- case OP_RET:
- if (ctx->arch != 5)
- asm_error(ctx, "'ret' instruction is only supported on arch 5");
- do_assemble_ret(ctx, insn, 0x003);
- break;
- case OP_RETS:
- if (ctx->arch != 15)
- asm_error(ctx, "'rets' instruction is only supported on arch 15");
- do_assemble_insn(ctx, insn, 0x005);
- break;
- case OP_TKIPH:
- case OP_TKIPHS:
- case OP_TKIPL:
- case OP_TKIPLS:
- do_assemble_insn(ctx, insn, 0x1E0);
- break;
- case OP_NAP:
- do_assemble_insn(ctx, insn, 0x001);
- break;
- case RAW_CODE:
- do_assemble_insn(ctx, insn, insn->opcode);
- break;
- default:
- asm_error(ctx, "Unknown op");
- }
- }
- static void assemble_instructions(struct assembler_context *ctx)
- {
- struct statement *s;
- struct instruction *insn;
- struct code_output *out;
- if (ctx->start_label) {
- /* Generate a jump instruction at offset 0 to
- * jump to the code start.
- */
- struct instruction sjmp;
- struct operlist ol;
- struct operand oper;
- oper.type = OPER_LABEL;
- oper.u.label = ctx->start_label;
- ol.oper[0] = &oper;
- sjmp.op = OP_JMP;
- sjmp.operands = &ol;
- assemble_instruction(ctx, &sjmp);
- out = list_entry(ctx->output.next, struct code_output, list);
- out->is_start_insn = 1;
- }
- for_each_statement(ctx, s) {
- switch (s->type) {
- case STMT_INSN:
- ctx->cur_stmt = s;
- insn = s->u.insn;
- assemble_instruction(ctx, insn);
- break;
- case STMT_LABEL:
- out = xmalloc(sizeof(*out));
- INIT_LIST_HEAD(&out->list);
- out->type = OUT_LABEL;
- out->labelname = s->u.label->name;
- list_add_tail(&out->list, &ctx->output);
- break;
- case STMT_ASMDIR:
- break;
- }
- } for_each_statement_end(ctx, s);
- }
- /* Resolve a label reference to the address it points to. */
- static int get_labeladdress(struct assembler_context *ctx,
- struct code_output *this_insn,
- struct label *labelref)
- {
- struct code_output *c;
- bool found = 0;
- int address = -1;
- switch (labelref->direction) {
- case LABELREF_ABSOLUTE:
- list_for_each_entry(c, &ctx->output, list) {
- if (c->type != OUT_LABEL)
- continue;
- if (strcmp(c->labelname, labelref->name) != 0)
- continue;
- if (found) {
- asm_error(ctx, "Ambiguous label reference \"%s\"",
- labelref->name);
- }
- found = 1;
- address = c->address;
- }
- break;
- case LABELREF_RELATIVE_BACK:
- for (c = list_entry(this_insn->list.prev, typeof(*c), list);
- &c->list != &ctx->output;
- c = list_entry(c->list.prev, typeof(*c), list)) {
- if (c->type != OUT_LABEL)
- continue;
- if (strcmp(c->labelname, labelref->name) == 0) {
- /* Found */
- address = c->address;
- break;
- }
- }
- break;
- case LABELREF_RELATIVE_FORWARD:
- for (c = list_entry(this_insn->list.next, typeof(*c), list);
- &c->list != &ctx->output;
- c = list_entry(c->list.next, typeof(*c), list)) {
- if (c->type != OUT_LABEL)
- continue;
- if (strcmp(c->labelname, labelref->name) == 0) {
- /* Found */
- address = c->address;
- break;
- }
- }
- break;
- }
- return address;
- }
- static void resolve_labels(struct assembler_context *ctx)
- {
- struct code_output *c;
- int addr;
- unsigned int i;
- unsigned int current_address;
- /* Calculate the absolute addresses for each instruction. */
- recalculate_addresses:
- current_address = 0;
- list_for_each_entry(c, &ctx->output, list) {
- switch (c->type) {
- case OUT_INSN:
- c->address = current_address;
- current_address++;
- break;
- case OUT_LABEL:
- c->address = current_address;
- break;
- }
- }
- /* Resolve the symbolic label references. */
- list_for_each_entry(c, &ctx->output, list) {
- switch (c->type) {
- case OUT_INSN:
- if (c->is_start_insn) {
- /* If the first %start-jump jumps to 001, we can
- * optimize it away, as it's unneeded.
- */
- i = 2;
- if (c->operands[i].type != OUTOPER_LABELREF)
- asm_error(ctx, "Internal error, %%start insn oper 2 not labelref");
- if (c->operands[i].u.label->direction != LABELREF_ABSOLUTE)
- asm_error(ctx, "%%start label reference not absolute");
- addr = get_labeladdress(ctx, c, c->operands[i].u.label);
- if (addr < 0)
- goto does_not_exist;
- if (addr == 1) {
- list_del(&c->list); /* Kill it */
- goto recalculate_addresses;
- }
- }
- for (i = 0; i < ARRAY_SIZE(c->operands); i++) {
- if (c->operands[i].type != OUTOPER_LABELREF)
- continue;
- addr = get_labeladdress(ctx, c, c->operands[i].u.label);
- if (addr < 0)
- goto does_not_exist;
- c->operands[i].u.operand = addr;
- 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:
- break;
- }
- }
- return;
- does_not_exist:
- asm_error(ctx, "Label \"%s\" does not exist",
- c->operands[i].u.label->name);
- }
- static void emit_code(struct assembler_context *ctx)
- {
- FILE *fd;
- const char *fn;
- struct code_output *c;
- uint64_t code;
- unsigned char outbuf[8];
- unsigned int insn_count = 0, insn_count_limit;
- struct fw_header hdr;
- fn = outfile_name;
- fd = fopen(fn, "w+");
- if (!fd) {
- fprintf(stderr, "Could not open microcode output file \"%s\"\n", fn);
- exit(1);
- }
- if (IS_VERBOSE_DEBUG)
- printf("\nCode:\n");
- list_for_each_entry(c, &ctx->output, list) {
- switch (c->type) {
- case OUT_INSN:
- insn_count++;
- break;
- default:
- break;
- }
- }
- switch (cmdargs.outformat) {
- case FMT_RAW_LE32:
- case FMT_RAW_BE32:
- /* Nothing */
- break;
- case FMT_B43:
- memset(&hdr, 0, sizeof(hdr));
- hdr.type = FW_TYPE_UCODE;
- hdr.ver = FW_HDR_VER;
- hdr.size = cpu_to_be32(8 * insn_count);
- if (fwrite(&hdr, sizeof(hdr), 1, fd) != 1) {
- fprintf(stderr, "Could not write microcode outfile\n");
- exit(1);
- }
- break;
- }
- switch (ctx->arch) {
- case 5:
- insn_count_limit = NUM_INSN_LIMIT_R5;
- break;
- case 15:
- insn_count_limit = ~0; //FIXME limit currently unknown.
- break;
- default:
- asm_error(ctx, "Internal error: emit_code unknown arch\n");
- }
- if (insn_count > insn_count_limit)
- asm_warn(ctx, "Generating more than %u instructions. This "
- "will overflow the device microcode memory.",
- insn_count_limit);
- list_for_each_entry(c, &ctx->output, list) {
- switch (c->type) {
- case OUT_INSN:
- if (IS_VERBOSE_DEBUG) {
- printf("%03X %04X,%04X,%04X\n",
- c->opcode,
- c->operands[0].u.operand,
- c->operands[1].u.operand,
- c->operands[2].u.operand);
- }
- switch (ctx->arch) {
- case 5:
- code = 0;
- code |= ((uint64_t)c->operands[2].u.operand);
- code |= ((uint64_t)c->operands[1].u.operand) << 12;
- code |= ((uint64_t)c->operands[0].u.operand) << 24;
- code |= ((uint64_t)c->opcode) << 36;
- break;
- case 15:
- code = 0;
- code |= ((uint64_t)c->operands[2].u.operand);
- code |= ((uint64_t)c->operands[1].u.operand) << 13;
- code |= ((uint64_t)c->operands[0].u.operand) << 26;
- code |= ((uint64_t)c->opcode) << 39;
- break;
- default:
- asm_error(ctx, "No emit format for arch %u",
- ctx->arch);
- }
- switch (cmdargs.outformat) {
- case FMT_B43:
- case FMT_RAW_BE32:
- code = ((code & (uint64_t)0xFFFFFFFF00000000ULL) >> 32) |
- ((code & (uint64_t)0x00000000FFFFFFFFULL) << 32);
- outbuf[0] = (code & (uint64_t)0xFF00000000000000ULL) >> 56;
- outbuf[1] = (code & (uint64_t)0x00FF000000000000ULL) >> 48;
- outbuf[2] = (code & (uint64_t)0x0000FF0000000000ULL) >> 40;
- outbuf[3] = (code & (uint64_t)0x000000FF00000000ULL) >> 32;
- outbuf[4] = (code & (uint64_t)0x00000000FF000000ULL) >> 24;
- outbuf[5] = (code & (uint64_t)0x0000000000FF0000ULL) >> 16;
- outbuf[6] = (code & (uint64_t)0x000000000000FF00ULL) >> 8;
- outbuf[7] = (code & (uint64_t)0x00000000000000FFULL) >> 0;
- break;
- case FMT_RAW_LE32:
- outbuf[7] = (code & (uint64_t)0xFF00000000000000ULL) >> 56;
- outbuf[6] = (code & (uint64_t)0x00FF000000000000ULL) >> 48;
- outbuf[5] = (code & (uint64_t)0x0000FF0000000000ULL) >> 40;
- outbuf[4] = (code & (uint64_t)0x000000FF00000000ULL) >> 32;
- outbuf[3] = (code & (uint64_t)0x00000000FF000000ULL) >> 24;
- outbuf[2] = (code & (uint64_t)0x0000000000FF0000ULL) >> 16;
- outbuf[1] = (code & (uint64_t)0x000000000000FF00ULL) >> 8;
- outbuf[0] = (code & (uint64_t)0x00000000000000FFULL) >> 0;
- break;
- }
- if (fwrite(&outbuf, ARRAY_SIZE(outbuf), 1, fd) != 1) {
- fprintf(stderr, "Could not write microcode outfile\n");
- exit(1);
- }
- break;
- case OUT_LABEL:
- break;
- }
- }
- if (cmdargs.print_sizes) {
- printf("%s: text = %u instructions (%u bytes)\n",
- fn, insn_count,
- (unsigned int)(insn_count * sizeof(uint64_t)));
- }
- fclose(fd);
- }
- static void assemble(void)
- {
- struct assembler_context ctx;
- memset(&ctx, 0, sizeof(ctx));
- INIT_LIST_HEAD(&ctx.output);
- eval_directives(&ctx);
- assemble_instructions(&ctx);
- resolve_labels(&ctx);
- emit_code(&ctx);
- }
- static void initialize(void)
- {
- INIT_LIST_HEAD(&infile.sl);
- INIT_LIST_HEAD(&infile.ivals);
- #if YYDEBUG
- if (IS_INSANE_DEBUG)
- yydebug = 1;
- else
- yydebug = 0;
- #endif /* YYDEBUG */
- }
- int main(int argc, char **argv)
- {
- int err, res = 1;
- err = parse_args(argc, argv);
- if (err < 0)
- goto out;
- if (err > 0) {
- res = 0;
- goto out;
- }
- err = open_input_file();
- if (err)
- goto out;
- initialize();
- yyparse();
- assemble();
- assemble_initvals();
- close_input_file();
- res = 0;
- out:
- /* Lazyman simply leaks all allocated memory. */
- return res;
- }
|