/*
 ******************************************************************************
 *              
 *     システム設定
 *     $Id: system.c,v 1.1 2003/05/30 21:25:28 tong Exp $
 *
 ******************************************************************************
 */
#define  SYSTEMp
#include "system.h"
#include "yoshi_spec.h"
#include "system_info.h"
#include "buffer.h"
#include "memory.h"
#include "bg_task.h"
#include "ThreadID.h"
#include "ThreadPriority.h"
#include "fbmgr.h"
#include "dmamgr.h"
#include "bg_msgmgr.h"
#include "bg_debug.h"
#include "irqmgr.h"

#include "dbgController.h"
#if USE_CIC6104
#include "cic6104.h"
#endif
#ifdef PARTNER
#include "pt.h"			/* ptstart */
#endif
#include "stackcheck.h"		/* stackcheck_t */

/*
 *    yoshi_spec.hのセグメント番号とyoshi.specの各セグメントに合わせる
 *    (セグメントアドレスからROMアドレスに変換するためのテーブル)
 */
EXTERN_DEFSEG(code);
EXTERN_DEFSEG(castData);
EXTERN_DEFSEG(worldDatabase);
EXTERN_DEFSEG(autoplayData);
const unsigned long SegmentRomStart[] = {
      (unsigned long)_codeSegmentRomStart,
      0,
      0,			/* Dynamic */
      (unsigned long)_castDataSegmentRomStart,
      (unsigned long)_worldDatabaseSegmentRomStart,
      (unsigned long)_autoplayDataSegmentRomStart,
};


ZSORT           zSortbuff[SIZE_ZSORTBUFF];
unsigned long   SegmentBaseAddress[NUM_SEGMENTS];
u64             bootStack[BOOT_STACK_SIZE / sizeof(u64)];
StackCheckDecl(boot);
OSMesg		dummyMesg;

#define IDLE_STACK_SIZE	(0x200+0x200)	/* 実績 0xc4 vimode用に増やした */
#define MAIN_STACK_SIZE	(0x1700+0x1000)	/* 実績 0x1614 */
#ifdef USE_RMON
#define RMON_STACK_SIZE	0x400	/* 実績 0xd0? */
#endif


static OSThread	idleThread;
static u64	idleStack[IDLE_STACK_SIZE / sizeof(u64)];
StackCheckDecl(idle);
static OSThread	mainThread;
static u64	mainStack[MAIN_STACK_SIZE / sizeof(u64)];
StackCheckDecl(main);
#ifdef USE_RMON
static OSThread	rmonThread;
static u64      rmonStack[RMON_STACKSIZE / sizeof(u64)];
StackCheckDecl(rmon);
#endif
static u64 	irqmgrStack[IRQMGR_STACKSIZE / sizeof(u64)];
StackCheckDecl(irqmgr);
irqmgr_t irqmgr;

/* DMAメッセージ領域 */
#define NUM_BG_MSGS 	4
#define NUM_RCP_MSGS 	1
#define NUM_PI_MSGS     64
static OSMesg      PiMsgs[NUM_PI_MSGS];
static OSMesgQueue PiMsgQ;
static OSMesg      serialMsgBuf;
       OSMesgQueue serialMsgQ;
static OSMesgQueue dmaMsgQ;
static OSMesg      dmaMsgBuf;
#if USE_PARALLELDMA
static OSIoMesg    dmaIOMessageBufs[MAX_DMANUM];
#endif
/*  */
int    vflag = 0;
int    WaitRetrace_disable = 0;
int    debugflag_bg = 0;
int    debugflag_fbmgr = 0;
int    debugflag_rcp = 0;
int    verbose = 0;

#if USE_DEBUGMODE
DebugMode debugmode;		/* デバッグモード */
#endif
/*
 *    オーバレイプログラムで guXxxxx() の関数を使用する場合は, 以下のように
 *  定義しておくか常駐プログラムで利用されていること。
 */
typedef void (*GUFUNC)(void);
static GUFUNC guFunc[] = {(GUFUNC)guTranslate, (GUFUNC)guScale};

/*
 * OSViMode構造体のカスタマイズ関数
 * ZELDAのz_vimode.cからのコピー
 */
