feat: profile nuance, pcee2 and pokketstation

This commit is contained in:
Abdessamad Derraz committed 2026-09-03 22:06:30 +02:00
1 parent df7346b3e0
commit 3c85f51314
19 files changed
+1331

No files matched your search

Binary file not shown.
@@ -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;
}
@@ -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);
}
@@ -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);
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+99
View File
@@ -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_<vkey_num>_0, JOYBUT_<num>_0, JOYAXIS_<axisnum>_[0,1], and JOYPOV_<POVNUM>_[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
+21
View File
@@ -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
+179
View File
@@ -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
+226
View File
@@ -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
Binary file not shown.
+173
View File
@@ -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 <system>/nuance/bios.cof and changes the working
directory to <system>/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).
+161
View File
@@ -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 <system>/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 <system>/pcsx2:
cheats and patches as <serial>_<crc>.pnach (pcsx2/Patch.cpp:333-363),
gamesettings/<serial>_<crc>.ini (pcsx2/VMManager.cpp:830-833),
textures/<serial>/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: "<bios>.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: "<bios>.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 <bios>.bin.rom1 then <bios>.rom1, opened case-insensitively,
skipped with a console line when absent.
- name: "<bios>.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: "<bios>.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 <bios>.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: "<bios>.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 <bios>.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 <serial>_<crc>.pnach, then <crc>.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.
+54
View File
@@ -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 <system>/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.
+12
View File
@@ -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"