
/**************************************************************************
 *                                                                        *
 *               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: teapot.c,v $
        $Revision: 1.21 $
        $Date: 1999/01/11 13:36:52 $
 *---------------------------------------------------------------------*/

/*
 * File:	teapot.c
 * Creator:	hsa@sgi.com
 * Create Date:	Sun Jun  4 18:40:31 PDT 1995
 *
 * VERY simple app, draws a teapot, demonstrating simple lighting and
 * texturing.
 *
 */

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

#include "teapot.h"
#include "controller.h"

/*
 * Symbol genererated by "makerom" to indicate the end of the code segment
 * in virtual (and physical) memory
 */
extern char _codeSegmentEnd[];

/*
 * Symbols generated by "makerom" to tell us where the static segment is
 * in ROM.
 */
extern char _staticSegmentRomStart[], _staticSegmentRomEnd[];

/*
 * Stacks for the threads as well as message queues for synchronization
 */
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;

OSMesgQueue	dmaMessageQ, rdpMessageQ, retraceMessageQ;
OSMesg		dmaMessageBuf, rdpMessageBuf, retraceMessageBuf;
OSMesg		dummyMessage;
OSIoMesg	dmaIOMessageBuf;

/*
 * Dynamic segment in code space. Needs OS_K0_TO_PHYSICAL()...
 */
Dynamic dynamic;

/*
 * must be in BSS, not on the stack for this to work:
 */
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 */
    &rdp_output[0],		   /* task fifo buffer start ptr */
    &rdp_output[0]+RDP_OUTPUT_LEN, /* task fifo buffer end 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) */
};

Gfx		*glistp;	/* global for test case procs */

/*
 * global variables for arguments, to control test cases
 */
static int	rdp_flag = 0;	/* 0:xbus , 1:fifo */
static int      draw_buffer = 0;
static void	*cfb_ptrs[2];
static float	theta = 0.0;
static u64	ramrombuf[RAMROM_MSG_SIZE/sizeof(u64)];
    
static void	draw_teapot(Dynamic *);
static void	draw_floor(Dynamic *);
static void	draw_shadow(Dynamic *);

int	controllerSlot;
float	shadowScaleX = 2.7;
float	shadowScaleZ = 2.7;
float	shadowTranslateX = -40.0;
float	shadowTranslateZ =  50.0;

OSPiHandle	*handler;

boot(void)
{


    /* notice that you can't call osSyncPrintf() until you set
     * up an idle thread.
     */
    
    osInitialize();

    handler = osCartRomInit();

    
    osCreateThread(&idleThread, 1, idle, NULL,
		   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]);
    osViSetSpecialFeatures(OS_VI_GAMMA_ON|OS_VI_GAMMA_DITHER_ON|OS_VI_DIVOT_ON|OS_VI_DITHER_FILTER_ON);
    
    /*
     * Start PI Mgr for access to cartridge
     */
    osCreatePiManager((OSPri)OS_PRIORITY_PIMGR, &PiMessageQ, PiMessages, 
		      NUM_PI_MSGS);
    
    /*
     * Create main thread
     */
    osCreateThread(&mainThread, 3, mainproc, NULL ,
		   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)
{
    int		i, j;
    OSTask	*tlistp;
    Dynamic	*dynamicp;
    char	*staticSegment;
    u32		clearcolor;
    u16		perspNorm;

#ifdef DEBUG
    int  *pr;
    char *ap;
    u32 *argp;
    u32 argbuf[16];

    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) == 'r') {
	    rdp_flag = 1;
	    ap += 2;
	} else ap++;
    }