static void
make_osvimode2(
    OSViMode *osvimodep,
    int type,
    int TVTYPE,			/* 1:NTSC/0:PAL/2:MPAL */
    int Lxxx,			/* 0:HighReso !0:LowReso */
    int xPxx,			/* 0:AntiAlias !0:PointSampling */
    int xxNx,			/* 0:FlickerFree !0:NI */
    int xxx1,			/* 0:32bit !0:16bit */
    int screen_width,		/* フレームバッファ横幅 */
    int screen_height,		/* フレームバッファ縦幅 */
    int hStart,			/* 横表示開始位置(0.5dot) */
    int hEnd,			/* 横表示終了位置(0.5dot) */
    int vStart,			/* 縦表示開始位置(0.5dot) */
    int vEnd			/* 縦表示終了位置(0.5dot) */
)
{
    const int Hxxx = !Lxxx;
    const int xAxx = !xPxx;
    const int xxFx = !xxNx;
    const int xxx2 = !xxx1;
    const int HxFx = (Hxxx && xxFx);
    const int HxNx = (Hxxx && xxNx);
    const int LxNx = (Lxxx && xxNx);
    const int LxFx = (Lxxx && xxFx);
    const int LAN1 = (Lxxx && xAxx && xxNx && xxx1);
    const int LPN2 = (Lxxx && xPxx && xxNx && xxx2);
    const int HPN2 = (Hxxx && xPxx && xxNx && xxx2);
    u32 yScale;
    u32 yScaleSubOff0;
    u32 yScaleSubOff1;

    vStart &= ~1;
    vEnd &= ~1;
    yScale = (u32)((HxFx ? 2 : 1) * (0x800 * screen_height
				     / (480 + vEnd - vStart) / (Lxxx ? 1 : 2)));
    yScaleSubOff0 = (u32)(xxFx ? (LxFx ? 0x01000000 : 0x02000000) : 0);
    yScaleSubOff1 = (u32)(xxFx ? (LxFx ? 0x03000000 : 0x02000000) : 0);

    osvimodep->type = (u8)type;

    /*
     * comRegs.ctrlの設定
     */
    osvimodep->comRegs.ctrl = (u32)(0x3000
				    | VI_CTRL_GAMMA_ON
				    | VI_CTRL_GAMMA_DITHER_ON
				    | (!LxNx ? VI_CTRL_SERRATE_ON : 0)
				    | (xAxx ? VI_CTRL_DIVOT_ON : 0)
				    | (xxx2 ? VI_CTRL_TYPE_32
				            : VI_CTRL_TYPE_16));
    //disp(04x, osvimodep->comRegs.ctrl);
    if (LAN1) {
	osvimodep->comRegs.ctrl |= 0x0100; /* 1: aa & resamp
					      (fetch extra lines if needed) */
    } else if (LPN2|HPN2) {
	osvimodep->comRegs.ctrl |= 0x0300; /* 3: neither
					      (replicate pixels,
					      no interpolate) */
    } else if (xPxx) {
	osvimodep->comRegs.ctrl |= 0x0200; /* 2: resamp only
					      (treat as all fully covered) */
    } else {
	osvimodep->comRegs.ctrl |= 0x0000; /* 0: aa & resamp
					      (always fetch extra lines) */
    }
    //disp(04x, osvimodep->comRegs.ctrl);

    osvimodep->comRegs.width = (u32)(screen_width * (HxNx ? 2 : 1));
    if (TVTYPE == OS_TV_NTSC) {
	osvimodep->comRegs.burst  = 0x03e52239;
	osvimodep->comRegs.vSync  = 524;	 // 垂直同期
	osvimodep->comRegs.hSync  = 0x00000c15;  // 水平同期
	osvimodep->comRegs.leap   = 0x0c150c15;
	osvimodep->comRegs.hStart = 0x006c02ec;  //横表示開始位置＋表示終了位置
	osvimodep->fldRegs[0].vStart = 0x002501ff;//縦表示開始位置+表示終了位置
	osvimodep->fldRegs[0].vBurst = 0x000e0204;//burst開始位置＋終了位置
    }
    else {
	if (TVTYPE == OS_TV_PAL) {		    /* pal */
	    osvimodep->comRegs.burst  = 0x04541e3a; /* 04541e3a 0404233a */
	    osvimodep->comRegs.vSync  = 624;
	    osvimodep->comRegs.hSync  = 0x00170c69; /* 00170c69 00150c69 */
	    osvimodep->comRegs.leap   = 0x0c6f0c6d; /* 0c6f0c6d 0c6f0c6e */
	    osvimodep->comRegs.hStart = 0x00800300;
	    osvimodep->fldRegs[0].vStart = 0x005f0239;
	    osvimodep->fldRegs[0].vBurst = 0x0009026b;
	}
	else {
	    if (TVTYPE == OS_TV_MPAL) {		    /* mpal */
		osvimodep->comRegs.burst  = 0x04651e39;
		osvimodep->comRegs.vSync  = 524;
		osvimodep->comRegs.hSync  = 0x00000c10;
		osvimodep->comRegs.leap   = 0x0c1c0c1c;
		osvimodep->comRegs.hStart = 0x006c02ec;
		osvimodep->fldRegs[0].vStart = 0x002501ff;
		osvimodep->fldRegs[0].vBurst = 0x000e0204;
	    }
	}
    }
    osvimodep->fldRegs[1].vStart = osvimodep->fldRegs[0].vStart;
#define xxx(s,e) ((u32)((s32)((u32)(s) << 16) + (s32)(s16)(e)))
    osvimodep->comRegs.hStart    += xxx(hStart, hEnd);
    osvimodep->fldRegs[0].vStart += xxx(vStart, vEnd);
    osvimodep->fldRegs[1].vStart += xxx(vStart, vEnd);
#undef xxx
    osvimodep->fldRegs[1].vBurst = osvimodep->fldRegs[0].vBurst;
    if (LxNx) {
	osvimodep->comRegs.vSync  += 1;
	if (TVTYPE == OS_TV_MPAL) osvimodep->comRegs.hSync  += 0x00040001;
	if (TVTYPE == OS_TV_MPAL) osvimodep->comRegs.leap   -= 0x00030002;
    } else {
	osvimodep->fldRegs[0].vStart -= 0x00020002;
	if (TVTYPE == OS_TV_MPAL) osvimodep->fldRegs[0].vBurst -= 0x00030002;
	if (TVTYPE == OS_TV_PAL)  osvimodep->fldRegs[1].vBurst += 0x0002fffe;
    }
    osvimodep->comRegs.xScale = (u32)(0x400 * screen_width / (640 + hEnd - hStart));
    osvimodep->comRegs.vCurrent = 0;
    osvimodep->fldRegs[0].origin = (u32)(screen_width * 2 * (xxx1 ? 1 : 2));
    osvimodep->fldRegs[1].origin = (u32)(screen_width * 2
					 * (xxx1 ? 1 : 2) * (Lxxx ? 1 : 2));
    osvimodep->fldRegs[0].yScale = (u32)(yScale | yScaleSubOff0);
    osvimodep->fldRegs[1].yScale = (u32)(yScale | yScaleSubOff1);
    osvimodep->fldRegs[0].vIntr = 2;
    osvimodep->fldRegs[1].vIntr = 2;
}

#if 0 // 1998.2.12 by otsuki.
static void disp_vimode(OSViMode *osvimodep)
{
    disp(08x,osvimodep);
    disp(08x,osvimodep->comRegs.ctrl);
    disp(08x,osvimodep->comRegs.width);
    disp(08x,osvimodep->comRegs.burst);
    disp(08x,osvimodep->comRegs.vSync);
    disp(08x,osvimodep->comRegs.hSync);
    disp(08x,osvimodep->comRegs.leap);
    disp(08x,osvimodep->comRegs.hStart);
    disp(08x,osvimodep->comRegs.xScale);
    disp(08x,osvimodep->fldRegs[0].vStart);
    disp(08x,osvimodep->fldRegs[0].vBurst);
    disp(08x,osvimodep->fldRegs[0].origin);
    disp(08x,osvimodep->fldRegs[0].yScale);
    disp(08x,osvimodep->fldRegs[0].vIntr);
    disp(08x,osvimodep->fldRegs[1].vStart);
    disp(08x,osvimodep->fldRegs[1].vBurst);
    disp(08x,osvimodep->fldRegs[1].origin);
    disp(08x,osvimodep->fldRegs[1].yScale);
    disp(08x,osvimodep->fldRegs[1].vIntr);
	
}
#endif

