/**************************************************************************
 *                                                                        *
 *               Copyright (C) 1995, Silicon Graphics, Inc.               *
 *                                                                        *
 *  These coded instructions, statements, and computer programs  contain  *
 *  unpublished  proprietary  information of Silicon Graphics, Inc., and  *
 *  are protected by Federal copyright  law.  They  may not be disclosed  *
 *  to  third  parties  or copied or duplicated in any form, in whole or  *
 *  in part, without the prior written consent of Silicon Graphics, Inc.  *
 *                                                                        *
 *************************************************************************/

/*---------------------------------------------------------------------*
        Copyright (C) 1997,1998 Nintendo. (Originated by SGI)
        
        $RCSfile: overlay.c,v $
        $Revision: 1.14 $
        $Date: 1999/01/07 14:14:05 $
 *---------------------------------------------------------------------*/

/*
 * File:	overlay.c
 * Create Date:	Mon Apr 17 11:45:57 PDT 1995
 *
 * Simple example using overlays
 *
 */

#include <PR/ramrom.h>	/* needed for argument passing into the app */

#include "overlay.h"
#include "assert.h"
/*
 * Symbols generated by "makerom" to tell us where the "plain" segment is
 * in rom and in the CPU address space.
 */
extern char _plainSegmentRomStart[], _plainSegmentRomEnd[];
extern char _plainSegmentStart[];
extern char _plainSegmentTextStart[], _plainSegmentTextEnd[];
extern char _plainSegmentDataStart[], _plainSegmentDataEnd[];
extern char _plainSegmentBssStart[], _plainSegmentBssEnd[];

/*
 * And where the "texture" segment is.
 */
extern char _textureSegmentRomStart[], _textureSegmentRomEnd[];
extern char _textureSegmentStart[];
extern char _textureSegmentTextStart[], _textureSegmentTextEnd[];
extern char _textureSegmentDataStart[], _textureSegmentDataEnd[];
extern char _textureSegmentBssStart[], _textureSegmentBssEnd[];

/*
 * Stacks for the threads as well as message queues for synchronization
 * This stack is ridiculously large, and could also be reclaimed once
 * the main thread is started.
 */
u64	bootStack[STACKSIZE/sizeof(u64)];

static void	idle(void *);
static void	mainproc(void *);

static OSThread	idleThread;
static u64	idleThreadStack[STACKSIZE/sizeof(u64)];

static OSThread	mainThread;
static u64	mainThreadStack[STACKSIZE/sizeof(u64)];

/* this number (the depth of the message queue) needs to be equal
 * to the maximum number of possible overlapping PI requests.
 * For this app, 1 or 2 is probably plenty, other apps might
 * require a lot more.
 */
#define NUM_PI_MSGS     8

static OSMesg PiMessages[NUM_PI_MSGS];
static OSMesgQueue PiMessageQ;

static OSMesgQueue	dmaMessageQ, rdpMessageQ, retraceMessageQ;
static OSMesg		dmaMessageBuf, rdpMessageBuf, retraceMessageBuf;
static OSIoMesg	dmaIOMessageBuf;	/* see man page to understand this */

/*
 * Task descriptor.
 */
OSTask	tlist =
{
    M_GFXTASK,			/* task type */
    OS_TASK_DP_WAIT,		/* task flags */
    NULL,			/* boot ucode pointer (fill in later) */
    0,				/* boot ucode size (fill in later) */
    NULL,			/* task ucode pointer (fill in later) */
    SP_UCODE_SIZE,		/* task ucode size */
    NULL,			/* task ucode data pointer (fill in later) */
    SP_UCODE_DATA_SIZE,		/* task ucode data size */
    &dram_stack[0],		/* task dram stack pointer */
    SP_DRAM_STACK_SIZE8,	/* task dram stack size */
    NULL,			/* task output buffer ptr (not used) */
    NULL,			/* task output buffer size ptr */
    NULL,			/* task data pointer (fill in later) */
    0,				/* task data size (fill in later) */
    NULL,			/* task yield buffer ptr (not used here) */
    0				/* task yield buffer size (not used here) */
};

/*
 * global variables
 */
static int	do_texture = 0;
static int      draw_buffer = 0;

OSPiHandle	*handler;

void
boot(void)
{
    /* notice that you can't call osSyncPrintf() until you 
     * have an idle thread running in the background.
     */
    
    osInitialize();

    handler = osCartRomInit();

    osCreateThread(&idleThread, 1, idle, (void *)0,
		   idleThreadStack+STACKSIZE/sizeof(u64), 10);
    osStartThread(&idleThread);

    /* never reached */
}

static void
idle(void *arg)
{
    /* Initialize video */
    osCreateViManager(OS_PRIORITY_VIMGR);
    osViSetMode(&osViModeTable[OS_VI_NTSC_LAN1]);
    
    /*
     * Start PI Mgr for access to cartridge
     */
    osCreatePiManager((OSPri)OS_PRIORITY_PIMGR, &PiMessageQ, PiMessages, 
		      NUM_PI_MSGS);
    
    /*
     * Create main thread
     */
    osCreateThread(&mainThread, 3, mainproc, arg,
		   mainThreadStack+STACKSIZE/sizeof(u64), 10);
    osStartThread(&mainThread);

    /*
     * Become the idle thread
     */
    osSetThreadPri(0, 0);

    for (;;);
}

