/* --------------------------------------------------------------  */
/* PLEASE DO NOT MODIFY THIS SECTION                               */
/* This prolog section is automatically generated.                 */
/*                                                                 */
/* (C)Copyright                                                    */
/* Sony Computer Entertainment, Inc.,                              */
/* Toshiba Corporation,                                            */
/* International Business Machines Corporation,                    */
/* 2001,2006.  All rights reserved.                                */
/* --------------------------------------------------------------  */
/* PROLOG END TAG zYx                                              */

/* SCE CONFIDENTIAL
PLAYSTATION(R)3 Programmer Tool Runtime Library 084.006
* Copyright (C) 2005 Sony Computer Entertainment Inc.
*/

#ifndef __CELL_ATOMIC_H__
#define __CELL_ATOMIC_H__ 1

#include <stdint.h>
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 *  @addtogroup bpa bpa
 */
/**
 *  @addtogroup bpa_pu pu
 *  @ingroup bpa
 */
/**
 *  @addtogroup mfc_pu_atomic atomic
 *  @ingroup bpa_pu
 */
/*@{*/

/***************************************************************
 * uint32_t atomic operation support
 ***************************************************************/

static inline
uint32_t cellAtomicLockLine32(uint32_t *ea)
{
	uint32_t value;

	__asm__ volatile (
		"# cellAtomicLockLine32(ea=%[ea],value=%[value])\n"
		"	lwarx   %[value], 0, %[ea]\n"
		: [value]"=&r"(value)
		: [ea]"b"(ea)
		);
	return value;
}

static inline
bool cellAtomicStoreConditional32(uint32_t *ea, uint32_t value)
{
	unsigned int cr;

	__asm__ volatile (
		"# cellAtomicStoreConditional32(ea=%[ea],value=%[value])\n"
		"	stwcx.  %[value], 0, %[ea]\n"
		"	mfcr    %[cr]\n"
		: [cr]"=&r"(cr)
		: [ea]"b"(ea), [value]"r"(value)
		: "cc", "memory");
	return ((cr & 0x20000000) == 0);	/* CR0[EQ] */
}

/* Sample: atomic fetch and increment
 * uint32_t sampleAtomicIncr(uint32_t *ea)
 * {
 *     uint32_t old;
 *
 *     do {
 *         old = cellAtomicLockLine32(ea);
 *     } while (cellAtomicStoreConditional32(ea, old + 1));
 *     return old;
 * }
 */

/***************************************************************
 * unsinged int atomic operations
 ***************************************************************/

static inline
uint32_t cellAtomicAdd32(uint32_t *ea, uint32_t value)
{
	uint32_t old, tmp;

	__asm__ volatile(
		"# cellAtomicAdd32(ea=%[ea],old=%[old],val=%[value],tmp=%[tmp])\n"
		".loop%=:\n"
		"	lwarx   %[old], 0, %[ea]\n"
		"	add     %[tmp], %[value], %[old]\n"
		"	stwcx.  %[tmp], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&r"(old), [tmp]"=&r"(tmp)
		: [ea]"b"(ea), [value]"r"(value)
		: "cc", "memory");

	return old;
}

static inline
uint32_t cellAtomicSub32(uint32_t *ea, uint32_t value)
{
	uint32_t old, tmp;

	__asm__ volatile(
		"# cellAtomicSub32(ea=%[ea],old=%[old],val=%[value],tmp=%[tmp])\n"
		".loop%=:\n"
		"	lwarx   %[old], 0, %[ea]\n"
		"	subf    %[tmp], %[value], %[old]\n"
		"	stwcx.  %[tmp], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&r"(old), [tmp]"=&r"(tmp)
		: [ea]"b"(ea), [value]"r"(value)
		: "cc", "memory");

	return old;
}

static inline
uint32_t cellAtomicAnd32(uint32_t *ea, uint32_t value)
{
	uint32_t old, tmp;

	__asm__ volatile(
		"# cellAtomicAnd32(ea=%[ea],old=%[old],val=%[value],tmp=%[tmp])\n"
		".loop%=:\n"
		"	lwarx   %[old], 0, %[ea]\n"
		"	and     %[tmp], %[value], %[old]\n"
		"	stwcx.  %[tmp], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&r"(old), [tmp]"=&r"(tmp)
		: [ea]"b"(ea), [value]"r"(value)
		: "cc", "memory");

	return old;
}