OSViMode osViModePalLan1_2 = {
        0x00000010,     /* type */
        /* Common regs */
        0x0000311e,     /* ctrl */      0x00000140,     /* width      */
        0x04541e3a,     /* burst */     0x00000271,     /* vSync      */
        0x00170c69,     /* hSync */     0x0c6f0c6d,     /* leap       */
        0x00800300,     /* hStart */    0x00000200,     /* xScale     */
        0x00000000,     /* vCurrent */
        /* Field 1 regs */
        0x00000280,     /* origin */    0x00000400,     /* yScale     */
        0x002f0269,     /* vStart */    0x0009026b,     /* vBurst     */
        0x00000002,     /* vIntr */
        /* Field 2 regs */
        0x00000280,     /* origin */    0x00000400,     /* yScale     */
        0x002f0269,     /* vStart */    0x0009026b,     /* vBurst     */
        0x00000002,     /* vIntr */
};

/*
 ******************************************************************************
 *   NTSC, PAL, MPAL 各ビデオモードの検出と設定
 ******************************************************************************
*/
static void set_videMode(void)
{
    static OSViMode osvimode;	/* 消滅する変数はだめです。 */
    
    PRINTF("make_osvimode2のテスト\n");
    
    // ビデオモードの検出と設定
    switch(osTvType) {
    case OS_TV_PAL  :
#if ((PRODUCT_ROM || CM_ROM) && PAL_VER)
	osViSetMode(&osViModePalLan1); 
	make_osvimode2(&osvimode,
		       255, OS_TV_PAL, 1, 0, 1, 1, 320, 240, 0, 0, -48, 48);
#else
	__STOP;
#endif
	break;
    case OS_TV_MPAL :
#if ((PRODUCT_ROM || CM_ROM) && MPAL_VER)
	osViSetMode(&osViModeMpalLan1);
	make_osvimode2(&osvimode,
		       255, OS_TV_MPAL, 1, 0, 1, 1, 320, 240, 0, 0, 0, 0);
#else
	__STOP;
#endif
	break;
    case OS_TV_NTSC :
    default :
#if (FOR_MAPTOOL || DEVELOP_PRG || NTSC_VER)
	osViSetMode(&osViModeNtscLan1); 
	make_osvimode2(&osvimode,
		       255, OS_TV_NTSC, 1, 0, 1, 1, 320, 240, 0, 0, 0, 0);
#else
	__STOP;
#endif
	break;
    }
    //disp_vimode(&osvimode);
    fbmgr_vimodeP = &osvimode;	/* ビデオモードの登録 */
    //ForceBlackOn();
    //osViSetMode(fbmgr_vimodeP); /* ビデオモードの設定 */
    osViBlack(TRUE);		// 画面をブラックアウトする
}

/*
 ******************************************************************************
 *   アイドル処理
 ******************************************************************************
*/
void idle(void *arg)
{
    extern  void mainproc(void *);

    /* ビデオの初期化 */
    osCreateViManager(OS_PRIORITY_VIMGR);
    set_videMode();		                   // ビデオモードの検出と設定

    /* ROMカートリッジアクセス用に PIマネージャを起動 */
    osCreatePiManager((OSPri)OS_PRIORITY_PIMGR, &PiMsgQ, PiMsgs, NUM_PI_MSGS);
#ifdef USE_RMON
    /* デバッグ用モニタースレッド(RMON)の設定と起動 */
    StackCheckInit(rmon);
    osCreateThread(&rmonThread, 0, rmonMain, NULL,
		   rmonStack + RMON_STACK_SIZE / sizeof(u64),
		   (OSPri)OS_PRIORITY_RMON);
    osStartThread(&rmonThread);
#endif
    /*
     * メインスレッドの設定と起動 (これによってmainproc()の処理が行なわれる)
     *    デバッグの時はメインスレッドをアイドルから起動せず、デバッガーから
     * 起動する。
     */
    StackCheckInitN(main, 0x1000);
    xxCreateThread(&mainThread, THREAD_ID_MAIN /* 3 */, mainproc, arg,
		   mainStack + MAIN_STACK_SIZE / sizeof(u64),
		   (OSPri)PRIORITY_MAIN);

#if USE_CIC6104
    osCic6104_ImemTest();	/* Imemの残存データ取得 */
    osCic6104_DmemTest();	/* Dmemの残存データ取得 */
    osCic6104_CartTest();	/* マスクROM上のデータ取得 */
    disp(d, osCic6104_CartTest_is_OK());
    disp(d, osCic6104_RamTest1_is_OK());
    disp(d, osCic6104_RamTest2_is_OK());
    disp(d, osCic6104_RamTest3_is_OK());
    disp(d, osCic6104_ImemTest_is_OK());
    disp(d, osCic6104_DmemTest_is_OK());
#endif

#if DEBUG
    if (!nowDebug()) {
	/* デバッガーを起動しない時に,ここからメインスレッドを起動する */
	xxStartThread(&mainThread);
    }
#else
    xxStartThread(&mainThread);
#endif
    /*
     * idleスレッドは何もしないスレッドになる
     */
    osSetThreadPri(NULL, (OSPri)OS_PRIORITY_IDLE);
    while(1) {
#if SYS_N64DISK
	LeoIdle();
#endif
	;
    }
}


#if DEBUG
/*
 ******************************************************************************
 *   オプションの解析
 ******************************************************************************
*/
static void parse_args(char *argStr)
{
    while (*argStr == ' ')
	argStr++;
    if ( *argStr == '-' && *(argStr+1) == 'g')
	debug(TRUE);	    /* gvdを起動する前に,"gload -a -g"でロードする */
    else
	debug(FALSE);
}
#endif /* DEBUG */

