root/src/system/libroot/os/arch/m68k/atomic.S
/*
** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
** Distributed under the terms of the MIT License.
*/

#include <asm_defs.h>

/*
 * gcc/config/m68k/m68k.h:CALL_USED_REGISTERS:
 * d0,d1,a0,a1 are scratch regs, not to be saved.
 */

/*
 * see http://mail-index.netbsd.org/amiga/1995/06/
 * for concerns about TAS, CAS and Amiga.
 * I suppose as long as we don't use it on a variable in 
 * chip ram it shouldn't be a problem, and since we don't
 * have any SMP Amiga anyway it should be ok.
 */

.text

/* int atomic_add(int *value, int increment)
 */
FUNCTION(atomic_add):
                move.l  (4,%a7),%a0
                move.l  (%a0),%d0
miss1:  move.l  %d0,%d1
                add.l   (8,%a7),%d1
                cas.l   %d0,%d1,(%a0)
                bne             miss1
                // d0 = old value
                rts
FUNCTION_END(atomic_add)

/* int atomic_and(int *value, int andValue)
 */
FUNCTION(atomic_and):
                move.l  (4,%a7),%a0
                move.l  (%a0),%d0
miss2:  move.l  %d0,%d1
                and.l   (8,%a7),%d1
                cas.l   %d0,%d1,(%a0)
                bne             miss2
                // d0 = old value
                rts
FUNCTION_END(atomic_and)

/* int atomic_or(int *value, int orValue)
 */
FUNCTION(atomic_or):
                move.l  (4,%a7),%a0
                move.l  (%a0),%d0
miss3:  move.l  %d0,%d1
                or.l    (8,%a7),%d1
                cas.l   %d0,%d1,(%a0)
                bne             miss3
                rts
FUNCTION_END(atomic_or)

/* int atomic_set(int *value, int setTo)
 */
FUNCTION(atomic_set):
                move.l  (4,%a7),%a0
                move.l  (%a0),%d0
                move.l  (8,%a7),%d1
miss4:  cas.l   %d0,%d1,(%a0)
                bne             miss4
                rts
FUNCTION_END(atomic_set)

/* int atomic_test_and_set(int *value, int setTo, int testValue) 
 */
FUNCTION(atomic_test_and_set):
                move.l  (4,%a7),%a0
                move.l  (8,%a7),%d1
                move.l  (12,%a7),%d0
                cas.l   %d0,%d1,(%a0)
                rts
FUNCTION_END(atomic_test_and_set)

/* int atomic_get(int *value) 
 */
FUNCTION(atomic_get):
                move.l  (4,%a7),%a0
                move.l  (%a0),%d0
                move.l  %d0,%d1
                cas.l   %d0,%d1,(%a0)
                // we must use cas... so we change to the same value if matching,
                // else we get the correct one anyway
                rts
FUNCTION_END(atomic_get)

/* m68k elf convention is to return structs in (a0)
 * but use d0/d1 for int64 and small structs.
 * d0 MSB, d1 LSB
 */
#warning M68K: 68060 doesn't have CAS2: use spinlock ??
/* see http://retropc.net/x68000/software/develop/as/has060/m68k.htm */

/* int64        atomic_add64(vint64 *value, int64 addValue) */
FUNCTION(atomic_add64):
                movem.l %d2-%d3/%a2,-(%a7)
                move.l  (4,%a7),%a2
                lea.l   (4,%a2),%a1
                // addValue
                move.l  (12,%a7),%d3    /*LSB*/
                move.l  (8,%a7),%d2             /*MSB*/
miss5:  // old value
                move.l  (%a1),%d1               /*LSB*/
                move.l  (%a2),%d0               /*MSB*/
                add.l   %d1,%d3
                addx.l  %d0,%d2
                cas2.l  %d0:%d1,%d2:%d3,(%a2):(%a1)
                bne             miss5
                // return value d0:d1
                movem.l (%a7)+,%d2-%d3/%a2
                rts
FUNCTION_END(atomic_add64)

/* int64        atomic_and64(vint64 *value, int64 andValue) */
FUNCTION(atomic_and64):
                movem.l %d2-%d3/%a2,-(%a7)
                move.l  (4,%a7),%a2
                lea.l   (4,%a2),%a1
                // addValue
                move.l  (12,%a7),%d3    /*LSB*/
                move.l  (8,%a7),%d2             /*MSB*/
