// dsp_task.c

//
// DSP task queue execution / switching
//

DSPTaskInfo *__DSP_curr_task;

BOOL __DSP_rude_task_pending;
DSPTaskInfo *__DSP_rude_task;

DSPTaskInfo *__DSP_first_task;
DSPTaskInfo *__DSP_last_task;
DSPTaskInfo *__DSP_tmp_task;

void __DSPHandler(__OSInterrupt interrupt, OSContext *context)
{
    u16 tmp;
    OSContext exceptionContext;
    u32 mail;

    // clear DSP interrupt
    tmp = *(u16 *)0xCC00500A & ~0x0028;
    tmp |= 0x0080;
    *(u16 *)0xCC00500A = tmp;

    OSClearContext(&exceptionContext);
    OSSetCurrentContext(&exceptionContext);

    if(__DSP_curr_task == NULL)
    {
        OSHalt("__DSPHandler(): No current task! Someone set us up the bomb!\n");
    } 

    while(DSPCheckMailFromDSP() == 0) ;

    mail = DSPReadMailFromDSP();

    if(__DSP_curr_task->flags & DSP_TASK_FLAG_CANCEL)
    {
        if(mail == 0xDCD10002)  // ??? actually this is if((mail + 0x232F0000) == 2)
        {
            mail = 0xDCD10003;
        }
    }

    switch(mail)
    {
        case 0xDCD10000:
            __DSP_curr_task->state = DSP_TASK_STATE_RUN;

            if(__DSP_curr_task->init_cb)
            {
                __DSP_curr_task->init_cb((void *)__DSP_curr_task);
            }
            break;

        case 0xDCD10001:
            __DSP_curr_task->state = DSP_TASK_STATE_RUN;

            if(__DSP_curr_task->res_cb)
            {
                __DSP_curr_task->res_cb((void *)__DSP_curr_task);
            }
            break;

        case 0xDCD10002:
            if(__DSP_rude_task_pending == TRUE)
            {
                if(__DSP_curr_task == __DSP_rude_task)
                {
                    DSPSendMailToDSP(0xCDD10003);
                    while(DSPCheckMailToDSP() == 0) ;

                    __DSP_rude_task = NULL;
                    __DSP_rude_task_pending = FALSE;

                    if(__DSP_curr_task->res_cb)
                    {
                        __DSP_curr_task->res_cb((void *)__DSP_curr_task);
                    }
                }
                else
                {
                    DSPSendMailToDSP(0xCDD10001);
                    while(DSPCheckMailToDSP() == 0) ;                    

                    __DSP_exec_task(__DSP_curr_task, __DSP_rude_task);
                    __DSP_curr_task->state = DSP_TASK_STATE_YIELD;
                    __DSP_curr_task = __DSP_rude_task;

                    __DSP_rude_task = NULL;
                    __DSP_rude_task_pending = FALSE;
                }

                break;
            }

            if(__DSP_curr_task->next == NULL)
            {
                if(__DSP_curr_task == __DSP_first_task)
                {
                    DSPSendMailToDSP(0xCDD10003);
                    while(DSPCheckMailToDSP() == 0) ;

                    if(__DSP_curr_task->res_cb)
                    {
                        __DSP_curr_task->res_cb(__DSP_curr_task);
                    }
                }
                else
                {
                    DSPSendMailToDSP(0xCDD10001);
                    while(DSPCheckMailToDSP() == 0) ;

                    __DSP_exec_task(__DSP_curr_task, __DSP_first_task);
                    __DSP_curr_task->state = DSP_TASK_STATE_YIELD;
                    __DSP_curr_task = __DSP_first_task;
                }
            }
            else
            {
                DSPSendMailToDSP(0xCDD10001);
                while(DSPCheckMailToDSP() == 0) ;

                __DSP_exec_task(__DSP_curr_task, __DSP_curr_task->next);
                __DSP_curr_task->state = DSP_TASK_STATE_YIELD;
                __DSP_curr_task = __DSP_curr_task->next;
            }
            break;

        case 0xDCD10003:
            if(__DSP_rude_task_pending == TRUE)
            {
                if(__DSP_curr_task->done_cb)
                {
                    __DSP_curr_task->done_cb((void *)__DSP_curr_task);
                }

                DSPSendMailToDSP(0xCDD10001);
                while(DSPCheckMailToDSP() == 0) ;

                __DSP_exec_task(NULL, __DSP_rude_task);
                __DSP_remove_task(__DSP_curr_task);
                __DSP_curr_task = __DSP_rude_task;

                __DSP_rude_task = NULL;
                __DSP_rude_task_pending = FALSE;

                break;
            }

            if(__DSP_curr_task->next == NULL)
            {
                if(__DSP_curr_task == __DSP_first_task)
                {
                    if(__DSP_curr_task->done_cb)
                    {
                        __DSP_curr_task->done_cb((void *)__DSP_curr_task);
                    }

                    DSPSendMailToDSP(0xCDD10002);
                    while(DSPCheckMailToDSP() == 0) ;

                    __DSP_curr_task->state = DSP_TASK_STATE_DONE;
                    __DSP_remove_task(__DSP_curr_task);
                }
                else
                {
                    if(__DSP_curr_task->done_cb)
                    {
                        __DSP_curr_task->done_cb((void *)__DSP_curr_task);
                    }

                    DSPSendMailToDSP(0xCDD10001);
                    while(DSPCheckMailToDSP() == 0) ;

                    __DSP_curr_task->state = DSP_TASK_STATE_DONE;

                    __DSP_exec_task(NULL, __DSP_first_task);
                    __DSP_curr_task = __DSP_first_task;
                    __DSP_remove_task(__DSP_last_task);
                }
            }
            else
            {
                if(__DSP_curr_task->done_cb)
                {
                    __DSP_curr_task->done_cb((void *)__DSP_curr_task);
                }

                DSPSendMailToDSP(0xCDD10001);
                while(DSPCheckMailToDSP() == 0) ;

                __DSP_curr_task->state = DSP_TASK_STATE_DONE;

                __DSP_exec_task(NULL, __DSP_curr_task->next);
                __DSP_curr_task = __DSP_curr_task->next;
                __DSP_remove_task(__DSP_curr_task->prev);
            }
            break;

        case 0xDCD10004:
            if(__DSP_curr_task->req_cb)
            {
                __DSP_curr_task->req_cb((void *)__DSP_curr_task);
            }
            break;

        default:
            OSHalt("__DSPHandler(): Unknown msg from DSP 0x%08X - task sync failed!\n", mail);
    }

    OSClearContext(&exceptionContext);
    OSSetCurrentContext(context);
}