/*
 * コードセグメント以降のすべてのＲＡＭをクリアする
 */
static void
ClearRDRAM(void)
{
    bzero(_codeSegmentEnd, (size_t)osMemSize - (size_t)OS_K0_TO_PHYSICAL(_codeSegmentEnd));
}

/*
 ******************************************************************************
 *   ブート処理
 ******************************************************************************
 */
#ifdef FOR_ISVIEWER
#include "is_debug.h"
#endif
void boot(void)
{
#if DEBUG
    int    i;
    u32    *argp;
    u32    argbuf[16];
#endif
    
    StackCheckInitBoot(boot);
    
    /* ハードとソフトの初期化を行なう */
    osInitialize();
    ClearRDRAM();		/* ブートセグメント以降のRAMをクリア */

#ifdef FOR_ISVIEWER
    isPrintfInit();		/* ＩＳＶＩＥＷＲ初期化 */
#endif
#ifdef FOR_PARTNER
    ptstart();			/* パートナーＮ６４初期化 */
#endif

#if DEBUG

    argp = (u32 *)RAMROM_APP_WRITE_ADDR;
    for (i = 0; i < (int)number(argbuf); i++) {
	osPiRawReadIo((u32)argp++, &argbuf[i]); /* Assume no DMA */
    }

    /* オプションの解析 */
    parse_args((char *)argbuf);

#endif /* DEBUG */

    /* アイドルスレッドの設定と起動 */
    StackCheckInit(idle);
    osCreateThread(&idleThread, THREAD_ID_IDLE /* 1 */, idle, NULL,
		   idleStack + IDLE_STACK_SIZE / sizeof(u64),
		   (OSPri)PRIORITY_MAIN /* 10 */ );
    osStartThread(&idleThread);
}

/*
 ******************************************************************************
 *    セグメントアドレスの設定
 ******************************************************************************
 */
static void setup_Segments(void)
{
    char   *segmentEnd;
    extern char _bufferSegmentStart[];
    extern char _bufferSegmentEnd[];

    segmentEnd = _bufferSegmentEnd;
    SetSegment(PHYSICAL_SEGMENT, 0x0);
    SetSegmentK0(STATIC_SEGMENT, segmentEnd);
}

/*
 ******************************************************************************
 *  メッセージキューの設定
 ******************************************************************************
 */
static void setup_MessageQueue(void)
{
    /* メッセージ待行列の初期化 */
    osCreateMesgQueue(&dmaMsgQ, &dmaMsgBuf, 1);
    /*  */
    osCreateMesgQueue(&serialMsgQ, &serialMsgBuf, 1);
    osSetEventMesg(OS_EVENT_SI, &serialMsgQ, (OSMesg)1);
}
/*
 ******************************************************************************
 *     ROMからデータをDMAする(表処理)
 ******************************************************************************
 */
void foregroundDMA(void *dst, void *src, unint size)
{
    dmacopy_fg(dst, (u32)SEGMENT_TO_ROM(src), (u32)size);
}
/*
 ******************************************************************************
 *     裏DMAのオン,オフ
 ******************************************************************************
 */
#if DEBUG
int cancel_backgroundDMA(void)
{
    static int cancelFg = 0;
#if 0
    if(chkTrigger(R_BTN) && chkButton(START)) {
	cancelFg ^= 1;
	if(cancelFg)
	    PRINTF("おもてDMA!!\n");
	else
	    PRINTF("うらDMA!!\n");
    }
#endif
    return cancelFg;
}
#endif
#if 0
/*
 ******************************************************************************
 *     ROMからデータをDMAする(裏処理)
 ******************************************************************************
 */
void backgroundDMA(void *dst, void *src, unint size)
{
#if DEBUG
    PRINTF("裏DMAは未対応!! \n");
#endif
#if 0
    static int current = 0;	/* １フレームにヨッシー１枚だけ */
    static dma_t dma[2];
    static u64 work[2][(4096+100)/8];
    
    dmacopy_bg(&dma[current], dst, (u32)SEGMENT_TO_ROM(src), (u32)size,
	       work[current], getP_bgDMA_MsgQ(), NULL);
    markingBgDMA();
    current = 1 - current;
#endif
}
#endif
/*
 ******************************************************************************
 *     ROM上の圧縮されたデータを読み込む
 *     (dmacopy に渡された処理ポインターによって
 ******************************************************************************
 */
void readrom(void *d_data, void *s_data, unint size, DMAPROC process)
{
    (*process)(d_data, s_data, size);
}
/*
 ******************************************************************************
 *     裏DMA待ち
 ******************************************************************************
 */
void wait_bgDMA(GAME *Game)
{
    DMAINF  *dmaInfo = DMAINF_P(Game);
    int     id;

    // DMA情報領域のチェンジ 
    dmaInfo->bgDMA_useID++;
    if(dmaInfo->bgDMA_useID >= MAX_DMAINF)
	dmaInfo->bgDMA_useID = 0;
    // 裏DMA待ち
    id = (int)dmaInfo->bgDMA_useID;
    //disp(d, id);
    //disp(d, dmaInfo->bgDMA_Cnt[id]);
    while(dmaInfo->bgDMA_Cnt[id]) {
	xxRecvMesg(&dmaInfo->bgDMA_MsgQ[id], NULL, OS_MESG_BLOCK);
	dmaInfo->bgDMA_Cnt[id]--;
    }
}
/*
 ******************************************************************************
 *     DMA情報領域の初期化
 ******************************************************************************
 */
void initial_dmaInfo(GAME *Game)
{
    int  i;
    
    Game->dmaInfo.bgDMA_useID = 0;
    for(i=0; i<MAX_DMAINF; i++) {
	Game->dmaInfo.bgDMA_Cnt[i] = 0;
	osCreateMesgQueue(&Game->dmaInfo.bgDMA_MsgQ[i],
			     Game->dmaInfo.bgDMA_Msg[i], MAX_BGDMA);
    }
}
/*
 ******************************************************************************
 *   システムの初期化
 ******************************************************************************
 */
