uimain.c 7.67 KB

#include <ultra64.h>
#include <PR/sched.h>
#include <PR/libaudio.h>

#include "uimain.h"
#include "audrenderer.h"
#include "gfxrenderer.h"

/**** threads used by this file ****/
static OSThread gameThread;
static OSThread initThread;

/**** Stack for boot code.  Space can be reused after 1st thread starts ****/
u64    bootStack[STACKSIZEBYTES/sizeof(u64)];

/**** Stacks for the threads, divide by 8 which is the size of a u64 ****/
static u64      gameThreadStack[STACKSIZEBYTES/sizeof(u64)];
static u64      initThreadStack[STACKSIZEBYTES/sizeof(u64)];

/**** function prototypes for private functions in this file ****/
static void     gameproc(void *);
static void     initproc(char *);
static void     initGame(void);
static void initCntrl(void);
static u16 readControllerEvent(void);


/**** message queues and message buffers used by this app ****/
static OSMesg           PiMessages[DMA_QUEUE_SIZE];
static OSMesgQueue      PiMessageQ;
static OSMesgQueue      dmaMessageQ;
static OSMesg           dmaMessageBuf;

/**** application message queue ****/
OSMesgQueue     appMsgQ;
OSMesg          appMsgBuf[MAX_MESGS];
OSScClient      appClient;

/**** Scheduler globals ****/
OSSched         sc;
u64             scheduleStack[OS_SC_STACKSIZE/8];

/**** variables used by this file for handling the controllers ****/
static u8     validcontrollers = 0; 
static OSContStatus  statusdata[MAXCONTROLLERS];
static OSContPad     controllerdata[MAXCONTROLLERS];
static OSScMsg       controllermsg;
static u16           lastBut = 0;

static OSPiHandle *handler;

/*
 * Graphics renderer message queue
 */
static OSMesgQueue *gfxRenderQueue;

/*
 * Sound player
 */
static ALSndPlayer soundPlayer;
static ALSndId *sndId;
static int numSounds;

/*
 * UI Pages
 */
extern int startInit(void);
extern int startPage(u16 cntrl, Sprite **sp);
extern int listInit(void);
extern int listPage(u16 cntrl, Sprite **sp);
static int currentPage = 0;
typedef int (*PageHandler)(u16, Sprite **);
typedef int (*PageInit)(void);

#define NUM_PAGES 2
static PageInit pageInits[NUM_PAGES] = {listInit, startInit};
static PageHandler pageHandlers[NUM_PAGES] = {startPage, listPage};

void boot(void *arg)
{
    
    osInitialize();

    handler = osCartRomInit();
    
    osCreateThread(&initThread, 1, (void(*)(void *))initproc, arg,
                  (void *)(initThreadStack+(STACKSIZEBYTES/sizeof(u64))), 
		   (OSPri)INIT_PRIORITY);
    osStartThread(&initThread);
}

/**********************************************************************
 *
 * initproc sets up the PI manager. It then creates
 * and starts our application's game thread. After returning from that,
 * it becomes the lowest priority thread, and functions as the idle thread.
 *
 **********************************************************************/
static void initproc(char *argv) 
{
    /**** Start PI Mgr for access to cartridge ****/
    osCreatePiManager((OSPri) OS_PRIORITY_PIMGR, &PiMessageQ, PiMessages,
                        DMA_QUEUE_SIZE);

    /**** Create the game thread and start it up ****/
    osCreateThread(&gameThread, 6, gameproc, NULL, gameThreadStack + 
		   (STACKSIZEBYTES/sizeof(u64)), (OSPri)GAME_PRIORITY);

    osStartThread(&gameThread);

    /**** Set the thread to be the idle thread ****/
    osSetThreadPri(0, 0);
    for(;;);
}

