#include "OS_spinLock.h"
#include "OS_system.h"
#include "MI_swap.h"
#include "code32.h"

void OS_InitLock(void)
{
    static BOOL isInitialized = FALSE;

    if (isInitialized)
        return;
    isInitialized = TRUE;

    OSLockWord *lockp = (OSLockWord *)HW_INIT_LOCK_BUF;

    lockp->extension = 0;
    while (lockp->ownerID != 0x7f)
    {
        OSi_WaitByLoop();
    }

    ((u32 *)HW_LOCK_ID_FLAG_SUB)[0] = 0xffffffff;
    ((u32 *)HW_LOCK_ID_FLAG_SUB)[1] = 0xffff0000;

    lockp->extension = 0xbf;
}

void sub_037F8CB4(s32 ct)
{
    SVC_WaitByLoop((u32)ct); //todo this should be linker generated, figure out why it broke
}

s32 OSi_DoLockByWord(u16 lockId, OSLockWord *lockp, void (*ctrlFuncp) (void),
                              BOOL disableFiq)
{
    s32 lastLockFlag;
    while ((lastLockFlag = OSi_DoTryLockByWord(lockId, lockp, ctrlFuncp, disableFiq)) > 0) {
        OSi_WaitByLoop();
    }

    return lastLockFlag;
}

s32 OSi_DoUnlockByWord(u16 lockID, OSLockWord *lockp, void (*ctrlFuncp) (void),
                                BOOL disableFIQ)
{
    if (lockID != lockp->ownerID)
    {
        return -2;
    }

    OSIntrMode lastIntrMode = (disableFIQ) ? OS_DisableInterrupts_IrqAndFiq() : OS_DisableInterrupts();
    lockp->ownerID = 0;
    if (ctrlFuncp)
    {
        ctrlFuncp();
    }
    lockp->lockFlag = 0;
    if (disableFIQ)
    {
        (void)OS_RestoreInterrupts_IrqAndFiq(lastIntrMode);
    }
    else
    {
        (void)OS_RestoreInterrupts(lastIntrMode);
    }
    return 0;
}

s32 OSi_DoTryLockByWord(u16 lockID, OSLockWord *lockp, void (*ctrlFuncp) (void),
                                 BOOL disableFiq)
{
    OSIntrMode lastIntrMode = (disableFiq) ? OS_DisableInterrupts_IrqAndFiq() : OS_DisableInterrupts();

    s32 lastLockFlag = (s32)MI_SwapWord(lockID, (u32 *)&lockp->lockFlag);

    if (!lastLockFlag)
    {
        if (ctrlFuncp)
        {
            ctrlFuncp();
        }
        lockp->ownerID = lockID;
    }

    if (disableFiq)
    {
        (void)OS_RestoreInterrupts_IrqAndFiq(lastIntrMode);
    }
    else
    {
        (void)OS_RestoreInterrupts(lastIntrMode);
    }

    return lastLockFlag;
}

s32 OS_LockCartridge(u16 lockID)
{
    return OSi_DoLockByWord(lockID, (OSLockWord *)HW_CTRDG_LOCK_BUF, OSi_AllocateCartridgeBus, TRUE);
}

s32 OS_UnlockCartridge(u16 lockID)
{
    return OSi_DoUnlockByWord(lockID, (OSLockWord *)HW_CTRDG_LOCK_BUF, OSi_FreeCartridgeBus, TRUE);
}

s32 OS_TryLockCartridge(u16 lockID)
{
    return OSi_DoTryLockByWord(lockID, (OSLockWord *)HW_CTRDG_LOCK_BUF, OSi_AllocateCartridgeBus, TRUE);
}

void OSi_AllocateCartridgeBus(void)
{
    //noop
}

void OSi_FreeCartridgeBus(void)
{
    //noop
}

u16 OS_ReadOwnerOfLockWord(OSLockWord * lock)
{
    return lock->ownerID;
}

asm s32 OS_UnLockCartridge(u16 lockID)
{
    ldr r1, =OS_UnlockCartridge
    bx r1
}

asm s32 OS_GetLockID(void)
{
    ldr	r3, =HW_LOCK_ID_FLAG_SUB
    ldr	r1, [r3]
    mov	r2, #0
    mov	r0, #0x80000000
_037F8A34:
    tst	r1, r0
    bne	_037F8A50
    add	r2, r2, #1
    cmp	r2, #32
    beq	_037F8A50
    mov	r0, r0, lsr #1
    b	_037F8A34
_037F8A50:
    cmp	r2, #32
    movne	r0, #0x80
    bne	_037F8A98
    add	r3, r3, #4
    ldr	r1, [r3]
    mov	r2, #0
    mov	r0, #0x80000000
_037F8A6C:
    tst	r1, r0
    bne	_037F8A88
    add	r2, r2, #1
    cmp	r2, #32
    beq	_037F8A88
    mov	r0, r0, lsr #1
    b	_037F8A6C
_037F8A88:
    cmp	r2, #32
    ldr	r0, =0xFFFFFFFD
    bxeq	lr
    mov	r0, #160
_037F8A98:
    add	r0, r0, r2
    mov	r1, #0x80000000
    mov	r1, r1, lsr r2
    ldr	r2, [r3]
    bic	r2, r2, r1
    str	r2, [r3]
    bx	lr
}

asm void OS_ReleaseLockID(register u16 lockID)
{
    ldr r3, =HW_LOCK_ID_FLAG_SUB
    cmp r0, #0xa0
    addpl r3, r3, #0x4
    subpl r0, r0, #0xa0
    submi r0, r0, #0x80
    mov r1, #0x80000000
    mov r1, r1, lsr r0
    ldr r2, [r3, #0x0]
    orr r2, r2, r1
    str r2, [r3, #0x0]
    bx lr
}