void initialSystem(GAME *Game)
{
    extern ZSORT zSortbuff[];

#if USE_DEBUGMODE
    debugmode_init(&debugmode, REG_MAX);
#endif /* USE_DEBUGMODE */
    /* 初期化 */
    setup_MessageQueue();
    setup_Segments();
    CreateDmaManager();
#if SYSTEM_INFO
    print_info();
#endif

    StackCheckInit(irqmgr);
    CreateIRQManager(&irqmgr, irqmgrStack + number(irqmgrStack),  PRIORITY_IRQMGR, 1);
    
    /* フレームバッファマネージャーのスレッドの起動と初期化 */
    CreateFramebufferManager(&irqmgr);
    {
	/* フレームバッファの設定 */
	static CFBInfo cfbtbl[CFB_MAX] = {
	    {
		(u64 *)&cfb[0], NULL,
		G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WD, SCREEN_HT,
		2 * SCREEN_WD * SCREEN_HT, 1, 0, NULL, 0
	    },
	    {
		(u64 *)&cfb[1], NULL,
		G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WD, SCREEN_HT,
		2 * SCREEN_WD * SCREEN_HT, 1, 0, NULL, 0
	    },
#if CFB_MAX > 2
	    {
		(u64 *)&cfb[2], NULL,
		G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WD, SCREEN_HT,
		2 * SCREEN_WD * SCREEN_HT, 1, 0, NULL, 0
	    },
#endif
#if CFB_MAX > 3
	    {
		(u64 *)&cfb[3], NULL,
		G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WD, SCREEN_HT,
		2 * SCREEN_WD * SCREEN_HT, 1, 0, NULL, 0
	    },
#endif
	};
	framebuf_init(CFB_MAX, cfbtbl);
    }

    /*  ＲＣＰマネージャーのスレッドの起動と初期化 */
    CreateRcpManager(&irqmgr);
    
    /* スプライトマネージャーのスレッドの起動と初期化 */
    CreateBgManager();
    /* スプライトライブラリーの初期化 */
    bgInit(&Game->SpriteInfo);
    BG_setZsort(zSortbuff, SIZE_ZSORTBUFF);
#ifndef NOUSE_ZSORT_TEST   // 完全版で削除
    /* グラフィックリストのオープン */
    open_Glist(main_Glist);
#endif
}
#if DEBUG_CIC
/*
 ******************************************************************************
 *
 *   CICの動作確認のためのダミー処理
 *
 *
 *   CICのチェック結果が不良の場合の現象リスト
 *
 *   ● osCic6104_CartTest_is_OK() が確認不良の場合
 *
 *      < 現象 >
 *          コントローラの入力を受け付けない。
 *
 *      < プログラム動作 >
 *          入力したコントローラ内容のクリアーを行う。
 *
 *      < チェック個所 >
 *          controllerx.c / readController()
 *
 *
 *   ● osCic6104_RamTest1() が確認不良の場合
 *
 *      < 現象 >
 *          繰り返しタイトル表示を行った場合やゲーム画面に移行できずハングする
 *
 *      < プログラム動作 >
 *          キャラクターの固有領域をメモリー解放せずメモリー不足の状態にする。
 *
 *      < チェック個所 >
 *          actorManager.c / free_miscInfoProc()
 *
 *
 *   ● osCic6104_RamTest2() が確認不良の場合
 *
 *      < 現象 >
 *          タイトル画面を続けるとハングする。
 *          ゲーム中にキャラクターが新たに出現しなくなる。場合によっては、ヨッ
 *        シーが透明の壁に衝突するような現象も見られる。
 *
 *      < プログラム動作 >
 *          出現キャラクターのデータ解放を行わずメモリー不足の状態にする。
 *
 *      < チェック個所 >
 *          actorManager.c / birth_dynamicActor()
 *
 *
 *   ● osCic6104_RamTest3() が確認不良の場合
 *
 *      < 現象 >
 *          Nexto-goto後ヨッシーが死に、プレイヤーセレクトでヨッシーを選択して
 *        ゲームを再会しようとしたときハングする。
 *          フルーツボーナスのデモが、画面が点滅して正常に動作せずハングする。
 *
 *      < プログラム動作 >
 *          ヨッシー関連のメモリー解放行わないようにプロテクトを掛けたままに
 *        し, ヨッシーが生き続ける状態にする。 
 *
 *      < チェック個所 >
 *          nxgotoManager.c / outOfExit_actor()
 *
 *
 *   ● osCic6104_ImemTest_is_OK() が確認不良の場合                            
 *                                                                             
 *      < 現象 >                                                               
 *          ヨッシーの絵が欠けて表示される。                                   
 *                                                                             
 *      < プログラム動作 >                                                     
 *          ヨッシーのイメージ転送を行わない。
 *                                                                   
 *      < チェック個所 >                                             
 *          cellManager.c / read_unpackageCell()                     
 *                                                                   
 *                                                                   
 *   ● osCic6104_DmemTest_is_OK() が確認不良の場合                  
 *                                                                   
 *      < 現象 >                                                     
 *          起動して画面を表示せずいきなりハングする。               
 *                                                                   
 *      < プログラム動作 >                                           
 *          イメージ関連データの読み込みと生成を行わない。           
 *                                                                   
 *      < チェック個所 >                                             
 *          castManager.c / construct_imageInfo()
 *                                                                   
 ******************************************************************************
 */
#define RET_CartTest          1
#define RamTest1              1
#define RamTest2              1
#define RamTest3              1
#define ImemTest              1
#define DmemTest              1

int osCic6104_CartTest_is_OK(void)
{
    return RET_CartTest;
}
int osCic6104_RamTest1_is_OK(void)
{
    return RamTest1;
}
int osCic6104_RamTest2_is_OK(void)
{
    return RamTest2;
}
int osCic6104_RamTest3_is_OK(void)
{
    return RamTest3;
}
int osCic6104_ImemTest_is_OK(void)
{
    return ImemTest;
}
int osCic6104_DmemTest_is_OK(void)
{
    return DmemTest;
}
#endif
#if SOUND
/*
 ******************************************************************************
 *
 *    サウンド関連処理
 *
 ******************************************************************************
 */
#if (FOR_MAPTOOL || DEVELOP_PRG)
#define sNA_EVENT                     CALL_SOUND
#define sNA_SCENE_CHANGE              CALL_SOUND