miss6:  // old value
                move.l  (%a1),%d1               /*LSB*/
                move.l  (%a2),%d0               /*MSB*/
                and.l   %d1,%d3
                and.l   %d0,%d2
                cas2.l  %d0:%d1,%d2:%d3,(%a2):(%a1)
                bne             miss6
                // return value d0:d1
                movem.l (%a7)+,%d2-%d3/%a2
                rts
FUNCTION_END(atomic_and64)

/* int64        atomic_or64(vint64 *value, int64 orValue) */
FUNCTION(atomic_or64):
                movem.l %d2-%d3/%a2,-(%a7)
                move.l  (4,%a7),%a2
                lea.l   (4,%a2),%a1
                // addValue
                move.l  (12,%a7),%d3    /*LSB*/
                move.l  (8,%a7),%d2             /*MSB*/
miss7:  // old value
                move.l  (%a1),%d1               /*LSB*/
                move.l  (%a2),%d0               /*MSB*/
                or.l    %d1,%d3
                or.l    %d0,%d2
                cas2.l  %d0:%d1,%d2:%d3,(%a2):(%a1)
                bne             miss7
                // return value d0:d1
                movem.l (%a7)+,%d2-%d3/%a2
                rts
FUNCTION_END(atomic_or64)

/* int64        atomic_set64(vint64 *value, int64 newValue) */
FUNCTION(atomic_set64):
                movem.l %d2-%d3/%a2,-(%a7)
                move.l  (4,%a7),%a2
                lea.l   (4,%a2),%a1
                // new value
                move.l  (12,%a7),%d3    /*LSB*/
                move.l  (8,%a7),%d2             /*MSB*/
                // old value
                move.l  (%a1),%d1               /*LSB*/
                move.l  (%a2),%d0               /*MSB*/
miss8:  cas2.l  %d0:%d1,%d2:%d3,(%a2):(%a1)
                bne             miss8
                // return value d0:d1
                movem.l (%a7)+,%d2-%d3/%a2
                rts
FUNCTION_END(atomic_set64)

/* int64        atomic_test_and_set64(vint64 *value, int64 newValue, int64 testAgainst) */
FUNCTION(atomic_test_and_set64):
                movem.l %d2-%d3/%a2,-(%a7)
                move.l  (4,%a7),%a2
                lea.l   (4,%a2),%a1
                // new value
                move.l  (12,%a7),%d3    /*LSB*/
                move.l  (8,%a7),%d2             /*MSB*/
                // test against value
                move.l  (20,%a7),%d1    /*LSB*/
                move.l  (16,%a7),%d0    /*MSB*/
                cas2.l  %d0:%d1,%d2:%d3,(%a2):(%a1)
                // return value d0:d1
                movem.l (%a7)+,%d2-%d3/%a2
                rts
FUNCTION_END(atomic_test_and_set64)

/* int64        atomic_get64(vint64 *value) */
FUNCTION(atomic_get64):
                movem.l %d2-%d3/%a2,-(%a7)
                move.l  (4,%a7),%a2
                lea.l   (4,%a2),%a1
                move.l  (%a1),%d1       /*LSB*/
                move.l  (%a2),%d0               /*MSB*/
                move.l  %d1,%d3
                move.l  %d0,%d2
                // we must use cas... so we change to the same value if matching,
                // else we get the correct one anyway
                cas2.l  %d0:%d1,%d2:%d3,(%a2):(%a1)
                // return value
                movem.l (%a7)+,%d2-%d3/%a2
                rts
FUNCTION_END(atomic_get64)
.text

#warning IMPLEMENT GCC 64-bit ATOMICS ON m68k!

/* These are to fill in 64-bit atomic calls emitted by
 * by GCC when 64-bit atomics are unavailable.
 */

FUNCTION(__atomic_fetch_add_8):
                nop
                rts
FUNCTION_END(__atomic_fetch_add_8)

FUNCTION(__atomic_store_8):
                nop
                rts
FUNCTION_END(__atomic_store_8)

FUNCTION(__atomic_load_8):
                nop
                rts
FUNCTION_END(__atomic_load_8)