static inline
uint32_t cellAtomicOr32(uint32_t *ea, uint32_t value)
{
	uint32_t old, tmp;

	__asm__ volatile(
		"# cellAtomicOr32(ea=%[ea],old=%[old],val=%[value],tmp=%[tmp])\n"
		".loop%=:\n"
		"	lwarx   %[old], 0, %[ea]\n"
		"	or      %[tmp], %[value], %[old]\n"
		"	stwcx.  %[tmp], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&r"(old), [tmp]"=&r"(tmp)
		: [ea]"b"(ea), [value]"r"(value)
		: "cc", "memory");

	return old;
}

static inline
uint32_t cellAtomicStore32(uint32_t *ea, uint32_t value)
{
	uint32_t old;

	__asm__ volatile(
		"# cellAtomicStore32(ea=%[ea],old=%[old],value=%[value])\n"
		".loop%=:\n"
		"	lwarx   %[old], 0, %[ea]\n"
		"	stwcx.  %[value], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&r"(old)
		: [ea]"b"(ea), [value]"r"(value)
		: "cc", "memory");

    return old;
}

static inline
uint32_t cellAtomicIncr32(uint32_t *ea)
{
	uint32_t old, tmp;

	__asm__ volatile(
		"# cellAtomicIncr32(ea=%[ea],old=%[old],tmp=%[tmp])\n"
		".loop%=:\n"
		"	lwarx   %[old], 0, %[ea]\n"
		"	addi    %[tmp], %[old], 1\n"
		"	stwcx.  %[tmp], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&b"(old), [tmp]"=&r"(tmp)
		: [ea]"b"(ea)
		: "cc", "memory");

	return old;
}

static inline
uint32_t cellAtomicDecr32(uint32_t *ea)
{
	uint32_t old, tmp;

	__asm__ volatile(
		"# cellAtomicDecr32(ea=%[ea],old=%[old],tmp=%[tmp])\n"
		".loop%=:\n"
		"	lwarx   %[old], 0, %[ea]\n"
		"	subi    %[tmp], %[old], 1\n"
		"	stwcx.  %[tmp], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&b"(old), [tmp]"=&r"(tmp)
		: [ea]"b"(ea)
		: "cc", "memory");

	return old;
}

static inline
uint32_t cellAtomicNop32(uint32_t *ea)
{
	uint32_t old;

	__asm__ volatile(
		"# cellAtomicNop32(ea=%[ea],old=%[old])\n"
		".loop%=:\n"
		"	lwarx   %[old], 0, %[ea]\n"
		"	stwcx.  %[old], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&b"(old)
		: [ea]"b"(ea)
		: "cc", "memory");

	return old;
}

/// test and decr using shared memory
/**
 * The cellAtomicTestAndDecr32() function atomically loads a
 * word from ea, ensures that the word in memory is a
 * nonzero value, and decrement it.
 * Zero is returned if the word was zero.  Non-zero is
 * returned if this operation is successfully done.
 * ls must be 128Byte aligend.
 */
static inline
uint32_t cellAtomicTestAndDecr32(uint32_t *ea)
{
	uint32_t old, tmp;

	__asm__ volatile(
		"# cellAtomicTestAndDecr32(ea=%[ea],old=%[old],tmp=%[tmp])\n"
		".loop%=:\n"
		"	lwarx   %[old], 0, %[ea]\n"
		"	cmpwi   %[old], 0\n"
		"	beq-    .Ldone%=\n"
		"	subi    %[tmp], %[old], 1\n"
		"	stwcx.  %[tmp], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		".Ldone%=:\n"
		: [old]"=&b"(old), [tmp]"=&r"(tmp)
		: [ea]"b"(ea)
		: "cc", "memory");

	return old;
}

/// test and set using shared memory
/**
 * The test and set function atomically loads a word from
 * memory specified by ea, ensures that the word in memory
 * is a nonzero value, and sets 1.
 * ls must be 128Byte aligend.
 */
