// **************************************************************************
// *				Sporting Clays for the Nintendo 64						*
// *					   (C) 1999 Charles Doty                           	*
// *	 		   Main Source file contains startup code					*
// **************************************************************************

#include <ultra64.h>

#include "main.h"
#include "display.h"
#include "scene3d.h"

void boot(void);
void idle(void *);
void mainproc(void *);

static u64      bootStack[STACKSIZE / 8];
static u64      idleThreadStack[STACKSIZE / 8];
static u64      mainThreadStack[STACKSIZE / 8];

static OSThread idleThread;
static OSThread mainThread;

OSMesg			PiMessages[NUM_PI_MSGS];
OSMesgQueue		PiMessageQ;

OSMesgQueue		rdpMessageQ;
OSMesg			rdpMessageBuf;

OSMesgQueue		rspMessageQ;
OSMesg			rspMessageBuf;

OSMesgQueue		retraceMessageQ;
OSMesg			retraceMessageBuf;

OSMesgQueue		dmaMessageQ;
OSMesg			dmaMessageBuf[DMA_QUEUE_SIZE];
OSIoMesg		dmaIOMessageBuf;

OSMesgQueue		InputMessageQ;
OSMesg			InputMessage;

DWORD           scene = 0;              // Current Scene
DWORD			course;					// Current Course

extern char 	_static_seg_org[],_static_seg_orgend[];
#define			_staticSegmentRomStart (_static_seg_org)
#define			_staticSegmentRomEnd (_static_seg_orgend)

extern char 	_code_bss_objend[];
#define			_codeSegmentEnd (_code_bss_objend)
extern BOOL		Is_NTSC;				// is the display in NTSC mode?

char   			*staticSegment = _codeSegmentEnd;

int tmpmain()
{
#ifdef DEBUG
    InitPrintf();
#endif

	DisplayInitialize();
	InputInitialize();
	SoundInitialize();
	GameInitialize();

    Run(scene);
}

void mainproc(void *arg)
{
	// DMA static segment to ram
  	osPiStartDma(&dmaIOMessageBuf, OS_MESG_PRI_NORMAL, OS_READ, (u32)_staticSegmentRomStart,
		staticSegment, (u32)_staticSegmentRomEnd - (u32)_staticSegmentRomStart, &dmaMessageQ);

	// Wait for DMA complete
	osRecvMesg(&dmaMessageQ, NULL, OS_MESG_BLOCK);

  	tmpmain();
}

void boot(void)
{
    osInitialize();
    osCreateThread(&idleThread, TID_IDLE, idle, (void *)0, idleThreadStack + STACKSIZE / sizeof(u64),
    	PRI_IDLE);

    osStartThread(&idleThread);
}

void idle(void *arg)
{
    // Initialize video
    osCreateViManager(OS_PRIORITY_VIMGR);

    switch(osTvType)
    {
        // PAL
		case 0:
			osViSetMode(&osViModePalLan1);
		    Is_NTSC = FALSE;

			break;

        // NTSC
		case 1:
			osViSetMode(&osViModeNtscLan1);
		    Is_NTSC = TRUE;

			break;

        // MPAL
		case 2:
			osViSetMode(&osViModeMpalLan1);
		    Is_NTSC = FALSE;

			break;
    };

    osViSetSpecialFeatures(OS_VI_GAMMA_OFF|OS_VI_GAMMA_DITHER_OFF);
    osViBlack(1);

    // Start PI Mgr for access to cartridge
	osCreatePiManager((OSPri)OS_PRIORITY_PIMGR, &PiMessageQ, PiMessages,
		NUM_PI_MSGS);

    // Setup message queues
    osCreateMesgQueue(&rspMessageQ, &rspMessageBuf, 1);
    osSetEventMesg(OS_EVENT_SP, &rspMessageQ, NULL);

    osCreateMesgQueue(&rdpMessageQ, &rdpMessageBuf, 1);
    osSetEventMesg(OS_EVENT_DP, &rdpMessageQ, NULL);

    osCreateMesgQueue(&retraceMessageQ, &retraceMessageBuf, 1);
    osViSetEvent(&retraceMessageQ, NULL, 2);

    osCreateMesgQueue(&InputMessageQ, &InputMessage, 1);
    osSetEventMesg(OS_EVENT_SI, &InputMessageQ, (OSMesg)1);

    osCreateMesgQueue(&dmaMessageQ, dmaMessageBuf, 1);

    // Create main thread
    osCreateThread(&mainThread, TID_MAINPROC, mainproc, (void*)0,
                   mainThreadStack+STACKSIZE/sizeof(u64), PRI_MAINPROC);

    // Start main thread
    osStartThread(&mainThread);

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

    for (;;);
}