#endif
    
    cfb_ptrs[0] = &(cfb_16_a[0]);
    cfb_ptrs[1] = &(cfb_16_b[0]);

    /*
     * Setup the message queues
     */
    osCreateMesgQueue(&dmaMessageQ, &dmaMessageBuf, 1);
    
    osCreateMesgQueue(&rdpMessageQ, &rdpMessageBuf, 1);
    osSetEventMesg(OS_EVENT_DP, &rdpMessageQ, dummyMessage);
    
    osCreateMesgQueue(&retraceMessageQ, &retraceMessageBuf, 1);
    osViSetEvent(&retraceMessageQ, dummyMessage, 1);
    
    /*
     * Stick the static segment right after the code/data segment
     */
    staticSegment = _codeSegmentEnd;

    dmaIOMessageBuf.hdr.pri      = OS_MESG_PRI_NORMAL;
    dmaIOMessageBuf.hdr.retQueue = &dmaMessageQ;
    dmaIOMessageBuf.dramAddr     = staticSegment;
    dmaIOMessageBuf.devAddr      = (u32)_staticSegmentRomStart;
    dmaIOMessageBuf.size         = _staticSegmentRomEnd-_staticSegmentRomStart;

    osEPiStartDma(handler, &dmaIOMessageBuf, OS_READ);
    
    /*
     * Wait for DMA to finish
     */
    (void)osRecvMesg(&dmaMessageQ, &dummyMessage, OS_MESG_BLOCK);
    
    /*
     * Initialize controller
     */
    controllerSlot = initControllers();

    /*
     * Main game loop
     */
    while (1) {
	
	/*
	 * Read controller
	 */
	readController(controllerSlot);

	/*
	 * pointers to build the display list.
	 */
	tlistp = &tlist;
	dynamicp = &dynamic;
	glistp = &(dynamicp->glist[0]);
	
	/*
	 * Tell RCP where each segment is
	 */
	gSPSegment(glistp++, 0, 0x0);	/* K0 (physical) address segment */
	gSPSegment(glistp++, STATIC_SEGMENT,
		   osVirtualToPhysical(staticSegment));
	
	gSPDisplayList(glistp++, rdpinit_dl);
	gSPDisplayList(glistp++, rspinit_dl);
	gDPSetDepthImage(glistp++, OS_K0_TO_PHYSICAL(zbuffer));
	
	/*
	 * clear z, z = max z, dz = 0
	 */
	gDPSetCycleType(glistp++, G_CYC_FILL);
	gDPSetColorImage(glistp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WD,
			 OS_K0_TO_PHYSICAL(zbuffer));
	gDPSetFillColor(glistp++, 
			GPACK_ZDZ(G_MAXFBZ,0) << 16 | GPACK_ZDZ(G_MAXFBZ,0));
	gDPFillRectangle(glistp++, 0, 0, SCREEN_WD-1, SCREEN_HT-1);
	
	
	/*
	 * Clear framebuffer
	 * doing this as a static display list is a little messier, due
	 * to cfb pointer conversion...
	 */
	gDPPipeSync(glistp++);
	gDPSetCycleType(glistp++, G_CYC_FILL);
	/*
	 * clear color, cvg = FULL or 1
	 */
	gDPSetColorImage(glistp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WD, 
			 OS_K0_TO_PHYSICAL(cfb_ptrs[draw_buffer]));
	clearcolor = ((GPACK_RGBA5551(0, 0, 0, 1) << 16) |
		      (GPACK_RGBA5551(0, 0, 0, 1)));
	gDPSetFillColor(glistp++, clearcolor);
	gDPFillRectangle(glistp++, 0, 0, SCREEN_WD-1, SCREEN_HT-1);
	gDPPipeSync(glistp++);
	gDPSetCycleType(glistp++, G_CYC_1CYCLE);
	
	/* end of standard display list part. */
	
	
	/*
	 * Viewing parameters are common for all objects in this frame:
	 */
	guPerspective(&dynamicp->projection, &perspNorm,
		      33, 320.0/240.0, 100, 2000, 1.0);
#ifdef USE_HIGHLIGHT
	guLookAtHilite(&dynamicp->viewing, 
		       &(dynamicp->lookat[0]), &(dynamicp->hilite[0]),
		       /*50, 100, 400,*/
		       50, 170, 400,
		       0, 0, 0,
		       0, 1, 0,
		       32.0, 64.0, 0.0, 	/* light 1 direction */
		       -50.0, 50.0, 0.0,	/* light 2 direction */
		       32, 32);	/* texture width */
#else
	guLookAt(&dynamicp->viewing, 
		 50, 100, 400,
		 0, 0, 0,
		 0, 1, 0);
#endif
	
	
	gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->projection)),
		  G_MTX_PROJECTION|G_MTX_LOAD|G_MTX_NOPUSH);
	gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->viewing)),
		  G_MTX_PROJECTION|G_MTX_MUL|G_MTX_NOPUSH);
	gSPPerspNormalize(glistp++, perspNorm);
	
	draw_floor(dynamicp);

	draw_shadow(dynamicp);

	draw_teapot(dynamicp);

	/* all done with this frame. */
	gDPFullSync(glistp++);
	gSPEndDisplayList(glistp++);
	
	
	/* 
	 * Build graphics task:
	 *
	 */
	tlistp->t.ucode_boot = (u64 *) rspbootTextStart;
	tlistp->t.ucode_boot_size = ((int)rspbootTextEnd - 
				     (int)rspbootTextStart);

	/*
	 * choose which ucode to run:
	 */
	if (rdp_flag) {
	    /* RSP output over FIFO to RDP: */
		tlistp->t.ucode = (u64 *) gspF3DEX2_fifoTextStart;
		tlistp->t.ucode_data = (u64 *) gspF3DEX2_fifoDataStart; 
	} else {
	    /* RSP output over XBUS to RDP: */
		tlistp->t.ucode = (u64 *) gspF3DEX2_xbusTextStart;
		tlistp->t.ucode_data = (u64 *) gspF3DEX2_xbusDataStart;
	}
	
	/* initial display list: */
	tlistp->t.data_ptr = (u64 *) dynamicp->glist;
	tlistp->t.data_size = ((int)(glistp - dynamicp->glist) *
			       sizeof (Gfx));

	/*
	 * Can just flush 16KB and forget about each individual pieces
	 * of data to flush.
	 */
	osWritebackDCacheAll();
	
	/* unwrap this macro for debugging:
	 *
	 *	osSpTaskStart(tlistp);
	 * becomes:
	 *	osSpTaskLoad(tlistp);
	 *	osSpTaskStartGo(tlistp);
	 */
	osSpTaskStart(tlistp);
	
	(void)osRecvMesg(&rdpMessageQ, &dummyMessage, OS_MESG_BLOCK);

	/* setup to swap buffers */
	osViSwapBuffer(cfb_ptrs[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, &dummyMessage, OS_MESG_BLOCK);
	
	/* Wait for Vertical retrace to finish swap buffers */
	(void)osRecvMesg(&retraceMessageQ, &dummyMessage, OS_MESG_BLOCK);
	draw_buffer ^= 1;

	theta += 1.0;

    }
}