static inline
uint32_t cellAtomicCompareAndSwap32(uint32_t *ea, uint32_t compare, uint32_t value)
{
	uint32_t old;

	__asm__ volatile(
		"# cellAtomicCompareAndSwap32(ea=%[ea],old=%[old],compare=%[compare],value=%[value])\n"
		".loop%=:\n"
		"	lwarx   %[old], 0, %[ea]\n"
		"	cmpw    %[old], %[compare]\n"
		"	bne-    .Ldone%=\n"
		"	stwcx.  %[value], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		".Ldone%=:\n"
		: [old]"=&r"(old)
		: [ea]"b"(ea), [compare]"r"(compare), [value]"r"(value)
		: "cc", "memory");

	return old;
}

/***************************************************************
 * uint64_t atomic operation support
 ***************************************************************/

static inline
uint64_t cellAtomicLockLine64(uint64_t *ea)
{
	uint64_t value;
	uint64_t uea = (uint64_t)(uintptr_t)ea;
	__asm__ volatile (
		"# cellAtomicLockLine64(uea=%[uea],value=%[value])\n"
		"	ldarx   %[value], 0, %[uea]\n"
		: [value]"=&r"(value)
		: [uea]"b"(uea)
		);
	return value;
}

static inline
bool cellAtomicStoreConditional64(uint64_t *ea, uint64_t value)
{
	unsigned int cr;
	uint64_t uea = (uint64_t)(uintptr_t)ea;
	__asm__ volatile (
		"# cellAtomicStoreConditional64(uea=%[uea],value=%[value])\n"
		"	stdcx.  %[value], 0, %[uea]\n"
		"	mfcr    %[cr]\n"
		: [cr]"=&r"(cr)
		: [uea]"b"(uea), [value]"r"(value)
		: "cc", "memory");
	return ((cr & 0x20000000) == 0);	/* CR0[EQ] */
}

/***************************************************************
 * uint64_t atomic operations
 ***************************************************************/

static inline
uint64_t cellAtomicAdd64(uint64_t*ea, uint64_t value)
{
	uint64_t old, tmp;

	__asm__ volatile(
		"# cellAtomicAdd64(ea=%[ea],old=%[old],val=%[value],tmp=%[tmp])\n"
		".loop%=:\n"
		"	ldarx   %[old], 0, %[ea]\n"
		"	add     %[tmp], %[value], %[old]\n"
		"	stdcx.  %[tmp], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&r"(old), [tmp]"=&r"(tmp)
		: [ea]"b"(ea), [value]"r"(value)
		: "cc", "memory");

	return old;
}

static inline
uint64_t cellAtomicSub64(uint64_t *ea, uint64_t value)
{
	uint64_t old, tmp;

	__asm__ volatile(
		"# cellAtomicSub64(ea=%[ea],old=%[old],val=%[value],tmp=%[tmp])\n"
		".loop%=:\n"
		"	ldarx   %[old], 0, %[ea]\n"
		"	subf    %[tmp], %[value], %[old]\n"
		"	stdcx.  %[tmp], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&r"(old), [tmp]"=&r"(tmp)
		: [ea]"b"(ea), [value]"r"(value)
		: "cc", "memory");

	return old;
}

static inline
uint64_t cellAtomicAnd64(uint64_t*ea, uint64_t value)
{
	uint64_t old, tmp;

	__asm__ volatile(
		"# cellAtomicAnd64(ea=%[ea],old=%[old],val=%[value],tmp=%[tmp])\n"
		".loop%=:\n"
		"	ldarx   %[old], 0, %[ea]\n"
		"	and     %[tmp], %[value], %[old]\n"
		"	stdcx.  %[tmp], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&r"(old), [tmp]"=&r"(tmp)
		: [ea]"b"(ea), [value]"r"(value)
		: "cc", "memory");

	return old;
}

static inline
uint64_t cellAtomicOr64(uint64_t *ea, uint64_t value)
{
	uint64_t old, tmp;

	__asm__ volatile(
		"# cellAtomicOr64(ea=%[ea],old=%[old],val=%[value],tmp=%[tmp])\n"
		".loop%=:\n"
		"	ldarx   %[old], 0, %[ea]\n"
		"	or      %[tmp], %[value], %[old]\n"
		"	stdcx.  %[tmp], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&r"(old), [tmp]"=&r"(tmp)
		: [ea]"b"(ea), [value]"r"(value)
		: "cc", "memory");

	return old;
}