#define sNA_SE_TRIG                   CALL_SOUND
#define sNA_SE_START                  CALL_SOUND
#define sNA_SE_STOP                   CALL_SOUND
#define sNA_LEVEL_SE_STOP             CALL_SOUND
#define sNA_ALL_SE_STOP               CALL_SOUND
#define sNA_SE_LEVEL_CHANGE           CALL_SOUND
#define sNA_SE_LEVEL_CHECK            CALL_SOUND

#define sNA_PAUSE_ON                  CALL_SOUND 
#define sNA_PAUSE_OFF                 CALL_SOUND 

#define sNA_OUTMODE_CHANGE            CALL_SOUND
#define sNA_YOSHI_STATUS_CHANGE       CALL_SOUND
#define sNA_GET_YOSHI_STEP_STATUS     CALL_SOUND
#define sNA_SET_ACTIVE_YOSHI          CALL_SOUND
#define sNA_SET_CHIBI_YOSHI           CALL_SOUND

#define sNA_SELECT_CURSOR             CALL_SOUND 

#define sNA_GAME_FRAME_WORK           CALL_SOUND

#define sNA_SET_TIMER_COUNT           CALL_SOUND

#define sNA_SET_RESULT                CALL_SOUND

#if SYS_GATEWAY_N64
#define sNA_GATEWAY_PAUSE_ON          CALL_SOUND
#define sNA_GATEWAY_PAUSE_OFF         CALL_SOUND
#endif

#define sAUDIO_TIMER_ENABLE           CALL_SOUND
#define sAUDIO_TIMER_DISABLE          CALL_SOUND
/*
 ******************************************************************************
 *    デバッグ用のサウンドダミー設定の処理
 ******************************************************************************
 */
#if (sNA_SE_START || sNA_SE_STOP || sNA_SE_LEVEL_CHANGE || sNA_SE_LEVEL_CHECK)
// ＳＥのサブハンドル値のチェック(正常なら TRUE を返す)
static int chk_NaSubHandle(s16 sub_handle)
{
    if((sub_handle < 0) || (sub_handle > 9)) {
#if DEBUG
	PRINTF(__ERROR
	       "check_NaSubHandle(): ＳＥのサブハンドル値がおかしい!!"
	       "sub_handle=%d\n", sub_handle);
#endif
	return FALSE;
    }
    return TRUE;
}
#endif
// サウンドダミー処理
void sNa_Event(NA_EVENT event)
{
#if sNA_EVENT
    Na_Event(event);
#else
    PRINTF("sound : Na_Event(%d)\n", event);
#endif
}
/* トリガーの設定 */
void sNa_SeTrig(NA_SE_TRIG se, NA_SE_INFO *info)
{
#if sNA_SE_TRIG
    Na_SeTrig(se, info);
#else
    PRINTF("sound : Na_SeTrig(%d, %xh)\n", se, info);
#endif
}
/* SEの開始 */
void sNa_SeStart(NA_SE_LEVEL se, s16 sub_handle, NA_SE_INFO *info)
{
#if sNA_SE_START
    if(!chk_NaSubHandle(sub_handle))
	return;
    Na_SeStart(se, sub_handle, info);
#else
    PRINTF("sound : Na_SeStart(%d, %d ,%xh)\n", se, sub_handle, info);
#endif
}
/* SEの停止 */
void sNa_SeStop(NA_SE_LEVEL se, s16 sub_handle)
{
#if sNA_SE_STOP
    if(!chk_NaSubHandle(sub_handle))
	return;
    Na_SeStop(se, sub_handle);
#else
    PRINTF("sound : Na_SeStop(%d, %d)\n", se, sub_handle);
#endif
}
/* レベルSEの停止 */
void sNa_LevelSeStop(void)
{
#if sNA_LEVEL_SE_STOP
    Na_LevelSeStop();
#else
    PRINTF("sound : Na_LevelSeStop()\n");
#endif
}
/* 全てのSEの停止 */
void sNa_AllSeStop(void)
{
#if sNA_ALL_SE_STOP
    Na_AllSeStop();
#else
    PRINTF("sound : Na_AllSeStop()\n");
#endif
}
/*  */
void sNa_SeLevelChange(NA_SE_LEVEL se, s16 sub_handle, NA_SE_INFO *info)
{
#if sNA_SE_LEVEL_CHANGE
    if(!chk_NaSubHandle(sub_handle))
	return;
    Na_SeLevelChange(se, sub_handle, info);
#else
    PRINTF("sound : Na_SeLevelChange(%d, %d, %xh)\n", se, sub_handle, info);
#endif
}
/*  */
s32 sNa_SeLevelCheck(NA_SE_LEVEL se, s16 sub_handle)
{
#if sNA_SE_LEVEL_CHECK
    if(!chk_NaSubHandle(sub_handle))
	return 0;
    return Na_SeLevelCheck(se, sub_handle);
#else
    PRINTF("sound : Na_SeLevelCheck(%d, %d)\n", se, sub_handle);
    return (0);
#endif
}
/*  */
void sNa_PauseOn(void)
{
#if sNA_PAUSE_ON 
    Na_PauseOn();
#else
    PRINTF("sound : Na_PauseOn()\n");
#endif
}
/*  */
void sNa_PauseOff(void)
{
#if sNA_PAUSE_OFF
    Na_PauseOff();
#else
    PRINTF("sound : Na_PauseOff()\n");
#endif
}
/*  */
void sNa_SceneChange(NA_SCENE scene)
{
#if sNA_SCENE_CHANGE
    Na_SceneChange(scene);
#else
    PRINTF("sound : Na_SceneChange(%d)\n", scene);
#endif
}
/*  */
void sNa_OutModeChange(NA_OUT_MODE mode)
{
#if sNA_OUTMODE_CHANGE
    Na_OutModeChange(mode);
#else
    PRINTF("sound : Na_OutModeChange(%d)\n", mode);
#endif
}
/*  */
void sNa_YoshiStatusChange(NA_YOSHI_STATUS status)
{
#if sNA_YOSHI_STATUS_CHANGE
    Na_YoshiStatusChange(status);
#else
    PRINTF("sound : Na_YoshiStatusChange(%xh)\n", status);
#endif
}
/*  */
s16 sNa_GetYoshiStepStatus(void)
{
#if sNA_GET_YOSHI_STEP_STATUS
    return Na_GetYoshiStepStatus();
#else
    PRINTF("sound : Na_GetYoshiStepStatus()\n");
    return (0);
#endif
}
/*  */
void sNa_SetActiveYoshi(NA_PLAYER yoshi)
{
#if sNA_SET_ACTIVE_YOSHI
    Na_SetActiveYoshi(yoshi);
#else
    PRINTF("sound : Na_SetActiveYoshi(%d)\n", yoshi);
#endif
}
/*  */
void sNa_SetChibiYoshi(s16 flag)
{
#if sNA_SET_CHIBI_YOSHI
    Na_SetChibiYoshi(flag);
#else
    PRINTF("sound : Na_SetChibiYoshi(%d)\n", flag);
#endif
}
/*  */
void sNa_SelectCursor(s16 x, s16 y)
{
#if sNA_SELECT_CURSOR
    Na_SelectCursor(x, y);
#else
    PRINTF("sound : Na_SelectCursor(%d, %d)\n", x, y);
#endif
}
/*  */
void sNa_GameFrameWork(void)
{
#if sNA_GAME_FRAME_WORK
    Na_GameFrameWork();
#else
    PRINTF("sound : Na_GameFrameWork()\n");
#endif
}
/*  */
void sNa_SetTimerCount(NA_TIMER type, s16 count)
{
#if sNA_SET_TIMER_COUNT
    Na_SetTimerCount(type, count);
#else
    PRINTF("sound : Na_SetTimerCount(%d, %d)\n", type, count);
#endif
}
/*  */
void sNa_SetResult(s16 result)
{
#if sNA_SET_RESULT
    Na_SetResult(result);
#else
    PRINTF("sound : Na_SetResult(%d)\n", result);
#endif
}
#if SYS_GATEWAY_N64
/* ゲートウェイ対応 サウンドの一時停止 */
void sNa_GatewayPauseOn(void)
{
#if sNA_GATEWAY_PAUSE_ON
    Na_GatewayPauseOn();
#endif
}
/* ゲートウェイ対応 サウンドの一時停止解除 */
void sNa_GatewayPauseOff(void)
{
#if sNA_GATEWAY_PAUSE_OFF
    Na_GatewayPauseOff();
#endif
}
#endif
/* サウンドの一時停止 */
void s_audio_timer_enable(void)
{
#if sAUDIO_TIMER_ENABLE
    audio_timer_enable();
#endif
}
/* サウンドの一時停止解除 */
void s_audio_timer_disable(void)
{
#if sAUDIO_TIMER_DISABLE
    audio_timer_disable();
#endif
}
#endif  // (FOR_MAPTOOL || DEVELOP_PRG)
/*
 ******************************************************************************
 *    サウンドの完全停止処理
 ******************************************************************************
 */