static void
draw_teapot(Dynamic *dynamicp)
{
#ifdef USE_HIGHLIGHT
    gSPLookAt(glistp++, &(dynamicp->lookat[0]));
#endif

    guRotate(&dynamicp->teapot_rotate, theta, 0.0, 1.0, 0.0);
    guTranslate(&dynamicp->teapot_trans, 0.0, 50.0, 0.0);
    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->teapot_trans)),
	      G_MTX_MODELVIEW|G_MTX_LOAD|G_MTX_NOPUSH);
    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->teapot_rotate)),
	      G_MTX_MODELVIEW|G_MTX_MUL|G_MTX_NOPUSH);
    gSPDisplayList(glistp++, teapot_setup_dl);
#ifdef USE_HIGHLIGHT
    gDPSetHilite1Tile(glistp++,G_TX_RENDERTILE  ,&(dynamicp->hilite[0]), 32,32);
    gDPSetHilite2Tile(glistp++,G_TX_RENDERTILE+1,&(dynamicp->hilite[0]), 32,32);
#endif
    gSPDisplayList(glistp++, teapot_geom_dl);


}

static void
draw_shadow(Dynamic *dynamicp)
{
    guScale(&dynamicp->shadow_scale, shadowScaleX, 0.01, shadowScaleZ);
    guRotate(&dynamicp->shadow_rotate, theta, 0.0, 1.0, 0.0);
    guTranslate(&dynamicp->shadow_trans, shadowTranslateX, 2.0, shadowTranslateZ);

    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->shadow_trans)),
	      G_MTX_MODELVIEW|G_MTX_LOAD|G_MTX_NOPUSH);
    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->shadow_scale)),
	      G_MTX_MODELVIEW|G_MTX_MUL|G_MTX_NOPUSH);
    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->shadow_rotate)),
	      G_MTX_MODELVIEW|G_MTX_MUL|G_MTX_NOPUSH);

    gSPDisplayList(glistp++, shadow_setup_dl);

    gSPDisplayList(glistp++, shadow_geom_dl);
}

