uint32_t HELPER(csst)(CPUS390XState *env, uint32_t r3, uint64_t a1, uint64_t a2)

{

#if !defined(CONFIG_USER_ONLY) || defined(CONFIG_ATOMIC128)

    uint32_t mem_idx = cpu_mmu_index(env, false);

#endif

    uintptr_t ra = GETPC();

    uint32_t fc = extract32(env->regs[0], 0, 8);

    uint32_t sc = extract32(env->regs[0], 8, 8);

    uint64_t pl = get_address(env, 1) & -16;

    uint64_t svh, svl;

    uint32_t cc;



    

    if (fc > 1 || sc > 3) {

        if (!s390_has_feat(S390_FEAT_COMPARE_AND_SWAP_AND_STORE_2)) {

            goto spec_exception;

        }

        if (fc > 2 || sc > 4 || (fc == 2 && (r3 & 1))) {

            goto spec_exception;

        }

    }



    

    if (extract32(a1, 0, 4 << fc) || extract32(a2, 0, 1 << sc)) {

        goto spec_exception;

    }



    

#ifndef CONFIG_USER_ONLY

    probe_write(env, a2, mem_idx, ra);

#endif



    







    if (parallel_cpus) {

        int mask = 0;

#if !defined(CONFIG_ATOMIC64)

        mask = -8;

#elif !defined(CONFIG_ATOMIC128)

        mask = -16;

#endif

        if (((4 << fc) | (1 << sc)) & mask) {

            cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);

        }

    }



    



    svh = cpu_ldq_data_ra(env, pl + 16, ra);

    svl = cpu_ldq_data_ra(env, pl + 24, ra);



    switch (fc) {

    case 0:

        {

            uint32_t nv = cpu_ldl_data_ra(env, pl, ra);

            uint32_t cv = env->regs[r3];

            uint32_t ov;



            if (parallel_cpus) {

#ifdef CONFIG_USER_ONLY

                uint32_t *haddr = g2h(a1);

                ov = atomic_cmpxchg__nocheck(haddr, cv, nv);

#else

                TCGMemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mem_idx);

                ov = helper_atomic_cmpxchgl_be_mmu(env, a1, cv, nv, oi, ra);

#endif

            } else {

                ov = cpu_ldl_data_ra(env, a1, ra);

                cpu_stl_data_ra(env, a1, (ov == cv ? nv : ov), ra);

            }

            cc = (ov != cv);

            env->regs[r3] = deposit64(env->regs[r3], 32, 32, ov);

        }

        break;



    case 1:

        {

            uint64_t nv = cpu_ldq_data_ra(env, pl, ra);

            uint64_t cv = env->regs[r3];

            uint64_t ov;



            if (parallel_cpus) {

#ifdef CONFIG_ATOMIC64

# ifdef CONFIG_USER_ONLY

                uint64_t *haddr = g2h(a1);

                ov = atomic_cmpxchg__nocheck(haddr, cv, nv);

# else

                TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN, mem_idx);

                ov = helper_atomic_cmpxchgq_be_mmu(env, a1, cv, nv, oi, ra);

# endif

#else

                

                g_assert_not_reached();

#endif

            } else {

                ov = cpu_ldq_data_ra(env, a1, ra);

                cpu_stq_data_ra(env, a1, (ov == cv ? nv : ov), ra);

            }

            cc = (ov != cv);

            env->regs[r3] = ov;

        }

        break;



    case 2:

        {

            uint64_t nvh = cpu_ldq_data_ra(env, pl, ra);

            uint64_t nvl = cpu_ldq_data_ra(env, pl + 8, ra);

            Int128 nv = int128_make128(nvl, nvh);

            Int128 cv = int128_make128(env->regs[r3 + 1], env->regs[r3]);

            Int128 ov;



            if (parallel_cpus) {

#ifdef CONFIG_ATOMIC128

                TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);

                ov = helper_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi, ra);

                cc = !int128_eq(ov, cv);

#else

                

                g_assert_not_reached();

#endif

            } else {

                uint64_t oh = cpu_ldq_data_ra(env, a1 + 0, ra);

                uint64_t ol = cpu_ldq_data_ra(env, a1 + 8, ra);



                ov = int128_make128(ol, oh);

                cc = !int128_eq(ov, cv);

                if (cc) {

                    nv = ov;

                }



                cpu_stq_data_ra(env, a1 + 0, int128_gethi(nv), ra);

                cpu_stq_data_ra(env, a1 + 8, int128_getlo(nv), ra);

            }



            env->regs[r3 + 0] = int128_gethi(ov);

            env->regs[r3 + 1] = int128_getlo(ov);

        }

        break;



    default:

        g_assert_not_reached();

    }



    





    if (cc == 0) {

        switch (sc) {

        case 0:

            cpu_stb_data_ra(env, a2, svh >> 56, ra);

            break;

        case 1:

            cpu_stw_data_ra(env, a2, svh >> 48, ra);

            break;

        case 2:

            cpu_stl_data_ra(env, a2, svh >> 32, ra);

            break;

        case 3:

            cpu_stq_data_ra(env, a2, svh, ra);

            break;

        case 4:

            if (parallel_cpus) {

#ifdef CONFIG_ATOMIC128

                TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);

                Int128 sv = int128_make128(svl, svh);

                helper_atomic_sto_be_mmu(env, a2, sv, oi, ra);

#else

                

                g_assert_not_reached();

#endif

            } else {

                cpu_stq_data_ra(env, a2 + 0, svh, ra);

                cpu_stq_data_ra(env, a2 + 8, svl, ra);

            }

            break;

        default:

            g_assert_not_reached();

        }

    }



    return cc;



 spec_exception:

    cpu_restore_state(ENV_GET_CPU(env), ra);

    program_interrupt(env, PGM_SPECIFICATION, 6);

    g_assert_not_reached();

}