void sNa_soundStop(void)
{
    sNa_AllSeStop();		       // 全ＳＥの停止
    sNa_SceneChange(NA_SCENE_STOP);    // ＢＧＭなし
}
#if DEBUG
/*
 ******************************************************************************
 *    サウンドの一時停止処理
 ******************************************************************************
 */
void musicPouse(void)
{
    static int  sound_offFg = FALSE;
    
    if(cdbg_chkButton(Z_BTN)
       && cdbg_chkButton(A_BTN) && cdbg_chkTrigger(B_BTN)) {
	if(!sound_offFg) {
	    PRINTF("サウンド オフ!!\n");
	    s_audio_timer_disable();
	    sound_offFg = TRUE;
	}
    }
    else {
	if(sound_offFg) {
	    PRINTF("サウンド オン!!\n");
	    s_audio_timer_enable();
	    sound_offFg = FALSE;
	}
    }
}
#endif
/*
 ******************************************************************************
 *     指定座標をサウンド座標に変換する
 ******************************************************************************
 */
#define SOUND_WIDE_L        (-SCREEN_WD)
#define SOUND_WIDE_R        (SCREEN_WD * 2)

s16 to_soundXY(int v)
{
    if(v < SOUND_WIDE_L)
	v = SOUND_WIDE_L - 1;
    else
	if(v > SOUND_WIDE_R)
	    v = SOUND_WIDE_R - 1;
    /* (v)値の"-319 〜 639"を"-127 〜 255"に変換する
       ((v)を2.5で割る固定小数点演算を行う) */
    return (s16)(int)(((v << 3) + (v << 1)) / 25);
}
/*
 ******************************************************************************
 *     (X)座標設定型の NA_SE_INFO 設定処理
 ******************************************************************************
 */
static NA_SE_INFO info;
NA_SE_INFO *sNaSeInfo_XY(int x, int y)
{
    info.x = to_soundXY(x);
    info.y = to_soundXY(y);
    return &info;
}
/*
 ******************************************************************************
 *    "プレイヤー","地面"情報設定型の NA_SE_INFO 設定処理
 ******************************************************************************
 */
NA_SE_INFO *sNaSeInfo_XY_PLY(NA_PLAYER player, int x, int y)
{
    sNaSeInfo_XY(x, y);
    info.player = player;
    return &info;
}
/*
 ******************************************************************************
 *    "地面"情報設定型の NA_SE_INFO 設定処理
 ******************************************************************************
 */
NA_SE_INFO *sNaSeInfo_XY_GRD(NA_GROUND ground, int x, int y)
{
    sNaSeInfo_XY(x, y);
    info.ground = ground;
    return &info;
}
/*
 ******************************************************************************
 *    "プレイヤー","地面"情報設定型の NA_SE_INFO 設定処理
 ******************************************************************************
 */
NA_SE_INFO *sNaSeInfo_XY_GRD_PLY(NA_PLAYER player,
				 NA_GROUND ground,
				 int x,
				 int y)
{
    sNaSeInfo_XY(x, y);
    info.ground = ground;
    info.player = player;
    return &info;
}
/*
 ******************************************************************************
 *    "呼吸"情報設定型の NA_SE_INFO 設定処理
 ******************************************************************************
 */
