/**************************************************************************
 *                                                                        *
 *               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: main.c,v $
        $Revision: 1.14 $
        $Date: 1999/04/16 09:47:20 $
 *---------------------------------------------------------------------*/

/* File:  main.c
 *
 *
 */

#include <ultra64.h>
#include <PR/ramrom.h>
#include <assert.h>
#include <PR/gs2dex.h>

#include "tile_rect.h"
#include "controller.h"

/*  Texture data files
 */
#include "RGBA16cv01.h"
#include "RGBA16cv02.h"
#include "RGBA16cv03.h"
#include "RGBA16cv04.h"


/* from tile_rectangle.c */
extern void tileRectangle (   Gfx **glistp,
	void *tex, u32 txlfmt, u32 txlsz,
	u32 txwd, u32 txht,
	u32 tilewd, u32 tileht,
	u32 s0, u32 t0,
	u32 s1, u32 t1,
	f32 x0, f32 y0 );

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

/* 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
 * 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;

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

/* Dynamic data.
 */
Dynamic         dynamic;


/* RSP 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
				 */
    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) 
				 */
};

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

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

/*  Circular Buffer for texture maps (90 degrees each)
 *
 *  Treat like one map with s coord of 0-1k.
 */
static u16 *TexMaps[] = {
    RGBA16cv03,
    RGBA16cv01,
    RGBA16cv04,
    RGBA16cv02
};


#ifdef TEST_PRED_MODE
static u8 dither_tex[] = {
    0x20, 0x80, 0xff, 0x80,
    0x40, 0xff, 0x80, 0xff,
    0xff, 0x80, 0x20, 0x80,
    0x80, 0x20, 0x40, 0xff,
};
#endif /* TEST_PRED_MODE */


OSPiHandle	*handler;

void
boot(void)
{
    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]);
    osViSetSpecialFeatures(OS_VI_DITHER_FILTER_ON | OS_VI_DIVOT_OFF | OS_VI_GAMMA_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)
{
    OSTask *tlistp;
    Dynamic *dynamicp;
    char *staticSegment;
    float x, y;
    float xs, xe; /* start/end in super-map */
    float xme;    /* end x for a map */
    float s, t;
    float sw, tw;
    int done, i, tindx;
    
    /* 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);
    
    /* 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         = (u32)_staticSegmentRomEnd-(u32)_staticSegmentRomStart;
	
    osEPiStartDma(handler, &dmaIOMessageBuf, OS_READ);
    
    /* Wait for DMA to finish
     */
    (void) osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK);
    
    initControllers();
    
    
    /* Main game loop
     */
    while (1) {
	
	osContStartReadData(&controllerMsgQ);
	ReadController();
	
	/* pointers to build the display list.
	 */
	tlistp = &tlist;
	dynamicp = &dynamic;
	
	guOrtho(&dynamicp->projection,
		-(float) SCREEN_WD / 2.0F, (float) SCREEN_WD / 2.0F,
		-(float) SCREEN_HT / 2.0F, (float) SCREEN_HT / 2.0F,
		1.0F, 10.0F, 1.0F);
	guRotate(&dynamicp->modeling, 0.0F, 0.0F, 0.0F, 1.0F);
	
	glistp = dynamicp->glist;
	
	/* Tell RCP where each segment is
	 */
	gSPSegment(glistp++, 0, 0x0); /* Physical address segment 
				       */
	gSPSegment(glistp++, STATIC_SEGMENT, OS_K0_TO_PHYSICAL(staticSegment));
	gSPSegment(glistp++, CFB_SEGMENT, OS_K0_TO_PHYSICAL(cfb[draw_buffer]));
	
	/* Initialize RDP state.
	 */
	gSPDisplayList(glistp++, rdpinit_dl);
	
	/* Initialize RSP state.
	 */
	gSPDisplayList(glistp++, rspinit_dl);

	/* Clear color framebuffer.
	 */
	gSPDisplayList(glistp++, clearcfb_dl);
	
	/*  Filtering on/off
	 */
	if(FilterEn) {
	    gDPSetTextureFilter(glistp++, G_TF_BILERP);
	} else {
	    gDPSetTextureFilter(glistp++, G_TF_POINT);
	}
	
	xs = (int)(CursorX-128) % 1024; /* address within 1K space */
	if(xs < 0)
	  xs += 1024;
	xe = xs + 256;	/* end address */
	
	done = 0;
	
	x = 30;
	y = 0;
	
#ifdef DEBUG
	if(PrintEn) {
	    osSyncPrintf("============ Start ================\n");
	    osSyncPrintf("xs = %f, xe = %f\n", xs, xe);
	}
#endif
	
	for(i = 0; i < 3 && !done; i++) {
	    tindx = ((int)xs / 256); /* tex map index */
	    xme = xe;	/* end address */
	    if (xme > (tindx+1)*256)
	      xme = (tindx+1)*256;
	    else
	      done = 1;
	    
	    tindx %= 4;
	    
	    s = (int)xs % 256; /* address within the map */
	    t = 0;		/* address within map */
	    
	    sw = xme-xs; 	/* width in texels */
	    tw = 240;	/* height in texels */
	    
#ifdef DEBUG
	    if(PrintEn) {
		osSyncPrintf("\n--------- Map %d ---------\n", tindx);
		osSyncPrintf("xms = %f, xme = %f, s = %f, t = %f, "
			     "sw = %f, tw = %f\n",
			     xs, xme, s, t, sw, tw);
		osSyncPrintf("x = %f, y = %f\n", x, y);
	    }
#endif
	    
	    tileRectangle(
			  &glistp,
			  TexMaps[tindx], G_IM_FMT_RGBA, G_IM_SIZ_16b,
			  256,  240,
			  32,   32,
			  s,    t,
			  s+sw, t+tw,
			  x,    y );
	    
	    xs = xme;	/* next x start address */
	    x += sw;
	}
	
	if(PrintEn) {
#ifdef DEBUG		
	    osSyncPrintf("\n\n");
#endif
	    PrintEn = 0;
	}
	
	
	gDPFullSync(glistp++);
	gSPEndDisplayList(glistp++);
	
#ifdef DEBUG
#ifndef __MWERKS__
	assert((glistp - dynamicp->glist) < GLIST_LEN);
#endif
#endif
	
	/* Build graphics task:
	 */
	tlistp->t.ucode_boot = (u64 *) rspbootTextStart;
	tlistp->t.ucode_boot_size = (u32) rspbootTextEnd - (u32) rspbootTextStart;
	
	/* 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 = (u32) ((glistp - dynamicp->glist) * sizeof(Gfx));
	
	/* Write back dirty cache lines that need to be read by the RCP.
	 */
	osWritebackDCache(&dynamic, sizeof(dynamic));
	
	/* start up the RSP task
	 */
	osSpTaskStart(tlistp);
	
	/* wait for DP 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;
    }
}