static void gameproc(void *argv)
{
    u32 cntrlReadInProg = 0;
    OSScMsg *msg = NULL;
    u16 cntrlEvent;
    GFXMsg m;
    Sprite *sp[10];
    static int id = 0;
    initGame();

    while (1) 
    {
        (void) osRecvMesg(&appMsgQ, (OSMesg *) &msg, OS_MESG_BLOCK);
        switch (msg->type) 
        {
        case (OS_SC_RETRACE_MSG):
            /* request latest controller information (ie poll) */
            if (validcontrollers && !cntrlReadInProg) {
                cntrlReadInProg = 1;
                osContStartReadData(&appMsgQ);
            }
            break;
                
        case APP_CONTROLLER_MSG:
            cntrlEvent = readControllerEvent();
            cntrlReadInProg = 0;
            /*
             * Make some noise
             */
	    if (cntrlEvent & CONT_A) {	
                playSound(0);
	    }
            if (cntrlEvent & CONT_B) {
                playSound(1);
            }

            /*
             * Call the current page to create sprites
             */
            currentPage = pageHandlers[currentPage](cntrlEvent, &sp[0]);
            m.gfx.type = GFX_NEW_FRAME;
            m.gfx.spList = &sp[0];
            osSendMesg(gfxRenderQueue, &m, OS_MESG_BLOCK);
            break;
            
        case (OS_SC_PRE_NMI_MSG): /* stop creation of audio and graphics */;
            break;
        }
    }
}

/**********************************************************************
 *
 * initGame sets up the message queues used, and starts the scheduler.
 * After that call routines to init the graphics, init the controllers,
 * and init the audio.
 *
 *********************************************************************/
static void initGame(void)
{    
    int i;

    /**** set up a needed message q's ****/
    osCreateMesgQueue(&dmaMessageQ, &dmaMessageBuf, 1);
    osCreateMesgQueue(&appMsgQ, appMsgBuf, MAX_MESGS);

    /**** Initialize the RCP task scheduler ****/
    osCreateScheduler(&sc, (void *)(scheduleStack + OS_SC_STACKSIZE/8),
                      SCHEDULER_PRIORITY, OS_VI_NTSC_LAN1, NUM_FIELDS);

    /**** Add ourselves to the scheduler to receive retrace messages ****/
    osScAddClient(&sc, &appClient, &appMsgQ);  

    /**** Call the initialization routines ****/
    initCntrl();
    gfxRenderQueue = initGfxRenderer(&sc, GAME_PRIORITY);
    initAudio(&sc); 
    /*
     * Initialize all pages
     */
    for (i=0; i<NUM_PAGES; i++) {
        pageInits[i]();
    }
}

static void initCntrl(void)
{
    OSMesgQueue         serialMsgQ;
    OSMesg              serialMsg;
    s32                 i;

    osCreateMesgQueue(&serialMsgQ, &serialMsg, 1);
    osSetEventMesg(OS_EVENT_SI, &serialMsgQ, (OSMesg)1);

    if((i = osContInit(&serialMsgQ, &validcontrollers, &statusdata[0])) != 0)
#ifndef _FINALROM
        PRINTF("Failure initing controllers\n");
#else
        ;
#endif
 
    /**** Set up message and queue, for read completion notification ****/
    controllermsg.type = APP_CONTROLLER_MSG;

    osSetEventMesg(OS_EVENT_SI, &appMsgQ, (OSMesg) &controllermsg);

}

static u16 readControllerEvent(void)
{
    OSContPad   *pad;
    u16         newbutton;

    osContGetReadData(controllerdata);

    /*
     * Read from first controller only
     */
    if(validcontrollers & 1) {
        pad = &controllerdata[0];
        /* ignore controllers with errors */
        if (pad->errno == 0) {

            /* get bits that have changed, ignore button ups */
            newbutton = pad->button ^ lastBut;
            newbutton &= pad->button;
            lastBut = pad->button;
            return newbutton;
        }
    }    
    return 0;

}

void romCopy(const char *src, const char *dest, const int len)
{
    OSIoMesg dmaIoMesgBuf;
    OSMesg dummyMesg;
    
    /*
     * Always invalidate cache before dma'ing data into the buffer.
     * This is to prevent a flush of the cache in the future from 
     * potentially trashing some data that has just been dma'ed in.
     * Since you don't care if old data makes it from cache out to 
     * memory, you can use the cheaper osInvalDCache() instead of one
     * of the writeback commands
     */
    osInvalDCache((void *)dest, (s32) len);

    dmaIoMesgBuf.hdr.pri      = OS_MESG_PRI_NORMAL;
    dmaIoMesgBuf.hdr.retQueue = &dmaMessageQ;
    dmaIoMesgBuf.dramAddr     = (void*)dest;
    dmaIoMesgBuf.devAddr      = (u32)src;
    dmaIoMesgBuf.size         = (u32)len;

    osEPiStartDma(handler, &dmaIoMesgBuf, OS_READ);
    (void) osRecvMesg(&dmaMessageQ, &dummyMesg, OS_MESG_BLOCK);
}