Commit 564b125f authored by Richard Henderson's avatar Richard Henderson Committed by Peter Maydell
Browse files

target/arm: Convert T16, push and pop



Reviewed-by: default avatarPeter Maydell <peter.maydell@linaro.org>
Signed-off-by: default avatarRichard Henderson <richard.henderson@linaro.org>
Message-id: 20190904193059.26202-62-richard.henderson@linaro.org
Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parent 279de61a
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -221,3 +221,13 @@ REVSH 1011 1010 11 ... ... @rdm
  # rest of the space is a reserved hint, behaves as nop.
  NOP           1011 1111 ---- 0000
}

# Push and Pop

%push_list      0:9 !function=t16_push_list
%pop_list       0:9 !function=t16_pop_list

STM             1011 010 ......... \
                &ldst_block i=0 b=1 u=0 w=1 rn=13 list=%push_list
LDM_t16         1011 110 ......... \
                &ldst_block i=1 b=0 u=0 w=1 rn=13 list=%pop_list
+12 −71
Original line number Diff line number Diff line
@@ -7516,6 +7516,16 @@ static int t16_setflags(DisasContext *s)
    return s->condexec_mask == 0;
}

static int t16_push_list(DisasContext *s, int x)
{
    return (x & 0xff) | (x & 0x100) << (14 - 8);
}

static int t16_pop_list(DisasContext *s, int x)
{
    return (x & 0xff) | (x & 0x100) << (15 - 8);
}

/*
 * Include the generated decoders.
 */
@@ -10713,7 +10723,6 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn)
{
    uint32_t val, op, rm, rd, shift, cond;
    int32_t offset;
    int i;
    TCGv_i32 tmp;
    TCGv_i32 tmp2;
    TCGv_i32 addr;
@@ -10786,76 +10795,8 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn)
            goto illegal_op;

        case 4: case 5: case 0xc: case 0xd:
            /*
             * 0b1011_x10x_xxxx_xxxx
             *  - push/pop
             */
            addr = load_reg(s, 13);
            if (insn & (1 << 8))
                offset = 4;
            else
                offset = 0;
            for (i = 0; i < 8; i++) {
                if (insn & (1 << i))
                    offset += 4;
            }
            if ((insn & (1 << 11)) == 0) {
                tcg_gen_addi_i32(addr, addr, -offset);
            }

            if (s->v8m_stackcheck) {
                /*
                 * Here 'addr' is the lower of "old SP" and "new SP";
                 * if this is a pop that starts below the limit and ends
                 * above it, it is UNKNOWN whether the limit check triggers;
                 * we choose to trigger.
                 */
                gen_helper_v8m_stackcheck(cpu_env, addr);
            }

            for (i = 0; i < 8; i++) {
                if (insn & (1 << i)) {
                    if (insn & (1 << 11)) {
                        /* pop */
                        tmp = tcg_temp_new_i32();
                        gen_aa32_ld32u(s, tmp, addr, get_mem_index(s));
                        store_reg(s, i, tmp);
                    } else {
                        /* push */
                        tmp = load_reg(s, i);
                        gen_aa32_st32(s, tmp, addr, get_mem_index(s));
                        tcg_temp_free_i32(tmp);
                    }
                    /* advance to the next address.  */
                    tcg_gen_addi_i32(addr, addr, 4);
                }
            }
            tmp = NULL;
            if (insn & (1 << 8)) {
                if (insn & (1 << 11)) {
                    /* pop pc */
                    tmp = tcg_temp_new_i32();
                    gen_aa32_ld32u(s, tmp, addr, get_mem_index(s));
                    /* don't set the pc until the rest of the instruction
                       has completed */
                } else {
                    /* push lr */
                    tmp = load_reg(s, 14);
                    gen_aa32_st32(s, tmp, addr, get_mem_index(s));
                    tcg_temp_free_i32(tmp);
                }
                tcg_gen_addi_i32(addr, addr, 4);
            }
            if ((insn & (1 << 11)) == 0) {
                tcg_gen_addi_i32(addr, addr, -offset);
            }
            /* write back the new stack pointer */
            store_reg(s, 13, addr);
            /* set the new PC value */
            if ((insn & 0x0900) == 0x0900) {
                store_reg_from_load(s, 15, tmp);
            }
            break;
            /* push/pop, in decodetree */
            goto illegal_op;

        case 1: case 3: case 9: case 11: /* czb */
            rm = insn & 7;