NA_SE_INFO *sNaSeInfo_XY_BREATH(NA_BREATH breath, int x, int y)
{
    sNaSeInfo_XY(x, y);
    info.breath = breath;
    return &info;
}
/*
 ******************************************************************************
 *    "プレイヤー","呼吸"情報設定型の NA_SE_INFO 設定処理
 ******************************************************************************
 */
NA_SE_INFO *sNaSeInfo_XY_BREATH_PLY(NA_PLAYER player,
				   NA_BREATH breath,
				   int x,
				   int y)
{
    sNaSeInfo_XY(x, y);
    info.breath = breath;
    info.player = player;
    return &info;
}
/*
 ******************************************************************************
 *    "バリュー"情報設定型の NA_SE_INFO 設定処理
 ******************************************************************************
 */
NA_SE_INFO *sNaSeInfo_VALUE(s32 s32_val)
{
    info.value.s32 = s32_val;
    return &info;
}
/*
 ******************************************************************************
 *    "バリュー２"情報設定型の NA_SE_INFO 設定処理
 ******************************************************************************
 */
NA_SE_INFO *sNaSeInfo_VALUE2(s32 s32_val)
{
    info.value2.s32 = s32_val;
    return &info;
}
/*
 ******************************************************************************
 *    "バリュー１とバリュー２" 情報設定型の NA_SE_INFO 設定処理
 ******************************************************************************
 */
NA_SE_INFO *sNaSeInfo_VALUE_VALUE2(s32 s32_val1, s32 s32_val2)
{
    info.value.s32 = s32_val1;
    info.value2.s32 = s32_val2;
    return &info;
}
/*
 ******************************************************************************
 *    "バリュー" "XY" 情報設定型の NA_SE_INFO 設定処理
 ******************************************************************************
 */
NA_SE_INFO *sNaSeInfo_XY_VALUE(s32 s32_val, int x, int y)
{
    sNaSeInfo_XY(x, y);
    info.value.s32 = s32_val;
    return &info;
}
/*
 ******************************************************************************
 *    "バリュー" "XY" "プレイヤー" 情報設定型の NA_SE_INFO 設定処理
 ******************************************************************************
 */
NA_SE_INFO *sNaSeInfo_XY_VALUE_PLY(NA_PLAYER player, s32 s32_val, int x, int y)
{
    sNaSeInfo_XY_VALUE(s32_val, x, y);
    info.player = player;
    return &info;
}
/*
 ******************************************************************************
 *    "バリュー1&2" "XY" "プレイヤー" 情報設定型の NA_SE_INFO 設定処理
 ******************************************************************************
 */
NA_SE_INFO *sNaSeInfo_XY_VALUE_VALUE2_PLY(NA_PLAYER player,
					  s32 s32_val1,
					  s32 s32_val2,
					  int x,
					  int y)
{
    sNaSeInfo_XY(x, y);
    sNaSeInfo_VALUE_VALUE2(s32_val1, s32_val2);
    info.player = player;
    return &info;
}
/*
 ******************************************************************************
 *    "地面""バリュー"情報設定型の NA_SE_INFO 設定処理
 ******************************************************************************
 */
NA_SE_INFO *sNaSeInfo_XY_GRD_VALUE(NA_GROUND ground, s32 s32_val, int x, int y)
{
    sNaSeInfo_XY(x, y);
    info.ground    = ground;
    info.value.s32 = s32_val;
    return &info;
}
/*
 ******************************************************************************
 *    "謎"情報設定型の NA_SE_INFO 設定処理
 ******************************************************************************
 */
NA_SE_INFO *sNaSeInfo_NAZO(NA_NAZOTOKI nazo)
{
    info.nazo = nazo;
    return &info;
}
/*
 ******************************************************************************
 *
 ******************************************************************************
 */
NA_SE_INFO *sNaSeInfo_X(int x)
{
    return sNaSeInfo_XY(x,60);
}
NA_SE_INFO *sNaSeInfo_X_PLY(NA_PLAYER a, int x)
{
    return sNaSeInfo_XY_PLY(a,x,60);  
}
NA_SE_INFO *sNaSeInfo_X_GRD(NA_GROUND a, int x)
{
    return sNaSeInfo_XY_GRD(a,x,60);
}
NA_SE_INFO *sNaSeInfo_X_GRD_PLY(NA_PLAYER a, NA_GROUND b, int x)
{
    return sNaSeInfo_XY_GRD_PLY(a,b,x,60);
}
NA_SE_INFO *sNaSeInfo_X_BREATH(NA_BREATH a, int x)
{
    return sNaSeInfo_XY_BREATH(a,x,60);
}
NA_SE_INFO *sNaSeInfo_X_BREATH_PLY(NA_PLAYER a, NA_BREATH b, int x)
{
    return sNaSeInfo_XY_BREATH_PLY(a,b,x,60);
}
NA_SE_INFO *sNaSeInfo_X_VALUE(s32 a, int x)
{
    return sNaSeInfo_XY_VALUE(a,x,60);
}
NA_SE_INFO *sNaSeInfo_X_GRD_VALUE(NA_GROUND a, s32 b, int x)
{
    return sNaSeInfo_XY_GRD_VALUE(a,b,x,60);
}
/*
 ******************************************************************************
 *    指定のレベルＳＥを開始し使用中のサブハンドルを返す
 *    (未使用のサブハンドルが無ければ, レベルＳＥを開始せず NULL_SUBHANDLE を
 *   返す)
 ******************************************************************************
 */
t_sh ysSeStart(NA_SE_LEVEL se_handle, NA_SE_INFO *info)
{
#if CALL_SOUND
    t_sh  sub_handle;
    
    for(sub_handle=0; sub_handle<MAX_SE_SUBHANDLE; sub_handle++) {
	if(is_stopSE(sNa_SeLevelCheck(se_handle, sub_handle))) {
	    sNa_SeStart(se_handle, sub_handle, info);
	    return sub_handle;
	}
    }
    return NULL_SUBHANDLE;
#else
    se_handle;
    (void *)info;
#endif
}
#endif
