video: Support for chroma subcarrier over VGA

Adds "subcarrier" config flag, used to enable chroma subcarrier output over VGA Vsync for external RGB to NTSC/PAL converters.
This commit is contained in:
misteraddons
2025-08-13 23:32:07 -07:00
committed by GitHub
parent d0fe3f0b8d
commit eef9cbd6d9
4 changed files with 29 additions and 2 deletions

View File

@@ -109,6 +109,13 @@ video_info=0
; modes as a base even for 50Hz systems.
vsync_adjust=0
; Subcarrier generator for composite video output on VGA_VS pin (AA24).
; 0 - disabled (default, safe mode)
; 1 - enabled (frequency determined by ntsc_mode setting)
; Works with regular analog video only - not with direct video.
; For external RGB to NTSC encoders: requires vga_mode=rgb, composite_sync=1, forced_scandoubler=0.
subcarrier=0
; If your monitor doesn't support either very low (NTSC monitors may not support PAL) or
; very high (PAL monitors may not support NTSC) then you can set refresh_min and/or refresh_max
; parameters, so vsync_adjust won't be applied for refreshes outside specified.

View File

@@ -48,6 +48,7 @@ static const ini_var_t ini_vars[] =
{ "VIDEO_MODE_NTSC", (void*)(cfg.video_conf_ntsc), STRING, 0, sizeof(cfg.video_conf_ntsc) - 1 },
{ "VIDEO_INFO", (void*)(&(cfg.video_info)), UINT8, 0, 10 },
{ "VSYNC_ADJUST", (void*)(&(cfg.vsync_adjust)), UINT8, 0, 2 },
{ "SUBCARRIER", (void*)(&(cfg.subcarrier)), UINT8, 0, 1 },
{ "HDMI_AUDIO_96K", (void*)(&(cfg.hdmi_audio_96k)), UINT8, 0, 1 },
{ "DVI_MODE", (void*)(&(cfg.dvi_mode)), UINT8, 0, 1 },
{ "HDMI_LIMITED", (void*)(&(cfg.hdmi_limited)), UINT8, 0, 2 },
@@ -581,6 +582,7 @@ void cfg_parse()
cfg.rumble = 1;
cfg.wheel_force = 50;
cfg.dvi_mode = 2;
cfg.subcarrier = 0;
cfg.lookahead = 2;
cfg.hdr = 0;
cfg.hdr_max_nits = 1000;

1
cfg.h
View File

@@ -25,6 +25,7 @@ typedef struct {
float refresh_max;
uint8_t controller_info;
uint8_t vsync_adjust;
uint8_t subcarrier;
uint8_t kbd_nomouse;
uint8_t mouse_throttle;
uint8_t bootscreen;

View File

@@ -2842,7 +2842,8 @@ bool video_mode_select(uint32_t vtime, vmode_custom_t* out_mode)
static void set_yc_mode()
{
if (cfg.vga_mode_int >= 2)
// Enable YC for S-Video/CVBS modes, or subcarrier for CXA2075 encoders
if (cfg.vga_mode_int >= 2 || (cfg.subcarrier && cfg.vga_mode_int == 0 && cfg.csync && !cfg.forced_scandoubler))
{
float fps = current_video_info.vtime ? (100000000.f / current_video_info.vtime) : 0.f;
int pal = fps < 55.f;
@@ -2875,12 +2876,28 @@ static void set_yc_mode()
}
spi_uio_cmd_cont(UIO_SET_YC_PAR);
spi_w(((pal || cfg.ntsc_mode) ? 4 : 0) | ((cfg.vga_mode_int == 3) ? 3 : 1));
// For traditional S-Video/CVBS modes, enable YC processing
// For subcarrier-only modes (RGB+subcarrier or direct video), keep yc_en=0
bool is_subcarrier_only = (cfg.subcarrier && (cfg.direct_video || (cfg.vga_mode_int == 0 && cfg.csync && !cfg.forced_scandoubler)));
uint16_t yc_config;
if (is_subcarrier_only) {
// Subcarrier-only: RGB mode with just PAL flag, yc_en=0
yc_config = ((pal || cfg.ntsc_mode) ? 4 : 0);
} else {
// Traditional YC modes: enable YC processing
yc_config = ((pal || cfg.ntsc_mode) ? 4 : 0) | ((cfg.vga_mode_int == 3) ? 3 : 1);
}
printf("Sending YC config to FPGA: 0x%02X (pal_en=%d, cvbs=%d, yc_en=%d)\n", yc_config, (yc_config >> 2) & 1, (yc_config >> 1) & 1, yc_config & 1);
spi_w(yc_config);
spi_w(PHASE_INC);
spi_w(PHASE_INC >> 16);
spi_w(PHASE_INC >> 32);
spi_w(COLORBURST_RANGE);
spi_w(COLORBURST_RANGE >> 16);
// Case 6: Send subcarrier enable flag
uint16_t subcarrier_enable = (cfg.subcarrier && cfg.vga_mode_int == 0 && cfg.csync && !cfg.forced_scandoubler) ? 1 : 0;
printf("Sending subcarrier enable to FPGA: %d\n", subcarrier_enable);
spi_w(subcarrier_enable);
DisableIO();
}
else