void __DSP_exec_task(DSPTaskInfo *curr, DSPTaskInfo *next)
{
    if(next == NULL)
    {
        OSHalt("__DSP_exec_task(): NULL task. It is to weep.\n");
    }

    if(curr)
    {
        DSPSendMailToDSP(curr->dram_mmem_addr);
        while(DSPCheckMailToDSP() == 0) ;

        DSPSendMailToDSP(curr->dram_length);
        while(DSPCheckMailToDSP() == 0) ;

        DSPSendMailToDSP(curr->dram_addr);
        while(DSPCheckMailToDSP() == 0) ;
    }
    else
    {
        DSPSendMailToDSP(0);
        while(DSPCheckMailToDSP() == 0) ;

        DSPSendMailToDSP(0);
        while(DSPCheckMailToDSP() == 0) ;

        DSPSendMailToDSP(0);
        while(DSPCheckMailToDSP() == 0) ;
    }

    DSPSendMailToDSP(next->iram_mmem_addr);
    while(DSPCheckMailToDSP() == 0) ;

    DSPSendMailToDSP(next->iram_length);
    while(DSPCheckMailToDSP() == 0) ;

    DSPSendMailToDSP(next->iram_addr);
    while(DSPCheckMailToDSP() == 0) ;

    if(next->state == DSP_TASK_STATE_INIT)
    {
        DSPSendMailToDSP(next->dsp_init_vector);
        while(DSPCheckMailToDSP() == 0) ;

        DSPSendMailToDSP(0);
        while(DSPCheckMailToDSP() == 0) ;

        DSPSendMailToDSP(0);
        while(DSPCheckMailToDSP() == 0) ;

        DSPSendMailToDSP(0);
        while(DSPCheckMailToDSP() == 0) ;
    }
    else
    {
        DSPSendMailToDSP(next->dsp_resume_vector);
        while(DSPCheckMailToDSP() == 0) ;

        DSPSendMailToDSP(next->dram_mmem_addr);
        while(DSPCheckMailToDSP() == 0) ;

        DSPSendMailToDSP(next->dram_length);
        while(DSPCheckMailToDSP() == 0) ;

        DSPSendMailToDSP(next->dram_addr);
        while(DSPCheckMailToDSP() == 0) ;
    }
}