/*
 * This is the main routine of the app.
 */
static void
mainproc(void *arg)
{
    OSTask		*tlistp = &tlist;
#ifdef DEBUG
    int i;
    char *ap;
    u32 *argp;
    u32 argbuf[16];
#endif

#ifdef DEBUG
    argp = (u32 *)RAMROM_APP_WRITE_ADDR;
    for (i=0; i<sizeof(argbuf)/4; i++, argp++) {
	osEPiReadIo(handler, (u32)argp, &argbuf[i]); /* Assume no DMA */
    }
    /* Parse the options */
    ap = (char *)argbuf;
    while (*ap != '\0') {
	while (*ap == ' ')
	    ap++;
	if ( *ap == '-' && *(ap+1) == 't') {
	    do_texture = 1;
	    ap += 2;
	}
	else ap++;
    }
#endif

    /*
     * Setup the message queues
     */
    osCreateMesgQueue(&dmaMessageQ, &dmaMessageBuf, 1);
    
    osCreateMesgQueue(&rdpMessageQ, &rdpMessageBuf, 1);
    osSetEventMesg(OS_EVENT_DP, &rdpMessageQ, NULL);
    
    osCreateMesgQueue(&retraceMessageQ, &retraceMessageBuf, 1);
    osViSetEvent(&retraceMessageQ, NULL, 1);

    /*
     * Bring in segment to do textured or plain square depending on
     * argument.  Be sure to invalidate the data cache BEFORE the
     * dma operation.  Otherwise we might accidentally write back some
     * dirty data to memory after the DMA is complete.
     */
    if (do_texture) {
	osInvalICache(_textureSegmentTextStart,
	    _textureSegmentTextEnd-_textureSegmentTextStart);
	osInvalDCache(_textureSegmentDataStart,
	    _textureSegmentDataEnd-_textureSegmentDataStart);

	dmaIOMessageBuf.hdr.pri      = OS_MESG_PRI_NORMAL;
	dmaIOMessageBuf.hdr.retQueue = &dmaMessageQ;
	dmaIOMessageBuf.dramAddr     = _textureSegmentStart;
	dmaIOMessageBuf.devAddr      = (u32)_textureSegmentRomStart;
	dmaIOMessageBuf.size         = (u32)_textureSegmentRomEnd-(u32)_textureSegmentRomStart;

	osEPiStartDma(handler, &dmaIOMessageBuf, OS_READ);

	bzero(_textureSegmentBssStart,
	      _textureSegmentBssEnd-_textureSegmentBssStart);
    } else {
	osInvalICache(_plainSegmentTextStart,
	    _plainSegmentTextEnd-_plainSegmentTextStart);
	osInvalDCache(_plainSegmentDataStart,
	    _plainSegmentDataEnd-_plainSegmentDataStart);

	dmaIOMessageBuf.hdr.pri      = OS_MESG_PRI_NORMAL;
	dmaIOMessageBuf.hdr.retQueue = &dmaMessageQ;
	dmaIOMessageBuf.dramAddr     = _plainSegmentStart;
	dmaIOMessageBuf.devAddr      = (u32)_plainSegmentRomStart;
	dmaIOMessageBuf.size         = (u32)_plainSegmentRomEnd-(u32)_plainSegmentRomStart;

	osEPiStartDma(handler, &dmaIOMessageBuf, OS_READ);

	bzero(_plainSegmentBssStart,
	      _plainSegmentBssEnd-_plainSegmentBssStart);
    }
    (void)osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK);

    /*
     * Main game loop
     */
    for (;;) {

	/*
	 * The same routine name is used in each overlay.  This
	 * only works since "drawSquare" is the first procedure in each
	 * segment, and thus resolves to the same address.
	 */
#ifdef __MWERKS__
	if (do_texture) {
	    drawSquare_Texture(tlistp, draw_buffer);
	} else {
	    drawSquare_Plain(tlistp, draw_buffer);
	}
#else
	drawSquare(tlistp, draw_buffer);

#endif

	osSpTaskStart(tlistp);
	
	/* wait for RDP completion */
	(void)osRecvMesg(&rdpMessageQ, NULL, OS_MESG_BLOCK);

	/* setup to swap buffers */
	osViSwapBuffer(cfb[draw_buffer]);

	/* Make sure there isn't an old retrace in queue 
	 * (assumes queue has a depth of 1) 
	 */
	if (MQ_IS_FULL(&retraceMessageQ))
	    (void)osRecvMesg(&retraceMessageQ, NULL, OS_MESG_BLOCK);
	
	/* Wait for Vertical retrace to finish swap buffers */
	(void)osRecvMesg(&retraceMessageQ, NULL, OS_MESG_BLOCK);

	draw_buffer ^= 1;
    }
}

/*
 * Dummy function ( for linked only ).
 */
static void dummy_func(void)
{
  Mtx m;
  assert( 0 );
  osWritebackDCache((void *)NULL, 0);
  guOrtho(&m, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
  guRotate(&m, 0.0, 0.0, 0.0, 0.0);
}
