diff --git a/bios/Other/nuance/.variants/bios.cof.f8e8facc b/bios/Other/nuance/.variants/bios.cof.f8e8facc new file mode 100644 index 00000000..8ee4916c Binary files /dev/null and b/bios/Other/nuance/.variants/bios.cof.f8e8facc differ diff --git a/bios/Other/nuance/.variants/video_generic.vs.64e56fb0 b/bios/Other/nuance/.variants/video_generic.vs.64e56fb0 new file mode 100644 index 00000000..c5629c0e --- /dev/null +++ b/bios/Other/nuance/.variants/video_generic.vs.64e56fb0 @@ -0,0 +1,15 @@ +/* +Nuance generic vertex shader for all pixel modes: + +This vertex shader simply passes in the texture coordinates set by +glMultiTexCoord and then sets gl_Position with the transformed vertex +coordinate. +*/ + +void main(void) +{ + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[1] = gl_MultiTexCoord1; + gl_TexCoord[2] = gl_MultiTexCoord2; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; +} \ No newline at end of file diff --git a/bios/Other/nuance/.variants/video_m32_o32.fs.a3c233df b/bios/Other/nuance/.variants/video_m32_o32.fs.a3c233df new file mode 100644 index 00000000..6e8d8367 --- /dev/null +++ b/bios/Other/nuance/.variants/video_m32_o32.fs.a3c233df @@ -0,0 +1,172 @@ +/* +Nuance pixel shader for 16/32-bit pixel modes: + +This pixel shader blends together two 16/32-bit YCrCbA format textures. The alpha +component for each pixel is expected to be a measure of transparency and not +opacity as is usually the case in OpenGL. The first set of texture coordinates +should correspond to the main video channel and the second set of texture +coordinates should correspond to the overlay video channel. Invalid YCrCbA +pixels of (0.0, 0.0, 0.0, X.X) are considered to be transparent for the overlay +channel only. + +The CCIR-601 definition of YCrCb is used. This means that the valid integer range +of Y is [16,235] and the valid integer range of Cr and Cb is [16,240]. + +After converting the texels to RGBA, the main channel texel is blended +with the overlay channel texel using the the overlay texel alpha value to +determine the transparency factor for the overlay color. That is, the final +color is [mainColor * (1 - overlayColor.a)] + (overlayColor * overlayColor.a) + +Note: Post-bias is not needed in this shader because the chromiance values +are being biased to [-0.5,0.5] unlike when using the imaging subset where +all intermediate values are required to stay within the range [0.0,1.0] +*/ + +uniform sampler2D mainChannelSampler; +uniform sampler2D overlayChannelSampler; +uniform sampler2D LUTSampler; + +uniform float structOverlayChannelAlpha; +uniform float mainIs16bit; + +uniform float resy; + +uniform vec4 scaleInternal; // main buffer xy and overlay buffer xy + +const vec3 preBiasExpansion = vec3(16.0/219.0,16.0/224.0,16.0/224.0); +const float chromianceBias = 0.5; +const vec3 expansion = vec3(255.0/219.0,255.0/224.0,255.0/224.0); + +vec3 ycrcb2rgb(vec3 ycrcb) +{ + ycrcb.yz -= chromianceBias; + return vec3(ycrcb.x+1.402*ycrcb.y, ycrcb.x-0.34413*ycrcb.z-0.714136*ycrcb.y, ycrcb.x+1.772*ycrcb.z); +} + +vec3 texture2D_main(vec2 uv_org) +{ + vec2 LUT[4]; + if(mainIs16bit != 0.) + for(int i = 0; i < 4; ++i) + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.x,(resy-1.)*scaleInternal.y)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + LUT[i] = texture2D(mainChannelSampler, uv).yx; + } + + vec3 bilerp[4]; + for(int i = 0; i < 4; ++i) + { + vec3 mainColor; + if(mainIs16bit != 0.) + mainColor = texture2D(LUTSampler, LUT[i]).xyz; + else + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.x,(resy-1.)*scaleInternal.y)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + mainColor = texture2D(mainChannelSampler, uv).bgr; //!! throws away alpha, but should not matter as this is the final displayed buffer anyway! + } + + mainColor = clamp(mainColor*expansion-preBiasExpansion, 0.0, 1.0); + bilerp[i] = clamp(ycrcb2rgb(mainColor), 0.0, 1.0); + } + + vec2 bilerpw = fract(uv_org); + // smoothstep: + bilerpw = bilerpw*bilerpw*(3.-2.*bilerpw); //bilerpw = bilerpw*bilerpw*bilerpw*(bilerpw*(bilerpw*6.0-15.0)+10.0); + return bilerp[0]*(1.-bilerpw.y-(bilerpw.x-bilerpw.x*bilerpw.y)) + + bilerp[1]*(bilerpw.x-bilerpw.x*bilerpw.y) + + bilerp[2]*(bilerpw.y-bilerpw.x*bilerpw.y) + + bilerp[3]*(bilerpw.x*bilerpw.y); +} + +vec4 texture2D_overlay(vec2 uv_org) +{ + vec2 LUT[4]; + if(structOverlayChannelAlpha >= 0.) + for(int i = 0; i < 4; ++i) + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.z,(resy-1.)*scaleInternal.w)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + LUT[i] = texture2D(overlayChannelSampler, uv).yx; + } + + vec4 bilerp[4]; + for(int i = 0; i < 4; ++i) + { + vec4 overlayColor; + if(structOverlayChannelAlpha >= 0.) + overlayColor = vec4(texture2D(LUTSampler, LUT[i]).xyz,structOverlayChannelAlpha); + else + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.z,(resy-1.)*scaleInternal.w)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + overlayColor = texture2D(overlayChannelSampler, uv).bgra; + } + + if(overlayColor.xyz == vec3(0.0,0.0,0.0)) // invalid overlay pixel -> fully transparent overlay pixel //!! should this also be interpolated?? + return vec4(overlayColor.xyz,1.0); + + overlayColor.xyz = clamp(overlayColor.xyz*expansion-preBiasExpansion, 0.0, 1.0); + overlayColor.rgb = clamp(ycrcb2rgb(overlayColor.xyz), 0.0, 1.0); + + bilerp[i] = overlayColor; + } + + vec2 bilerpw = fract(uv_org); + // smoothstep: + bilerpw = bilerpw*bilerpw*(3.-2.*bilerpw); //bilerpw = bilerpw*bilerpw*bilerpw*(bilerpw*(bilerpw*6.0-15.0)+10.0); + return bilerp[0]*(1.-bilerpw.y-(bilerpw.x-bilerpw.x*bilerpw.y)) + + bilerp[1]*(bilerpw.x-bilerpw.x*bilerpw.y) + + bilerp[2]*(bilerpw.y-bilerpw.x*bilerpw.y) + + bilerp[3]*(bilerpw.x*bilerpw.y); +} + +vec3 texture2D_composite(vec2 mainuv, vec2 overlayuv) +{ + vec3 mainColor; + if(mainIs16bit >= 0.) // main buffer enabled? + mainColor = texture2D_main(mainuv); + else + mainColor = vec3(0.,0.,0.); //!! ?? or disable mixing with it completely then? + + if(structOverlayChannelAlpha == 1.0) // overlay buffer not enabled? or fully transparent? + return mainColor; + + vec4 overlayColor = texture2D_overlay(overlayuv); + + //On the Nuon, alpha represents transparency with 0x00 being fully opaque. + return mix(mainColor, overlayColor.rgb, 1.0 - overlayColor.a); +} + +void main() +{ + vec2 mainuv = gl_TexCoord[0].st*vec2(720.,resy)-0.499; //!! 0.499 because of precision problems if src width/height is rather small (Space Invaders XL) + vec2 overlayuv = gl_TexCoord[1].st*vec2(720.,resy)-0.499; //!! dto. + + gl_FragColor = vec4(texture2D_composite(mainuv, overlayuv), 1.0); +} diff --git a/bios/Other/nuance/.variants/video_m32_o32_crt.fs.0681be4e b/bios/Other/nuance/.variants/video_m32_o32_crt.fs.0681be4e new file mode 100644 index 00000000..0e01d55c --- /dev/null +++ b/bios/Other/nuance/.variants/video_m32_o32_crt.fs.0681be4e @@ -0,0 +1,219 @@ +/* +Nuance pixel shader for 16/32-bit pixel modes: + +This pixel shader blends together two 16/32-bit YCrCbA format textures. The alpha +component for each pixel is expected to be a measure of transparency and not +opacity as is usually the case in OpenGL. The first set of texture coordinates +should correspond to the main video channel and the second set of texture +coordinates should correspond to the overlay video channel. Invalid YCrCbA +pixels of (0.0, 0.0, 0.0, X.X) are considered to be transparent for the overlay +channel only. + +The CCIR-601 definition of YCrCb is used. This means that the valid integer range +of Y is [16,235] and the valid integer range of Cr and Cb is [16,240]. + +After converting the texels to RGBA, the main channel texel is blended +with the overlay channel texel using the the overlay texel alpha value to +determine the transparency factor for the overlay color. That is, the final +color is [mainColor * (1 - overlayColor.a)] + (overlayColor * overlayColor.a) + +Note: Post-bias is not needed in this shader because the chromiance values +are being biased to [-0.5,0.5] unlike when using the imaging subset where +all intermediate values are required to stay within the range [0.0,1.0] +*/ + +uniform sampler2D mainChannelSampler; +uniform sampler2D overlayChannelSampler; +uniform sampler2D LUTSampler; + +uniform float structOverlayChannelAlpha; +uniform float mainIs16bit; + +uniform float resy; + +uniform vec2 windowRes; + +uniform vec4 scaleInternal; // main buffer xy and overlay buffer xy + +const vec3 preBiasExpansion = vec3(16.0/219.0,16.0/224.0,16.0/224.0); +const float chromianceBias = 0.5; +const vec3 expansion = vec3(255.0/219.0,255.0/224.0,255.0/224.0); + +vec3 ycrcb2rgb(vec3 ycrcb) +{ + ycrcb.yz -= chromianceBias; + return vec3(ycrcb.x+1.402*ycrcb.y, ycrcb.x-0.34413*ycrcb.z-0.714136*ycrcb.y, ycrcb.x+1.772*ycrcb.z); +} + +vec3 texture2D_main(vec2 uv_org) +{ + vec2 LUT[4]; + if(mainIs16bit != 0.) + for(int i = 0; i < 4; ++i) + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.x,(resy-1.)*scaleInternal.y)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + LUT[i] = texture2D(mainChannelSampler, uv).yx; + } + + vec3 bilerp[4]; + for(int i = 0; i < 4; ++i) + { + vec3 mainColor; + if(mainIs16bit != 0.) + mainColor = texture2D(LUTSampler, LUT[i]).xyz; + else + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.x,(resy-1.)*scaleInternal.y)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + mainColor = texture2D(mainChannelSampler, uv).bgr; //!! throws away alpha, but should not matter as this is the final displayed buffer anyway! + } + + mainColor = clamp(mainColor*expansion-preBiasExpansion, 0.0, 1.0); + bilerp[i] = clamp(ycrcb2rgb(mainColor), 0.0, 1.0); + } + + vec2 bilerpw = fract(uv_org); + // smoothstep: + // bilerpw = bilerpw*bilerpw*(3.-2.*bilerpw); //bilerpw = bilerpw*bilerpw*bilerpw*(bilerpw*(bilerpw*6.0-15.0)+10.0); + return bilerp[0]*(1.-bilerpw.y-(bilerpw.x-bilerpw.x*bilerpw.y)) + + bilerp[1]*(bilerpw.x-bilerpw.x*bilerpw.y) + + bilerp[2]*(bilerpw.y-bilerpw.x*bilerpw.y) + + bilerp[3]*(bilerpw.x*bilerpw.y); +} + +vec4 texture2D_overlay(vec2 uv_org) +{ + vec2 LUT[4]; + if(structOverlayChannelAlpha >= 0.) + for(int i = 0; i < 4; ++i) + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.z,(resy-1.)*scaleInternal.w)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + LUT[i] = texture2D(overlayChannelSampler, uv).yx; + } + + vec4 bilerp[4]; + for(int i = 0; i < 4; ++i) + { + vec4 overlayColor; + if(structOverlayChannelAlpha >= 0.) + overlayColor = vec4(texture2D(LUTSampler, LUT[i]).xyz,structOverlayChannelAlpha); + else + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.z,(resy-1.)*scaleInternal.w)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + overlayColor = texture2D(overlayChannelSampler, uv).bgra; + } + + if(overlayColor.xyz == vec3(0.0,0.0,0.0)) // invalid overlay pixel -> fully transparent overlay pixel //!! should this also be interpolated?? + return vec4(overlayColor.xyz,1.0); + + overlayColor.xyz = clamp(overlayColor.xyz*expansion-preBiasExpansion, 0.0, 1.0); + overlayColor.rgb = clamp(ycrcb2rgb(overlayColor.xyz), 0.0, 1.0); + + bilerp[i] = overlayColor; + } + + vec2 bilerpw = fract(uv_org); + // smoothstep: + // bilerpw = bilerpw*bilerpw*(3.-2.*bilerpw); //bilerpw = bilerpw*bilerpw*bilerpw*(bilerpw*(bilerpw*6.0-15.0)+10.0); + return bilerp[0]*(1.-bilerpw.y-(bilerpw.x-bilerpw.x*bilerpw.y)) + + bilerp[1]*(bilerpw.x-bilerpw.x*bilerpw.y) + + bilerp[2]*(bilerpw.y-bilerpw.x*bilerpw.y) + + bilerp[3]*(bilerpw.x*bilerpw.y); +} + +vec3 texture2D_composite(vec2 mainuv, vec2 overlayuv) +{ + vec3 mainColor; + if(mainIs16bit >= 0.) // main buffer enabled? + mainColor = texture2D_main(mainuv*vec2(720.,resy)-0.499); //!! 0.499 because of precision problems if src width/height is rather small (Space Invaders XL) + else + mainColor = vec3(0.,0.,0.); //!! ?? or disable mixing with it completely then? + + if(structOverlayChannelAlpha == 1.0) // overlay buffer not enabled? or fully transparent? + return mainColor; + + vec4 overlayColor = texture2D_overlay(overlayuv*vec2(720.,resy)-0.499); //!! dto. + + //On the Nuon, alpha represents transparency with 0x00 being fully opaque. + return mix(mainColor, overlayColor.rgb, 1.0 - overlayColor.a); +} + +float sqr(float x) +{ + return x*x; +} + +void main() +{ + vec2 uvw = vec2(gl_TexCoord[2].x,windowRes.y-gl_TexCoord[2].y); // window/screen coords in pixels (flipped y just to match reshade) + vec2 uvw01 = uvw/windowRes; // window/screen coords in 0..1,0..1 + + // limit mask size + float tmp = min(windowRes.x,720.*4.); + while(tmp > 720.*3.) //!! not optimal yet, but good enough to look good on 1920x1080 and 4k screens at all kinds of resolutions + { + uvw.x *= 0.5; + tmp *= 0.5; + } + // limit scanline size + if(windowRes.y > resy*2.) + uvw.y *= resy*2./windowRes.y; + + vec2 uvm = gl_TexCoord[0].xy; // main buffer + vec2 uvo = gl_TexCoord[1].xy; // overlay buffer + + vec3 col; + vec2 offs = vec2( 0.001, 0.001); + col.r = texture2D_composite(uvm+offs*scaleInternal.xy,uvo+offs*scaleInternal.zw).r+0.05; + offs = vec2( 0.000,-0.002); + col.g = texture2D_composite(uvm+offs*scaleInternal.xy,uvo+offs*scaleInternal.zw).g+0.05; + offs = vec2(-0.002, 0.000); + col.b = texture2D_composite(uvm+offs*scaleInternal.xy,uvo+offs*scaleInternal.zw).b+0.05; + // ghosting + offs = 0.45*vec2(-0.014,-0.027)+vec2( 0.001, 0.001); + col.r += 0.03505*sqr(clamp(3.*texture2D_composite(uvm+offs*scaleInternal.xy,uvo+offs*scaleInternal.zw).r,0.,1.)); + offs = 0.45*vec2(-0.019, -0.02)+vec2( 0.000,-0.002); + col.g += 0.02065*sqr(clamp(3.*texture2D_composite(uvm+offs*scaleInternal.xy,uvo+offs*scaleInternal.zw).g,0.,1.)); + offs = 0.35*vec2(-0.017,-0.003)+vec2(-0.002, 0.000); + col.b += 0.0443 *sqr(clamp(3.*texture2D_composite(uvm+offs*scaleInternal.xy,uvo+offs*scaleInternal.zw).b,0.,1.)); + + col = clamp(0.6*col + 0.4*col*col, 0.0,1.0); + + float vignetting = 0.1+16.0*(uvw01.x-uvw01.x*uvw01.x)*(uvw01.y-uvw01.y*uvw01.y); + col *= pow(vignetting, 0.2); + + col *= vec3(0.95,1.05,0.95) * 1.15 * 2.8; + + col *= pow(0.35 + 0.18*sin(uvw.y*1.5), 0.9); // scanline + + col *= 1.0 - 0.23*clamp(2.0*uvw.x - (4.0*floor(uvw.x*0.5) + 2.0), 0.0,1.0); // mask pattern + + gl_FragColor = vec4(col,1.0); +} diff --git a/bios/Other/nuance/FlashEEPROM.bin b/bios/Other/nuance/FlashEEPROM.bin new file mode 100644 index 00000000..4455abb5 Binary files /dev/null and b/bios/Other/nuance/FlashEEPROM.bin differ diff --git a/bios/Other/nuance/bios.cof b/bios/Other/nuance/bios.cof new file mode 100644 index 00000000..53d3086f Binary files /dev/null and b/bios/Other/nuance/bios.cof differ diff --git a/bios/Other/nuance/minibios.cof b/bios/Other/nuance/minibios.cof new file mode 100644 index 00000000..6f78ec7a Binary files /dev/null and b/bios/Other/nuance/minibios.cof differ diff --git a/bios/Other/nuance/minibiosX.cof b/bios/Other/nuance/minibiosX.cof new file mode 100644 index 00000000..64e6c241 Binary files /dev/null and b/bios/Other/nuance/minibiosX.cof differ diff --git a/bios/Other/nuance/nuance.cfg b/bios/Other/nuance/nuance.cfg new file mode 100644 index 00000000..e08e71b4 --- /dev/null +++ b/bios/Other/nuance/nuance.cfg @@ -0,0 +1,99 @@ +;The DVDBase option allows configuration of a directory path that is prepended to all file names passed to the +;media and filesystem routines. +;[DVDBase] +;c:/blah/ + +;Disabling can be faster, but will make some games buggy +[AudioInterrupts] +Enabled + +;Enabling is always faster, but still has some few issues (see GameCompatibility.txt/MPE3PacketHack-option) +[DynamicCompiler] +Enabled + +;Enabling is always faster and should be working nowadays, BUT if something does not work as expected, try to set this to Disabled first (and report the issue)! +[CompilerConstantPropagation] +Enabled + +;Still buggy +[CompilerDeadCodeElimination] +Disabled + +;When DumpCompiledBlocks is enabled, the compiler will keep logs of the compiled blocks for each processor +[DumpCompiledBlocks] +Disabled + +;When enabled, MPE3's system-bus native blocks are compiled one packet at a time, so MPE3 yields to the other MPEs at per-packet granularity (matching disabled DynamicCompiler). +;This is needed for games with a tight cross-MPE handshake in MPE3 system-bus code (e.g. suspected for T3K's level select), +;which otherwise livelock/hang. Leave enabled, performance cost should be minimal +;(it's also possible to choose "T3K" for the minimal support/perf-impact version, i.e. to only resolve T3Ks level select hang) +[MPE3PacketHack] +Enabled + +;Automatically triggers the Load Game / File button on startup +[AutomaticLoadPopup] +Enabled + +;Use an approximate postprocessing CRT shader for display +[UseCRTshader] +Enabled + +;Available bindings are DPAD_[UP,DOWN,LEFT,RIGHT], CPAD_[UP,DOWN,LEFT,RIGHT], A, L, R, NUON, and START. +;Bindings can be set to KEY__0, JOYBUT__0, JOYAXIS__[0,1], and JOYPOV__[0,1,2,3] +;See win32 documentation for VKEY definitions. Regular character/number vkey values are the same as their uppercase ASCII values. +[Controller1Mappings] +; Default keyboard config Nuance has always shipped with: +; '3' key +CPAD_UP = KEY_51_0 +; 'R' key +CPAD_RIGHT = KEY_82_0 +; 'E' key +CPAD_DOWN = KEY_69_0 +; 'W' key +CPAD_LEFT = KEY_87_0 +; 'D' key +A = KEY_68_0 +; 'F' key +B = KEY_70_0 +; 'Q' key +L = KEY_81_0 +; 'T' key +R = KEY_84_0 +; Up-arrow key +DPAD_UP = KEY_38_0 +; Right-arrow key +DPAD_RIGHT = KEY_39_0 +; Down-arrow key +DPAD_DOWN = KEY_40_0 +; Left-arrow key +DPAD_LEFT = KEY_37_0 +; 'S' key +NUON = KEY_83_0 +; 'A' key +START = KEY_65_0 + +; Mappings for the LogitechWingMan Action Pad w/Mode light lit (analog enable): +;CPAD_UP = JOYBUT_5_0 +;CPAD_RIGHT = JOYBUT_2_0 +;CPAD_DOWN = JOYBUT_1_0 +;CPAD_LEFT = JOYBUT_4_0 +;A = JOYBUT_0_0 +;B = JOYBUT_3_0 +;L = JOYBUT_6_0 +;R = JOYBUT_7_0 +;DPAD_UP = JOYPOV_0_0 +;DPAD_RIGHT = JOYPOV_0_1 +;DPAD_DOWN = JOYPOV_0_2 +;DPAD_LEFT = JOYPOV_0_3 +; No good button to map to Nuon button. Leave assigned to 'S' on Keyboard. +;NUON = KEY_83_0 +;START = JOYBUT_8_0 + +; The BIOS function void kprintf(const char *fmt, ...) can be used to log data +; to Nuance. It will be displayed when the "kprintf Log" button in the status +; dialog is selected. If a debug log file is specified, it will also be logged +; to that file. Data is always appended to the file, so restarting Nuance +; won't wipe out past debug sessions, but it will need to be manually deleted +; to avoid indefinite growth, so this option is not set by default. +;[DebugLogFile] +;nuance_debug.log diff --git a/bios/Other/nuance/video_generic.vs b/bios/Other/nuance/video_generic.vs new file mode 100644 index 00000000..5d87b28d --- /dev/null +++ b/bios/Other/nuance/video_generic.vs @@ -0,0 +1,21 @@ +#ifdef EMBED_HLSL +R"NUONSHADER( +#endif +/* +Nuance generic vertex shader for all pixel modes: + +This vertex shader simply passes in the texture coordinates set by +glMultiTexCoord and then sets gl_Position with the transformed vertex +coordinate. +*/ + +void main(void) +{ + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[1] = gl_MultiTexCoord1; + gl_TexCoord[2] = gl_MultiTexCoord2; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; +} +#ifdef EMBED_HLSL +)NUONSHADER" +#endif diff --git a/bios/Other/nuance/video_m32_o32.fs b/bios/Other/nuance/video_m32_o32.fs new file mode 100644 index 00000000..0a9e6c22 --- /dev/null +++ b/bios/Other/nuance/video_m32_o32.fs @@ -0,0 +1,179 @@ +#ifdef EMBED_HLSL +R"NUONSHADER( +#endif +/* +Nuance pixel shader for 16/32-bit pixel modes: + +This pixel shader blends together two 16/32-bit YCrCbA format textures. The alpha +component for each pixel is expected to be a measure of transparency and not +opacity as is usually the case in OpenGL. The first set of texture coordinates +should correspond to the main video channel and the second set of texture +coordinates should correspond to the overlay video channel. Invalid YCrCbA +pixels of (0.0, 0.0, 0.0, X.X) are considered to be transparent for the overlay +channel only. + +The CCIR-601 definition of YCrCb is used. This means that the valid integer range +of Y is [16,235] and the valid integer range of Cr and Cb is [16,240]. + +After converting the texels to RGBA, the main channel texel is blended +with the overlay channel texel using the the overlay texel alpha value to +determine the transparency factor for the overlay color. That is, the final +color is [mainColor * (1 - overlayColor.a)] + (overlayColor * overlayColor.a) + +Note: Post-bias is not needed in this shader because the chromiance values +are being biased to [-0.5,0.5] unlike when using the imaging subset where +all intermediate values are required to stay within the range [0.0,1.0] +*/ + +uniform sampler2D mainChannelSampler; +uniform sampler2D overlayChannelSampler; +uniform sampler2D LUTSampler; + +uniform float structOverlayChannelAlpha; +uniform float mainIs16bit; + +uniform float resy; + +uniform vec4 scaleInternal; // main buffer xy and overlay buffer xy + +const vec3 preBiasExpansion = vec3(16.0/219.0,16.0/224.0,16.0/224.0); +const float chromianceBias = 0.5; +const vec3 expansion = vec3(255.0/219.0,255.0/224.0,255.0/224.0); + +vec3 ycrcb2rgb(vec3 ycrcb) +{ + ycrcb.yz -= chromianceBias; + return vec3(ycrcb.x+1.402*ycrcb.y, ycrcb.x-0.34413*ycrcb.z-0.714136*ycrcb.y, ycrcb.x+1.772*ycrcb.z); +} + +vec3 texture2D_main(vec2 uv_org) +{ + vec2 LUT[4]; + if(mainIs16bit != 0.) + for(int i = 0; i < 4; ++i) + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.x,(resy-1.)*scaleInternal.y)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + LUT[i] = texture2D(mainChannelSampler, uv).yx; + } + + vec3 bilerp[4]; + for(int i = 0; i < 4; ++i) + { + vec3 mainColor; + if(mainIs16bit != 0.) + mainColor = texture2D(LUTSampler, LUT[i]).xyz; + else + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.x,(resy-1.)*scaleInternal.y)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + mainColor = texture2D(mainChannelSampler, uv).bgr; //!! throws away alpha, but should not matter as this is the final displayed buffer anyway! + } + + mainColor = clamp(mainColor*expansion-preBiasExpansion, 0.0, 1.0); + bilerp[i] = clamp(ycrcb2rgb(mainColor), 0.0, 1.0); + } + + vec2 bilerpw = fract(uv_org); + // smoothstep: + bilerpw = bilerpw*bilerpw*(3.-2.*bilerpw); //bilerpw = bilerpw*bilerpw*bilerpw*(bilerpw*(bilerpw*6.0-15.0)+10.0); + return bilerp[0]*(1.-bilerpw.y-(bilerpw.x-bilerpw.x*bilerpw.y)) + + bilerp[1]*(bilerpw.x-bilerpw.x*bilerpw.y) + + bilerp[2]*(bilerpw.y-bilerpw.x*bilerpw.y) + + bilerp[3]*(bilerpw.x*bilerpw.y); +} + +vec4 texture2D_overlay(vec2 uv_org) +{ + vec2 LUT[4]; + if(structOverlayChannelAlpha >= 0.) + for(int i = 0; i < 4; ++i) + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.z,(resy-1.)*scaleInternal.w)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + LUT[i] = texture2D(overlayChannelSampler, uv).yx; + } + + vec4 bilerp[4]; + for(int i = 0; i < 4; ++i) + { + vec4 overlayColor; + if(structOverlayChannelAlpha >= 0.) + overlayColor = vec4(texture2D(LUTSampler, LUT[i]).xyz,structOverlayChannelAlpha); + else + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.z,(resy-1.)*scaleInternal.w)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + overlayColor = texture2D(overlayChannelSampler, uv).bgra; + } + + if(overlayColor.xyz == vec3(0.0,0.0,0.0)) // invalid overlay pixel -> fully transparent overlay pixel //!! should this also be interpolated?? + return vec4(overlayColor.xyz,1.0); + + overlayColor.xyz = clamp(overlayColor.xyz*expansion-preBiasExpansion, 0.0, 1.0); + overlayColor.rgb = clamp(ycrcb2rgb(overlayColor.xyz), 0.0, 1.0); + + bilerp[i] = overlayColor; + } + + vec2 bilerpw = fract(uv_org); + // smoothstep: + bilerpw = bilerpw*bilerpw*(3.-2.*bilerpw); //bilerpw = bilerpw*bilerpw*bilerpw*(bilerpw*(bilerpw*6.0-15.0)+10.0); + return bilerp[0]*(1.-bilerpw.y-(bilerpw.x-bilerpw.x*bilerpw.y)) + + bilerp[1]*(bilerpw.x-bilerpw.x*bilerpw.y) + + bilerp[2]*(bilerpw.y-bilerpw.x*bilerpw.y) + + bilerp[3]*(bilerpw.x*bilerpw.y); +} + +vec3 texture2D_composite(vec2 mainuv, vec2 overlayuv) +{ + vec3 mainColor; + if(mainIs16bit >= 0.) // main buffer enabled? + mainColor = texture2D_main(mainuv); + else + mainColor = vec3(0.,0.,0.); //!! ?? or disable mixing with it completely then? + + if(structOverlayChannelAlpha == 1.0) // overlay buffer not enabled? or fully transparent? + return mainColor; + + vec4 overlayColor = texture2D_overlay(overlayuv); + + //On the Nuon, alpha represents transparency with 0x00 being fully opaque. + return mix(mainColor, overlayColor.rgb, 1.0 - overlayColor.a); +} + +void main() +{ + vec2 mainuv = gl_TexCoord[0].st*vec2(720.,resy)-0.499; //!! 0.499 because of precision problems if src width/height is rather small (Space Invaders XL) + vec2 overlayuv = gl_TexCoord[1].st*vec2(720.,resy)-0.499; //!! dto. + + gl_FragColor = vec4(texture2D_composite(mainuv, overlayuv), 1.0); +} + +#ifdef EMBED_HLSL +)NUONSHADER" +#endif diff --git a/bios/Other/nuance/video_m32_o32_crt.fs b/bios/Other/nuance/video_m32_o32_crt.fs new file mode 100644 index 00000000..fb46044d --- /dev/null +++ b/bios/Other/nuance/video_m32_o32_crt.fs @@ -0,0 +1,226 @@ +#ifdef EMBED_HLSL +R"NUONSHADER( +#endif +/* +Nuance pixel shader for 16/32-bit pixel modes: + +This pixel shader blends together two 16/32-bit YCrCbA format textures. The alpha +component for each pixel is expected to be a measure of transparency and not +opacity as is usually the case in OpenGL. The first set of texture coordinates +should correspond to the main video channel and the second set of texture +coordinates should correspond to the overlay video channel. Invalid YCrCbA +pixels of (0.0, 0.0, 0.0, X.X) are considered to be transparent for the overlay +channel only. + +The CCIR-601 definition of YCrCb is used. This means that the valid integer range +of Y is [16,235] and the valid integer range of Cr and Cb is [16,240]. + +After converting the texels to RGBA, the main channel texel is blended +with the overlay channel texel using the the overlay texel alpha value to +determine the transparency factor for the overlay color. That is, the final +color is [mainColor * (1 - overlayColor.a)] + (overlayColor * overlayColor.a) + +Note: Post-bias is not needed in this shader because the chromiance values +are being biased to [-0.5,0.5] unlike when using the imaging subset where +all intermediate values are required to stay within the range [0.0,1.0] +*/ + +uniform sampler2D mainChannelSampler; +uniform sampler2D overlayChannelSampler; +uniform sampler2D LUTSampler; + +uniform float structOverlayChannelAlpha; +uniform float mainIs16bit; + +uniform float resy; + +uniform vec2 windowRes; + +uniform vec4 scaleInternal; // main buffer xy and overlay buffer xy + +const vec3 preBiasExpansion = vec3(16.0/219.0,16.0/224.0,16.0/224.0); +const float chromianceBias = 0.5; +const vec3 expansion = vec3(255.0/219.0,255.0/224.0,255.0/224.0); + +vec3 ycrcb2rgb(vec3 ycrcb) +{ + ycrcb.yz -= chromianceBias; + return vec3(ycrcb.x+1.402*ycrcb.y, ycrcb.x-0.34413*ycrcb.z-0.714136*ycrcb.y, ycrcb.x+1.772*ycrcb.z); +} + +vec3 texture2D_main(vec2 uv_org) +{ + vec2 LUT[4]; + if(mainIs16bit != 0.) + for(int i = 0; i < 4; ++i) + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.x,(resy-1.)*scaleInternal.y)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + LUT[i] = texture2D(mainChannelSampler, uv).yx; + } + + vec3 bilerp[4]; + for(int i = 0; i < 4; ++i) + { + vec3 mainColor; + if(mainIs16bit != 0.) + mainColor = texture2D(LUTSampler, LUT[i]).xyz; + else + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.x,(resy-1.)*scaleInternal.y)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + mainColor = texture2D(mainChannelSampler, uv).bgr; //!! throws away alpha, but should not matter as this is the final displayed buffer anyway! + } + + mainColor = clamp(mainColor*expansion-preBiasExpansion, 0.0, 1.0); + bilerp[i] = clamp(ycrcb2rgb(mainColor), 0.0, 1.0); + } + + vec2 bilerpw = fract(uv_org); + // smoothstep: + // bilerpw = bilerpw*bilerpw*(3.-2.*bilerpw); //bilerpw = bilerpw*bilerpw*bilerpw*(bilerpw*(bilerpw*6.0-15.0)+10.0); + return bilerp[0]*(1.-bilerpw.y-(bilerpw.x-bilerpw.x*bilerpw.y)) + + bilerp[1]*(bilerpw.x-bilerpw.x*bilerpw.y) + + bilerp[2]*(bilerpw.y-bilerpw.x*bilerpw.y) + + bilerp[3]*(bilerpw.x*bilerpw.y); +} + +vec4 texture2D_overlay(vec2 uv_org) +{ + vec2 LUT[4]; + if(structOverlayChannelAlpha >= 0.) + for(int i = 0; i < 4; ++i) + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.z,(resy-1.)*scaleInternal.w)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + LUT[i] = texture2D(overlayChannelSampler, uv).yx; + } + + vec4 bilerp[4]; + for(int i = 0; i < 4; ++i) + { + vec4 overlayColor; + if(structOverlayChannelAlpha >= 0.) + overlayColor = vec4(texture2D(LUTSampler, LUT[i]).xyz,structOverlayChannelAlpha); + else + { + vec2 uv = uv_org; + if(i == 1 || i == 3) + uv.x += 1.; + if(i > 1) + uv.y += 1.; + uv = clamp(uv,vec2(0.,0.),vec2((720.0-1.0)*scaleInternal.z,(resy-1.)*scaleInternal.w)); + uv *= vec2(0.0013888889,1./resy); // 1./720 + + overlayColor = texture2D(overlayChannelSampler, uv).bgra; + } + + if(overlayColor.xyz == vec3(0.0,0.0,0.0)) // invalid overlay pixel -> fully transparent overlay pixel //!! should this also be interpolated?? + return vec4(overlayColor.xyz,1.0); + + overlayColor.xyz = clamp(overlayColor.xyz*expansion-preBiasExpansion, 0.0, 1.0); + overlayColor.rgb = clamp(ycrcb2rgb(overlayColor.xyz), 0.0, 1.0); + + bilerp[i] = overlayColor; + } + + vec2 bilerpw = fract(uv_org); + // smoothstep: + // bilerpw = bilerpw*bilerpw*(3.-2.*bilerpw); //bilerpw = bilerpw*bilerpw*bilerpw*(bilerpw*(bilerpw*6.0-15.0)+10.0); + return bilerp[0]*(1.-bilerpw.y-(bilerpw.x-bilerpw.x*bilerpw.y)) + + bilerp[1]*(bilerpw.x-bilerpw.x*bilerpw.y) + + bilerp[2]*(bilerpw.y-bilerpw.x*bilerpw.y) + + bilerp[3]*(bilerpw.x*bilerpw.y); +} + +vec3 texture2D_composite(vec2 mainuv, vec2 overlayuv) +{ + vec3 mainColor; + if(mainIs16bit >= 0.) // main buffer enabled? + mainColor = texture2D_main(mainuv*vec2(720.,resy)-0.499); //!! 0.499 because of precision problems if src width/height is rather small (Space Invaders XL) + else + mainColor = vec3(0.,0.,0.); //!! ?? or disable mixing with it completely then? + + if(structOverlayChannelAlpha == 1.0) // overlay buffer not enabled? or fully transparent? + return mainColor; + + vec4 overlayColor = texture2D_overlay(overlayuv*vec2(720.,resy)-0.499); //!! dto. + + //On the Nuon, alpha represents transparency with 0x00 being fully opaque. + return mix(mainColor, overlayColor.rgb, 1.0 - overlayColor.a); +} + +float sqr(float x) +{ + return x*x; +} + +void main() +{ + vec2 uvw = vec2(gl_TexCoord[2].x,windowRes.y-gl_TexCoord[2].y); // window/screen coords in pixels (flipped y just to match reshade) + vec2 uvw01 = uvw/windowRes; // window/screen coords in 0..1,0..1 + + // limit mask size + float tmp = min(windowRes.x,720.*4.); + while(tmp > 720.*3.) //!! not optimal yet, but good enough to look good on 1920x1080 and 4k screens at all kinds of resolutions + { + uvw.x *= 0.5; + tmp *= 0.5; + } + // limit scanline size + if(windowRes.y > resy*2.) + uvw.y *= resy*2./windowRes.y; + + vec2 uvm = gl_TexCoord[0].xy; // main buffer + vec2 uvo = gl_TexCoord[1].xy; // overlay buffer + + vec3 col; + vec2 offs = vec2( 0.001, 0.001); + col.r = texture2D_composite(uvm+offs*scaleInternal.xy,uvo+offs*scaleInternal.zw).r+0.05; + offs = vec2( 0.000,-0.002); + col.g = texture2D_composite(uvm+offs*scaleInternal.xy,uvo+offs*scaleInternal.zw).g+0.05; + offs = vec2(-0.002, 0.000); + col.b = texture2D_composite(uvm+offs*scaleInternal.xy,uvo+offs*scaleInternal.zw).b+0.05; + // ghosting + offs = 0.45*vec2(-0.014,-0.027)+vec2( 0.001, 0.001); + col.r += 0.03505*sqr(clamp(3.*texture2D_composite(uvm+offs*scaleInternal.xy,uvo+offs*scaleInternal.zw).r,0.,1.)); + offs = 0.45*vec2(-0.019, -0.02)+vec2( 0.000,-0.002); + col.g += 0.02065*sqr(clamp(3.*texture2D_composite(uvm+offs*scaleInternal.xy,uvo+offs*scaleInternal.zw).g,0.,1.)); + offs = 0.35*vec2(-0.017,-0.003)+vec2(-0.002, 0.000); + col.b += 0.0443 *sqr(clamp(3.*texture2D_composite(uvm+offs*scaleInternal.xy,uvo+offs*scaleInternal.zw).b,0.,1.)); + + col = clamp(0.6*col + 0.4*col*col, 0.0,1.0); + + float vignetting = 0.1+16.0*(uvw01.x-uvw01.x*uvw01.x)*(uvw01.y-uvw01.y*uvw01.y); + col *= pow(vignetting, 0.2); + + col *= vec3(0.95,1.05,0.95) * 1.15 * 2.8; + + col *= pow(0.35 + 0.18*sin(uvw.y*1.5), 0.9); // scanline + + col *= 1.0 - 0.23*clamp(2.0*uvw.x - (4.0*floor(uvw.x*0.5) + 2.0), 0.0,1.0); // mask pattern + + gl_FragColor = vec4(col,1.0); +} + +#ifdef EMBED_HLSL +)NUONSHADER" +#endif diff --git a/bios/Sony/PlayStation 2/.variants/patches.zip.5a4d0a00 b/bios/Sony/PlayStation 2/.variants/patches.zip.5a4d0a00 new file mode 100644 index 00000000..b65c2faa Binary files /dev/null and b/bios/Sony/PlayStation 2/.variants/patches.zip.5a4d0a00 differ diff --git a/bios/Sony/PocketStation/.variants/pocketstation.bin.3bcdcef3 b/bios/Sony/PocketStation/.variants/pocketstation.bin.3bcdcef3 new file mode 100644 index 00000000..f22dafd6 Binary files /dev/null and b/bios/Sony/PocketStation/.variants/pocketstation.bin.3bcdcef3 differ diff --git a/bios/Sony/PocketStation/pocketstation.bin b/bios/Sony/PocketStation/pocketstation.bin new file mode 100644 index 00000000..ab2fced9 Binary files /dev/null and b/bios/Sony/PocketStation/pocketstation.bin differ diff --git a/emulators/nuance.yml b/emulators/nuance.yml new file mode 100644 index 00000000..3c87df49 --- /dev/null +++ b/emulators/nuance.yml @@ -0,0 +1,173 @@ +emulator: Nuance +type: libretro +core_classification: official_port +source: "https://github.com/WizzardSK/nuance-libretro" +upstream: "https://github.com/andkrau/NuanceResurrection" +profiled_date: "2026-09-03" +source_commit: "700d28553f3a06b16385ed9e48f194377e131b03" +upstream_commit: "700d28553f3a06b16385ed9e48f194377e131b03" +core_version: "0.6.7" +display_name: "VM Labs - NUON (Nuance)" +cores: [nuance] +systems: [vm-labs-nuon] + +notes: | + NUON emulator written by Mike Perry (2002-2007) and continued as + NuanceResurrection by Carsten Waechter from the source archive released + after the author's death. The Linux and libretro ports were merged into the + upstream tree in 2026 (pull request 75), so the port and the upstream share + the same revision. + + The three BIOS files are not firmware dumps. bios.cof, minibios.cof and + minibiosX.cof are Aries assembly implementations of BIOS routines, + assembled from bios.s, minibios.s and minibiosX.s in the source tree; every + other BIOS call is handled in C++ through BiosJumpTable (src/bios.cpp:395-547). + The entries left to the assembly code are the comm bus calls, TimeToSleep, + MPEWait and BiosGetInfo. + + retro_load_game looks for /nuance/bios.cof and changes the working + directory to /nuance when it exists, else to the directory of the + core library when bios.cof sits beside it (src/libretro.cpp:313-352). Every + data file is then opened by bare name from the working directory. InitBios + loads bios.cof into MPE3 and MediaInitMPE loads minibios.cof into MPE0 + (minibiosX.cof on MPEs 1 to 3). A failed load prints "Missing File!" and + emulation continues without the routines. LoadCoffFile checks the COFF magic + 0x0120 and copies the sections to their physical addresses; no size or hash + is checked (src/coff.cpp:47-176). + + Two builds of bios.cof exist. The source tree carries the NuanceExperimental + build (23938 bytes, 12 sections, with commrecv_vars and commrecv_queues); + the 0.6.6 and 0.6.7 release archives ship the 2007 build (19410 bytes, 10 + sections). Both carry the same biosvars, haltab, biosinfo, halstub, + biosdata, halts and controllerdata sections and the C++ side references + neither extra section. + + nuance.cfg is read from the working directory, then from the directory of + the running executable (src/NuonEnvironment.cpp:533-549); without it the + values set in NuonEnvironment::Init apply. The GLSL shaders are opened from + the working directory and fall back to the copies compiled into the binary + (src/ShaderProgram.cpp:85-135, src/embedded_shaders.h). FlashEEPROM.bin is + read by the FlashEEPROM constructor and written by the destructor, both + relative to the process working directory at load and unload of the core + (src/FlashEEPROM.h:36-51). Game data (nuon.run, nuon.dat) is read from the + loaded disc image or directory; no other fixed file is opened. + +files: + - name: "bios.cof" + path: "nuance/bios.cof" + description: "BIOS routines for MPE3, Aries COFF assembled from bios.s" + size: 23938 + sha1: "515a3d9a9e43b7902d161ba27bf67aecee922e41" + md5: "23bf80c259f182f582beeb368aba98f5" + required: true + bundled: true + source_ref: "src/libretro.cpp:313-352, src/NuonEnvironment.cpp:581, src/NuonEnvironment.cpp:603-605, src/bios.cpp:566-582, src/coff.cpp:47-70" + note: > + Loaded into MPE3 by InitBios. The 2007 build shipped in the release + archives (19410 bytes, sha1 4f42ed8f5e171cc79112d1c1e201381e83b1fc0d) + loads the same way. + + - name: "minibios.cof" + path: "nuance/minibios.cof" + description: "Media MPE minibios for MPE0, assembled from minibios.s" + size: 7873 + sha1: "5265dbc871ada59dbd384104c578d807438af4c7" + md5: "1abc7f6265b82742154be881cff2cd3f" + required: true + bundled: true + source_ref: "src/media.cpp:243-321, src/bios.cpp:661, src/Bios.h:29-33" + note: > + Loaded by MediaInitMPE when the media MPE is MPE0, which InitBios does at + every BIOS initialisation. Entry point and interrupt vectors come from + the fixed addresses in Bios.h. + + - name: "minibiosX.cof" + path: "nuance/minibiosX.cof" + description: "Media MPE minibios for MPEs 1 to 3, addresses shifted down by 4 KB" + size: 7873 + sha1: "e45a96ce18486573c4d30efbc01658ce1ff8cd55" + md5: "ceb37f4e78c1f396b49bb6372c83fd7c" + required: true + bundled: true + source_ref: "src/media.cpp:277-296, src/Bios.h:35-38" + note: > + Loaded by MediaInitMPE when a program installs the minibios on an MPE + other than MPE0. + + - name: "nuance.cfg" + path: "nuance/nuance.cfg" + description: "Emulator configuration (compiler options, audio interrupts, controller mappings)" + size: 3369 + sha1: "dfbac2a22f17cf82c7cd40f3727eecf31b013d15" + md5: "20becfdb932042f8ab47c098e92a12ed" + required: false + bundled: true + source_ref: "src/NuonEnvironment.cpp:533-549, src/NuonEnvironment.cpp:917-1056" + note: > + Read from the working directory, then beside the running executable. + Absent file leaves the defaults of NuonEnvironment::Init in place. + + - name: "video_generic.vs" + path: "nuance/video_generic.vs" + description: "GLSL vertex shader" + size: 491 + sha1: "92fca43b92298d308e88edada93453a9ef21adbd" + md5: "5944fea1ff20645f10aecdda06235fb7" + required: false + bundled: true + has_builtin: true + source_ref: "src/video.cpp:119, src/ShaderProgram.cpp:85-135, src/embedded_shaders.h:31-52" + note: > + Opened from the working directory; the copy compiled into the binary is + used when the file is absent. The release archives ship the file without + the EMBED_HLSL guards (413 bytes). + + - name: "video_m32_o32.fs" + path: "nuance/video_m32_o32.fs" + description: "GLSL fragment shader, YCrCb to RGB conversion" + size: 6168 + sha1: "d0477bd73ba524bb74c1faf30fd8f32bd254f924" + md5: "e23f4668fea649cb14feab840fd8377f" + required: false + bundled: true + has_builtin: true + source_ref: "src/video.cpp:120, src/ShaderProgram.cpp:85-135, src/embedded_shaders.h:31-52" + note: > + Selected when UseCRTshader is disabled in nuance.cfg. Same lookup and + fallback as video_generic.vs; the release copy is 6090 bytes. + + - name: "video_m32_o32_crt.fs" + path: "nuance/video_m32_o32_crt.fs" + description: "GLSL fragment shader with CRT post-processing" + size: 8130 + sha1: "e57671f844c3bf96f0bf77ea4d550e13e077b0d2" + md5: "2e1335cd9b14165897eac031504abf8e" + required: false + bundled: true + has_builtin: true + source_ref: "src/video.cpp:120, src/ShaderProgram.cpp:85-135, src/embedded_shaders.h:31-52" + note: > + Selected when UseCRTshader is enabled, the shipped default. Same lookup + and fallback as video_generic.vs; the release copy is 8052 bytes. + + - name: "FlashEEPROM.bin" + description: "Atmel AT49BV162A flash ROM image, 2 MB at 0xF0000000" + size: 2097152 + required: false + bundled: true + source_ref: "src/FlashEEPROM.h:36-51, src/FlashEEPROM.cpp:273-292, src/dma.cpp:679-693" + note: > + Read by the FlashEEPROM constructor from the process working directory + when the core is loaded, truncated to 2 MB, and written back by the + destructor. The release archives ship one; its content is the flash + state of the session that produced it. + + - name: "breakpoint.txt" + description: "Single breakpoint address, first line in hexadecimal" + required: false + mode: standalone + source_ref: "src/NuanceMain_linux.cpp:124, src/NuanceMain.cpp:446" + note: > + Read from the working directory by the standalone executables only; + NuanceMain.cpp and NuanceMain_linux.cpp are excluded from the libretro + build (CMakeLists.txt:190). diff --git a/emulators/pcee2.yml b/emulators/pcee2.yml new file mode 100644 index 00000000..727d9ae1 --- /dev/null +++ b/emulators/pcee2.yml @@ -0,0 +1,161 @@ +emulator: PCEE2 +type: libretro +core_classification: community_fork +bios_mode: agnostic +source: "https://github.com/WizzardSK/pcee2-libretro" +upstream: "https://github.com/PCSX2/pcsx2" +profiled_date: "2026-09-03" +source_commit: "93aded21a46e60f3def27b2590eccb49d30d6a05" +upstream_commit: "6db638dc0373769b4c5d5b5dd7ed6da641b9a18e" +core_version: "v2.7.523" +display_name: "Sony - PlayStation 2 (PCEE2)" +cores: [pcee2] +systems: [sony-playstation-2] + +bios_directory: "pcsx2/bios/" +resources_directory: "pcsx2/resources/" + +data_directories: + - ref: pcsx2-resources + destination: "pcsx2/resources" + source_ref: "pcee2-libretro/Libretro.cpp:414-442, pcsx2/Pcsx2Config.cpp:2388-2400" + +notes: | + libretro port of current PCSX2, merged up to upstream 6db638dc (v2.7.523, + pcee2-libretro/upstream.version); the build reports that version as + library_version. Everything the port adds lives in pcee2-libretro/ and in + the libretro renderer bridges; the BIOS, CDVD, patch and database code is + upstream PCSX2. + + InitializeConfig maps the PCSX2 folder layout under /pcsx2: + AppRoot and DataRoot are that directory, Resources and UserResources are + pcsx2/resources, Settings is pcsx2/inis, and EmuFolders::LoadConfig + derives bios, memcards, cheats, patches, textures, gamesettings, cache, + snaps, sstates, logs, covers, inputprofiles and videos from DataRoot + (pcee2-libretro/Libretro.cpp:414-428, pcsx2/Pcsx2Config.cpp:2319-2336). + A standalone PCSX2.ini dropped in pcsx2/inis contributes nine emulation + sections as the baseline (Libretro.cpp:504-535); the core options and the + port overrides apply on top. + + BIOS detection is filename-agnostic. The core option pcsx2_bios lists every + file of pcsx2/bios that IsBIOS accepts: a romdir whose RESET entry is + followed by a readable ROMVER, whose fifth character gives the zone (Japan, + USA, Europe, Asia, China, T10K, COH-H, Test, Free). With the option on + auto, SettingsOverride selects the first accepted file (Libretro.cpp:578-618, + pcsx2/ps2/BiosTools.cpp:80-197, 284-294). When no BIOS is configured or the + configured name is missing, LoadBIOS falls back to FindBiosImage, which + also filters on size between 4 and 8 MB (BiosTools.cpp:258-282, 317-369). + The image is read into the 4 MB ROM region; a file shorter than 2465792 + bytes turns off the OSDSys parameter HLE. No hash is matched. Booting with + no content starts the BIOS (Libretro.cpp:2090-2096). + + Companions derive from the selected image: rom1 and rom2 are tried as the + full name plus extension, then with the extension replaced, and are skipped + when absent; the nvm is read at the replaced extension and rebuilt with + region and language defaults when missing or blank; the mec is written back + as 0x00020603 when it cannot be read (BiosTools.cpp:214-241, 366-367, + pcsx2/CDVD/CDVD.cpp:136-205). + + The shaders, four interface fonts and GameIndex.yaml are built into the + core (cmake/EmbedResources.cmake:19-31, pcsx2/CMakeLists.txt:1137-1157) + and win over the copies on disk unless PCEE2_EXTERNAL_RESOURCES is set in + the environment (pcsx2/EmbeddedResources.cpp:8-12, pcsx2/GS/Renderers/Common/GSDevice.cpp:372-387, + pcsx2/ImGui/ImGuiManager.cpp:470-492, pcsx2/GameDatabase.cpp:1021-1050, + Libretro.cpp:451-473). A missing resources directory is a warning, not a + failure (Libretro.cpp:433-442). What the directory still supplies to this + build is patches.zip. RedumpDatabase.yaml is read only by the Qt game + summary (pcsx2/GameDatabase.cpp:1197-1210 has no caller in the core), + game_controller_db.txt only by the SDL input source the port disables + (Libretro.cpp:561-562, pcsx2/Input/SDLInputSource.cpp:701-706), the + achievement sounds only with achievements enabled, which no core option or + adopted ini section does (pcsx2/Achievements.cpp:943-951), and + fullscreenui/no-save.png only by the save state selector, whose hotkeys the + port clears (Libretro.cpp:564, pcsx2/ImGui/ImGuiOverlays.cpp:1461-1462). + The port supplies Roboto alone to ImGui, so no emoji font is read. + + Per-game user files keep their PCSX2 locations under /pcsx2: + cheats and patches as _.pnach (pcsx2/Patch.cpp:333-363), + gamesettings/_.ini (pcsx2/VMManager.cpp:830-833), + textures//replacements (pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp:262-264), + memcards/*.ps2, which the core creates and lists in the memory card + options (Libretro.cpp:620-640). The DEV9 HDD image is named in the ini and + created on demand. + +files: + - name: ".bin" + path: "pcsx2/bios/" + description: "PS2 BIOS image" + min_size: 4194304 + max_size: 8388608 + required: true + validation: [size] + source_ref: "pcee2-libretro/Libretro.cpp:578-618, pcsx2/ps2/BiosTools.cpp:16-17, pcsx2/ps2/BiosTools.cpp:80-197, pcsx2/ps2/BiosTools.cpp:258-294, pcsx2/ps2/BiosTools.cpp:317-369" + note: > + Any file of pcsx2/bios whose romdir carries RESET and ROMVER. The 4 to + 8 MB filter applies in FindBiosImage only; the core option scan and the + auto selection accept any size that passes the romdir check. + + - name: ".rom1" + path: "pcsx2/bios/" + description: "DVD player ROM" + max_size: 4194304 + required: false + source_ref: "pcsx2/ps2/BiosTools.cpp:214-241, pcsx2/ps2/BiosTools.cpp:366, pcsx2/MemoryTypes.h:13" + note: > + Tried as .bin.rom1 then .rom1, opened case-insensitively, + skipped with a console line when absent. + + - name: ".rom2" + path: "pcsx2/bios/" + description: "Chinese ROM extension" + max_size: 4194304 + required: false + source_ref: "pcsx2/ps2/BiosTools.cpp:214-241, pcsx2/ps2/BiosTools.cpp:367, pcsx2/MemoryTypes.h:14" + note: > + Same lookup as rom1. Present on Chinese region consoles only. + + - name: ".nvm" + path: "pcsx2/bios/" + description: "Console NVRAM (region parameters, language, iLink ID, OSD settings)" + size: 1024 + required: false + hle_fallback: true + source_ref: "pcsx2/CDVD/CDVD.cpp:47, pcsx2/CDVD/CDVD.cpp:136-188, pcsx2/CDVD/CDVD.cpp:207-238" + note: > + Read at .nvm, rebuilt in memory with region and language defaults + when it is missing, short, or has a blank config block, and written back + on save. + + - name: ".mec" + path: "pcsx2/bios/" + description: "Mechacon version, one u32" + size: 4 + required: false + hle_fallback: true + source_ref: "pcsx2/CDVD/CDVD.cpp:49, pcsx2/CDVD/CDVD.cpp:190-204" + note: > + Read at .mec; written as 0x00020603 when it cannot be read. + + - name: "patches.zip" + path: "pcsx2/resources/patches.zip" + description: "Built-in game patch archive (pnach per serial and CRC)" + required: false + bundled: true + category: game_data + source_ref: "pcsx2/Patch.cpp:117, pcsx2/Patch.cpp:289-317, pcsx2/Patch.cpp:365-405" + note: > + Opened read only from the resources directory when a game's patches are + enumerated and searched for _.pnach, then .pnach. Files + in the patches folder take precedence. A failure to open is reported once + on screen. Not part of the PCSX2 source tree: PCSX2 builds fetch it from + the pcsx2_patches releases, a rolling asset rebuilt on every patch commit. + + - name: "eeprom.dat" + description: "DEV9 network adapter EEPROM" + size: 64 + required: false + hle_fallback: true + source_ref: "pcsx2/DEV9/DEV9.cpp:60-73, pcsx2/DEV9/DEV9.cpp:97-158" + note: > + Opened read-write and mapped from the process working directory by + DEV9init; the built-in eeprom table is used when it cannot be opened. diff --git a/emulators/pokketstation.yml b/emulators/pokketstation.yml new file mode 100644 index 00000000..c7f0d045 --- /dev/null +++ b/emulators/pokketstation.yml @@ -0,0 +1,54 @@ +emulator: pokketstation +type: libretro +core_classification: official_port +source: "https://github.com/mentalfoundry/pokketstation" +upstream: "https://github.com/mentalfoundry/pokketstation" +profiled_date: "2026-09-03" +source_commit: "11fb6de0139d5cbdbd44e37839571a0bf25e5ab2" +core_version: "v1.11.1" +display_name: "Sony - PocketStation (pokketstation)" +cores: [pokketstation] +systems: [sony-pocketstation] + +notes: | + PocketStation emulator with a portable C core (core/) shared by the libretro + core, an SDL2 desktop application and a PS Vita port. The libretro core + reports library_version "0.1"; the project version is the release tag, + v1.11.1 in the desktop frontend (frontends/desktop/main.c:40). + + load_bios opens /pocketstation.bin, reads 16384 bytes and fails when + fewer are read; a longer file is accepted and truncated. psemu_load_bios + copies the image and rejects any size other than PSEMU_BIOS_SIZE. + retro_load_game returns false when load_bios fails, and psemu_run executes + nothing while no BIOS is loaded: there is no HLE and no embedded copy. + Content (.mcr, .mcd, .mcs, .pss) is identified by its bytes, not by the + extension. Save RAM is the full 128 KB flash image. + + psemu_settings_offsets_known hashes the loaded image with FNV-1a and compares + it with 0xB2E46838, the retail 110 revision the project traced; a match + enables the date, time and volume override writes into kernel RAM. Any + 16 KB image boots. Two mask ROM revisions are documented: 061, from + prototype hardware, and 110, the retail one; the ASCII tags at offsets + 0x1DFC (C061 or C110) and 0x3FFC (J061 or J110) identify them + (docs/hardware-notes.md:778-788). SWI 0Fh FlashWriteSerial works on 061 + and locks the CPU on 110. + + The desktop application takes the BIOS path from the command line, then + from the bios= key of settings.cfg beside the executable, then falls back + to bios.bin beside the executable; File > Load BIOS opens any path + (frontends/desktop/main.c:630-668, 1000-1035, 2380-2451). The Vita frontend + loads no BIOS yet (frontends/vita/main.c:31-33). + +files: + - name: "pocketstation.bin" + description: "PocketStation BIOS ROM, kernel and GUI, 16 KB" + size: 16384 + required: true + validation: [size] + standalone_path: "bios.bin" + source_ref: "frontends/libretro/libretro.c:178-196, frontends/libretro/libretro.c:206-208, core/src/psemu.c:90-97, core/src/psemu.c:458-461, core/src/psemu.c:795-820, core/include/psemu/psemu.h:19" + note: > + Read from the frontend system directory. The desktop application reads + the whole file and psemu_load_bios rejects any size other than 16384; + the libretro core reads the first 16384 bytes. FNV-1a 0xB2E46838 + identifies the retail 110 revision for the settings overrides only. diff --git a/platforms/_data_dirs.yml b/platforms/_data_dirs.yml index 1c16d305..d1433c9e 100644 --- a/platforms/_data_dirs.yml +++ b/platforms/_data_dirs.yml @@ -53,6 +53,18 @@ data_directories: local_cache: data/lrps2 description: "LRPS2 GameIndex.yaml + resources" + # ref: pcee2-libretro/Libretro.cpp:426 - system/pcsx2/resources/ + # PCSX2 bin/resources at the upstream revision the pcee2 core is merged up to + # (pcee2-libretro/upstream.version). patches.zip is a separate release asset + # of PCSX2/pcsx2_patches and is carried as a file entry of the profile. + pcsx2-resources: + source_url: "https://github.com/PCSX2/pcsx2/archive/{version}.tar.gz" + source_type: tarball + source_path: "pcsx2-{version}/bin/resources" + version: 6db638dc0373769b4c5d5b5dd7ed6da641b9a18e + local_cache: data/pcsx2-resources + description: "PCSX2 resources (GameIndex.yaml, RedumpDatabase.yaml, fonts, shaders, icons, sounds)" + # ref: scummvm/backends/platform/libretro — system/scummvm/ scummvm: source_url: "https://buildbot.libretro.com/assets/system/ScummVM.zip"