void __DSP_boot_task(DSPTaskInfo *task)
{
    u32 mail;

    if(task == NULL)
    {
        OSHalt("__DSP_boot_task(): NULL task!\n");
    }

    while(DSPCheckMailFromDSP() == 0) ;

    mail = DSPReadMailFromDSP();

    if(mail == (addis r0,mail,7f8f, cmplwi r0,feed))
    {
        OSHalt("__DSP_boot_task(): Failed to sync DSP on boot! (0x%08X)\n", mail);
    }

    DSPSendMailToDSP(0x80F4A001);
    while(DSPCheckMailToDSP() == 0) ;
    DSPSendMailToDSP(task->iram_mmem_addr);
    while(DSPCheckMailToDSP() == 0) ;

    DSPSendMailToDSP(0x80F4C002);
    while(DSPCheckMailToDSP() == 0) ;
    DSPSendMailToDSP(task->iram_addr);
    while(DSPCheckMailToDSP() == 0) ;

    DSPSendMailToDSP(0x80F4A002);
    while(DSPCheckMailToDSP() == 0) ;
    DSPSendMailToDSP(task->iram_length);
    while(DSPCheckMailToDSP() == 0) ;

    DSPSendMailToDSP(0x80F4B002);
    while(DSPCheckMailToDSP() == 0) ;
    DSPSendMailToDSP(0);
    while(DSPCheckMailToDSP() == 0) ;

    DSPSendMailToDSP(0x80F4D001);
    while(DSPCheckMailToDSP() == 0) ;
    DSPSendMailToDSP(task->dsp_init_vector);
    while(DSPCheckMailToDSP() == 0) ;

#ifdef DEBUG
    __DSP_debug_printf("DSP is booting task: 0x%08X\n", task);

    __DSP_debug_printf("__DSP_boot_task()  : IRAM MMEM ADDR: 0x%08X\n", task->iram_mmem_addr);
    __DSP_debug_printf("__DSP_boot_task()  : IRAM DSP ADDR : 0x%08X\n", task->iram_addr);
    __DSP_debug_printf("__DSP_boot_task()  : IRAM LENGTH   : 0x%08X\n", task->iram_length);
    __DSP_debug_printf("__DSP_boot_task()  : DRAM MMEM ADDR: 0x%08X\n", task->dram_mmem_addr);
    __DSP_debug_printf("__DSP_boot_task()  : Start Vector  : 0x%08X\n", task->dsp_init_vector);
#endif    
}

void __DSP_insert_task(DSPTaskInfo *task)
{
    if(__DSP_first_task == NULL)
    {
        __DSP_first_task =
        __DSP_last_task  =
        __DSP_curr_task  = task;

        task->next = task->prev = NULL; 
    }
    else
    {
        DSPTaskInfo *temp;

        for(temp=__DSP_first_task; temp; temp=temp->next)
        {
            if(DSPGetTaskPriority(task) < DSPGetTaskPriority(temp))
            {
                task->prev = temp->prev;
                temp->prev = task;
                task->next = temp;
                
                if(task->prev == NULL) __DSP_first_task = task;
                else task->prev->next = task;

                break;
            }
        }

        if(temp == NULL)
        {
            __DSP_last_task->next = task;
            task->next = NULL;
            task->prev = __DSP_last_task;
            __DSP_last_task = task;
        }
    }
}

__DSP_add_task, __DSP_remove_task : later, too simple
