// dsp.c

//
// low-level DSP mail delivery
//

u32 DSPCheckMailToDSP(void)
{
    return ( *(u16 *)0xCC005000 & 0x8000 );
}

u32 DSPCheckMailFromDSP(void)
{
    return ( *(u16 *)0xCC005004 & 0x8000 );
}

u32 DSPReadCPUToDSPMbox(void)
{
    return ( (*(u16 *)0xCC005000 << 16) | *(u16 *)0xCC005002 );
}

u32 DSPReadMailFromDSP(void)
{
    return ( (*(u16 *)0xCC005004 << 16) | *(u16 *)0xCC005006 );
}

void DSPSendMailToDSP(u32 mail)
{
    *(u16 *)0xCC005000 = mail >> 16;
    *(u16 *)0xCC005002 = (u16)mail;
}

//
// assert DSP interrupt
//

void DSPAssertInt(void)
{
    u16 old, tmp;
    BOOL lvl = OSDisableInterrupts();

    old = *(u16 *)0xCC00500A;
    tmp = old & ~0x00A8;
    tmp |= 0x0002;
    *(u16 *)0xCC00500A = tmp;

    OSRestoreInterrupts(lvl);
}

//
// DSP management
//

BOOL __DSP_init_flag;

void DSPInit(void)
{
    __DSP_debug_printf("DSPInit(): Build Date: %s %s\n", __DATE__, __TIME__);

    if(__DSP_init_flag == FALSE)
    {
        u16 old, tmp;
        BOOL lvl = OSDisableInterrupts();

        __OSSetInterruptHandler(__OS_INTERRUPT_DSP_DSP, __DSPHandler);
        __OSUnmaskInterrupts(OS_INTERRUPTMASK_DSP_DSP);

        old = *(u16 *)0xCC00500A;
        tmp = old & ~0x00A8;
        tmp |= 0x0800;
        *(u16 *)0xCC00500A = tmp;

        old = *(u16 *)0xCC00500A;
        tmp = old & ~0x00AC;
        *(u16 *)0xCC00500A = tmp;

        __DSP_tmp_task   = 
        __DSP_curr_task  =
        __DSP_last_task  =
        __DSP_first_task = NULL;

        __DSP_init_flag = TRUE;

        OSRestoreInterrupts(lvl);
    }
}

BOOL DSPCheckInit(void)
{
    return __DSP_init_flag;
}

void DSPReset(void)
{
    u16 old, tmp;
    BOOL lvl = OSDisableInterrupts();

    old = *(u16 *)0xCC00500A;
    tmp = old & ~0x00A8;
    tmp |= 0x0800;
    tmp |= 0x0001;
    *(u16 *)0xCC00500A = tmp;

    __DSP_init_flag = FALSE;

    OSRestoreInterrupts(lvl);
}

void DSPHalt(void)
{
    u16 old, tmp;
    BOOL lvl = OSDisableInterrupts();

    old = *(u16 *)0xCC00500A;
    tmp = old & ~0x00A8;
    tmp |= 0x0004;
    *(u16 *)0xCC00500A = tmp;

    OSRestoreInterrupts(lvl);
}

void DSPUnhalt(void)
{
    u16 old, tmp;
    BOOL lvl = OSDisableInterrupts();

    old = *(u16 *)0xCC00500A;
    tmp = old & ~0x00AC;
    *(u16 *)0xCC00500A = tmp;

    OSRestoreInterrupts(lvl);
}

u32 DSPGetDMAStatus(void)
{
    return ( *(u16 *)0xCC00500A & 0x0200 );
}

//
// DSP task control
//

DSPTaskInfo *DSPAddTask(DSPTaskInfo *task)
{
    BOOL lvl;

    if(__DSP_init_flag == FALSE)
    {
        OSHalt("DSPAddTask(): DSP driver not initialized!\n");
    }

    lvl = OSDisableInterrupts();

    __DSP_insert_task(task);

    state->state = DSP_TASK_STATE_INIT;
    state->flags = DSP_TASK_FLAG_ATTACHED;

    OSRestoreInterrupts(lvl);

    if(task == __DSP_first_task)
    {
        __DSP_boot_task(task);
    }

    return task;
}

DSPTaskInfo *DSPCancelTask(DSPTaskInfo *task)
{
    BOOL lvl;

    if(__DSP_init_flag == FALSE)
    {
        OSHalt("DSPCancelTask(): DSP driver not initialized!\n");
    }

    lvl = OSDisableInterrupts();

    task->flags |= DSP_TASK_FLAG_CANCEL;

    OSRestoreInterrupts(lvl);
    return task;
}

DSPTaskInfo *DSPAssertTask(DSPTaskInfo *task)
{
    BOOL lvl;

    if(__DSP_init_flag == FALSE)
    {
        OSHalt("DSPAssertTask(): DSP driver not initialized!\n");
    }

    if((task->flags & DSP_TASK_FLAG_ATTACHED) == 0)
    {
        OSHalt("DSPAssertTask(): Specified task not in active task list!\n");
    }

    lvl = OSDisableInterrupts();

    if(task == __DSP_curr_task)
    {
        __DSP_rude_task = task;
        __DSP_rude_task_pending = TRUE;

        OSRestoreInterrupts(lvl);
        return task;
    }
    else
    {
        if(DSPGetTaskPriority(task) < DSPGetTaskPriority(__DSP_curr_task))
        {
            __DSP_rude_task = task;
            __DSP_rude_task_pending = TRUE;

            if(DSPGetTaskState(__DSP_curr_task) == DSP_TASK_STATE_RUN)
            {
                DSPAssertInt();
            }

            OSRestoreInterrupts(lvl);
            return task
        }
    }

    OSRestoreInterrupts(lvl);
    return NULL;
}