static inline
uint64_t cellAtomicStore64(uint64_t *ea, uint64_t value)
{
	uint64_t old;

	__asm__ volatile(
		"# cellAtomicStore64(ea=%[ea],old=%[old],value=%[value])\n"
		".loop%=:\n"
		"	ldarx   %[old], 0, %[ea]\n"
		"	stdcx.  %[value], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&r"(old)
		: [ea]"b"(ea), [value]"r"(value)
		: "cc", "memory");

	return old;
}

static inline
uint64_t cellAtomicIncr64(uint64_t *ea)
{
	uint64_t old, tmp;

	__asm__ volatile(
		"# cellAtomicIncr64(ea=%[ea],old=%[old],tmp=%[tmp])\n"
		".loop%=:\n"
		"	ldarx   %[old], 0, %[ea]\n"
		"	addi    %[tmp], %[old], 1\n"
		"	stdcx.  %[tmp], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&b"(old), [tmp]"=&r"(tmp)
		: [ea]"b"(ea)
		: "cc", "memory");

	return old;
}

static inline
uint64_t cellAtomicDecr64(uint64_t *ea)
{
	uint64_t old, tmp;

	__asm__ volatile(
		"# cellAtomicDecr64(ea=%[ea],old=%[old],tmp=%[tmp])\n"
		".loop%=:\n"
		"	ldarx   %[old], 0, %[ea]\n"
		"	subi    %[tmp], %[old], 1\n"
		"	stdcx.  %[tmp], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&b"(old), [tmp]"=&r"(tmp)
		: [ea]"b"(ea)
		: "cc", "memory");

	return old;
}

static inline
uint64_t cellAtomicNop64(uint64_t *ea)
{
	uint64_t old;

	__asm__ volatile(
		"# cellAtomicNop64(ea=%[ea],old=%[old])\n"
		".loop%=:\n"
		"	ldarx   %[old], 0, %[ea]\n"
		"	stdcx.  %[old], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		: [old]"=&b"(old)
		: [ea]"b"(ea)
		: "cc", "memory");

	return old;
}

/// test and decr using shared memory
/**
 * The cellAtomicTestAndDecr64() function atomically loads a
 * word from ea, ensures that the word in memory is a
 * nonzero value, and decrement it.
 * Zero is returned if the word was zero.  Non-zero is
 * returned if this operation is successfully done.
 * ls must be 128Byte aligend.
 */
static inline
uint64_t cellAtomicTestAndDecr64(uint64_t *ea)
{
	uint64_t old, tmp;

	__asm__ volatile(
		"# cellAtomicTestAndDecr64(ea=%[ea],old=%[old],tmp=%[tmp])\n"
		".loop%=:\n"
		"	ldarx   %[old], 0, %[ea]\n"
		"	cmpdi   %[old], 0\n"
		"	beq-    .Ldone%=\n"
		"	subi    %[tmp], %[old], 1\n"
		"	stdcx.  %[tmp], 0, %[ea]\n"
		"	bne-    .loop%=\n"
		".Ldone%=:\n"
		: [old]"=&b"(old), [tmp]"=&r"(tmp)
		: [ea]"b"(ea)
		: "cc", "memory");

	return old;
}

/// test and set using shared memory
/**
 * The test and set function atomically loads a word from
 * memory specified by ea, ensures that the word in memory
 * is a nonzero value, and sets 1.
 * ls must be 128Byte aligend.
 */
static inline
uint64_t cellAtomicCompareAndSwap64(uint64_t *ea, uint64_t compare, uint64_t value)
{
	uint64_t old;

	__asm__ volatile(
		"# cellAtomicCompareAndSwap64(ea=%[ea],old=%[old],compare=%[compare],value=%[value])\n"
		".loop%=:\n"
		"	ldarx   %[old], 0, %[ea]\n"
		"	cmpd    %[old], %[compare]\n"
		"	bne     .Ldone%=\n"
		"	stdcx.  %[value], 0, %[ea]\n"
		"	bne .loop%=\n"
		".Ldone%=:\n"
		: [old]"=&r"(old)
		: [ea]"b"(ea), [compare]"r"(compare), [value]"r"(value)
		: "cc", "memory");

	return old;
}

#ifdef __cplusplus
} /* extern "C" */
#endif

/*@}*/
#endif /* __CELL_ATOMIC_H__ */

/*
 * Local Variables:
 * mode: C
 * tab-width: 4
 * End:
 * vim:sw=4:sts=4:ts=4
 */
