/*
 * hal_atomic.c - implementation of Generic Atomic Operations API for TSAR-MIPS32
 * 
 * Author  Alain Greiner (2016)
 *
 * Copyright (c) UPMC Sorbonne Universites
 * 
 * This file is part of ALMOS-MKH..
 *
 * ALMOS-MKH. is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MKH. is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-MKH.; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <hal_types.h>

////////////////////////////////////
void hal_atomic_and( uint32_t * ptr,
                     int32_t    val )
{
	asm volatile (
		 ".set noreorder		             \n"	
		 "1:                                 \n"
		 "ll      $3,      (%0)              \n"
		 "and     $3,      $3,       %1      \n"
		 "sc      $3,      (%0)              \n"
		 "beq     $3,      $0,       1b      \n"
		 "nop                                \n"
		 "sync                               \n"
		 ".set reorder			      \n"	
		 : : "r" (ptr), "r" (val) : "$3" , "memory" );
}

///////////////////////////////////
void hal_atomic_or( uint32_t * ptr,
                    int32_t    val )
{
	asm volatile (
		 ".set noreorder		             \n"	
		 "1:                                 \n"
		 "ll      $3,      (%0)              \n"
		 "or      $3,      $3,       %1      \n"
		 "sc      $3,      (%0)              \n"
		 "beq     $3,      $0,       1b      \n"
		 "nop                                \n"
		 "sync                               \n"
		 ".set reorder			      \n"	
		 : : "r" (ptr), "r" (val) : "$3" , "memory" );
}

///////////////////////////////////////
uint32_t hal_atomic_add( void    * ptr,
                         int32_t   val )
{
	int32_t current;
 
	asm volatile (
		 ".set noreorder		             \n"	
		 "1:                                 \n"
		 "ll      %0,      (%1)              \n"
		 "addu    $3,      %0,       %2      \n"
		 "sc      $3,      (%1)              \n"
		 "beq     $3,      $0,       1b      \n"
		 "nop                                \n"
		 "sync                               \n"
		 ".set reorder			      \n"	
		 :"=&r"(current) : "r" (ptr), "r" (val) : "$3" , "memory" );

	return current;
}

///////////////////////////////////////
int32_t hal_atomic_inc( uint32_t * ptr)
{
	return hal_atomic_add( ptr , 1 );
}

///////////////////////////////////////
int32_t hal_atomic_dec( uint32_t * ptr)
{
	return hal_atomic_add( ptr , -1 );
}

//////////////////////////////////////
bool_t hal_atomic_cas( uint32_t * ptr,
                       uint32_t   old, 
                       uint32_t   new )
{
	bool_t isAtomic;
  
	asm volatile (
		 ".set noreorder                     \n"
		 "sync                               \n"
		 "or      $8,      $0,       %3      \n"
		 "ll      $3,      (%1)              \n"
		 "bne     $3,      %2,       1f      \n"
		 "li      $7,      0                 \n"
		 "sc      $8,      (%1)              \n"
		 "or      $7,      $8,       $0      \n"
		 "sync                               \n"
		 ".set reorder                       \n"
		 "1:                                 \n"
		 "or      %0,      $7,       $0      \n"
		 : "=&r" (isAtomic): "r" (ptr), "r" (old) , "r" (new) : "$3", "$7", "$8");

	return isAtomic;
}

///////////////////////////////////////////
bool_t hal_atomic_test_set( uint32_t * ptr,
                            int32_t    val )
{
	return hal_atomic_cas( ptr , 0 , val );
}

