[PATCH] arm-asm: Add ldrh, ldrsh, ldrsb, strh

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[PATCH] arm-asm: Add ldrh, ldrsh, ldrsb, strh

Danny Milosavljevic
---
 arm-asm.c                  | 142 +++++++++++++++++++++++++++++++++++++
 arm-tok.h                  |   4 ++
 tests/arm-asm-testsuite.sh |   2 +
 3 files changed, 148 insertions(+)

diff --git a/arm-asm.c b/arm-asm.c
index 0f7340c..f753849 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -1116,6 +1116,142 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token)
     }
 }
 
+static void asm_misc_single_data_transfer_opcode(TCCState *s1, int token)
+{
+    Operand ops[3];
+    int exclam = 0;
+    int closed_bracket = 0;
+    int op2_minus = 0;
+    uint32_t opcode = (1 << 7) | (1 << 4);
+
+    /* Note:
+       The argument syntax is exactly the same as in arm_single_data_transfer_opcode, except that there's no STREX argument form.
+       The main difference between this function and asm_misc_single_data_transfer_opcode is that the immediate values here must be smaller.
+       Also, the combination (P=0, W=1) is unpredictable here.
+       The immediate flag has moved to bit index 22--and its meaning has flipped.
+       The immediate value itself has been split into two parts: one at bits 11...8, one at bits 3...0
+       bit 26 (Load/Store instruction) is unset here.
+       bits 7 and 4 are set here. */
+
+    // Here: 0 0 0 P U I W L << 20
+    // [compare single data transfer: 0 1 I P U B W L << 20]
+
+    parse_operand(s1, &ops[0]);
+    if (ops[0].type == OP_REG32)
+        opcode |= ENCODE_RD(ops[0].reg);
+    else {
+        expect("(destination operand) register");
+        return;
+    }
+    if (tok != ',')
+        expect("at least two arguments");
+    else
+        next(); // skip ','
+
+    if (tok != '[')
+        expect("'['");
+    else
+        next(); // skip '['
+
+    parse_operand(s1, &ops[1]);
+    if (ops[1].type == OP_REG32)
+        opcode |= ENCODE_RN(ops[1].reg);
+    else {
+        expect("(first source operand) register");
+        return;
+    }
+    if (tok == ']') {
+        next();
+        closed_bracket = 1;
+        // exclam = 1; // implicit in hardware; don't do it in software
+    }
+    if (tok == ',') {
+        next(); // skip ','
+        if (tok == '-') {
+            op2_minus = 1;
+            next();
+        }
+        parse_operand(s1, &ops[2]);
+    } else {
+        // end of input expression in brackets--assume 0 offset
+        ops[2].type = OP_IM8;
+        ops[2].e.v = 0;
+        opcode |= 1 << 24; // add offset before transfer
+    }
+    if (!closed_bracket) {
+        if (tok != ']')
+            expect("']'");
+        else
+            next(); // skip ']'
+        opcode |= 1 << 24; // add offset before transfer
+        if (tok == '!') {
+            exclam = 1;
+            next(); // skip '!'
+        }
+    }
+
+    if (exclam) {
+        if ((opcode & (1 << 24)) == 0) {
+            tcc_error("result would be unpredicable");
+            return;
+        }
+        opcode |= 1 << 21; // write offset back into register
+    }
+
+    if (ops[2].type == OP_IM32 || ops[2].type == OP_IM8 || ops[2].type == OP_IM8N) {
+        int v = ops[2].e.v;
+        if (op2_minus)
+            tcc_error("minus before '#' not supported for immediate values");
+        if (v >= 0) {
+            opcode |= 1 << 23; // up
+            if (v >= 0x100)
+                tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
+            else {
+                // bits 11...8: immediate hi nibble
+                // bits 3...0: immediate lo nibble
+                opcode |= (v & 0xF0) << 4;
+                opcode |= v & 0xF;
+            }
+        } else { // down
+            if (v <= -0x100)
+                tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
+            else {
+                v = -v;
+                // bits 11...8: immediate hi nibble
+                // bits 3...0: immediate lo nibble
+                opcode |= (v & 0xF0) << 4;
+                opcode |= v & 0xF;
+            }
+        }
+        opcode |= 1 << 22; // not ENCODE_IMMEDIATE_FLAG;
+    } else if (ops[2].type == OP_REG32) {
+        if (!op2_minus)
+            opcode |= 1 << 23; // up
+        opcode |= ops[2].reg;
+    } else
+        expect("register");
+
+    switch (ARM_INSTRUCTION_GROUP(token)) {
+    case TOK_ASM_ldrsheq:
+        opcode |= 1 << 5; // halfword, not byte
+        /* fallthrough */
+    case TOK_ASM_ldrsbeq:
+        opcode |= 1 << 6; // sign extend
+        opcode |= 1 << 20; // L
+        asm_emit_opcode(token, opcode);
+        break;
+    case TOK_ASM_ldrheq:
+        opcode |= 1 << 5; // halfword, not byte
+        opcode |= 1 << 20; // L
+        asm_emit_opcode(token, opcode);
+        break;
+    case TOK_ASM_strheq:
+        opcode |= 1 << 5; // halfword, not byte
+        asm_emit_opcode(token, opcode);
+        break;
+    }
+}
+
 /* Note: almost dupe of encbranch in arm-gen.c */
 static uint32_t encbranchoffset(int pos, int addr, int fail)
 {
@@ -1232,6 +1368,12 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
     case TOK_ASM_strexbeq:
         return asm_single_data_transfer_opcode(s1, token);
 
+    case TOK_ASM_ldrheq:
+    case TOK_ASM_ldrsheq:
+    case TOK_ASM_ldrsbeq:
+    case TOK_ASM_strheq:
+       return asm_misc_single_data_transfer_opcode(s1, token);
+
     case TOK_ASM_andeq:
     case TOK_ASM_eoreq:
     case TOK_ASM_subeq:
diff --git a/arm-tok.h b/arm-tok.h
index be927cc..f73add1 100644
--- a/arm-tok.h
+++ b/arm-tok.h
@@ -97,6 +97,10 @@
  DEF_ASM_CONDED(ldrexb)
  DEF_ASM_CONDED(strex)
  DEF_ASM_CONDED(strexb)
+ DEF_ASM_CONDED(ldrh)
+ DEF_ASM_CONDED(ldrsh)
+ DEF_ASM_CONDED(ldrsb)
+ DEF_ASM_CONDED(strh)
 
  DEF_ASM_CONDED(stmda)
  DEF_ASM_CONDED(ldmda)
diff --git a/tests/arm-asm-testsuite.sh b/tests/arm-asm-testsuite.sh
index 273111b..e997e07 100755
--- a/tests/arm-asm-testsuite.sh
+++ b/tests/arm-asm-testsuite.sh
@@ -65,6 +65,8 @@ do
             "r2, r3, [r4]" \
             "r2, [r3, #4]" \
             "r2, [r3, #-4]" \
+            "r2, [r3, #0x45]" \
+            "r2, [r3, #-0x45]" \
             "r2, r3, #4" \
             "r2, r3, #-4" \
             "r2, #4" \

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel