/****************************************************************************** * * Genesis Plus - Sega Megadrive / Genesis Emulator * * NGC/Wii Video support * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***************************************************************************/ #include "shared.h" #include "font.h" #include "gcaram.h" #include "md_ntsc.h" #include "sms_ntsc.h" /*** NTSC Filters ***/ md_ntsc_setup_t md_setup; md_ntsc_t md_ntsc; sms_ntsc_setup_t sms_setup; sms_ntsc_t sms_ntsc; /* Aspect Ratio */ int xscale, yscale, xshift, yshift; /*** PAL 50hz flag ***/ int gc_pal = 0; /*** VI ***/ unsigned int *xfb[2]; /*** Double buffered ***/ int whichfb = 0; /*** External framebuffer index ***/ GXRModeObj *vmode; /*** Menu video mode ***/ /*** GX ***/ #define TEX_WIDTH 360 * 2 #define TEX_HEIGHT 576 #define DEFAULT_FIFO_SIZE 256 * 1024 #define HASPECT 320 #define VASPECT 240 static u8 gp_fifo[DEFAULT_FIFO_SIZE] ATTRIBUTE_ALIGN (32); static u8 texturemem[TEX_WIDTH * (TEX_HEIGHT + 8) * 2] ATTRIBUTE_ALIGN (32); static GXTexObj texobj; static Mtx view; static int vwidth, vheight; static long long int stride; /*** custom Video modes (used to emulate original console video modes) ***/ /* 288 lines progressive (PAL 50Hz) */ GXRModeObj TV50hz_288p = { VI_TVMODE_PAL_DS, // viDisplayMode 640, // fbWidth 286, // efbHeight 286, // xfbHeight (VI_MAX_WIDTH_PAL - 720)/2, // viXOrigin (VI_MAX_HEIGHT_PAL - 572)/2, // viYOrigin 720, // viWidth 572, // viHeight VI_XFBMODE_SF, // xFBmode GX_FALSE, // field_rendering GX_FALSE, // aa // sample points arranged in increasing Y order { {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each {3,2},{9,6},{3,10}, // pix 1 {9,2},{3,6},{9,10}, // pix 2 {9,2},{3,6},{9,10} // pix 3 }, // vertical filter[7], 1/64 units, 6 bits each { 0, // line n-1 0, // line n-1 21, // line n 22, // line n 21, // line n 0, // line n+1 0 // line n+1 } }; /* 288 lines interlaced (PAL 50Hz) */ GXRModeObj TV50hz_288i = { VI_TVMODE_PAL_INT, // viDisplayMode 640, // fbWidth 286, // efbHeight 286, // xfbHeight (VI_MAX_WIDTH_PAL - 720)/2, // viXOrigin (VI_MAX_HEIGHT_PAL - 572)/2, // viYOrigin 720, // viWidth 572, // viHeight VI_XFBMODE_SF, // xFBmode GX_TRUE, // field_rendering GX_FALSE, // aa // sample points arranged in increasing Y order { {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each {3,2},{9,6},{3,10}, // pix 1 {9,2},{3,6},{9,10}, // pix 2 {9,2},{3,6},{9,10} // pix 3 }, // vertical filter[7], 1/64 units, 6 bits each { 0, // line n-1 0, // line n-1 21, // line n 22, // line n 21, // line n 0, // line n+1 0 // line n+1 } }; /* 576 lines interlaced (PAL 50Hz, scaled) */ GXRModeObj TV50hz_576i = { VI_TVMODE_PAL_INT, // viDisplayMode 640, // fbWidth 480, // efbHeight 574, // xfbHeight (VI_MAX_WIDTH_PAL - 720)/2, // viXOrigin (VI_MAX_HEIGHT_PAL - 574)/2, // viYOrigin 720, // viWidth 574, // viHeight VI_XFBMODE_DF, // xFBmode GX_FALSE, // field_rendering GX_FALSE, // aa // sample points arranged in increasing Y order { {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each {3,2},{9,6},{3,10}, // pix 1 {9,2},{3,6},{9,10}, // pix 2 {9,2},{3,6},{9,10} // pix 3 }, // vertical filter[7], 1/64 units, 6 bits each { 4, // line n-1 8, // line n-1 12, // line n 16, // line n 12, // line n 8, // line n+1 4 // line n+1 } }; /* 240 lines progressive (NTSC or PAL 60Hz) */ GXRModeObj TV60hz_240p = { VI_TVMODE_EURGB60_DS, // viDisplayMode 640, // fbWidth 240, // efbHeight 240, // xfbHeight (VI_MAX_WIDTH_NTSC - 720)/2, // viXOrigin (VI_MAX_HEIGHT_NTSC/2 - 480/2)/2, // viYOrigin 720, // viWidth 480, // viHeight VI_XFBMODE_SF, // xFBmode GX_FALSE, // field_rendering GX_FALSE, // aa // sample points arranged in increasing Y order { {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each {3,2},{9,6},{3,10}, // pix 1 {9,2},{3,6},{9,10}, // pix 2 {9,2},{3,6},{9,10} // pix 3 }, // vertical filter[7], 1/64 units, 6 bits each { 0, // line n-1 0, // line n-1 21, // line n 22, // line n 21, // line n 0, // line n+1 0 // line n+1 } }; /* 240 lines interlaced (NTSC or PAL 60Hz) */ GXRModeObj TV60hz_240i = { VI_TVMODE_EURGB60_INT, // viDisplayMode 640, // fbWidth 240, // efbHeight 240, // xfbHeight (VI_MAX_WIDTH_NTSC - 720)/2, // viXOrigin (VI_MAX_HEIGHT_NTSC - 480)/2, // viYOrigin 720, // viWidth 480, // viHeight VI_XFBMODE_SF, // xFBmode GX_TRUE, // field_rendering GX_FALSE, // aa // sample points arranged in increasing Y order { {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each {3,2},{9,6},{3,10}, // pix 1 {9,2},{3,6},{9,10}, // pix 2 {9,2},{3,6},{9,10} // pix 3 }, // vertical filter[7], 1/64 units, 6 bits each { 0, // line n-1 0, // line n-1 21, // line n 22, // line n 21, // line n 0, // line n+1 0 // line n+1 } }; /* 480 lines interlaced (NTSC or PAL 60Hz) */ GXRModeObj TV60hz_480i = { VI_TVMODE_EURGB60_INT, // viDisplayMode 640, // fbWidth 480, // efbHeight 480, // xfbHeight (VI_MAX_WIDTH_NTSC - 720)/2, // viXOrigin (VI_MAX_HEIGHT_NTSC - 480)/2, // viYOrigin 720, // viWidth 480, // viHeight VI_XFBMODE_DF, // xFBmode GX_FALSE, // field_rendering GX_FALSE, // aa // sample points arranged in increasing Y order { {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each {3,2},{9,6},{3,10}, // pix 1 {9,2},{3,6},{9,10}, // pix 2 {9,2},{3,6},{9,10} // pix 3 }, // vertical filter[7], 1/64 units, 6 bits each { 4, // line n-1 8, // line n-1 12, // line n 16, // line n 12, // line n 8, // line n+1 4 // line n+1 } }; /* TV Modes table */ GXRModeObj *tvmodes[6] = { &TV60hz_240p, &TV60hz_240i, &TV60hz_480i, /* 60hz modes */ &TV50hz_288p, &TV50hz_288i, &TV50hz_576i /* 50Hz modes */ }; typedef struct tagcamera { Vector pos; Vector up; Vector view; } camera; /*** Square Matrix This structure controls the size of the image on the screen. Think of the output as a -80 x 80 by -60 x 60 graph. ***/ static s16 square[] ATTRIBUTE_ALIGN (32) = { /* * X, Y, Z * Values set are for roughly 4:3 aspect */ -HASPECT, VASPECT, 0, // 0 HASPECT, VASPECT, 0, // 1 HASPECT, -VASPECT, 0, // 2 -HASPECT, -VASPECT, 0, // 3 }; static camera cam = { {0.0F, 0.0F, -100.0F}, {0.0F, -1.0F, 0.0F}, {0.0F, 0.0F, 0.0F} }; /* rendering initialization */ /* should be called each time you change quad aspect ratio */ static void draw_init(void) { /* Clear all Vertex params */ GX_ClearVtxDesc (); /* Set Position Params (quad aspect ratio) */ GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_S16, 0); GX_SetVtxDesc (GX_VA_POS, GX_INDEX8); GX_SetArray (GX_VA_POS, square, 3 * sizeof (s16)); /* Set Tex Coord Params */ GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); GX_SetVtxDesc (GX_VA_TEX0, GX_DIRECT); GX_SetTevOp (GX_TEVSTAGE0, GX_REPLACE); GX_SetTevOrder (GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL); GX_SetNumTexGens (1); GX_SetNumChans(0); /* Set Modelview */ memset (&view, 0, sizeof (Mtx)); guLookAt(view, &cam.pos, &cam.up, &cam.view); GX_LoadPosMtxImm (view, GX_PNMTX0); GX_InvVtxCache (); } /* vertex rendering */ static void draw_vert(u8 pos, f32 s, f32 t) { GX_Position1x8 (pos); GX_TexCoord2f32 (s, t); } /* textured quad rendering */ static void draw_square (void) { GX_Begin (GX_QUADS, GX_VTXFMT0, 4); draw_vert (3, 0.0, 0.0); draw_vert (2, 1.0, 0.0); draw_vert (1, 1.0, 1.0); draw_vert (0, 0.0, 1.0); GX_End (); } /* retrace handler */ static void framestart(u32 retraceCnt) { /* simply increment the tick counter */ frameticker++; } /* Initialize GX */ static void gxStart(void) { Mtx p; GXColor gxbackground = { 0, 0, 0, 0xff }; /*** Clear out FIFO area ***/ memset (&gp_fifo, 0, DEFAULT_FIFO_SIZE); /*** GX default ***/ GX_Init (&gp_fifo, DEFAULT_FIFO_SIZE); GX_SetCopyClear (gxbackground, 0x00ffffff); GX_SetViewport (0.0F, 0.0F, vmode->fbWidth, vmode->efbHeight, 0.0F, 1.0F); GX_SetScissor (0, 0, vmode->fbWidth, vmode->efbHeight); f32 yScale = GX_GetYScaleFactor(vmode->efbHeight, vmode->xfbHeight); u16 xfbHeight = GX_SetDispCopyYScale (yScale); GX_SetDispCopySrc (0, 0, vmode->fbWidth, vmode->efbHeight); GX_SetDispCopyDst (vmode->fbWidth, xfbHeight); GX_SetCopyFilter (vmode->aa, vmode->sample_pattern, GX_TRUE, vmode->vfilter); GX_SetFieldMode (vmode->field_rendering, ((vmode->viHeight == 2 * vmode->xfbHeight) ? GX_ENABLE : GX_DISABLE)); GX_SetPixelFmt (GX_PF_RGB8_Z24, GX_ZC_LINEAR); GX_SetCullMode (GX_CULL_NONE); GX_SetDispCopyGamma (GX_GM_1_0); GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_TRUE); GX_SetColorUpdate (GX_TRUE); guOrtho(p, vmode->efbHeight/2, -(vmode->efbHeight/2), -(vmode->fbWidth/2), vmode->fbWidth/2, 100, 1000); GX_LoadProjectionMtx (p, GX_ORTHOGRAPHIC); /*** reset XFB ***/ GX_CopyDisp (xfb[whichfb ^ 1], GX_TRUE); /*** Initialize texture data ***/ memset (texturemem, 0, TEX_WIDTH * TEX_HEIGHT * 2); } /* Reset GX/VI scaler */ static void gxScale(GXRModeObj *rmode) { int scale = 0; /* First configure EFB width */ switch (config.gxscaler) { case 0: /* let VI handles upscaling completely */ rmode->fbWidth = (vwidth <= 640) ? vwidth : 640; break; case 1: /* GX only doubles original width */ rmode->fbWidth = (vwidth*2 <= 640) ? (vwidth*2) : ((vwidth <= 640) ? vwidth : 640); break; case 2: /* GX upscale up to max EFB width (640 pixels) */ rmode->fbWidth = 640; break; } /* Configure GX scaler and VI width */ if (xscale > (rmode->fbWidth/2)) { /* check max upscaling */ if (xscale > 360) { scale = xscale - 360; /* save offset for later */ xscale = 360; } /* VI handles the remaining upscaling */ rmode->viWidth = xscale * 2; rmode->viXOrigin = (720 - (xscale * 2)) / 2; /* set GX scaling to max EFB width */ scale += (rmode->fbWidth/2); } else { /* VI should not upscale anything */ rmode->viWidth = rmode->fbWidth; rmode->viXOrigin = (720 - rmode->fbWidth) / 2; /* set GX scaling to max EFB width */ scale = xscale; } /* update GX scaler (Vertex Position Matrix) */ square[6] = square[3] = scale + xshift; square[0] = square[9] = -scale + xshift; square[4] = square[1] = yscale + yshift; square[7] = square[10] = -yscale + yshift; draw_init(); } /* Set Aspect Ratio (depending on current configuration) */ void ogc_video__aspect() { if (config.aspect) { /* original aspect ratio */ /* the following values have been detected from comparison with a real 50/60hz Mega Drive */ if (config.overscan) { /* borders are emulated */ xscale = (reg[12] & 1) ? 360 : 358; if (gc_pal) xscale -= 1; yscale = (gc_pal && !config.render) ? (vdp_pal ? 144:143) : (vdp_pal ? 121:120); } else { /* borders are simulated (black) */ xscale = 327; yscale = bitmap.viewport.h / 2; if (vdp_pal && (!gc_pal || config.render)) yscale = yscale * 243 / 288; else if (!vdp_pal && gc_pal && !config.render) yscale = yscale * 288 / 243; } xshift = 8 + config.xshift; /* default RGB offset, composite might be shifted less */ yshift = (vdp_pal ? 1 : 3) - (config.overscan ? 0 : 1) + config.yshift; } else { /* manual aspect ratio (default is fullscreen) */ if (config.overscan) { /* borders are emulated */ xscale = 352; yscale = (gc_pal && !config.render) ? (vdp_pal ? (268*144 / bitmap.viewport.h):143) : (vdp_pal ? (224*144 / bitmap.viewport.h):120); } else { xscale = 320; yscale = (gc_pal && !config.render) ? 134 : 112; } /* user scaling */ xscale += config.xscale; yscale += config.yscale; xshift = config.xshift; yshift = config.yshift; } /* Double resolution modes */ if (config.render) { yscale *= 2; yshift *= 2; } } /* Reinitialize Video */ void ogc_video__reset() { Mtx p; GXRModeObj *rmode; /* Set 50Hz/60Hz mode */ if ((config.tv_mode == 1) || ((config.tv_mode == 2) && vdp_pal)) gc_pal = 1; else gc_pal = 0; /* Set current TV mode */ if (config.render) rmode = tvmodes[gc_pal*3 + 2]; else rmode = tvmodes[gc_pal*3 + interlaced]; /* Aspect ratio */ ogc_video__aspect(); gxScale(rmode); /* Progressive mode support */ if (config.render == 2) { /* 480p */ rmode->viTVMode = VI_TVMODE_NTSC_PROG; rmode->xfbMode = VI_XFBMODE_SF; } else if (config.render == 1) { /* 480i */ rmode->viTVMode = tvmodes[0]->viTVMode & ~3; rmode->xfbMode = VI_XFBMODE_DF; } /* Configure VI */ VIDEO_Configure (rmode); VIDEO_ClearFrameBuffer(rmode, xfb[whichfb], COLOR_BLACK); VIDEO_Flush(); VIDEO_WaitVSync(); if (rmode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync(); else while (VIDEO_GetNextField()) VIDEO_WaitVSync(); odd_frame = 1; /* Configure GX */ GX_SetViewport (0.0F, 0.0F, rmode->fbWidth, rmode->efbHeight, 0.0F, 1.0F); GX_SetScissor (0, 0, rmode->fbWidth, rmode->efbHeight); f32 yScale = GX_GetYScaleFactor(rmode->efbHeight, rmode->xfbHeight); u16 xfbHeight = GX_SetDispCopyYScale (yScale); GX_SetDispCopySrc (0, 0, rmode->fbWidth, rmode->efbHeight); GX_SetDispCopyDst (rmode->fbWidth, xfbHeight); GX_SetCopyFilter (rmode->aa, rmode->sample_pattern, config.render ? GX_TRUE : GX_FALSE, rmode->vfilter); GX_SetFieldMode (rmode->field_rendering, ((rmode->viHeight == 2 * rmode->xfbHeight) ? GX_ENABLE : GX_DISABLE)); GX_SetPixelFmt (rmode->aa ? GX_PF_RGB565_Z16 : GX_PF_RGB8_Z24, GX_ZC_LINEAR); guOrtho(p, rmode->efbHeight/2, -(rmode->efbHeight/2), -(rmode->fbWidth/2), rmode->fbWidth/2, 100, 1000); GX_LoadProjectionMtx (p, GX_ORTHOGRAPHIC); /* Software NTSC filter */ if (config.ntsc == 1) { sms_setup = sms_ntsc_composite; md_setup = md_ntsc_composite; sms_ntsc_init( &sms_ntsc, &sms_setup ); md_ntsc_init( &md_ntsc, &md_setup ); } else if (config.ntsc == 2) { sms_setup = sms_ntsc_svideo; md_setup = md_ntsc_svideo; sms_ntsc_init( &sms_ntsc, &sms_setup ); md_ntsc_init( &md_ntsc, &md_setup ); } else if (config.ntsc == 3) { sms_setup = sms_ntsc_rgb; md_setup = md_ntsc_rgb; sms_ntsc_init( &sms_ntsc, &sms_setup ); md_ntsc_init( &md_ntsc, &md_setup ); } } /* GX render update */ void ogc_video__update() { int h, w; /* texture and bitmap buffers (buffers width is fixed to 720 pixels) */ long long int *dst = (long long int *)texturemem; long long int *src1 = (long long int *)(bitmap.data); /* line n */ long long int *src2 = src1 + 180; /* line n+1 */ long long int *src3 = src2 + 180; /* line n+2 */ long long int *src4 = src3 + 180; /* line n+3 */ /* check if viewport has changed */ if (bitmap.viewport.changed) { bitmap.viewport.changed = 0; /* update texture size */ vwidth = bitmap.viewport.w + 2 * bitmap.viewport.x; vheight = bitmap.viewport.h + 2 * bitmap.viewport.y; /* special cases */ if (config.render && (interlaced || config.ntsc)) vheight *= 2; if (config.ntsc) vwidth = (reg[12]&1) ? MD_NTSC_OUT_WIDTH(vwidth) : SMS_NTSC_OUT_WIDTH(vwidth); /* texels size must be multiple of 4 */ vwidth = (vwidth / 4) * 4; vheight = (vheight / 4) * 4; /* final offset */ stride = bitmap.width - (vwidth >> 2); /* image size has changed, reset GX */ ogc_video__reset(); /* reinitialize texture */ GX_InvalidateTexAll (); GX_InitTexObj (&texobj, texturemem, vwidth, vheight, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE); /* enable/disable bilinear filtering */ if (!config.bilinear) { GX_InitTexObjLOD(&texobj,GX_NEAR,GX_NEAR_MIP_NEAR,2.5,9.0,0.0,GX_FALSE,GX_FALSE,GX_ANISO_1); } } GX_InvalidateTexAll (); /* update texture data */ for (h = 0; h < vheight; h += 4) { for (w = 0; w < (vwidth >> 2); w++) { *dst++ = *src1++; *dst++ = *src2++; *dst++ = *src3++; *dst++ = *src4++; } /* jump to next four lines */ src1 += stride; src2 += stride; src3 += stride; src4 += stride; } /* load texture into GX */ DCFlushRange (texturemem, vwidth * vheight * 2); GX_LoadTexObj (&texobj, GX_TEXMAP0); /* render textured quad */ draw_square (); GX_DrawDone (); /* switch external framebuffers then copy EFB to XFB */ whichfb ^= 1; GX_CopyDisp (xfb[whichfb], GX_TRUE); GX_Flush (); /* set next XFB */ VIDEO_SetNextFramebuffer (xfb[whichfb]); VIDEO_Flush (); } /* Initialize VIDEO subsystem */ void ogc_video__init(void) { /* * Before doing anything else under libogc, * Call VIDEO_Init */ VIDEO_Init (); /* * Before any memory is allocated etc. * Rescue any tagged ROM in data 2 */ int *romptr = (int *)0x80700000; StartARAM(); genromsize = 0; if ( memcmp((char *)romptr,"GENPLUSR",8) == 0 ) { genromsize = romptr[2]; ARAMPut ((char *) 0x80700000 + 0x20, (char *) 0x8000, genromsize); } /* Get the current video mode then : - set menu video mode (480p, 480i or 576i) - set emulator rendering TV modes (PAL/MPAL/NTSC/EURGB60) */ vmode = VIDEO_GetPreferredMode(NULL); /* adjust display settings */ switch (vmode->viTVMode >> 2) { case VI_PAL: /* 576 lines (PAL 50Hz) */ TV60hz_240p.viTVMode = VI_TVMODE_EURGB60_DS; TV60hz_240i.viTVMode = VI_TVMODE_EURGB60_INT; TV60hz_480i.viTVMode = VI_TVMODE_EURGB60_INT; config.tv_mode = 1; gc_pal = 1; /* display should be centered vertically (borders) */ vmode = &TVPal574IntDfScale; vmode->xfbHeight = 480; vmode->viYOrigin = (VI_MAX_HEIGHT_PAL - 480)/2; vmode->viHeight = 480; break; case VI_NTSC: /* 480 lines (NTSC 60hz) */ TV60hz_240p.viTVMode = VI_TVMODE_NTSC_DS; TV60hz_240i.viTVMode = VI_TVMODE_NTSC_INT; TV60hz_480i.viTVMode = VI_TVMODE_NTSC_INT; config.tv_mode = 0; gc_pal = 0; #ifndef HW_RVL /* force 480p on GameCube if the Component Cable is present */ if (VIDEO_HaveComponentCable()) vmode = &TVNtsc480Prog; #endif break; default: /* 480 lines (PAL 60Hz) */ TV60hz_240p.viTVMode = VI_TVMODE(vmode->viTVMode >> 2, VI_NON_INTERLACE); TV60hz_240i.viTVMode = VI_TVMODE(vmode->viTVMode >> 2, VI_INTERLACE); TV60hz_480i.viTVMode = VI_TVMODE(vmode->viTVMode >> 2, VI_INTERLACE); config.tv_mode = 2; gc_pal = 0; break; } /* configure video mode */ VIDEO_Configure (vmode); /* Configure the framebuffers (double-buffering) */ xfb[0] = (u32 *) MEM_K0_TO_K1((u32 *) SYS_AllocateFramebuffer(&TV50hz_576i)); xfb[1] = (u32 *) MEM_K0_TO_K1((u32 *) SYS_AllocateFramebuffer(&TV50hz_576i)); /* Define a console */ console_init(xfb[0], 20, 64, 640, 574, 574 * 2); /* Clear framebuffers to black */ VIDEO_ClearFrameBuffer (vmode, xfb[0], COLOR_BLACK); VIDEO_ClearFrameBuffer (vmode, xfb[1], COLOR_BLACK); /* Set the framebuffer to be displayed at next VBlank */ VIDEO_SetNextFramebuffer (xfb[0]); /* Register Video Retrace handlers */ VIDEO_SetPreRetraceCallback(framestart); /* Enable Video Interface */ VIDEO_SetBlack (FALSE); /* Update video settings for next VBlank */ VIDEO_Flush (); /* Wait for VBlank */ VIDEO_WaitVSync(); VIDEO_WaitVSync(); /* initialize GUI */ unpackBackdrop (); init_font(); /* Initialize GX */ gxStart(); }