static void
draw_floor(Dynamic *dynamicp)
{
    gSPDisplayList(glistp++, floor_setup_dl);

    guTranslate(&dynamicp->floor_model0, -128.0, 0.0, -128.0);
    guTranslate(&dynamicp->floor_model1, 0.0, 0.0, -128.0);
    guTranslate(&dynamicp->floor_model2, 128.0, 0.0, -128.0);
    guTranslate(&dynamicp->floor_model3, -128.0, 0.0, 0.0);
    guTranslate(&dynamicp->floor_model4, 0.0, 0.0, 0.0);
    guTranslate(&dynamicp->floor_model5, 128.0, 0.0, 0.0);
    guTranslate(&dynamicp->floor_model6, -128.0, 0.0, 128.0);
    guTranslate(&dynamicp->floor_model7, 0.0, 0.0, 128.0);
    guTranslate(&dynamicp->floor_model8, 128.0, 0.0, 128.0);

    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->floor_model0)),
	      G_MTX_MODELVIEW|G_MTX_LOAD|G_MTX_NOPUSH);
    gSPDisplayList(glistp++, floor_geom_dl);

    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->floor_model1)),
	      G_MTX_MODELVIEW|G_MTX_LOAD|G_MTX_NOPUSH);
    gSPDisplayList(glistp++, floor_geom_dl);

    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->floor_model2)),
	      G_MTX_MODELVIEW|G_MTX_LOAD|G_MTX_NOPUSH);
    gSPDisplayList(glistp++, floor_geom_dl);

    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->floor_model3)),
	      G_MTX_MODELVIEW|G_MTX_LOAD|G_MTX_NOPUSH);
    gSPDisplayList(glistp++, floor_geom_dl);

    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->floor_model4)),
	      G_MTX_MODELVIEW|G_MTX_LOAD|G_MTX_NOPUSH);
    gSPDisplayList(glistp++, floor_geom_dl);

    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->floor_model5)),
	      G_MTX_MODELVIEW|G_MTX_LOAD|G_MTX_NOPUSH);
    gSPDisplayList(glistp++, floor_geom_dl);

    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->floor_model6)),
	      G_MTX_MODELVIEW|G_MTX_LOAD|G_MTX_NOPUSH);
    gSPDisplayList(glistp++, floor_geom_dl);

    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->floor_model7)),
	      G_MTX_MODELVIEW|G_MTX_LOAD|G_MTX_NOPUSH);
    gSPDisplayList(glistp++, floor_geom_dl);

    gSPMatrix(glistp++, OS_K0_TO_PHYSICAL(&(dynamicp->floor_model8)),
	      G_MTX_MODELVIEW|G_MTX_LOAD|G_MTX_NOPUSH);
    gSPDisplayList(glistp++, floor_geom_dl);

    gSPTexture(glistp++, 0, 0, 0, G_TX_RENDERTILE, G_OFF); /* done; turn texture off */
}
