/* * Glide64 - Glide video plugin for Nintendo 64 emulators. * Copyright (c) 2002 Dave2001 * * 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 * 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 */ //**************************************************************** // // Glide64 - Glide Plugin for Nintendo 64 emulators (tested mostly with Project64) // Project started on December 29th, 2001 // // To modify Glide64: // * Write your name and (optional)email, commented by your work, so I know who did it, and so that you can find which parts you modified when it comes time to send it to me. // * Do NOT send me the whole project or file that you modified. Take out your modified code sections, and tell me where to put them. If people sent the whole thing, I would have many different versions, but no idea how to combine them all. // // Official Glide64 development channel: #Glide64 on EFnet // // Original author: Dave2001 (Dave2999@hotmail.com) // Other authors: Gonetz, Gugaman // //**************************************************************** #include "Gfx #1.3.h" #include "Util.h" #include "3dmath.h" #include "Debugger.h" #include "Combine.h" #include "Util.h" #include "Ini.h" #include "Tmem.h" #include "TexCache.h" #include "TexCache.h" #include "TexBuffer.h" #include "CRC.h" #ifndef _WIN32 #include "messagebox.h" #include #endif // _WIN32 char out_buf[2048]; DWORD frame_count; // frame counter BOOL ucode_error_report = TRUE; BOOL vi_swap = FALSE; int wrong_tile = -1; // ** RDP graphics functions ** static void undef(); static void spnoop(); static void rdp_noop(); static void rdp_texrect(); //static void rdp_texrectflip(); static void rdp_loadsync(); static void rdp_pipesync(); static void rdp_tilesync(); static void rdp_fullsync(); static void rdp_setkeygb(); static void rdp_setkeyr(); static void rdp_setconvert(); static void rdp_setscissor(); static void rdp_setprimdepth(); static void rdp_setothermode(); static void rdp_loadtlut(); static void rdp_settilesize(); static void rdp_loadblock(); static void rdp_loadtile(); static void rdp_settile(); static void rdp_fillrect(); static void rdp_setfillcolor(); static void rdp_setfogcolor(); static void rdp_setblendcolor(); static void rdp_setprimcolor(); static void rdp_setenvcolor(); static void rdp_setcombine(); static void rdp_settextureimage(); static void rdp_setdepthimage(); static void rdp_setcolorimage(); static void rdp_trifill(); static void rdp_trishade(); static void rdp_tritxtr(); static void rdp_trishadetxtr(); static void rdp_trifillz(); static void rdp_trishadez(); static void rdp_tritxtrz(); static void rdp_trishadetxtrz(); static void rsp_reserved0(); static void rsp_reserved1(); static void rsp_reserved2(); static void rsp_reserved3(); static void ys_memrect(); BYTE microcode[4096]; DWORD uc_crc; void microcheck (); // static constants static char *ACmp[] = { "NONE", "THRESHOLD", "DITHER", "UNKNOWN" }; static char *Mode0[] = { "COMBINED", "TEXEL0", "TEXEL1", "PRIMITIVE", "SHADE", "ENVIORNMENT", "1", "NOISE", "0", "0", "0", "0", "0", "0", "0", "0" }; static char *Mode1[] = { "COMBINED", "TEXEL0", "TEXEL1", "PRIMITIVE", "SHADE", "ENVIORNMENT", "CENTER", "K4", "0", "0", "0", "0", "0", "0", "0", "0" }; static char *Mode2[] = { "COMBINED", "TEXEL0", "TEXEL1", "PRIMITIVE", "SHADE", "ENVIORNMENT", "SCALE", "COMBINED_ALPHA", "T0_ALPHA", "T1_ALPHA", "PRIM_ALPHA", "SHADE_ALPHA", "ENV_ALPHA", "LOD_FRACTION", "PRIM_LODFRAC", "K5", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0" }; static char *Mode3[] = { "COMBINED", "TEXEL0", "TEXEL1", "PRIMITIVE", "SHADE", "ENVIORNMENT", "1", "0" }; static char *Alpha0[] = { "COMBINED", "TEXEL0", "TEXEL1", "PRIMITIVE", "SHADE", "ENVIORNMENT", "1", "0" }; #define Alpha1 Alpha0 static char *Alpha2[] = { "LOD_FRACTION", "TEXEL0", "TEXEL1", "PRIMITIVE", "SHADE", "ENVIORNMENT", "PRIM_LODFRAC", "0" }; #define Alpha3 Alpha0 static char *str_zs[] = { "G_ZS_PIXEL", "G_ZS_PRIM" }; static char *str_offon[] = { "OFF", "ON" }; // I=intensity probably static char *str_format[] = { "RGBA", "YUV", "CI", "IA", "I", "?", "?", "?" }; static char *str_size[] = { "4bit", "8bit", "16bit", "32bit" }; static char *str_cm[] = { "WRAP/NO CLAMP", "MIRROR/NO CLAMP", "WRAP/CLAMP", "MIRROR/CLAMP" }; static char *str_filter[] = { "Point Sampled", "Average (box)", "Bilinear" }; static char *str_tlut[] = { "TT_NONE", "TT_UNKNOWN", "TT_RGBA_16", "TT_IA_16" }; // ** UCODE FUNCTIONS ** #include "Ucode00.h" #include "ucode01.h" #include "ucode02.h" #include "ucode03.h" #include "ucode04.h" #include "ucode05.h" #include "ucode06.h" #include "ucode07.h" #include "ucode08.h" #include "ucode.h" static BOOL reset = 0; static int old_ucode = -1; // rdp_reset - resets the RDP_E void rdp_reset () { reset = 1; rdp.model_i = 0; rdp.n_cached[0] = 0; rdp.n_cached[1] = 0; rdp.cur_cache[0] = NULL; rdp.cur_cache[1] = NULL; rdp.tmem_ptr[0] = offset_textures; rdp.tmem_ptr[1] = offset_textures; if (grTextureBufferExt) rdp.tmem_ptr[1] = TEXMEM_2MB_EDGE * 2; rdp.c_a0 = 0; rdp.c_b0 = 0; rdp.c_c0 = 0; rdp.c_d0 = 0; rdp.c_Aa0 = 0; rdp.c_Ab0 = 0; rdp.c_Ac0 = 0; rdp.c_Ad0 = 0; rdp.c_a1 = 0; rdp.c_b1 = 0; rdp.c_c1 = 0; rdp.c_d1 = 0; rdp.c_Aa1 = 0; rdp.c_Ab1 = 0; rdp.c_Ac1 = 0; rdp.c_Ad1 = 0; // Clear the palette CRC int i; for (i=0; i<16; i++) rdp.pal_8_crc[i] = 0; // Clear the palettes for (i=0; i<256; i++) rdp.pal_8[i] = 0; rdp.tlut_mode = 0; // Clear all segments ** VERY IMPORTANT FOR ZELDA ** for (i=0; i<16; i++) rdp.segment[i] = 0; for (i=0; i<512; i++) rdp.addr[i] = 0; // set all vertex numbers for (i=0; i>2; i++) { uc_crc += ((DWORD*)microcode)[i]; } FRDP_E ("crc: %08lx\n", uc_crc); #ifdef LOG_UCODE ofstream ucf; ucf.open ("ucode.txt", ios::out | ios::binary); char d; for (i=0; i<0x400000; i++) { d = ((char*)gfx.RDRAM)[i^3]; ucf.write (&d, 1); } ucf.close (); #endif char str[9]; sprintf (str, "%08lx", (unsigned long)uc_crc); INI_Open (); INI_FindSection ("UCODE"); FRDP("ucode = %s\n", str); int uc = INI_ReadInt (str, -2, 0); if (uc == -2 && ucode_error_report) { INI_FindSection ("SETTINGS"); settings.ucode = INI_ReadInt ("ucode", 0); INI_Close (); ReleaseGfx (); sprintf (out_buf, "Error: uCode crc not found in INI, using currently selected uCode\n\n%08lx", (unsigned long)uc_crc); #ifdef _WIN32 MessageBox (gfx.hWnd, out_buf, "Error", MB_OK|MB_ICONEXCLAMATION); #else // _WIN32 messagebox("Error", MB_OK|MB_ICONEXCLAMATION, out_buf); #endif // _WIN32 ucode_error_report = FALSE; // don't report any more ucode errors from this game } else if (uc == -1 && ucode_error_report) { INI_FindSection ("SETTINGS"); settings.ucode = INI_ReadInt ("ucode", 0); INI_Close (); ReleaseGfx (); sprintf (out_buf, "Error: Unsupported uCode!\n\ncrc: %08lx", (unsigned long)uc_crc); #ifdef _WIN32 MessageBox (gfx.hWnd, out_buf, "Error", MB_OK|MB_ICONEXCLAMATION); #else // _WIN32 messagebox("Error", MB_OK|MB_ICONEXCLAMATION, out_buf); #endif // _WIN32 ucode_error_report = FALSE; // don't report any more ucode errors from this game } else { old_ucode = settings.ucode; settings.ucode = uc; FRDP("microcheck: old ucode: %d, new ucode: %d\n", old_ucode, uc); //INI_FindSection ("SETTINGS"); //INI_WriteInt ("ucode", uc); INI_Close (); } } #ifdef _WIN32 RECT prev_rect; #endif // _WIN32 void drawNoFullscreenMessage() { printf("drawNoFullscreenMessage ()\n"); #ifdef _WIN32 LOG ("drawNoFullscreenMessage ()\n"); SIZE str_size; RECT win_rect; HWND active_wnd = GetForegroundWindow (); GetClientRect (gfx.hWnd, &win_rect); if (win_rect.bottom != prev_rect.bottom || win_rect.right != prev_rect.right || rdp.window_changed) { rdp.window_changed = FALSE; prev_rect.bottom = win_rect.bottom; prev_rect.right = win_rect.right; HDC hdc = GetDC(gfx.hWnd); SetBkMode (hdc, TRANSPARENT); SetTextColor (hdc, RGB(255,255,255)); FillRect (hdc, &win_rect, (HBRUSH)GetStockObject(DKGRAY_BRUSH)); win_rect.bottom >>= 1; win_rect.right >>= 1; sprintf (out_buf, "Glide64"); GetTextExtentPoint32 (hdc, out_buf, strlen(out_buf), &str_size); TextOut (hdc, win_rect.right - (str_size.cx>>1), win_rect.bottom - str_size.cy - 32, out_buf, strlen(out_buf)); sprintf (out_buf, "Gfx cannot be drawn in windowed mode"); GetTextExtentPoint32 (hdc, out_buf, strlen(out_buf), &str_size); TextOut (hdc, win_rect.right - (str_size.cx>>1), win_rect.bottom - str_size.cy - 2, out_buf, strlen(out_buf)); sprintf (out_buf, "Press Alt+Enter to switch to fullscreen"); GetTextExtentPoint32 (hdc, out_buf, strlen(out_buf), &str_size); TextOut (hdc, win_rect.right - (str_size.cx>>1), win_rect.bottom + 2, out_buf, strlen(out_buf)); } #endif // _WIN32 } static WORD yuv_to_rgb(BYTE y, BYTE u, BYTE v) { float r = y + (1.370705f * (v-128)); float g = y - (0.698001f * (v-128)) - (0.337633f * (u-128)); float b = y + (1.732446f * (u-128)); r *= 0.125f; g *= 0.125f; b *= 0.125f; //clipping the result if (r > 32) r = 32; if (g > 32) g = 32; if (b > 32) b = 32; if (r < 0) r = 0; if (g < 0) g = 0; if (b < 0) b = 0; WORD c = (WORD)(((WORD)(r) << 11) | ((WORD)(g) << 6) | ((WORD)(b) << 1) | 1); return c; } static void DrawYUVImageToFrameBuffer() { WORD width = (WORD)(rdp.yuv_lr_x - rdp.yuv_ul_x); WORD height = (WORD)(rdp.yuv_lr_y - rdp.yuv_ul_y); DWORD * mb = (DWORD*)(gfx.RDRAM+rdp.yuv_im_begin); //pointer to the first macro block WORD * cimg = (WORD*)(gfx.RDRAM+rdp.cimg); //yuv macro block contains 16x16 texture. we need to put it in the proper place inside cimg for (WORD y = 0; y < height; y+=16) { for (WORD x = 0; x < width; x+=16) { WORD *dst = cimg + x + y * rdp.ci_width; for (WORD h = 0; h < 16; h++) { for (WORD w = 0; w < 8; w++) { DWORD t = *(mb++); //each DWORD contains 2 pixels if ((x < rdp.ci_width) && (y < rdp.ci_height)) //clipping. texture image may be larger than color image { BYTE y0 = (BYTE)t&0xFF; BYTE v = (BYTE)(t>>8)&0xFF; BYTE y1 = (BYTE)(t>>16)&0xFF; BYTE u = (BYTE)(t>>24)&0xFF; *(dst++) = yuv_to_rgb(y0, u, v); *(dst++) = yuv_to_rgb(y1, u, v); } } dst += rdp.ci_width - 16; } mb += 64; //macro block is 768 bytes long, last 256 bytes are useless } } } static DWORD d_ul_x, d_ul_y, d_lr_x, d_lr_y; typedef struct { int ul_x, ul_y, lr_x, lr_y; } FB_PART; static void DrawPart(int scr_ul_x, int scr_ul_y, int prt_ul_x, int prt_ul_y, int width, int height, float scale_x, float scale_y) { WORD * dst = new WORD[width*height]; DWORD shift = ((d_ul_y+prt_ul_y) * rdp.ci_width + d_ul_x + prt_ul_x) << 1; WORD * src = (WORD*)(gfx.RDRAM+rdp.cimg+shift); WORD c; for (int y=0; y < height; y++) { for (int x=0; x < width; x++) { c = src[(int(x*scale_x)+int(y*scale_y)*rdp.ci_width)^1]; dst[x+y*width] = c?((c >> 1) | 0x8000):0; } } grLfbWriteRegion(GR_BUFFER_BACKBUFFER, scr_ul_x, scr_ul_y, GR_LFB_SRC_FMT_1555, width, height, FXTRUE, width<<1, dst); delete[] dst; } static void DrawFrameBufferToScreen() { FRDP("DrawFrameBufferToScreen. cimg: %08lx, ul_x: %d, uly: %d, lr_x: %d, lr_y: %d\n", rdp.cimg, d_ul_x, d_ul_y, d_lr_x, d_lr_y); if (!fullscreen) return; grColorCombine (GR_COMBINE_FUNCTION_SCALE_OTHER, GR_COMBINE_FACTOR_ONE, GR_COMBINE_LOCAL_NONE, GR_COMBINE_OTHER_TEXTURE, FXFALSE); grAlphaCombine (GR_COMBINE_FUNCTION_SCALE_OTHER, GR_COMBINE_FACTOR_ONE, GR_COMBINE_LOCAL_NONE, GR_COMBINE_OTHER_TEXTURE, FXFALSE); grConstantColorValue (0xFFFFFFFF); grAlphaBlendFunction( GR_BLEND_SRC_ALPHA, GR_BLEND_ONE_MINUS_SRC_ALPHA, GR_BLEND_ONE, GR_BLEND_ZERO); rdp.update |= UPDATE_COMBINE; float scale_x_dst = (float)settings.scr_res_x / rdp.vi_width;//(float)max(rdp.frame_buffers[rdp.main_ci_index].width, rdp.ci_width); float scale_y_dst = (float)settings.scr_res_y / rdp.vi_height;//(float)max(rdp.frame_buffers[rdp.main_ci_index].height, rdp.ci_lower_bound); float scale_x_src = (float)rdp.vi_width / (float)settings.scr_res_x;//(float)max(rdp.frame_buffers[rdp.main_ci_index].width, rdp.ci_width); float scale_y_src = (float)rdp.vi_height / (float)settings.scr_res_y;//(float)max(rdp.frame_buffers[rdp.main_ci_index].height, rdp.ci_lower_bound); int src_width = d_lr_x - d_ul_x + 1; int src_height = d_lr_y - d_ul_y + 1; int dst_width, dst_height, ul_x, ul_y; if ((src_width < 33) && (src_height < 33)) { dst_width = int(src_width*scale_x_dst); dst_height = int(src_height*scale_y_dst); ul_x = int(d_ul_x*scale_x_dst); ul_y = int(d_ul_y*scale_y_dst); DrawPart(ul_x, ul_y, 0, 0, dst_width, dst_height, scale_x_src, scale_y_src); memset(gfx.RDRAM+rdp.cimg, 0, rdp.ci_width*rdp.ci_height*rdp.ci_size); return; } FB_PART parts[8]; int p; for (p = 0; p < 8; p++) { parts[p].lr_x = parts[p].lr_y = 0; parts[p].ul_x = parts[p].ul_y = 0xFFFF; } int num_of_parts = 0; int cur_part = 0; int most_left = d_ul_x; int most_right = d_lr_x; DWORD shift = (d_ul_y * rdp.ci_width + d_ul_x) << 1; WORD * src = (WORD*)(gfx.RDRAM+rdp.cimg+shift); for (int h = 0; h < src_height; h++) { cur_part = 0; int w = 0; while (w < src_width) { while (w < src_width) { if (src[(w+h*rdp.ci_width)^1] == 0) w++; else break; } if (w == src_width) break; if (num_of_parts == 0) //first part { parts[0].ul_x = w; most_left = w; parts[0].ul_y = h; cur_part = 0; } else if (w < most_left - 2) //new part { parts[num_of_parts].ul_x = w; most_left = w; parts[num_of_parts].ul_y = h; cur_part = num_of_parts; num_of_parts++; } else if (w > most_right + 2) //new part { parts[num_of_parts].ul_x = w; most_right = w; parts[num_of_parts].ul_y = h; cur_part = num_of_parts; num_of_parts++; } else { for (p = 0; p < num_of_parts; p++) { if ((w > parts[p].ul_x - 2) && (w < parts[p].lr_x+2)) { if (w < parts[p].ul_x) parts[p].ul_x = w; break; } } cur_part = p; } while (w < src_width) { if (src[(w+h*rdp.ci_width)^1] != 0) w++; else break; } if (num_of_parts == 0) //first part { parts[0].lr_x = w; most_right = w; num_of_parts++; } else { if (parts[cur_part].lr_x < w) parts[cur_part].lr_x = w; if (most_right < w) most_right = w; parts[cur_part].lr_y = h; } } } /* for (p = 0; p < num_of_parts; p++) { FRDP("part#%d ul_x: %d, ul_y: %d, lr_x: %d, lr_y: %d\n", p, parts[p].ul_x, parts[p].ul_y, parts[p].lr_x, parts[p].lr_y); } */ for (p = 0; p < num_of_parts; p++) { dst_width = int((parts[p].lr_x-parts[p].ul_x + 1)*scale_x_dst); dst_height = int((parts[p].lr_y-parts[p].ul_y + 1)*scale_y_dst); ul_x = int((d_ul_x+parts[p].ul_x)*scale_x_dst); ul_y = int((d_ul_y+parts[p].ul_y)*scale_y_dst); DrawPart(ul_x, ul_y, parts[p].ul_x, parts[p].ul_y, dst_width, dst_height, scale_x_src, scale_y_src); } memset(gfx.RDRAM+rdp.cimg, 0, rdp.ci_width*rdp.ci_height*rdp.ci_size); } /****************************************************************** Function: ProcessDList Purpose: This function is called when there is a Dlist to be processed. (High level GFX list) input: none output: none *******************************************************************/ void DetectFrameBufferUsage (); DWORD fbreads = 0; BOOL cpu_fb_write = FALSE; EXPORT void CALL ProcessDList(void) { no_dlist = FALSE; /*sprintf (out_buf, "VI_X_SCALE_REG: %08lx, " "VI_Y_SCALE_REG: %08lx\n", *gfx.VI_X_SCALE_REG, *gfx.VI_Y_SCALE_REG); LOG (out_buf);*/ ChangeSize (); #ifdef ALTTAB_FIX if (!hhkLowLevelKybd) { hhkLowLevelKybd = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hInstance, 0); } #endif LOG ("ProcessDList ()\n"); if (settings.swapmode > 0) SwapOK = TRUE; rdp.updatescreen = 1; #ifdef _WIN32 DWORD cur_time = timeGetTime (); #else // _WIN32 /*struct timeval tv; gettimeofday(&tv, 0); DWORD cur_time = tv.tv_usec;*/ #endif // _WIN32 if (!fullscreen) drawNoFullscreenMessage(); if (reset) { reset = 0; memset (microcode, 0, 4096); if (settings.autodetect_ucode) { // Thanks to ZeZu for ucode autodetection!!! DWORD startUcode = *(DWORD*)(gfx.DMEM+0xFD0); memcpy (microcode, gfx.RDRAM+startUcode, 4096); microcheck (); } } else if ( ((old_ucode == 6) && (settings.ucode == 1)) || settings.PPL) { DWORD startUcode = *(DWORD*)(gfx.DMEM+0xFD0); memcpy (microcode, gfx.RDRAM+startUcode, 4096); microcheck (); } // Set an interrupt to allow the game to continue *gfx.MI_INTR_REG |= 0x20; gfx.CheckInterrupts(); if (exception) return; // Switch to fullscreen? if (to_fullscreen) { to_fullscreen = FALSE; if (!InitGfx (FALSE)) { LOG ("FAILED!!!\n"); return; } fullscreen = TRUE; #ifdef _WIN32 if (gfx.hStatusBar) ShowWindow( gfx.hStatusBar, SW_HIDE ); ShowCursor( FALSE ); #endif // _WIN32 } if (!fullscreen && !settings.run_in_window) return; // Clear out the RDP log #ifdef RDP_LOGGING if (settings.logging && settings.log_clear) { CLOSE_RDP_LOG (); OPEN_RDP_LOG (); } #endif #ifdef UNIMP_LOG if (settings.log_unk && settings.unk_clear) { ofstream unimp; unimp.open("unimp.txt"); unimp.close(); } #endif rdp.tri_n = 0; // 0 triangles so far this frame rdp.debug_n = 0; //Added by Gonetz. VERY IMPORTANT FOR WAVE RACE US rdp.model_i = 0; // 0 matrices so far in stack //stack_size can be less then 32! Important for Silicon Vally. Thanks Orkin! rdp.model_stack_size = min(32, (*(DWORD*)(gfx.DMEM+0x0FE4))>>6); if (rdp.model_stack_size == 0) rdp.model_stack_size = 32; rdp.fb_drawn = FALSE; rdp.update = 0x7FFFFFFF; // All but clear cache rdp.geom_mode = 0; rdp.maincimg[1] = rdp.maincimg[0]; rdp.skip_drawing = FALSE; rdp.s2dex_tex_loaded = FALSE; fbreads = 0; if (cpu_fb_write == TRUE) DrawFrameBufferToScreen(); cpu_fb_write = FALSE; d_ul_x = 0xffff; d_ul_y = 0xffff; d_lr_x = 0; d_lr_y = 0; //analize possible frame buffer usage if (settings.fb_smart) DetectFrameBufferUsage(); // Get the start of the display list and the length of it DWORD dlist_start = *(DWORD*)(gfx.DMEM+0xFF0); DWORD dlist_length = *(DWORD*)(gfx.DMEM+0xFF4); FRDP("--- NEW DLIST --- crc: %08lx, ucode: %d, fbuf: %08lx, dlist start: %08lx, dlist_lenght: %d\n", uc_crc, settings.ucode, *gfx.VI_ORIGIN_REG, dlist_start, dlist_length); FRDP_E("--- NEW DLIST --- crc: %08lx, ucode: %d, fbuf: %08lx\n", uc_crc, settings.ucode, *gfx.VI_ORIGIN_REG); if (settings.tonic && dlist_length < 16) { FRDP_E("DLIST is too short!\n"); return; } DWORD a; rdp.zsrc = 0; // Start executing at the start of the display list rdp.pc_i = 0; rdp.pc[rdp.pc_i] = dlist_start; rdp.dl_count = -1; rdp.halt = 0; // catches exceptions so that it doesn't freeze #ifdef CATCH_EXCEPTIONS try { #endif // MAIN PROCESSING LOOP do { // Get the address of the next command a = rdp.pc[rdp.pc_i] & BMASK; // Load the next command and its input rdp.cmd0 = ((DWORD*)gfx.RDRAM)[a>>2]; // \ Current command, 64 bit rdp.cmd1 = ((DWORD*)gfx.RDRAM)[(a>>2)+1]; // / // cmd2 and cmd3 are filled only when needed, by the function that needs them // Output the address before the command #ifdef LOG_COMMANDS FRDP ("%08lx (c0:%08lx, c1:%08lx): ", a, rdp.cmd0, rdp.cmd1); #else FRDP ("%08lx: ", a); #endif // Go to the next instruction rdp.pc[rdp.pc_i] = (a+8) & BMASK; #ifdef PERFORMANCE QueryPerformanceCounter ((LARGE_INTEGER*)&perf_cur); #endif // Process this instruction gfx_instruction[settings.ucode][rdp.cmd0>>24] (); // check DL counter if (rdp.dl_count != -1) { rdp.dl_count --; if (rdp.dl_count == 0) { rdp.dl_count = -1; RDP ("End of DL\n"); rdp.pc_i --; } } #ifdef PERFORMANCE QueryPerformanceCounter ((LARGE_INTEGER*)&perf_next); __int64 t = perf_next-perf_cur; sprintf (out_buf, "perf %08lx: %016I64d\n", a-8, t); rdp_log << out_buf; #endif } while (!rdp.halt); #ifdef CATCH_EXCEPTIONS } catch (...) { if (fullscreen) ReleaseGfx (); # ifdef _WIN32 if (MessageBox (gfx.hWnd, "The GFX plugin caused an exception and has been disabled.\nWould you like to turn it back on and attempt to continue?", "Glide64 Exception", MB_YESNO|MB_ICONEXCLAMATION) == IDNO) exception = TRUE; # else // _WIN32 if (messagebox("Glide64 Exception", MB_YESNO|MB_ICONEXCLAMATION, "The GFX plugin caused an exception and has been disabled.\nWould you like to turn it back on and attempt to continue?") == 2) exception = TRUE; # endif // _WIN32 } #endif if (settings.fb_smart) { rdp.scale_x = rdp.scale_x_bak; rdp.scale_y = rdp.scale_y_bak; } if (settings.fb_read_always) { DrawFrameBuffer (); } if (rdp.yuv_image) { DrawYUVImageToFrameBuffer(); rdp.yuv_image = FALSE; // FRDP("yuv image draw. ul_x: %f, ul_y: %f, lr_x: %f, lr_y: %f, begin: %08lx\n", // rdp.yuv_ul_x, rdp.yuv_ul_y, rdp.yuv_lr_x, rdp.yuv_lr_y, rdp.yuv_im_begin); rdp.yuv_ul_x = rdp.yuv_ul_y = rdp.yuv_lr_x = rdp.yuv_lr_y = 0; rdp.yuv_im_begin = 0x00FFFFFF; } if (rdp.cur_image) CloseTextureBuffer(); RDP("ProcessDList end\n"); } // undef - undefined instruction, always ignore static void undef() { FRDP_E("** undefined ** (%08lx)\n", rdp.cmd0); FRDP("** undefined ** (%08lx) - IGNORED\n", rdp.cmd0); #ifdef _FINAL_RELEASE_ rdp.halt = 1; #endif } // spnoop - no operation, always ignore static void spnoop() { RDP("spnoop\n"); } // noop - no operation, always ignore static void rdp_noop() { RDP("noop\n"); } static void ys_memrect () { if (settings.PPL) //pokemon puzzle league uses standart texrect { rdp_texrect(); return; } DWORD tile = (WORD)((rdp.cmd1 & 0x07000000) >> 24); DWORD lr_x = (WORD)((rdp.cmd0 & 0x00FFF000) >> 14); DWORD lr_y = (WORD)((rdp.cmd0 & 0x00000FFF) >> 2); DWORD ul_x = (WORD)((rdp.cmd1 & 0x00FFF000) >> 14); DWORD ul_y = (WORD)((rdp.cmd1 & 0x00000FFF) >> 2); rdp.pc[rdp.pc_i] += 16; // texrect is 196-bit if (lr_y > rdp.scissor_o.lr_y) lr_y = rdp.scissor_o.lr_y; FRDP ("memrect (%d, %d, %d, %d), ci_width: %d\n", ul_x, ul_y, lr_x, lr_y, rdp.ci_width); DWORD y, width = lr_x - ul_x; DWORD texaddr = rdp.addr[rdp.tiles[tile].t_mem]; DWORD tex_width = rdp.tiles[tile].line << 3; for (y = ul_y; y < lr_y; y++) { BYTE *src = gfx.RDRAM + texaddr + (y - ul_y) * tex_width; BYTE *dst = gfx.RDRAM + rdp.cimg + ul_x + y * rdp.ci_width; memcpy (dst, src, width); } } static void rdp_texrect() { DWORD a = rdp.pc[rdp.pc_i]; rdp.cmd2 = ((DWORD*)gfx.RDRAM)[(a>>2)+1]; rdp.cmd3 = ((DWORD*)gfx.RDRAM)[(a>>2)+3]; if (settings.ASB) //modified Rice's hack for All-Star Baseball games { DWORD dwHalf1 = (((DWORD*)gfx.RDRAM)[(a>>2)+0]) >> 24; if ((dwHalf1 != 0xF1) && (dwHalf1 != 0xb3)) { rdp.pc[rdp.pc_i] += 16; // texrect is 196-bit } else { rdp.pc[rdp.pc_i] += 8; rdp.cmd3 = rdp.cmd2; rdp.cmd2 = 0; } } else { rdp.pc[rdp.pc_i] += 16; // texrect is 196-bit } if (rdp.skip_drawing) { RDP("Texrect skipped\n"); return; } if ((settings.ucode == 8) && (rdp.cur_image)) { FRDP("Wrong Texrect. texaddr: %08lx, cimg: %08lx, cimg_end: %08lx\n", rdp.timg.addr, rdp.maincimg[1], rdp.maincimg[1]+rdp.ci_width*rdp.ci_height*rdp.ci_size); rdp.tri_n += 2; return; } WORD ul_x = (WORD)((rdp.cmd1 & 0x00FFF000) >> 14); WORD ul_y = (WORD)((rdp.cmd1 & 0x00000FFF) >> 2); WORD lr_x = (WORD)((rdp.cmd0 & 0x00FFF000) >> 14); WORD lr_y = (WORD)((rdp.cmd0 & 0x00000FFF) >> 2); if (ul_x >= lr_x) return; ///* if (settings.fb_optimize_texrect && rdp.hires_tex) { if (!rdp.hires_tex->drawn) { DRAWIMAGE d; d.imageX = 0; d.imageW = (WORD)rdp.hires_tex->width; d.frameX = 0; d.frameW = (WORD)(rdp.hires_tex->width);//(WORD)(ul_x + rdp.hires_tex->width);//lr_x; d.imageY = 0; d.imageH = (WORD)rdp.hires_tex->height; d.frameY = 0; d.frameH = (WORD)(rdp.hires_tex->height);//(ul_y + rdp.hires_tex->height); FRDP("texrect. ul_x: %d, ul_y: %d, lr_x: %d, lr_y: %d, width: %d, height: %d\n", ul_x, ul_y, lr_x, lr_y, rdp.hires_tex->width, rdp.hires_tex->height); d.scaleX = 1.0f; d.scaleY = 1.0f; DrawHiresImage(&d); rdp.hires_tex->drawn = TRUE; } return; } //*/ // framebuffer workaround for Zelda: MM LOT if ((rdp.othermode_l & 0xFFFF0000) == 0x0f5a0000) return; /*Gonetz*/ //hack for Zelda MM. it removes black texrects which cover all geometry in "Link meets Zelda" cut scene if (settings.zelda && rdp.timg.addr >= rdp.cimg && rdp.timg.addr < rdp.ci_end) { // FRDP("Wrong Texrect. texaddr: %08lx, cimg: %08lx, cimg_end: %08lx\n", rdp.cur_cache[0]->addr, rdp.cimg, rdp.cimg+rdp.ci_width*rdp.ci_height*2); rdp.tri_n += 2; return; } //* //hack for Banjo2. it removes black texrects under Banjo if (!settings.fb_hires && ((rdp.cycle1 << 16) | (rdp.cycle2 & 0xFFFF)) == 0xFFFFFFFF && (rdp.othermode_l & 0xFFFF0000) == 0x00500000) { rdp.tri_n += 2; return; } //*/ ///* if ((settings.ucode == 7) && (rdp.timg.addr >= rdp.maincimg[1]) && (rdp.timg.addr < (rdp.maincimg[1]+rdp.ci_width*rdp.ci_height*rdp.ci_size))) { if (settings.fb_smart) if (rdp.frame_buffers[rdp.ci_count-1].status == ci_copy_self || !settings.fb_motionblur) { FRDP("Wrong Texrect. texaddr: %08lx, cimg: %08lx, cimg_end: %08lx\n", rdp.timg.addr, rdp.maincimg[1], rdp.maincimg[1]+rdp.ci_width*rdp.ci_height*rdp.ci_size); rdp.tri_n += 2; return; } } //*/ int i; DWORD tile = (WORD)((rdp.cmd1 & 0x07000000) >> 24); // update MUST be at the beginning, b/c of update_scissor if (rdp.cycle_mode == 2) { rdp.tex = 1; rdp.allow_combine = 0; tmu1_func = tmu0_func = GR_COMBINE_FUNCTION_LOCAL; tmu1_fac = tmu0_fac = GR_COMBINE_FACTOR_NONE; tmu1_a_func = tmu0_a_func = GR_COMBINE_FUNCTION_LOCAL; tmu1_a_fac = tmu0_a_fac = GR_COMBINE_FACTOR_NONE; tmu1_invert = tmu0_invert = FXFALSE; tmu1_a_invert = tmu0_a_invert = FXFALSE; } rdp.texrecting = 1; DWORD prev_tile = rdp.cur_tile; rdp.cur_tile = tile; rdp.update |= UPDATE_COMBINE; update (); rdp.texrecting = 0; rdp.allow_combine = 1; if (!rdp.cur_cache[0]) { rdp.cur_tile = prev_tile; rdp.tri_n += 2; return; } // **** // ** Texrect offset by Gugaman ** float off_x = (float)((short)((rdp.cmd2 & 0xFFFF0000) >> 16)) / 32.0f; if ((int(off_x) == 512) && (rdp.timg.width < 512)) off_x = 0.0f; float off_y = (float)((short)(rdp.cmd2 & 0x0000FFFF)) / 32.0f; float dsdx = (float)((short)((rdp.cmd3 & 0xFFFF0000) >> 16)) / 1024.0f; float dtdy = (float)((short)(rdp.cmd3 & 0x0000FFFF)) / 1024.0f; if (rdp.cycle_mode > 1) { lr_x ++; lr_y ++; } if (rdp.cycle_mode == 2) dsdx /= 4.0f; float s_ul_x = ul_x * rdp.scale_x + rdp.offset_x; float s_lr_x = lr_x * rdp.scale_x + rdp.offset_x; float s_ul_y = ul_y * rdp.scale_y + rdp.offset_y; float s_lr_y = lr_y * rdp.scale_y + rdp.offset_y; FRDP("texrect (%d, %d, %d, %d), tile: %d, #%d, #%d\n", ul_x, ul_y, lr_x, lr_y, tile, rdp.tri_n, rdp.tri_n+1); FRDP ("(%f, %f) -> (%f, %f), s: (%d, %d) -> (%d, %d)\n", s_ul_x, s_ul_y, s_lr_x, s_lr_y, rdp.scissor.ul_x, rdp.scissor.ul_y, rdp.scissor.lr_x, rdp.scissor.lr_y); FRDP("\toff_x: %f, off_y: %f, dsdx: %f, dtdy: %f\n", off_x, off_y, dsdx, dtdy); float off_size_x; float off_size_y; if ( ((rdp.cmd0>>24)&0xFF) == 0xE5 ) //texrectflip { off_size_x = (float)((lr_y - ul_y - 1) * dsdx); off_size_y = (float)((lr_x - ul_x - 1) * dtdy); } else { off_size_x = (float)((lr_x - ul_x - 1) * dsdx); off_size_y = (float)((lr_y - ul_y - 1) * dtdy); } float lr_u0, lr_v0, ul_u0, ul_v0, lr_u1, lr_v1, ul_u1, ul_v1; if (rdp.cur_cache[0] && (rdp.tex & 1)) { if (rdp.hires_tex) { off_x += rdp.tiles[0].ul_s; off_y += rdp.hires_tex->v_shift + rdp.tiles[0].ul_t; } float sx=1, sy=1; if (rdp.tiles[rdp.cur_tile].shift_s) { if (rdp.tiles[rdp.cur_tile].shift_s > 10) sx = (float)(1 << (16 - rdp.tiles[rdp.cur_tile].shift_s)); else sx = (float)1.0f/(1 << rdp.tiles[rdp.cur_tile].shift_s); } if (rdp.tiles[rdp.cur_tile].shift_t) { if (rdp.tiles[rdp.cur_tile].shift_t > 10) sy = (float)(1 << (16 - rdp.tiles[rdp.cur_tile].shift_t)); else sy = (float)1.0f/(1 << rdp.tiles[rdp.cur_tile].shift_t); } ul_u0 = off_x * sx; ul_v0 = off_y * sy; ul_u0 -= rdp.tiles[rdp.cur_tile].f_ul_s; ul_v0 -= rdp.tiles[rdp.cur_tile].f_ul_t; lr_u0 = ul_u0 + off_size_x * sx; lr_v0 = ul_v0 + off_size_y * sy; if (rdp.hires_tex) { ul_u0 *= rdp.hires_tex->tex_scale; ul_v0 *= rdp.hires_tex->tex_scale; lr_u0 *= rdp.hires_tex->tex_scale; lr_v0 *= rdp.hires_tex->tex_scale; } else { ul_u0 = rdp.cur_cache[0]->c_off + rdp.cur_cache[0]->c_scl_x * ul_u0; lr_u0 = rdp.cur_cache[0]->c_off + rdp.cur_cache[0]->c_scl_x * lr_u0; ul_v0 = rdp.cur_cache[0]->c_off + rdp.cur_cache[0]->c_scl_y * ul_v0; lr_v0 = rdp.cur_cache[0]->c_off + rdp.cur_cache[0]->c_scl_y * lr_v0; } } else { ul_u0 = ul_v0 = lr_u0 = lr_v0 = 0; } if (rdp.cur_cache[1] && (rdp.tex & 2)) { float sx=1, sy=1; if (rdp.tiles[rdp.cur_tile+1].shift_s) { if (rdp.tiles[rdp.cur_tile+1].shift_s > 10) sx = (float)(1 << (16 - rdp.tiles[rdp.cur_tile+1].shift_s)); else sx = (float)1.0f/(1 << rdp.tiles[rdp.cur_tile+1].shift_s); } if (rdp.tiles[rdp.cur_tile+1].shift_t) { if (rdp.tiles[rdp.cur_tile+1].shift_t > 10) sy = 1;//(float)(1 << (16 - rdp.tiles[rdp.cur_tile+1].shift_t)); else sy = (float)1.0f/(1 << rdp.tiles[rdp.cur_tile+1].shift_t); } ul_u1 = off_x * sx; ul_v1 = off_y * sy; ul_u1 -= rdp.tiles[rdp.cur_tile+1].f_ul_s; ul_v1 -= rdp.tiles[rdp.cur_tile+1].f_ul_t; lr_u1 = ul_u1 + off_size_x * sx; lr_v1 = ul_v1 + off_size_y * sy; ul_u1 = rdp.cur_cache[1]->c_off + rdp.cur_cache[1]->c_scl_x * ul_u1; lr_u1 = rdp.cur_cache[1]->c_off + rdp.cur_cache[1]->c_scl_x * lr_u1; ul_v1 = rdp.cur_cache[1]->c_off + rdp.cur_cache[1]->c_scl_y * ul_v1; lr_v1 = rdp.cur_cache[1]->c_off + rdp.cur_cache[1]->c_scl_y * lr_v1; } else { ul_u1 = ul_v1 = lr_u1 = lr_v1 = 0; } rdp.cur_tile = prev_tile; // **** FRDP (" scissor: (%d, %d) -> (%d, %d)\n", rdp.scissor.ul_x, rdp.scissor.ul_y, rdp.scissor.lr_x, rdp.scissor.lr_y); CCLIP2 (s_ul_x, s_lr_x, ul_u0, lr_u0, ul_u1, lr_u1, (float)rdp.scissor.ul_x, (float)rdp.scissor.lr_x); CCLIP2 (s_ul_y, s_lr_y, ul_v0, lr_v0, ul_v1, lr_v1, (float)rdp.scissor.ul_y, (float)rdp.scissor.lr_y); // CCLIP2 (s_lr_y, s_ul_y, lr_v0, ul_v0, lr_v1, ul_v1, (float)rdp.scissor.ul_y, (float)rdp.scissor.lr_y); FRDP (" draw at: (%f, %f) -> (%f, %f)\n", s_ul_x, s_ul_y, s_lr_x, s_lr_y); // DO NOT SET CLAMP MODE HERE float W = 1.0f; float Z = 1.0f; if (rdp.zsrc == 1 && (rdp.othermode_l & 0x00000030)) // othermode check makes sure it // USES the z-buffer. Otherwise it returns bad (unset) values for lot and telescope //in zelda:mm. { if (rdp.C2 != 0.0f) { float C3 = (rdp.prim_depth - 16352.0f)/16352.0f; FRDP ("prim_depth = %d, C3 = %f, z = %f\n", rdp.prim_depth, C3, W); } else { FRDP ("prim_depth = %d\n", rdp.prim_depth); } Z = rdp.prim_depth; if (settings.ewjim) Z += 8.0f; if (settings.force_texrect_zbuf) { grDepthBufferFunction (GR_CMP_LEQUAL); grDepthMask (FXTRUE); rdp.update |= UPDATE_ZBUF_ENABLED; } } else { RDP ("no prim_depth used, using 1.0\n"); } float q = 1.0f / W; VERTEX vstd[4] = { { s_ul_x, s_ul_y, Z, q, ul_u0, ul_v0, ul_u1, ul_v1, 0, 0, 0, 0, 255 }, { s_lr_x, s_ul_y, Z, q, lr_u0, ul_v0, lr_u1, ul_v1, 0, 0, 0, 0, 255 }, { s_ul_x, s_lr_y, Z, q, ul_u0, lr_v0, ul_u1, lr_v1, 0, 0, 0, 0, 255 }, { s_lr_x, s_lr_y, Z, q, lr_u0, lr_v0, lr_u1, lr_v1, 0, 0, 0, 0, 255 } }; if ( ((rdp.cmd0>>24)&0xFF) == 0xE5 ) //texrectflip { vstd[1].u0 = ul_u0; vstd[1].v0 = lr_v0; vstd[1].u1 = ul_u1; vstd[1].v1 = lr_v1; vstd[2].u0 = lr_u0; vstd[2].v0 = ul_v0; vstd[2].u1 = lr_u1; vstd[2].v1 = ul_v1; } VERTEX *vptr = vstd; int n_vertices = 4; VERTEX *vnew = NULL; // for (int j =0; j < 4; j++) // FRDP("v[%d] u0: %f, v0: %f, u1: %f, v1: %f\n", j, vstd[j].u0, vstd[j].v0, vstd[j].u1, vstd[j].v1); if (!rdp.hires_tex && rdp.cur_cache[0]->splits != 1) { // ** LARGE TEXTURE HANDLING ** // *VERY* simple algebra for texrects float min_u, min_x, max_u, max_x; if (vstd[0].u0 < vstd[1].u0) { min_u = vstd[0].u0; min_x = vstd[0].x; max_u = vstd[1].u0; max_x = vstd[1].x; } else { min_u = vstd[1].u0; min_x = vstd[1].x; max_u = vstd[0].u0; max_x = vstd[0].x; } int start_u_256, end_u_256; if (settings.ucode == 7) { start_u_256 = 0; end_u_256 = (lr_x - ul_x - 1)>>8; } else { start_u_256 = (int)min_u >> 8; end_u_256 = (int)max_u >> 8; } //FRDP(" min_u: %f, max_u: %f start: %d, end: %d\n", min_u, max_u, start_u_256, end_u_256); int splitheight = rdp.cur_cache[0]->splitheight; int num_verts_line = 2 + ((end_u_256-start_u_256)<<1); vnew = new VERTEX [num_verts_line << 1]; n_vertices = num_verts_line << 1; vptr = vnew; vnew[0] = vstd[0]; vnew[0].u0 -= 256.0f * start_u_256; vnew[0].v0 += splitheight * start_u_256; vnew[1] = vstd[2]; vnew[1].u0 -= 256.0f * start_u_256; vnew[1].v0 += splitheight * start_u_256; vnew[n_vertices-2] = vstd[1]; vnew[n_vertices-2].u0 -= 256.0f * end_u_256; vnew[n_vertices-2].v0 += splitheight * end_u_256; vnew[n_vertices-1] = vstd[3]; vnew[n_vertices-1].u0 -= 256.0f * end_u_256; vnew[n_vertices-1].v0 += splitheight * end_u_256; // find the equation of the line of u,x float m = (max_x - min_x) / (max_u - min_u); // m = delta x / delta u float b = min_x - m * min_u; // b = y - m * x for (i=start_u_256; iu0 *= z->q; z->v0 *= z->q; z->u1 *= z->q; z->v1 *= z->q; apply_shade_mods (z); } if (fullscreen) { grFogMode (GR_FOG_DISABLE); grClipWindow (0, 0, settings.res_x, settings.res_y); grCullMode (GR_CULL_DISABLE); if (rdp.cycle_mode == 2) { grColorCombine (GR_COMBINE_FUNCTION_SCALE_OTHER, GR_COMBINE_FACTOR_ONE, GR_COMBINE_LOCAL_NONE, GR_COMBINE_OTHER_TEXTURE, FXFALSE); grAlphaCombine (GR_COMBINE_FUNCTION_SCALE_OTHER, GR_COMBINE_FACTOR_ONE, GR_COMBINE_LOCAL_NONE, GR_COMBINE_OTHER_TEXTURE, FXFALSE); grAlphaBlendFunction (GR_BLEND_ONE, GR_BLEND_ZERO, GR_BLEND_ZERO, GR_BLEND_ZERO); if (rdp.othermode_l & 1) { grAlphaTestFunction (GR_CMP_GEQUAL); grAlphaTestReferenceValue (0x80); } else grAlphaTestFunction (GR_CMP_ALWAYS); rdp.update |= UPDATE_ALPHA_COMPARE | UPDATE_COMBINE; } ConvertCoordsConvert (vptr, n_vertices); //AllowShadeMods (vptr, n_vertices); if (settings.wireframe) { SetWireframeCol (); grDrawLine (&vstd[0], &vstd[2]); grDrawLine (&vstd[2], &vstd[1]); grDrawLine (&vstd[1], &vstd[0]); grDrawLine (&vstd[2], &vstd[3]); grDrawLine (&vstd[3], &vstd[1]); } else { grDrawVertexArrayContiguous (GR_TRIANGLE_STRIP, n_vertices, vptr, sizeof(VERTEX)); } if (debug.capture) { VERTEX vl[3]; vl[0] = vstd[0]; vl[1] = vstd[2]; vl[2] = vstd[1]; add_tri (vl, 3, TRI_TEXRECT); rdp.tri_n ++; vl[0] = vstd[2]; vl[1] = vstd[3]; vl[2] = vstd[1]; add_tri (vl, 3, TRI_TEXRECT); rdp.tri_n ++; } else rdp.tri_n += 2; if ((rdp.flags & FOG_ENABLED) && settings.fog_enabled && rdp.C2 != 0.0f) grFogMode (GR_FOG_WITH_TABLE_ON_Q); rdp.update |= UPDATE_CULL_MODE | UPDATE_VIEWPORT; } else { rdp.tri_n += 2; } if (vnew) delete [] vnew; } static void rdp_loadsync() { RDP("loadsync - ignored\n"); } static void rdp_pipesync() { RDP("pipesync - ignored\n"); } static void rdp_tilesync() { RDP("tilesync - ignored\n"); } static void rdp_fullsync() { RDP("fullsync - ignored\n"); } static void rdp_setkeygb() { RDP_E("setkeygb - IGNORED\n"); RDP("setkeygb - IGNORED\n"); } static void rdp_setkeyr() { RDP_E("setkeyr - IGNORED\n"); RDP("setkeyr - IGNORED\n"); } static void rdp_setconvert() { /* rdp.YUV_C0 = 1.1647f ; rdp.YUV_C1 = 0.79931f ; rdp.YUV_C2 = -0.1964f ; rdp.YUV_C3 = -0.40651f; rdp.YUV_C4 = 1.014f ; */ rdp.K5 = (BYTE)(rdp.cmd1&0x1FF); RDP_E("setconvert - IGNORED\n"); RDP("setconvert - IGNORED\n"); } // // setscissor - sets the screen clipping rectangle // static void rdp_setscissor() { // clipper resolution is 320x240, scale based on computer resolution rdp.scissor_o.ul_x = /*min(*/(DWORD)(((rdp.cmd0 & 0x00FFF000) >> 14))/*, 320)*/; rdp.scissor_o.ul_y = /*min(*/(DWORD)(((rdp.cmd0 & 0x00000FFF) >> 2))/*, 240)*/; rdp.scissor_o.lr_x = /*min(*/(DWORD)(((rdp.cmd1 & 0x00FFF000) >> 14))/*, 320)*/; rdp.scissor_o.lr_y = /*min(*/(DWORD)(((rdp.cmd1 & 0x00000FFF) >> 2))/*, 240)*/; //rdp.scissor_o.interlace_mode = (rdp.cmd1 & 0x03000000) >> 24; rdp.ci_upper_bound = rdp.scissor_o.ul_y; rdp.ci_lower_bound = rdp.scissor_o.lr_y; FRDP("setscissor: (%d,%d) -> (%d,%d)\n", rdp.scissor_o.ul_x, rdp.scissor_o.ul_y, rdp.scissor_o.lr_x, rdp.scissor_o.lr_y); rdp.update |= UPDATE_SCISSOR; } static void rdp_setprimdepth() { rdp.prim_depth = (WORD)((rdp.cmd1 >> 16) & 0x7FFF); FRDP("setprimdepth: %d\n", rdp.prim_depth); } static void rdp_setothermode() { #define F3DEX2_SETOTHERMODE(cmd,sft,len,data) { \ rdp.cmd0 = (cmd<<24) | ((32-(sft)-(len))<<8) | (((len)-1)); \ rdp.cmd1 = data; \ gfx_instruction[settings.ucode][cmd] (); \ } #define SETOTHERMODE(cmd,sft,len,data) { \ rdp.cmd0 = (cmd<<24) | ((sft)<<8) | (len); \ rdp.cmd1 = data; \ gfx_instruction[settings.ucode][cmd] (); \ } RDP("rdp_setothermode\n"); if ((settings.ucode == 2) || (settings.ucode == 8)) { int cmd0 = rdp.cmd0; F3DEX2_SETOTHERMODE(0xE2, 0, 32, rdp.cmd1); // SETOTHERMODE_L F3DEX2_SETOTHERMODE(0xE3, 0, 32, cmd0 & 0x00FFFFFF); // SETOTHERMODE_H } else { int cmd0 = rdp.cmd0; SETOTHERMODE(0xB9, 0, 32, rdp.cmd1); // SETOTHERMODE_L SETOTHERMODE(0xBA, 0, 32, cmd0 & 0x00FFFFFF); // SETOTHERMODE_H } } void load_palette (DWORD addr, WORD start, WORD count) { RDP ("Loading palette... "); WORD *dpal = rdp.pal_8 + start; WORD end = start+count; // WORD *spal = (WORD*)(gfx.RDRAM + (addr & BMASK)); for (WORD i=start; i>= 4; end = start + (count >> 4); for (WORD p = start; p < end; p++) { rdp.pal_8_crc[p] = CRC_Calculate( 0xFFFFFFFF, &rdp.pal_8[(p << 4)], 32 ); } rdp.pal_256_crc = CRC_Calculate( 0xFFFFFFFF, rdp.pal_8_crc, 64 ); RDP ("Done.\n"); } static void rdp_loadtlut() { DWORD tile = (rdp.cmd1 >> 24) & 0x07; WORD start = rdp.tiles[tile].t_mem - 256; // starting location in the palettes // WORD start = ((WORD)(rdp.cmd1 >> 2) & 0x3FF) + 1; WORD count = ((WORD)(rdp.cmd1 >> 14) & 0x3FF) + 1; // number to copy if (rdp.timg.addr + (count<<1) > BMASK) count = (WORD)((BMASK - rdp.timg.addr) >> 1); if (start+count > 256) count = 256-start; FRDP("loadtlut: tile: %d, start: %d, count: %d, from: %08lx\n", tile, start, count, rdp.timg.addr); load_palette (rdp.timg.addr, start, count); rdp.timg.addr += count << 1; } BOOL tile_set = 0; static void rdp_settilesize() { DWORD tile = (rdp.cmd1 >> 24) & 0x07; rdp.last_tile_size = tile; rdp.tiles[tile].f_ul_s = (float)((rdp.cmd0 >> 12) & 0xFFF) / 4.0f; rdp.tiles[tile].f_ul_t = (float)(rdp.cmd0 & 0xFFF) / 4.0f; int ul_s = (((WORD)(rdp.cmd0 >> 14)) & 0x03ff); int ul_t = (((WORD)(rdp.cmd0 >> 2 )) & 0x03ff); int lr_s = (((WORD)(rdp.cmd1 >> 14)) & 0x03ff); int lr_t = (((WORD)(rdp.cmd1 >> 2 )) & 0x03ff); if (lr_s == 0 && ul_s == 0) //pokemon puzzle league set such tile size wrong_tile = tile; else if (wrong_tile == (int)tile) wrong_tile = -1; if (settings.use_sts1_only) { // ** USE FIRST SETTILESIZE ONLY ** // This option helps certain textures while using the 'Alternate texture size method', // but may break others. (should help more than break) if (tile_set) { // coords in 10.2 format rdp.tiles[tile].ul_s = ul_s; rdp.tiles[tile].ul_t = ul_t; rdp.tiles[tile].lr_s = lr_s; rdp.tiles[tile].lr_t = lr_t; tile_set = 0; } } else { // coords in 10.2 format rdp.tiles[tile].ul_s = ul_s; rdp.tiles[tile].ul_t = ul_t; rdp.tiles[tile].lr_s = lr_s; rdp.tiles[tile].lr_t = lr_t; } // handle wrapping if (rdp.tiles[tile].lr_s < rdp.tiles[tile].ul_s) rdp.tiles[tile].lr_s += 0x400; if (rdp.tiles[tile].lr_t < rdp.tiles[tile].ul_t) rdp.tiles[tile].lr_t += 0x400; rdp.update |= UPDATE_TEXTURE; rdp.first = 1; if (tile == 0 && rdp.hires_tex) //if ((rdp.tiles[tile].size != 2) || ((rdp.timg.width == 1) && (rdp.hires_tex->width != (DWORD)(lr_s+1)))) if (((rdp.tiles[tile].format == 0) && (rdp.tiles[tile].size != 2)) || ((rdp.timg.width == 1) && (rdp.hires_tex->width != (DWORD)(lr_s+1)))) rdp.hires_tex = 0; FRDP ("settilesize: tile: %d, ul_s: %d, ul_t: %d, lr_s: %d, lr_t: %d\n", tile, ul_s, ul_t, lr_s, lr_t); } static DWORD fakeCnt, fakeDst, fake_dxt, fakeSwapMethod; static void rdp_loadblock() { if (rdp.skip_drawing) { RDP("loadblock skipped\n"); return; } DWORD tile = (DWORD)((rdp.cmd1 >> 24) & 0x07); DWORD dxt = (DWORD)(rdp.cmd1 & 0x0FFF); rdp.addr[rdp.tiles[tile].t_mem] = rdp.timg.addr; // ** DXT is used for swapping every other line /* double fdxt = (double)0x8000000F/(double)((DWORD)(2047/(dxt-1))); // F for error DWORD _dxt = (DWORD)fdxt;*/ // 0x00000800 -> 0x80000000 (so we can check the sign bit instead of the 11th bit) DWORD _dxt = dxt << 20; DWORD addr = segoffset(rdp.timg.addr) & BMASK; // lr_s specifies number of 64-bit words to copy // 10.2 format WORD ul_s = (WORD)(rdp.cmd0 >> 14) & 0x3FF; WORD ul_t = (WORD)(rdp.cmd0 >> 2) & 0x3FF; WORD lr_s = (WORD)(rdp.cmd1 >> 14) & 0x3FF; rdp.tiles[tile].ul_s = ul_s; rdp.tiles[tile].ul_t = ul_t; rdp.tiles[tile].lr_s = lr_s; rdp.timg.set_by = 0; // load block rdp.timg.force_rdram = FALSE; // do a quick boundary check before copying to eliminate the possibility for exception if (ul_s >= 512) { lr_s = 1; // 1 so that it doesn't die on memcpy ul_s = 511; } if (ul_s+lr_s > 512) lr_s = 512-ul_s; if (addr+(lr_s<<3) > BMASK+1) lr_s = (WORD)((BMASK-addr)>>3); DWORD off = rdp.timg.addr; DWORD dst = (DWORD)rdp.tmem+(rdp.tiles[tile].t_mem<<3); DWORD cnt = lr_s+1; if (rdp.tiles[tile].size == 3) cnt <<= 1; //DWORD start_line = 0; // Since it's not loading 32-bit textures as the N64 would, 32-bit textures need to // be swapped by 64-bits, not 32. DWORD SwapMethod = (rdp.tiles[tile].size==3)?(DWORD)SwapBlock64:(DWORD)SwapBlock32; rdp.timg.addr += cnt << 3; #ifdef _WIN32 __asm { // copy the data mov edi,dword ptr [dst] mov esi,dword ptr [gfx.RDRAM] mov ecx,dword ptr [cnt] mov edx,dword ptr [off] call CopyBlock // now swap it mov eax,dword ptr [cnt] // eax = count remaining xor edx,edx // edx = dxt counter mov edi,dword ptr [dst] mov ebx,dword ptr [_dxt] xor ecx,ecx // ecx = how much to copy dxt_test: add edi,8 dec eax jz end_dxt_test add edx,ebx jns dxt_test dxt_s_test: inc ecx dec eax jz end_dxt_test add edx,ebx js dxt_s_test // swap this data (ecx set, dst set) call dword ptr [SwapMethod] // (ecx reset to 0 after) jmp dxt_test // and repeat end_dxt_test: // swap any remaining data call dword ptr [SwapMethod] } #else // _WIN32 fakeCnt = cnt; fakeDst = dst; fake_dxt = _dxt; fakeSwapMethod = SwapMethod; asm volatile( // copy the data "pusha \n" "call CopyBlock \n" // now swap it "mov fakeCnt, %%eax \n" // eax = count remaining "xor %%edx,%%edx \n" // edx = dxt counter "mov fakeDst, %%edi \n" "mov fake_dxt, %%ebx \n" "xor %%ecx, %%ecx \n" // ecx = how much to copy "0: \n" // dxt_test: "add $8, %%edi \n" "dec %%eax \n" "jz 2f \n" // jz end_dxt_test "add %%ebx, %%edx \n" "jns 0b \n" // jns dxt_test "1: \n" // dxt_s_test: "inc %%ecx \n" "dec %%eax \n" "jz 2f \n" // jz end_dxt_test "add %%ebx, %%edx \n" "js 1b \n" // js dxt_s_test // swap this data (ecx set, dst set) // call dword ptr [SwapMethod] // (ecx reset to 0 after) "push %%eax \n" "mov fakeSwapMethod, %%eax \n" "call *%%eax \n" "pop %%eax \n" "jmp 0b \n" // jmp dxt_test // and repeat "2: \n" //end_dxt_test: // swap any remaining data //call dword ptr [SwapMethod] "push %%eax \n" "mov fakeSwapMethod, %%eax \n" "call *%%eax \n" "pop %%eax \n" "popa \n" : : "D"(dst), "S"(gfx.RDRAM), "c"(cnt), "d"(off) : "memory", "cc" ); #endif // _WIN32 rdp.update |= UPDATE_TEXTURE; FRDP ("loadblock: tile: %d, ul_s: %d, ul_t: %d, lr_s: %d, dxt: %08lx -> %08lx\n", tile, ul_s, ul_t, lr_s, dxt, _dxt); } static DWORD fakeEnd; static int fakeLine_n; static void rdp_loadtile() { if (rdp.skip_drawing) return; rdp.timg.set_by = 1; // load tile rdp.timg.force_rdram = FALSE; DWORD tile = (DWORD)((rdp.cmd1 >> 24) & 0x07); if (rdp.tiles[tile].format == 1) { rdp.yuv_image = TRUE; if (rdp.timg.addr < rdp.yuv_im_begin) rdp.yuv_im_begin = rdp.timg.addr; return; } rdp.addr[rdp.tiles[tile].t_mem] = rdp.timg.addr; WORD ul_s = (WORD)((rdp.cmd0 >> 14) & 0x03FF); WORD ul_t = (WORD)((rdp.cmd0 >> 2 ) & 0x03FF); WORD lr_s = (WORD)((rdp.cmd1 >> 14) & 0x03FF); WORD lr_t = (WORD)((rdp.cmd1 >> 2 ) & 0x03FF); if (lr_s < ul_s || lr_t < ul_t) return; if (wrong_tile >= 0) //there was a tile with zero length { rdp.tiles[wrong_tile].lr_s = lr_s; if (rdp.tiles[tile].size > rdp.tiles[wrong_tile].size) rdp.tiles[wrong_tile].lr_s <<= (rdp.tiles[tile].size - rdp.tiles[wrong_tile].size); else if (rdp.tiles[tile].size < rdp.tiles[wrong_tile].size) rdp.tiles[wrong_tile].lr_s >>= (rdp.tiles[wrong_tile].size - rdp.tiles[tile].size); rdp.tiles[wrong_tile].lr_t = lr_t; // wrong_tile = -1; } if (rdp.hires_tex) { rdp.hires_tex->tile_uls = ul_s; rdp.hires_tex->tile_ult = ul_t; } if (settings.tonic && tile == 7) { rdp.tiles[0].ul_s = ul_s; rdp.tiles[0].ul_t = ul_t; rdp.tiles[0].lr_s = lr_s; rdp.tiles[0].lr_t = lr_t; } DWORD height = lr_t - ul_t + 1; // get height DWORD width = lr_s - ul_s + 1; DWORD wid_64 = rdp.tiles[tile].line; // CHEAT: it's very unlikely that it loads more than 1 32-bit texture in one command, // so i don't bother to write in two different places at once. Just load once with // twice as much data. if (rdp.tiles[tile].size == 3) wid_64 <<= 1; int line_n = rdp.timg.width; if (rdp.tiles[tile].size == 0) line_n >>= 1; else line_n <<= (rdp.tiles[tile].size-1); int offs = ul_t * line_n; if (rdp.tiles[tile].size == 0) offs += ul_s >> 1; else offs += ul_s << (rdp.tiles[tile].size - 1); offs += rdp.timg.addr; // check if points to bad location int size = width * height; if (rdp.tiles[tile].size == 0) size >>= 1; else size <<= (rdp.tiles[tile].size-1); if (offs + size > BMASK) return; DWORD SwapMethod = (rdp.tiles[tile].size==3)?(DWORD)SwapBlock64:(DWORD)SwapBlock32; DWORD dst = (DWORD)rdp.tmem+(rdp.tiles[tile].t_mem<<3); DWORD end = (DWORD)rdp.tmem+4096 - (wid_64<<3); #ifdef _WIN32 __asm { // set initial values mov edi,dword ptr [dst] mov ecx,dword ptr [wid_64] mov esi,dword ptr [gfx.RDRAM] mov edx,dword ptr [offs] xor ebx,ebx // swap this line? mov eax,dword ptr [height] loadtile_loop: cmp dword ptr [end],edi // end of tmem: error jc loadtile_end // copy this line push edi push ecx call CopyBlock pop ecx // swap it? xor ebx,1 jnz loadtile_no_swap // (ecx set, restore edi) pop edi push ecx call dword ptr [SwapMethod] pop ecx jmp loadtile_swap_end loadtile_no_swap: add sp,4 // forget edi, we are already at the next position loadtile_swap_end: add edx,dword ptr [line_n] dec eax jnz loadtile_loop loadtile_end: } #else // _WIN32 fakeEnd = end; fakeSwapMethod = SwapMethod; fakeLine_n = line_n; asm volatile ( // set initial values "pusha \n" "xor %%ebx,%%ebx \n" // swap this line? "0: \n" // loadtile_loop: "cmp %%edi, fakeEnd \n" // end of tmem: error "jc 3f \n" // jc loadtile_end // copy this line "push %%edi \n" "push %%ecx \n" "call CopyBlock \n" "pop %%ecx \n" // swap it? "xor $1, %%ebx \n" "jnz 1f \n" // jnz loadtile_no_swap // (ecx set, restore edi) "pop %%edi \n" "push %%ecx \n" //call dword ptr [SwapMethod] "push %%eax \n" "mov fakeSwapMethod, %%eax \n" "call *%%eax \n" "pop %%eax \n" "pop %%ecx \n" "jmp 2f \n" // jmp loadtile_swap_end "1: \n" // loadtile_no_swap: "add $4, %%esp \n" // forget edi, we are already at the next position "2: \n" // loadtile_swap_end: "add fakeLine_n, %%edx \n" "dec %%eax \n" "jnz 0b \n" // jnz loadtile_loop "3: \n" // loadtile_end: "popa \n" : : "D"(dst), "c"(wid_64), "S"(gfx.RDRAM), "d"(offs), "a"(height) : "memory", "cc" ); #endif // _WIN32 FRDP("loadtile: tile: %d, ul_s: %d, ul_t: %d, lr_s: %d, lr_t: %d\n", tile, ul_s, ul_t, lr_s, lr_t); } static void rdp_settile() { tile_set = 1; // used to check if we only load the first settilesize rdp.first = 0; //rdp.cur_tile_n = (DWORD)((rdp.cmd1 >> 24) & 0x07); //rdp.cur_tile = &rdp.tiles[rdp.cur_tile_n]; rdp.last_tile = (DWORD)((rdp.cmd1 >> 24) & 0x07); TILE *tile = &rdp.tiles[rdp.last_tile]; tile->format = (BYTE)((rdp.cmd0 >> 21) & 0x07); tile->size = (BYTE)((rdp.cmd0 >> 19) & 0x03); tile->line = (WORD)((rdp.cmd0 >> 9) & 0x01FF); tile->t_mem = (WORD)(rdp.cmd0 & 0x1FF); tile->palette = (BYTE)((rdp.cmd1 >> 20) & 0x0F); tile->clamp_t = (BYTE)((rdp.cmd1 >> 19) & 0x01); tile->mirror_t = (BYTE)((rdp.cmd1 >> 18) & 0x01); tile->mask_t = (BYTE)((rdp.cmd1 >> 14) & 0x0F); tile->shift_t = (BYTE)((rdp.cmd1 >> 10) & 0x0F); tile->clamp_s = (BYTE)((rdp.cmd1 >> 9) & 0x01); tile->mirror_s = (BYTE)((rdp.cmd1 >> 8) & 0x01); tile->mask_s = (BYTE)((rdp.cmd1 >> 4) & 0x0F); tile->shift_s = (BYTE)(rdp.cmd1 & 0x0F); rdp.update |= UPDATE_TEXTURE; FRDP ("settile: tile: %d, format: %s, size: %s, line: %d, " "t_mem: %08lx, palette: %d, clamp_t/mirror_t: %s, mask_t: %d, " "shift_t: %d, clamp_s/mirror_s: %s, mask_s: %d, shift_s: %d\n", rdp.last_tile, str_format[tile->format], str_size[tile->size], tile->line, tile->t_mem, tile->palette, str_cm[(tile->clamp_t<<1)|tile->mirror_t], tile->mask_t, tile->shift_t, str_cm[(tile->clamp_s<<1)|tile->mirror_s], tile->mask_s, tile->shift_s); } // // fillrect - fills a rectangle // static void rdp_fillrect() { if ((rdp.cimg == rdp.zimg) || (settings.fb_smart && rdp.frame_buffers[rdp.ci_count-1].status == ci_zimg)) { RDP ("Fillrect - cleared the depth buffer\n"); if (fullscreen) { grDepthMask (FXTRUE); grColorMask (FXFALSE, FXFALSE); grBufferClear (0, 0, 0xFFFF); grColorMask (FXTRUE, FXTRUE); rdp.update |= UPDATE_ZBUF_ENABLED; if (settings.fb_depth_clear) { DWORD size_in_dwords = (rdp.ci_width * rdp.ci_height * rdp.ci_size) >> 2; DWORD * dst = (DWORD*)(gfx.RDRAM+rdp.cimg); for (DWORD f = 0; f < size_in_dwords; f++) dst[f] = rdp.real_fill_color; } } return; } if (rdp.skip_drawing) { RDP("Fillrect skipped\n"); return; } // Update scissor update_scissor (); DWORD ul_x = ((rdp.cmd1 & 0x00FFF000) >> 14); DWORD ul_y = (rdp.cmd1 & 0x00000FFF) >> 2; DWORD lr_x = ((rdp.cmd0 & 0x00FFF000) >> 14) + 1; DWORD lr_y = ((rdp.cmd0 & 0x00000FFF) >> 2) + 1; if ((ul_x > lr_x) || (ul_y > lr_y)) return; if (settings.bomberman64 && (lr_x == rdp.ci_width) && (rdp.cimg == rdp.ocimg)) //bomberman64 hack return; if (rdp.cur_image && (rdp.cur_image->format != 0) && (rdp.cur_image->width == lr_x)) { DWORD color = rdp.real_fill_color; color = ((color&0x0001)?0xFF000000:0x00000000) | ((DWORD)((float)((color&0xF800) >> 11) / 31.0f * 255.0f) << 16) | ((DWORD)((float)((color&0x07C0) >> 6) / 31.0f * 255.0f) << 8) | (DWORD)((float)((color&0x003E) >> 1) / 31.0f * 255.0f); grDepthMask (FXFALSE); grBufferClear (color, 0, 0xFFFF); grDepthMask (FXTRUE); rdp.update |= UPDATE_ZBUF_ENABLED; return; } if (settings.ucode == 7 && rdp.cycle_mode == 0) { lr_x--; lr_y--; } FRDP("fillrect (%d,%d) -> (%d,%d), cycle mode: %d, #%d, #%d\n", ul_x, ul_y, lr_x, lr_y, rdp.cycle_mode, rdp.tri_n, rdp.tri_n+1); FRDP("scissor (%d,%d) -> (%d,%d)\n", rdp.scissor.ul_x, rdp.scissor.ul_y, rdp.scissor.lr_x, rdp.scissor.lr_y); // KILL the floating point error with 0.01f DWORD s_ul_x = (DWORD)min(max(ul_x * rdp.scale_x + rdp.offset_x + 0.01f, rdp.scissor.ul_x), rdp.scissor.lr_x); DWORD s_lr_x = (DWORD)min(max(lr_x * rdp.scale_x + rdp.offset_x + 0.01f, rdp.scissor.ul_x), rdp.scissor.lr_x); DWORD s_ul_y = (DWORD)min(max(ul_y * rdp.scale_y + rdp.offset_y + 0.01f, rdp.scissor.ul_y), rdp.scissor.lr_y); DWORD s_lr_y = (DWORD)min(max(lr_y * rdp.scale_y + rdp.offset_y + 0.01f, rdp.scissor.ul_y), rdp.scissor.lr_y); if (s_lr_x < 0.0f) s_lr_x = 0; if (s_lr_y < 0.0f) s_lr_y = 0; if (s_ul_x > (float)settings.res_x) s_ul_x = settings.res_x; if (s_ul_y > (float)settings.res_y) s_ul_y = settings.res_y; FRDP (" - %d, %d, %d, %d\n", s_ul_x, s_ul_y, s_lr_x, s_lr_y); if (fullscreen) { grFogMode (GR_FOG_DISABLE); grClipWindow (0, 0, settings.res_x, settings.res_y); // Draw the rectangle VERTEX v[4] = { { (float)s_ul_x, (float)s_ul_y, 65535.0f, 1.0f/65535.0f, 0,0,0,0, 0, 0,0,0,0 }, { (float)s_lr_x, (float)s_ul_y, 65535.0f, 1.0f/65535.0f, 0,0,0,0, 0, 0,0,0,0 }, { (float)s_ul_x, (float)s_lr_y, 65535.0f, 1.0f/65535.0f, 0,0,0,0, 0, 0,0,0,0 }, { (float)s_lr_x, (float)s_lr_y, 65535.0f, 1.0f/65535.0f, 0,0,0,0, 0, 0,0,0,0 } }; if (rdp.cycle_mode == 3) { DWORD color = (settings.fillcolor_fix) ? rdp.real_fill_color : (rdp.real_fill_color >> 16); color = ((color&0x0001)?0xFF000000:0x00000000) | ((DWORD)((float)((color&0xF800) >> 11) / 31.0f * 255.0f) << 16) | ((DWORD)((float)((color&0x07C0) >> 6) / 31.0f * 255.0f) << 8) | (DWORD)((float)((color&0x003E) >> 1) / 31.0f * 255.0f); grConstantColorValue (color); grColorCombine (GR_COMBINE_FUNCTION_LOCAL, GR_COMBINE_FACTOR_NONE, GR_COMBINE_LOCAL_CONSTANT, GR_COMBINE_OTHER_NONE, FXFALSE); grAlphaCombine (GR_COMBINE_FUNCTION_LOCAL, GR_COMBINE_FACTOR_NONE, GR_COMBINE_LOCAL_CONSTANT, GR_COMBINE_OTHER_NONE, FXFALSE); grAlphaBlendFunction (GR_BLEND_ONE, GR_BLEND_ZERO, GR_BLEND_ONE, GR_BLEND_ZERO); rdp.update |= UPDATE_COMBINE; } else { Combine (); TexCache (); // (to update combiner) } if ( settings.fillrect_depth_compare && (rdp.cycle_mode != 3) && ((ccolor&0xFF000000) == 0xFF000000) ) { grDepthBufferFunction (GR_CMP_LEQUAL); grDepthMask (FXTRUE); } else { grDepthBufferFunction (GR_CMP_ALWAYS); grDepthMask (FXFALSE); } grAlphaTestFunction (GR_CMP_ALWAYS); grCullMode(GR_CULL_DISABLE); if (settings.wireframe) { SetWireframeCol (); grDrawLine (&v[0], &v[2]); grDrawLine (&v[2], &v[1]); grDrawLine (&v[1], &v[0]); grDrawLine (&v[2], &v[3]); grDrawLine (&v[3], &v[1]); //grDrawLine (&v[1], &v[2]); } else { grDrawTriangle (&v[0], &v[2], &v[1]); grDrawTriangle (&v[2], &v[3], &v[1]); } if (debug.capture) { VERTEX v1[3]; v1[0] = v[0]; v1[1] = v[2]; v1[2] = v[1]; add_tri (v1, 3, TRI_FILLRECT); rdp.tri_n ++; v1[0] = v[2]; v1[1] = v[3]; add_tri (v1, 3, TRI_FILLRECT); rdp.tri_n ++; } else rdp.tri_n += 2; if ((rdp.flags & FOG_ENABLED) && settings.fog_enabled && rdp.C2 != 0.0f) grFogMode (GR_FOG_WITH_TABLE_ON_Q); rdp.update |= UPDATE_CULL_MODE | UPDATE_ALPHA_COMPARE | UPDATE_ZBUF_ENABLED; } else { rdp.tri_n += 2; } } // // setfillcolor - sets the filling color // static void rdp_setfillcolor() { rdp.real_fill_color = rdp.cmd1; rdp.fill_color = ((rdp.cmd1 & 0xFFFFFF00) >> 8) | ((rdp.cmd1 & 0x000000FF) << 24); rdp.update |= UPDATE_ALPHA_COMPARE | UPDATE_COMBINE; FRDP("setfillcolor: %08lx\n", rdp.cmd1); } static void rdp_setfogcolor() { rdp.real_fog_color = rdp.cmd1; rdp.fog_color = ((rdp.cmd1 & 0xFFFFFF00) >> 8) | ((rdp.cmd1 & 0x000000FF) << 24); rdp.update |= UPDATE_COMBINE | UPDATE_FOG_ENABLED; /*if (fullscreen) grFogColorValue (rdp.fog_color);*/ FRDP("setfogcolor - %08lx\n", rdp.cmd1); } static void rdp_setblendcolor() { rdp.real_blend_color = rdp.cmd1; rdp.blend_color = ((rdp.cmd1 & 0xFFFFFF00) >> 8) | ((rdp.cmd1 & 0x000000FF) << 24); rdp.update |= UPDATE_COMBINE; FRDP("setblendcolor: %08lx\n", rdp.cmd1); } static void rdp_setprimcolor() { rdp.real_prim_color = rdp.cmd1; rdp.prim_color = ((rdp.cmd1 & 0xFFFFFF00) >> 8) | ((rdp.cmd1 & 0x000000FF) << 24); rdp.prim_lodmin = (rdp.cmd0 >> 8) & 0xFF; rdp.prim_lodfrac = rdp.cmd0 & 0xFF; if (rdp.prim_lodfrac == 0) rdp.prim_lodfrac = rdp.prim_lodmin; rdp.update |= UPDATE_COMBINE; FRDP("setprimcolor: %08lx, lodmin: %d, lodfrac: %d\n", rdp.cmd1, rdp.prim_lodmin, rdp.prim_lodfrac); } static void rdp_setenvcolor() { rdp.real_env_color = rdp.cmd1; rdp.env_color = ((rdp.cmd1 & 0xFFFFFF00) >> 8) | ((rdp.cmd1 & 0x000000FF) << 24); rdp.update |= UPDATE_COMBINE; FRDP("setenvcolor: %08lx\n", rdp.cmd1); } static void rdp_setcombine() { rdp.c_a0 = (BYTE)((rdp.cmd0 >> 20) & 0xF); rdp.c_b0 = (BYTE)((rdp.cmd1 >> 28) & 0xF); rdp.c_c0 = (BYTE)((rdp.cmd0 >> 15) & 0x1F); rdp.c_d0 = (BYTE)((rdp.cmd1 >> 15) & 0x7); rdp.c_Aa0 = (BYTE)((rdp.cmd0 >> 12) & 0x7); rdp.c_Ab0 = (BYTE)((rdp.cmd1 >> 12) & 0x7); rdp.c_Ac0 = (BYTE)((rdp.cmd0 >> 9) & 0x7); rdp.c_Ad0 = (BYTE)((rdp.cmd1 >> 9) & 0x7); rdp.c_a1 = (BYTE)((rdp.cmd0 >> 5) & 0xF); rdp.c_b1 = (BYTE)((rdp.cmd1 >> 24) & 0xF); rdp.c_c1 = (BYTE)((rdp.cmd0 >> 0) & 0x1F); rdp.c_d1 = (BYTE)((rdp.cmd1 >> 6) & 0x7); rdp.c_Aa1 = (BYTE)((rdp.cmd1 >> 21) & 0x7); rdp.c_Ab1 = (BYTE)((rdp.cmd1 >> 3) & 0x7); rdp.c_Ac1 = (BYTE)((rdp.cmd1 >> 18) & 0x7); rdp.c_Ad1 = (BYTE)((rdp.cmd1 >> 0) & 0x7); rdp.cycle1 = (rdp.c_a0<<0) | (rdp.c_b0<<4) | (rdp.c_c0<<8) | (rdp.c_d0<<13)| (rdp.c_Aa0<<16)| (rdp.c_Ab0<<19)| (rdp.c_Ac0<<22)| (rdp.c_Ad0<<25); rdp.cycle2 = (rdp.c_a1<<0) | (rdp.c_b1<<4) | (rdp.c_c1<<8) | (rdp.c_d1<<13)| (rdp.c_Aa1<<16)| (rdp.c_Ab1<<19)| (rdp.c_Ac1<<22)| (rdp.c_Ad1<<25); rdp.update |= UPDATE_COMBINE; FRDP("setcombine\na0=%s b0=%s c0=%s d0=%s\nAa0=%s Ab0=%s Ac0=%s Ad0=%s\na1=%s b1=%s c1=%s d1=%s\nAa1=%s Ab1=%s Ac1=%s Ad1=%s\n", Mode0[rdp.c_a0], Mode1[rdp.c_b0], Mode2[rdp.c_c0], Mode3[rdp.c_d0], Alpha0[rdp.c_Aa0], Alpha1[rdp.c_Ab0], Alpha2[rdp.c_Ac0], Alpha3[rdp.c_Ad0], Mode0[rdp.c_a1], Mode1[rdp.c_b1], Mode2[rdp.c_c1], Mode3[rdp.c_d1], Alpha0[rdp.c_Aa1], Alpha1[rdp.c_Ab1], Alpha2[rdp.c_Ac1], Alpha3[rdp.c_Ad1]); } // // settextureimage - sets the source for an image copy // static void rdp_settextureimage() { static char *format[] = { "RGBA", "YUV", "CI", "IA", "I", "?", "?", "?" }; static char *size[] = { "4bit", "8bit", "16bit", "32bit" }; rdp.timg.format = (BYTE)((rdp.cmd0 >> 21) & 0x07); rdp.timg.size = (BYTE)((rdp.cmd0 >> 19) & 0x03); rdp.timg.width = (WORD)(1 + (rdp.cmd0 & 0x00000FFF)); rdp.timg.addr = segoffset(rdp.cmd1); rdp.s2dex_tex_loaded = TRUE; rdp.update |= UPDATE_TEXTURE; if (/*(settings.ucode == 7) &&*/ (rdp.timg.addr == rdp.cimg) && rdp.frame_buffers[rdp.ci_count-1].status == ci_copy_self) { if (settings.fb_smart) { if (!rdp.fb_drawn) { if (!CloseTextureBuffer(TRUE)) DrawFrameBuffer(); rdp.fb_drawn = TRUE; } } else rdp.skip_drawing = TRUE; } if (settings.fb_hires) //search this texture among drawn texture buffers { if (settings.zelda) { if (rdp.timg.size == 2) FindTextureBuffer(rdp.timg.addr, rdp.timg.width); } else FindTextureBuffer(rdp.timg.addr, rdp.timg.width); } FRDP("settextureimage: format: %s, size: %s, width: %d, addr: %08lx\n", format[rdp.timg.format], size[rdp.timg.size], rdp.timg.width, rdp.timg.addr); } static void rdp_setdepthimage() { rdp.zimg = segoffset(rdp.cmd1) & BMASK; FRDP("setdepthimage - %08lx\n", rdp.zimg); } BOOL SwapOK = TRUE; static void rdp_setcolorimage() { if (settings.fb_smart) { switch (rdp.frame_buffers[rdp.ci_count].status) { case ci_main: { if (rdp.ci_count == 0) { if ((rdp.ci_status == ci_aux)) //for PPL { float sx = rdp.scale_x; float sy = rdp.scale_y; rdp.scale_x = 1.0f; rdp.scale_y = 1.0f; DrawFrameBuffer (); rdp.scale_x = sx; rdp.scale_y = sy; } if (!settings.fb_hires) { if ((rdp.num_of_ci > 1) && (rdp.frame_buffers[rdp.ci_count+1].status == ci_aux) && (rdp.frame_buffers[rdp.ci_count+1].width >= rdp.frame_buffers[rdp.ci_count].width)) { rdp.scale_x = 1.0f; rdp.scale_y = 1.0f; } } } else if (!rdp.motionblur && settings.fb_hires && !SwapOK && (rdp.ci_count <= rdp.copy_ci_index)) OpenTextureBuffer(rdp.frame_buffers[rdp.copy_ci_index]); //else if (rdp.ci_status == ci_aux && !rdp.copy_ci_index) // CloseTextureBuffer(); rdp.skip_drawing = FALSE; } break; case ci_copy: { if (!rdp.motionblur || settings.fb_motionblur) { if (CloseTextureBuffer(TRUE)) ; else if (rdp.frame_buffers[rdp.ci_count].width == rdp.ci_width) { if (!rdp.fb_drawn) { DrawFrameBuffer (); rdp.fb_drawn = TRUE; } memcpy(gfx.RDRAM+rdp.frame_buffers[rdp.ci_count].addr,gfx.RDRAM+rdp.cimg, rdp.frame_buffers[rdp.ci_count].width*rdp.frame_buffers[rdp.ci_count].height*rdp.frame_buffers[rdp.ci_count].size); } /* else { rdp.frame_buffers[rdp.ci_count].status = ci_aux; rdp.skip_drawing = FALSE; } */ } else { memset(gfx.RDRAM+rdp.frame_buffers[rdp.ci_count].addr, 0, rdp.frame_buffers[rdp.ci_count].width*rdp.frame_buffers[rdp.ci_count].height*rdp.ci_size); } rdp.skip_drawing = TRUE; } break; case ci_old_copy: { if (!rdp.motionblur || settings.fb_motionblur) { if (rdp.frame_buffers[rdp.ci_count].width == rdp.ci_width) { memcpy(gfx.RDRAM+rdp.frame_buffers[rdp.ci_count].addr,gfx.RDRAM+rdp.maincimg[1], rdp.frame_buffers[rdp.ci_count].width*rdp.frame_buffers[rdp.ci_count].height*rdp.frame_buffers[rdp.ci_count].size); } //rdp.skip_drawing = TRUE; } else { memset(gfx.RDRAM+rdp.frame_buffers[rdp.ci_count].addr, 0, rdp.frame_buffers[rdp.ci_count].width*rdp.frame_buffers[rdp.ci_count].height*rdp.ci_size); } } break; /* else if (rdp.frame_buffers[rdp.ci_count].status == ci_main_i) { // DrawFrameBuffer (); rdp.scale_x = rdp.scale_x_bak; rdp.scale_y = rdp.scale_y_bak; rdp.skip_drawing = FALSE; } */ case ci_aux: { if (!settings.fb_hires && rdp.frame_buffers[rdp.ci_count].format != 0) rdp.skip_drawing = TRUE; else { rdp.skip_drawing = FALSE; if (settings.fb_hires && OpenTextureBuffer(rdp.frame_buffers[rdp.ci_count])) ; else { if (rdp.ci_count == 0) { // if (rdp.num_of_ci > 1) // { rdp.scale_x = 1.0f; rdp.scale_y = 1.0f; // } } else if (!settings.fb_hires && (rdp.frame_buffers[rdp.ci_count-1].status == ci_main) && (rdp.frame_buffers[rdp.ci_count-1].width == rdp.frame_buffers[rdp.ci_count].width)) // for Pokemon Stadium DrawFrameBuffer (); } } } break; case ci_zimg: case ci_useless: //case ci_zcopy: rdp.skip_drawing = TRUE; break; case ci_copy_self: if (settings.fb_hires && !SwapOK && (rdp.ci_count <= rdp.copy_ci_index)) OpenTextureBuffer(rdp.frame_buffers[rdp.copy_ci_index]); rdp.skip_drawing = FALSE; /* if (settings.fb_hires) { if (SwapOK) { rdp.cimg = rdp.frame_buffers[rdp.ci_count].addr; rdp.maincimg[0] = rdp.cimg; newSwapBuffers(); SwapOK = FALSE; OpenTextureBuffer(rdp.frame_buffers[rdp.ci_count]); } } */ break; default: rdp.skip_drawing = FALSE; } if ((rdp.ci_count > 0) && (rdp.frame_buffers[rdp.ci_count-1].status == ci_aux)) //for Pokemon Stadium { if (!rdp.cur_image && rdp.frame_buffers[rdp.ci_count-1].format == 0) DrawFrameBuffer (); } if (rdp.frame_buffers[rdp.ci_count].status == ci_copy) { if ((rdp.num_of_ci > rdp.ci_count+1) && (rdp.frame_buffers[rdp.ci_count+1].status != ci_aux)) { FRDP("ci_copy. return to original scale: x = %f, y = %f\n", rdp.scale_x_bak, rdp.scale_y_bak); rdp.scale_x = rdp.scale_x_bak; rdp.scale_y = rdp.scale_y_bak; } } if (!settings.fb_hires && rdp.frame_buffers[rdp.ci_count].status == ci_aux) { if (rdp.frame_buffers[rdp.ci_count].format == 0) { if (settings.PPL && (rdp.scale_x < 1.1f)) //need to put current image back to frame buffer { int width = rdp.frame_buffers[rdp.ci_count].width; int height = rdp.frame_buffers[rdp.ci_count].height; WORD *ptr_dst = new WORD[width*height]; WORD *ptr_src = (WORD*)(gfx.RDRAM+rdp.frame_buffers[rdp.ci_count].addr); WORD c; for (int y=0; y> 1) | 0x8000; ptr_dst[x + y * width] = c; } } grLfbWriteRegion(GR_BUFFER_BACKBUFFER, 0, 0, GR_LFB_SRC_FMT_555, width, height, FXFALSE, width<<1, ptr_dst); delete[] ptr_dst; } /* else //just clear buffer { grColorMask(FXTRUE, FXTRUE); grBufferClear (0, 0, 0xFFFF); } */ } } if ((rdp.frame_buffers[rdp.ci_count].status == ci_main) && (rdp.ci_count > 0)) { BOOL to_org_res = TRUE; for (int i = rdp.ci_count + 1; i < rdp.num_of_ci; i++) { if ((rdp.frame_buffers[i].status != ci_main) && (rdp.frame_buffers[i].status != ci_zimg)) { to_org_res = FALSE; break; } } if (to_org_res) { FRDP("ci_main. return to original scale: x = %f, y = %f\n", rdp.scale_x_bak, rdp.scale_y_bak); rdp.scale_x = rdp.scale_x_bak; rdp.scale_y = rdp.scale_y_bak; if (settings.fb_hires) CloseTextureBuffer(); } if ((rdp.frame_buffers[rdp.ci_count-1].status == ci_aux) && (rdp.ci_count > rdp.copy_ci_index)) CloseTextureBuffer(); } rdp.ci_status = rdp.frame_buffers[rdp.ci_count].status; rdp.ci_count++; } rdp.ocimg = rdp.cimg; rdp.cimg = segoffset(rdp.cmd1) & BMASK; rdp.ci_width = (rdp.cmd0 & 0xFFF) + 1; if (rdp.ci_width == 32) rdp.ci_height = 32; else rdp.ci_height = rdp.scissor_o.lr_y; DWORD format = (rdp.cmd0 >> 21) & 0x7; rdp.ci_size = (rdp.cmd0 >> 19) & 0x3; rdp.ci_end = rdp.cimg + rdp.ci_width*rdp.ci_height*rdp.ci_size; FRDP("setcolorimage - %08lx, width: %d, height: %d, format: %d, size: %d\n", rdp.cmd1, rdp.ci_width, rdp.ci_height, format, rdp.ci_size); FRDP("cimg: %08lx, ocimg: %08lx, SwapOK: %d\n", rdp.cimg, rdp.ocimg, SwapOK); if (format != 0 && !rdp.cur_image) //can't draw into non RGBA buffer { if (settings.fb_hires && rdp.ci_width <= 64) OpenTextureBuffer(rdp.frame_buffers[rdp.ci_count - 1]); else if (format > 2) rdp.skip_drawing = TRUE; return; } else { if (!settings.fb_smart) rdp.skip_drawing = FALSE; } if (settings.swapmode > 0) { if (rdp.zimg == rdp.cimg) rdp.updatescreen = 1; BOOL viSwapOK = (settings.swapmode == 2)?vi_swap:TRUE; if ((rdp.zimg != rdp.cimg) && (rdp.ocimg != rdp.cimg) && SwapOK && viSwapOK && !rdp.cur_image) { if (settings.fb_smart) rdp.maincimg[0] = rdp.frame_buffers[rdp.main_ci_index].addr; else rdp.maincimg[0] = rdp.cimg; newSwapBuffers(); SwapOK = FALSE; vi_swap = FALSE; if (settings.fb_hires && rdp.copy_ci_index && (rdp.frame_buffers[rdp.ci_count-1].status != ci_zimg)) { FRDP("attempt open tex buffer. status: %d, addr: %08lx\n", rdp.frame_buffers[rdp.copy_ci_index].status, rdp.frame_buffers[rdp.copy_ci_index].addr); OpenTextureBuffer(rdp.frame_buffers[rdp.copy_ci_index]); } } } } static void rdp_trifill() { RDP_E("trifill - IGNORED\n"); RDP("trifill - IGNORED\n"); } static void rdp_trishade() { RDP_E("trishade - IGNORED\n"); RDP("trishade - IGNORED\n"); } static void rdp_tritxtr() { RDP_E("tritxtr - IGNORED\n"); RDP("tritxtr - IGNORED\n"); } static void rdp_trishadetxtr() { RDP_E("trishadetxtr - IGNORED\n"); RDP("trishadetxtr - IGNORED\n"); } static void rdp_trifillz() { RDP_E("trifillz - IGNORED\n"); RDP("trifillz - IGNORED\n"); } static void rdp_trishadez() { RDP_E("trishadez - IGNORED\n"); RDP("trishadez - IGNORED\n"); } static void rdp_tritxtrz() { RDP_E("tritxtrz - IGNORED\n"); RDP("tritxtrz - IGNORED\n"); } static void rdp_trishadetxtrz() { RDP_E("trishadetxtrz - IGNORED\n"); RDP("trishadetxtrz - IGNORED\n"); } static void rsp_reserved0() { RDP_E("reserved0 - IGNORED\n"); RDP("reserved0 - IGNORED\n"); } static void rsp_reserved1() { RDP("reserved1 - ignored\n"); } static void rsp_reserved2() { RDP("reserved2\n"); } static void rsp_reserved3() { RDP("reserved3 - ignored\n"); } void SetWireframeCol () { if (!fullscreen) return; switch (settings.wfmode) { //case 0: // normal colors, don't do anything case 1: // vertex colors grColorCombine (GR_COMBINE_FUNCTION_LOCAL, GR_COMBINE_FACTOR_NONE, GR_COMBINE_LOCAL_ITERATED, GR_COMBINE_OTHER_NONE, FXFALSE); grAlphaCombine (GR_COMBINE_FUNCTION_LOCAL, GR_COMBINE_FACTOR_NONE, GR_COMBINE_LOCAL_ITERATED, GR_COMBINE_OTHER_NONE, FXFALSE); grAlphaBlendFunction (GR_BLEND_ONE, GR_BLEND_ZERO, GR_BLEND_ZERO, GR_BLEND_ZERO); grTexCombine (GR_TMU0, GR_COMBINE_FUNCTION_ZERO, GR_COMBINE_FACTOR_NONE, GR_COMBINE_FUNCTION_ZERO, GR_COMBINE_FACTOR_NONE, FXFALSE, FXFALSE); grTexCombine (GR_TMU1, GR_COMBINE_FUNCTION_ZERO, GR_COMBINE_FACTOR_NONE, GR_COMBINE_FUNCTION_ZERO, GR_COMBINE_FACTOR_NONE, FXFALSE, FXFALSE); break; case 2: // red only grColorCombine (GR_COMBINE_FUNCTION_LOCAL, GR_COMBINE_FACTOR_NONE, GR_COMBINE_LOCAL_CONSTANT, GR_COMBINE_OTHER_NONE, FXFALSE); grAlphaCombine (GR_COMBINE_FUNCTION_LOCAL, GR_COMBINE_FACTOR_NONE, GR_COMBINE_LOCAL_CONSTANT, GR_COMBINE_OTHER_NONE, FXFALSE); grConstantColorValue (0xFFFF0000); grAlphaBlendFunction (GR_BLEND_ONE, GR_BLEND_ZERO, GR_BLEND_ZERO, GR_BLEND_ZERO); grTexCombine (GR_TMU0, GR_COMBINE_FUNCTION_ZERO, GR_COMBINE_FACTOR_NONE, GR_COMBINE_FUNCTION_ZERO, GR_COMBINE_FACTOR_NONE, FXFALSE, FXFALSE); grTexCombine (GR_TMU1, GR_COMBINE_FUNCTION_ZERO, GR_COMBINE_FACTOR_NONE, GR_COMBINE_FUNCTION_ZERO, GR_COMBINE_FACTOR_NONE, FXFALSE, FXFALSE); break; } grAlphaTestFunction (GR_CMP_ALWAYS); grCullMode (GR_CULL_DISABLE); //grDepthBufferFunction (GR_CMP_ALWAYS); //grDepthMask (FXFALSE); rdp.update |= UPDATE_COMBINE | UPDATE_ALPHA_COMPARE; } /****************************************************************** Function: FrameBufferRead Purpose: This function is called to notify the dll that the frame buffer memory is beening read at the given address. DLL should copy content from its render buffer to the frame buffer in N64 RDRAM DLL is responsible to maintain its own frame buffer memory addr list DLL should copy 4KB block content back to RDRAM frame buffer. Emulator should not call this function again if other memory is read within the same 4KB range input: addr rdram address val val size 1 = BYTE, 2 = WORD, 4 = DWORD output: none *******************************************************************/ EXPORT void CALL FBRead(DWORD addr) { LOG ("FBRead ()\n"); DWORD a = segoffset(addr); FRDP("FBRead. addr: %08lx\n", a); cpu_fb_write = FALSE; //read and write at the same time? if (!rdp.fb_drawn && (a >= rdp.cimg) && (a < rdp.ci_end)) { fbreads++; if ((fbreads > 2) && (rdp.ci_width <= 320)) { DrawFrameBuffer (); rdp.fb_drawn = TRUE; } } } /****************************************************************** Function: FrameBufferWriteList Purpose: This function is called to notify the dll that the frame buffer has been modified by CPU at the given address. input: FrameBufferModifyEntry *plist size = size of the plist, max = 1024 output: none *******************************************************************/ EXPORT void CALL FBWList(FrameBufferModifyEntry *plist, DWORD size) { LOG ("FBWList ()\n"); FRDP("FBWList. size: %d\n", size); } /****************************************************************** Function: FrameBufferWrite Purpose: This function is called to notify the dll that the frame buffer has been modified by CPU at the given address. input: addr rdram address val val size 1 = BYTE, 2 = WORD, 4 = DWORD output: none *******************************************************************/ EXPORT void CALL FBWrite(DWORD addr, DWORD size) { LOG ("FBWrite ()\n"); DWORD a = segoffset(addr); FRDP("FBWrite. addr: %08lx\n", a); if (a < rdp.cimg || a > rdp.ci_end) return; cpu_fb_write = TRUE; DWORD shift_l = (a-rdp.cimg) >> 1; DWORD shift_r = shift_l+2; d_ul_x = min(d_ul_x, shift_l%rdp.ci_width); d_ul_y = min(d_ul_y, shift_l/rdp.ci_width); d_lr_x = max(d_lr_x, shift_r%rdp.ci_width); d_lr_y = max(d_lr_y, shift_r/rdp.ci_width); } /************************************************************************ Function: FBGetFrameBufferInfo Purpose: This function is called by the emulator core to retrieve frame buffer information from the video plugin in order to be able to notify the video plugin about CPU frame buffer read/write operations size: = 1 byte = 2 word (16 bit) <-- this is N64 default depth buffer format = 4 dword (32 bit) when frame buffer information is not available yet, set all values in the FrameBufferInfo structure to 0 input: FrameBufferInfo pinfo[6] pinfo is pointed to a FrameBufferInfo structure which to be filled in by this function output: Values are return in the FrameBufferInfo structure Plugin can return up to 6 frame buffer info ************************************************************************/ ///* typedef struct { DWORD addr; DWORD size; DWORD width; DWORD height; } FrameBufferInfo; EXPORT void CALL FBGetFrameBufferInfo(void *p) { FrameBufferInfo * pinfo = (FrameBufferInfo *)p; memset(pinfo,0,sizeof(FrameBufferInfo)*6); /* if (settings.fb_smart) { pinfo[0].addr = rdp.frame_buffers[0].addr; pinfo[0].size = rdp.frame_buffers[0].size; pinfo[0].width = rdp.frame_buffers[0].width; pinfo[0].height = rdp.frame_buffers[0].height; int info_index = 0; int fb_index = 1; while ((info_index < 5) && (fb_index < rdp.ci_count)) { if (pinfo[info_index].addr != rdp.frame_buffers[fb_index].addr) { info_index++; pinfo[info_index].addr = rdp.frame_buffers[fb_index].addr; pinfo[info_index].size = rdp.frame_buffers[fb_index].size; pinfo[info_index].width = rdp.frame_buffers[fb_index].width; pinfo[info_index].height = rdp.frame_buffers[fb_index].height; } fb_index++; } if (info_index < 5) { for (int i = info_index; i < 5; i++) { pinfo[info_index].addr = 0; pinfo[info_index].size = 0; pinfo[info_index].width = 0; pinfo[info_index].height = 0; } pinfo[5].addr = rdp.cimg; pinfo[5].size = rdp.ci_size; pinfo[5].width = rdp.ci_width; pinfo[5].height = rdp.ci_height; } } //*/ } //*/ #include "UcodeFB.h" void DetectFrameBufferUsage () { RDP("DetectFrameBufferUsage\n"); DWORD dlist_start = *(DWORD*)(gfx.DMEM+0xFF0); #ifdef _WIN32 DWORD dlist_length = *(DWORD*)(gfx.DMEM+0xFF4); #endif // _WIN32 DWORD a; DWORD ci = rdp.cimg, zi = rdp.zimg, ci_width = rdp.ci_width; rdp.main_ci = 0, rdp.main_ci_end = 0, rdp.main_ci_bg = 0, rdp.ci_count = 0; rdp.main_ci_index = 0, rdp.copy_ci_index = 0; rdp.zimg_end = 0; rdp.tmpzimg = 0; rdp.motionblur = FALSE; BOOL previous_ci_was_read = rdp.read_previous_ci; rdp.read_previous_ci = FALSE; //rdp.scissor_o.lr_y = 0; // Start executing at the start of the display list rdp.pc_i = 0; rdp.pc[rdp.pc_i] = dlist_start; rdp.dl_count = -1; rdp.halt = 0; rdp.scale_x_bak = rdp.scale_x; rdp.scale_y_bak = rdp.scale_y; // MAIN PROCESSING LOOP do { // Get the address of the next command a = rdp.pc[rdp.pc_i] & BMASK; // Load the next command and its input rdp.cmd0 = ((DWORD*)gfx.RDRAM)[a>>2]; // \ Current command, 64 bit rdp.cmd1 = ((DWORD*)gfx.RDRAM)[(a>>2)+1]; // / // Output the address before the command // Go to the next instruction rdp.pc[rdp.pc_i] = (a+8) & BMASK; if ((int)(gfx_instruction_lite[settings.ucode][rdp.cmd0>>24])) gfx_instruction_lite[settings.ucode][rdp.cmd0>>24] (); // check DL counter if (rdp.dl_count != -1) { rdp.dl_count --; if (rdp.dl_count == 0) { rdp.dl_count = -1; RDP ("End of DL\n"); rdp.pc_i --; } } } while (!rdp.halt); if (rdp.frame_buffers[rdp.ci_count-1].status == ci_unknown) { if (rdp.ci_count > 1) rdp.frame_buffers[rdp.ci_count-1].status = ci_aux; else rdp.frame_buffers[rdp.ci_count-1].status = ci_main; } if ((rdp.frame_buffers[rdp.ci_count-1].status == ci_aux) && (rdp.frame_buffers[rdp.main_ci_index].width < 320) && (rdp.frame_buffers[rdp.ci_count-1].width > rdp.frame_buffers[rdp.main_ci_index].width)) { for (int i = 0; i < rdp.ci_count; i++) { if (rdp.frame_buffers[i].status == ci_main) rdp.frame_buffers[i].status = ci_aux; else if (rdp.frame_buffers[i].addr == rdp.frame_buffers[rdp.ci_count-1].addr) rdp.frame_buffers[i].status = ci_main; // FRDP("rdp.frame_buffers[%d].status = %d\n", i, rdp.frame_buffers[i].status); } rdp.main_ci_index = rdp.ci_count-1; } BOOL all_zimg = TRUE; int i; for (i = 0; i < rdp.ci_count; i++) { if (rdp.frame_buffers[i].status != ci_zimg) { all_zimg = FALSE; break; } } if (all_zimg) { for (i = 0; i < rdp.ci_count; i++) rdp.frame_buffers[i].status = ci_main; } RDP("detect fb final results: \n"); for (i = 0; i < rdp.ci_count; i++) { FRDP("rdp.frame_buffers[%d].status = %d\n", i, rdp.frame_buffers[i].status); } rdp.cimg = ci; rdp.zimg = zi; rdp.num_of_ci = rdp.ci_count; if (!settings.fb_hires && rdp.read_previous_ci && previous_ci_was_read) rdp.motionblur = TRUE; if (rdp.motionblur || settings.fb_hires) { rdp.scale_x = rdp.scale_x_bak; rdp.scale_y = rdp.scale_y_bak; } if (rdp.read_previous_ci && !rdp.copy_ci_index)// && !settings.fb_hires) { if (rdp.motionblur) { if (settings.fb_motionblur) DrawFrameBuffer(); else memset(gfx.RDRAM+rdp.cimg, 0, rdp.ci_width*rdp.ci_height*rdp.ci_size); } else if (ci_width == rdp.frame_buffers[rdp.main_ci_index].width) DrawFrameBuffer(); } if (settings.fb_hires) { for (i = 0; i < num_tmu; i++) { rdp.texbufs[i].clear_allowed = TRUE; for (int j = 0; j < rdp.texbufs[i].count; j++) { rdp.texbufs[i].images[j].drawn = FALSE; rdp.texbufs[i].images[j].clear = TRUE; } } } rdp.ci_count = 0; RDP("DetectFrameBufferUsage End\n"); }