extern void mem_offset_instr(quad opcode, int cc, int rd, int qa, int rb);
extern void mem_postincr_instr(quad opcode, int cc, int rd, int rs);
extern void mem_address_instr(quad opcode, int rd, struct expr_t* expr);
+extern void branch_addcmp_reg_reg_instr(int cc, int rd, int ra, int rs, struct expr_t* expr);
+extern void branch_addcmp_lit_reg_instr(int cc, int rd, long va, int rs, struct expr_t* expr);
+extern void branch_addcmp_reg_lit_instr(int cc, int rd, int ra, long vs, struct expr_t* expr);
+extern void branch_addcmp_lit_lit_instr(int cc, int rd, long va, long vs, struct expr_t* expr);
%token <y_word> CC
%token <y_word> OP
-%token <y_word> OP_BRANCH
+%token <y_word> OP_BRANCH OP_BRANCHLINK OP_ADDCMPB
%token <y_word> OP_ONEREG
%token <y_word> OP_ONELREG
%token <y_word> OP_ALU
0, OP, B16(00000000,00001010), "rti",
0, OP_BRANCH, 0, "b",
-0, OP_BRANCH, 1, "bl",
+0, OP_BRANCHLINK, 0, "bl",
+0, OP_ADDCMPB, 0, "addcmpb",
0, OP_ONELREG, B16(00000000,10000000), "tbb",
0, OP_ONELREG, B16(00000000,10100000), "tbs",
operation
: OP { emit2($1); }
- | OP_BRANCH GPR { emit2($1 | ($2<<0)); }
- | OP_BRANCH expr { branch_instr($1, ALWAYS, &$2); }
- | OP_BRANCH CC expr { branch_instr($1, $2, &$3); }
+ | OP_BRANCH GPR { emit2(B16(00000000,01000000) | ($2<<0)); }
+ | OP_BRANCHLINK GPR { emit2(B16(00000000,01100000) | ($2<<0)); }
+
+ | OP_BRANCH expr { branch_instr(0, ALWAYS, &$2); }
+ | OP_BRANCHLINK expr { branch_instr(1, ALWAYS, &$2); }
+ | OP_BRANCH CC expr { branch_instr(0, $2, &$3); }
+ | OP_BRANCHLINK CC expr { branch_instr(1, $2, &$3); }
+
+ | OP_BRANCH GPR ',' GPR ',' expr { branch_addcmp_lit_reg_instr(ALWAYS, 0, $2, $4, &$6); }
+ | OP_BRANCH CC GPR ',' GPR ',' expr { branch_addcmp_lit_reg_instr($2, 0, $3, $5, &$7); }
+ | OP_BRANCH GPR ',' '#' absexp ',' expr { branch_addcmp_lit_lit_instr(ALWAYS, 0, $2, $5, &$7); }
+ | OP_BRANCH CC GPR ',' '#' absexp ',' expr { branch_addcmp_lit_lit_instr($2, 0, $3, $6, &$8); }
+ | OP_ADDCMPB GPR ',' GPR ',' GPR ',' expr { branch_addcmp_reg_reg_instr(ALWAYS, $2, $4, $6, &$8); }
+ | OP_ADDCMPB CC GPR ',' GPR ',' GPR ',' expr { branch_addcmp_reg_reg_instr($2, $3, $5, $7, &$9); }
+ | OP_ADDCMPB GPR ',' '#' absexp ',' GPR ',' expr { branch_addcmp_reg_reg_instr(ALWAYS, $2, $5, $7, &$9); }
+ | OP_ADDCMPB CC GPR ',' '#' absexp ',' GPR ',' expr { branch_addcmp_reg_reg_instr($2, $3, $6, $8, &$10); }
+ | OP_ADDCMPB GPR ',' GPR ',' '#' absexp ',' expr { branch_addcmp_reg_lit_instr(ALWAYS, $2, $4, $7, &$9); }
+ | OP_ADDCMPB CC GPR ',' GPR ',' '#' absexp ',' expr { branch_addcmp_reg_lit_instr($2, $3, $5, $8, &$10); }
+ | OP_ADDCMPB GPR ',' '#' absexp ',' '#' absexp ',' expr { branch_addcmp_lit_lit_instr(ALWAYS, $2, $5, $8, &$10); }
+ | OP_ADDCMPB CC GPR ',' '#' absexp ',' '#' absexp ',' expr { branch_addcmp_lit_lit_instr($2, $3, $6, $9, &$11); }
| OP_ONELREG GPR
{
}
}
+/* Common code for handling addcmp: merge in as much of expr as will fit to
+ * the second pair of the addcmp opcode. */
+
+static void branch_addcmp_common(quad opcode, int bits, struct expr_t* expr)
+{
+ quad type = expr->typ & S_TYP;
+
+ switch (pass)
+ {
+ case 0:
+ /* Calculate size of instructions only. */
+
+ emit2(0);
+ break;
+
+ case 1:
+ case 2:
+ {
+ if (type != DOTTYP)
+ serror("can't use this type of branch to jump outside the section");
+
+ /* The VC4 branch instructions express distance in 2-byte
+ * words. */
+
+ int d = (expr->val - DOTVAL-2 + 4) / 2;
+
+ if (!fitx(d, bits))
+ serror("target of branch is too far away");
+
+ emit2(opcode | maskx(d, bits));
+ break;
+ }
+ }
+}
+
+void branch_addcmp_reg_reg_instr(int cc, int rd, int ra, int rs, struct expr_t* expr)
+{
+ if ((rd >= 0x10) || (ra >= 0x10) || (rs >= 0x10))
+ serror("can only use r0-r15 in this instruction");
+
+ emit2(B16(10000000,00000000) | (cc<<8) | (ra<<4) | (rd<<0));
+ branch_addcmp_common(B16(00000000,00000000) | (rs<<10), 10, expr);
+}
+
+void branch_addcmp_lit_reg_instr(int cc, int rd, long va, int rs, struct expr_t* expr)
+{
+ if ((rd >= 0x10) || (rs >= 0x10))
+ serror("can only use r0-r15 in this instruction");
+
+ if (!fitx(va, 4))
+ serror("value too big to encode into instruction");
+ va = maskx(va, 4);
+
+ emit2(B16(10000000,00000000) | (cc<<8) | (va<<4) | (rd<<0));
+ branch_addcmp_common(B16(01000000,00000000) | (rs<<10), 10, expr);
+}
+
+void branch_addcmp_reg_lit_instr(int cc, int rd, int ra, long vs, struct expr_t* expr)
+{
+ if ((rd >= 0x10) || (ra >= 0x10))
+ serror("can only use r0-r15 in this instruction");
+
+ if (!fitx(vs, 6))
+ serror("value too big to encode into instruction");
+ vs = maskx(vs, 6);
+
+ emit2(B16(10000000,00000000) | (cc<<8) | (ra<<4) | (rd<<0));
+ branch_addcmp_common(B16(10000000,00000000) | (vs<<8), 8, expr);
+}
+
+void branch_addcmp_lit_lit_instr(int cc, int rd, long va, long vs, struct expr_t* expr)
+{
+ if (rd >= 0x10)
+ serror("can only use r0-r15 in this instruction");
+
+ if (!fitx(va, 4) || !fitx(vs, 6))
+ serror("value too big to encode into instruction");
+ va = maskx(va, 4);
+ vs = maskx(vs, 6);
+
+ emit2(B16(10000000,00000000) | (cc<<8) | (va<<4) | (rd<<0));
+ branch_addcmp_common(B16(11000000,00000000) | (vs<<8), 8, expr);
+}
+
ldb r0, main
stb r0, near
stb r0, main
+
+ b.eq r0, r1, near
+ b r0, r1, near
+ addcmpb r0, r1, r2, .
+ addcmpb r0, #1, r2, .
+ addcmpb r0, r1, #1, .
+ addcmpb r0, #1, #2, .