From 906ec6d57414c2800d7d48da8e246aaa570c9ea0 Mon Sep 17 00:00:00 2001 From: marqs Date: Mon, 27 Oct 2025 00:23:42 +0200 Subject: [PATCH] add support for Lumacode Intellivision and custom palettes --- ip/sc_config/inc/sc_config_regs.h | 6 +- rtl/tvp7002_frontend.v | 63 +++++++++++++------ software/sys_controller/av_controller.c | 58 +++++++++-------- software/sys_controller/inc/av_controller.h | 13 +++- software/sys_controller/src/avconfig.c | 2 + .../sys_controller/src/lumacode_palettes.c | 1 + software/sys_controller/src/userdata.c | 4 ++ 7 files changed, 100 insertions(+), 47 deletions(-) diff --git a/ip/sc_config/inc/sc_config_regs.h b/ip/sc_config/inc/sc_config_regs.h index 540d264..775a48d 100644 --- a/ip/sc_config/inc/sc_config_regs.h +++ b/ip/sc_config/inc/sc_config_regs.h @@ -111,7 +111,8 @@ typedef union { uint8_t shmask_enable:1; uint8_t shmask_iv_x:4; uint8_t shmask_iv_y:4; - uint32_t misc_rsv:8; + uint8_t panasonic_hack:1; + uint32_t misc_rsv:7; } __attribute__((packed, __may_alias__)); uint32_t data; } misc_config_reg; @@ -152,8 +153,7 @@ typedef struct { // lumacode palatte ram typedef struct { - uint32_t padding[16]; - uint32_t data[496]; + uint32_t data[512]; } lc_pal_ram; typedef struct { diff --git a/rtl/tvp7002_frontend.v b/rtl/tvp7002_frontend.v index 68bbda4..669164f 100644 --- a/rtl/tvp7002_frontend.v +++ b/rtl/tvp7002_frontend.v @@ -64,6 +64,15 @@ localparam FID_ODD = 1'b1; localparam VSYNC_SEPARATED = 1'b0; localparam VSYNC_RAW = 1'b1; +localparam LC_DISABLED = 3'd0; +localparam LC_C64 = 3'd1; +localparam LC_SPECTRUM = 3'd2; +localparam LC_MSX = 3'd3; +localparam LC_INTV = 3'd4; +localparam LC_NES = 3'd5; +localparam LC_GTIA = 3'd6; +localparam LC_CTIA = 3'd7; + localparam PP_PL_START = 1; localparam PP_DE_POS_START = PP_PL_START; localparam PP_DE_POS_LENGTH = 1; @@ -150,8 +159,8 @@ wire HSYNC_i_np = (HSYNC_i ^ ~hsync_i_pol); wire [3:0] H_SKIP = hv_in_config3[27:24]; wire [3:0] H_SAMPLE_SEL = hv_in_config3[31:28]; -// Lumacode uses 2 samples for {C64, C128, VIC20, Spectrum, TMS99xxA}, 3 samples for NES, 6 samples for Atari 8bit (3 per pixel) and 4 samples for VCS (2 per half-pixel) -wire [2:0] LC_SAMPLES = (MISC_LUMACODE_MODE <= 3) ? 2 : ((MISC_LUMACODE_MODE <= 5) ? 3 : 2); +// Lumacode +wire [2:0] LC_SAMPLES; wire [2:0] LC_H_SKIP = ((H_SKIP+1) / LC_SAMPLES) - 1; // SOF position for scaler @@ -168,6 +177,15 @@ function [7:0] apply_reverse_lpf; end endfunction +// Lumacode number of samples selection +always @(*) begin + case(MISC_LUMACODE_MODE) + LC_C64,LC_SPECTRUM,LC_MSX,LC_CTIA : LC_SAMPLES = 2; // 2 samples for {C64, C128, VIC20, Spectrum, TMS99xxA} / 4 samples for VCS (2 per half-pixel) + LC_INTV : LC_SAMPLES = 1; // 2 samples for Intellivision (1 per doubled pixel) + LC_NES,LC_GTIA : LC_SAMPLES = 3; // 3 samples for NES / 6 samples for Atari 8bit (3 per pixel) + default : LC_SAMPLES = 1; + endcase +end // Pipeline stage 1 always @(posedge PCLK_i) begin @@ -264,11 +282,16 @@ always @(posedge PCLK_i) begin // Lumacode related source-specific registers (used as part of palette RAM addressing / data processing) // Lumacode NES - if (MISC_LUMACODE_MODE == 4) begin + if (MISC_LUMACODE_MODE == LC_NES) begin if ((h_ctr == H_SAMPLE_SEL) & ({lc_code[1], lc_code[2], lc_code[3]} < 8)) lc_emp_nes <= {lc_code[2][0], lc_code[3]}; + // Lumacode Intellivision + end else if (MISC_LUMACODE_MODE == LC_INTV) begin + if ((h_ctr == H_SAMPLE_SEL) & lc_atari_ctr) begin + lc_atari_luma[1:0] <= lc_code[1]; + end // Lumacode Atari GTIA - end else if (MISC_LUMACODE_MODE == 5) begin + end else if (MISC_LUMACODE_MODE == LC_GTIA) begin if (h_ctr == H_SAMPLE_SEL) begin if (lc_atari_ctr) begin // Store hue and luma (high bits) for 1st pixel, and display last pixel of previous pair @@ -344,15 +367,15 @@ always @(posedge PCLK_i) begin B_diff_s15 <= (B_diff_s15_pre * MISC_REV_LPF_STR); // Cycle 3 - if (MISC_REV_LPF_ENABLE & (MISC_LUMACODE_MODE == 0)) begin + if (MISC_REV_LPF_ENABLE & (MISC_LUMACODE_MODE == LC_DISABLED)) begin R_pp[PP_RLPF_END] <= apply_reverse_lpf(R_pp[PP_RLPF_START+2], R_diff_s15); G_pp[PP_RLPF_END] <= apply_reverse_lpf(G_pp[PP_RLPF_START+2], G_diff_s15); B_pp[PP_RLPF_END] <= apply_reverse_lpf(B_pp[PP_RLPF_START+2], B_diff_s15); end /* --- Lumacode overwrite data from RAM --- */ - if (MISC_LUMACODE_MODE != 0) begin - if (MISC_LUMACODE_MODE == 4) begin + if (MISC_LUMACODE_MODE != LC_DISABLED) begin + if (MISC_LUMACODE_MODE == LC_NES) begin // NES R_pp[PP_RLPF_START+1] <= &lc_emp_nes[1:0] ? (lumacode_data[23:16]/2) : (|lc_emp_nes[1:0] ? (lumacode_data[23:16] - lumacode_data[23:16]/4) : lumacode_data[23:16]); G_pp[PP_RLPF_START+1] <= (lc_emp_nes[2] & lc_emp_nes[0]) ? (lumacode_data[15:8]/2) : ((lc_emp_nes[2] | lc_emp_nes[0]) ? (lumacode_data[15:8] - lumacode_data[15:8]/4) : lumacode_data[15:8]); @@ -369,30 +392,34 @@ end // Lumacode palette RAM inputs always @(*) case (MISC_LUMACODE_MODE) - 1: begin // C64, C128, VIC20 + LC_C64: begin // C64, C128, VIC20 + lumacode_addr = {5'h0, lc_code[1], lc_code[2]}; + lumacode_rden = 1; + end + LC_SPECTRUM: begin // Spectrum lumacode_addr = {5'h1, lc_code[1], lc_code[2]}; lumacode_rden = 1; end - 2: begin // Spectrum + LC_MSX: begin // TMS99xxA lumacode_addr = {5'h2, lc_code[1], lc_code[2]}; lumacode_rden = 1; end - 3: begin // TMS99xxA - lumacode_addr = {5'h3, lc_code[1], lc_code[2]}; - lumacode_rden = 1; + LC_INTV: begin // Intellivision + lumacode_addr = {5'h3, lc_atari_luma[1:0], lc_code[1]}; + lumacode_rden = (h_ctr == H_SAMPLE_SEL) & !lc_atari_ctr; end - 4: begin // NES + LC_NES: begin // NES lumacode_addr = {3'h1, lc_code[1], lc_code[2], lc_code[3]}; lumacode_rden = 1; end - 6: begin // Atari CTIA/TIA - lumacode_addr = {2'h1, lc_atari_hue, lc_code[1], lc_code[2][1]}; - lumacode_rden = (h_ctr == H_SAMPLE_SEL) & !lc_atari_ctr; - end - 5: begin // Atari GTIA + LC_GTIA: begin // Atari GTIA lumacode_addr = lc_atari_ctr ? {1'h1, lc_atari_hue, lc_atari_luma} : {1'h1, lc_atari_hue, lc_atari_luma[3:2], lc_code[1]}; lumacode_rden = (h_ctr == H_SAMPLE_SEL); end + LC_CTIA: begin // Atari CTIA/TIA + lumacode_addr = {2'h1, lc_atari_hue, lc_code[1], lc_code[2][1]}; + lumacode_rden = (h_ctr == H_SAMPLE_SEL) & !lc_atari_ctr; + end default: begin // Standard output lumacode_addr = '0; lumacode_rden = 0; diff --git a/software/sys_controller/av_controller.c b/software/sys_controller/av_controller.c index 5692be7..64240ac 100644 --- a/software/sys_controller/av_controller.c +++ b/software/sys_controller/av_controller.c @@ -143,7 +143,8 @@ int shmask_loaded_array = 0; int shmask_loaded_str = -1; #define SHMASKS_SIZE (sizeof(shmask_data_arr_list) / sizeof((shmask_data_arr_list)[0])) -const lc_palette_set* lc_palette_set_list[] = {&lc_palette_pal}; +c_lc_palette_set_t c_lc_palette_set; +const lc_palette_set* lc_palette_set_list[] = {&lc_palette_pal, &c_lc_palette_set.pal}; int loaded_lc_palette = -1; void ui_disp_menu(alt_u8 osd_mode) @@ -461,11 +462,15 @@ status_t get_status(tvp_sync_input_t syncinput) return status; } -void update_sc_config(mode_data_t *vm_in, mode_data_t *vm_out, vm_proc_config_t *vm_conf, avconfig_t *avconfig) +void update_sc_config() { int i, p, t; shmask_data_arr *shmask_data_arr_ptr; + mode_data_t *vm_in = &vmode_in; + mode_data_t *vm_out = &vmode_out; + avconfig_t *avconfig = &cm.cc; + hv_config_reg hv_in_config = {.data=0x00000000}; hv_config2_reg hv_in_config2 = {.data=0x00000000}; hv_config3_reg hv_in_config3 = {.data=0x00000000}; @@ -511,8 +516,8 @@ void update_sc_config(mode_data_t *vm_in, mode_data_t *vm_out, vm_proc_config_t hv_in_config3.v_backporch = vm_in->timings.v_backporch; hv_in_config2.interlaced = vm_in->timings.interlaced; hv_in_config3.v_startline = vm_in->timings.v_synclen+vm_in->timings.v_backporch+12; - hv_in_config3.h_skip = vm_conf->h_skip; - hv_in_config3.h_sample_sel = vm_conf->h_sample_sel; + hv_in_config3.h_skip = vm_conf.h_skip; + hv_in_config3.h_sample_sel = vm_conf.h_sample_sel; // Set output params hv_out_config.h_total = vm_out->timings.h_total; @@ -524,16 +529,16 @@ void update_sc_config(mode_data_t *vm_in, mode_data_t *vm_out, vm_proc_config_t hv_out_config3.v_synclen = vm_out->timings.v_synclen; hv_out_config3.v_backporch = vm_out->timings.v_backporch; hv_out_config2.interlaced = vm_out->timings.interlaced; - hv_out_config3.v_startline = vm_conf->framesync_line; + hv_out_config3.v_startline = vm_conf.framesync_line; - xy_out_config.x_size = vm_conf->x_size; - xy_out_config.y_size = vm_conf->y_size; - xy_out_config.y_offset = vm_conf->y_offset; - xy_out_config2.x_offset = vm_conf->x_offset; - xy_out_config2.x_start_lb = vm_conf->x_start_lb; - xy_out_config2.y_start_lb = vm_conf->y_start_lb; - xy_out_config2.x_rpt = vm_conf->x_rpt; - xy_out_config2.y_rpt = vm_conf->y_rpt; + xy_out_config.x_size = vm_conf.x_size; + xy_out_config.y_size = vm_conf.y_size; + xy_out_config.y_offset = vm_conf.y_offset; + xy_out_config2.x_offset = vm_conf.x_offset; + xy_out_config2.x_start_lb = vm_conf.x_start_lb; + xy_out_config2.y_start_lb = vm_conf.y_start_lb; + xy_out_config2.x_rpt = vm_conf.x_rpt; + xy_out_config2.y_rpt = vm_conf.y_rpt; misc_config.mask_br = avconfig->mask_br; misc_config.mask_color = avconfig->mask_color; @@ -542,16 +547,13 @@ void update_sc_config(mode_data_t *vm_in, mode_data_t *vm_out, vm_proc_config_t misc_config.shmask_iv_x = shmask_data_arr_ptr->iv_x; misc_config.shmask_iv_y = shmask_data_arr_ptr->iv_y; misc_config.lumacode_mode = avconfig->lumacode_mode; + misc_config.panasonic_hack = avconfig->panasonic_hack; /*misc_config.lm_deint_mode = 0; - misc_config.nir_even_offset = 0; - misc_config.ypbpr_cs = (avconfig->ypbpr_cs == 0) ? ((vm_in->type & VIDEO_HDTV) ? 1 : 0) : avconfig->ypbpr_cs-1; - misc_config.vip_enable = 0; - misc_config.bfi_enable = 0; - misc_config.bfi_str = 0;*/ + misc_config.nir_even_offset = 0;*/ // set default/custom scanline interval - sl_def_iv_y = (vm_conf->y_rpt > 0) ? vm_conf->y_rpt : 1; - sl_def_iv_x = (vm_conf->x_rpt > 0) ? vm_conf->x_rpt : sl_def_iv_y; + sl_def_iv_y = (vm_conf.y_rpt > 0) ? vm_conf.y_rpt : 1; + sl_def_iv_x = (vm_conf.x_rpt > 0) ? vm_conf.x_rpt : sl_def_iv_y; sl_config3.sl_iv_x = ((avconfig->sl_type == 3) && (avconfig->sl_cust_iv_x)) ? avconfig->sl_cust_iv_x : sl_def_iv_x; sl_config3.sl_iv_y = ((avconfig->sl_type == 3) && (avconfig->sl_cust_iv_y)) ? avconfig->sl_cust_iv_y : sl_def_iv_y; @@ -592,7 +594,7 @@ void update_sc_config(mode_data_t *vm_in, mode_data_t *vm_out, vm_proc_config_t sl_config3.sl_hybr_str = avconfig->sl_hybr_str; // disable scanlines if configured so - if (((avconfig->sl_mode == 1) && (!vm_conf->y_rpt)) || (avconfig->sl_mode == 0)) { + if (((avconfig->sl_mode == 1) && (!vm_conf.y_rpt)) || (avconfig->sl_mode == 0)) { sl_config.sl_l_overlay = 0; sl_config3.sl_c_overlay = 0; } @@ -710,7 +712,7 @@ void program_mode() update_osd_size(&vmode_out); - update_sc_config(&vmode_in, &vmode_out, &vm_conf, &cm.cc); + update_sc_config(); TX_SetPixelRepetition(vm_conf.tx_pixelrep, ((cm.cc.tx_mode!=TX_DVI) && (vm_conf.tx_pixelrep == vm_conf.hdmitx_pixr_ifr)) ? 1 : 0); @@ -756,7 +758,7 @@ void set_sampler_phase(uint8_t sampler_phase, uint8_t update_sc) { tvp_set_hpll_phase(tvp_phase); if (update_sc) - update_sc_config(&vmode_in, &vmode_out, &vm_conf, &cm.cc); + update_sc_config(); } int load_profile() { @@ -802,6 +804,11 @@ void set_default_c_shmask() { strncpy(c_shmask.name, "Custom: ", 20); } +void set_default_c_lc_palette_set() { + memset(&c_lc_palette_set, 0, sizeof(c_lc_palette_set)); + strncpy(c_lc_palette_set.name, "Custom: ", 20); +} + // Initialize hardware int init_hw() { @@ -872,6 +879,7 @@ int init_hw() // Set defaults set_default_profile(1); set_default_c_shmask(); + set_default_c_lc_palette_set(); set_default_settings(); // Init menu @@ -886,7 +894,7 @@ int init_hw() // Setup test pattern get_vmode(VMODE_480p, &vmode_in, &vmode_out, &vm_conf); - update_sc_config(&vmode_in, &vmode_out, &vm_conf, &cm.cc); + update_sc_config(); // init always in HDMI mode (fixes yellow screen bug) TX_enable(TX_HDMI_RGB); @@ -1288,7 +1296,7 @@ int main() case SC_CONFIG_CHANGE: if (cm.sync_active) { printf("Scanconverter config change\n"); - update_sc_config(&vmode_in, &vmode_out, &vm_conf, &cm.cc); + update_sc_config(); } break; default: diff --git a/software/sys_controller/inc/av_controller.h b/software/sys_controller/inc/av_controller.h index 4180038..e7f4108 100644 --- a/software/sys_controller/inc/av_controller.h +++ b/software/sys_controller/inc/av_controller.h @@ -119,21 +119,32 @@ typedef union { uint32_t c64_pal[16]; uint32_t zx_pal[16]; uint32_t msx_pal[16]; + uint32_t intv_pal[16]; uint32_t nes_pal[64]; uint32_t tia_pal[128]; uint32_t gtia_pal[256]; } __attribute__((packed, __may_alias__)); - uint32_t data[496]; + uint32_t data[512]; } lc_palette_set; +typedef struct { + char name[20]; + lc_palette_set pal; +} c_lc_palette_set_t; + void ui_disp_menu(alt_u8 osd_mode); void ui_disp_status(alt_u8 refresh_osd_timer); void set_sampler_phase(uint8_t sampler_phase, uint8_t update_sc); +void set_default_c_shmask(); +void set_default_c_lc_palette_set(); + void print_vm_stats(); int latency_test(); +void update_sc_config(); + void update_settings(int init_setup); #endif diff --git a/software/sys_controller/src/avconfig.c b/software/sys_controller/src/avconfig.c index fa763c6..79f1dc8 100644 --- a/software/sys_controller/src/avconfig.c +++ b/software/sys_controller/src/avconfig.c @@ -77,6 +77,8 @@ int set_default_profile(int update_cc) if (update_cc) memcpy(&cm.cc, &tc, sizeof(avconfig_t)); + set_default_c_shmask(); + set_default_c_lc_palette_set(); set_default_vm_table(); update_cur_vm = 1; diff --git a/software/sys_controller/src/lumacode_palettes.c b/software/sys_controller/src/lumacode_palettes.c index c00eab2..dc98835 100644 --- a/software/sys_controller/src/lumacode_palettes.c +++ b/software/sys_controller/src/lumacode_palettes.c @@ -3,6 +3,7 @@ const lc_palette_set lc_palette_pal = {.c64_pal={0x000000,0x2a1b9d,0x7d202c,0x84258c,0x4c2e00,0x3c3c3c,0x646464,0x4fb3a5,0x7f410d,0x6351db,0x939393,0xbfd04a,0x339840,0xb44f5c,0x7ce587,0xffffff}, .zx_pal= {0x000000,0x000000,0x0200FD,0xCF01CE,0x0100CE,0xCF0100,0xFF02FD,0x01CFCF,0xFF0201,0x00CF15,0x02FFFF,0xFFFF1D,0x00FF1C,0xCFCF15,0xCFCFCF,0xFFFFFF}, .msx_pal={0x000000,0x5455ed,0xfc5554,0xff7978,0x000000,0xd4524d,0x7d76fc,0x42ebf5,0x21b03b,0x21c842,0xff7978,0xcccccc,0xc95bba,0xd4c154,0xe6ce80,0xffffff}, + .intv_pal={0x0c0005,0xa7a8a8,0xfffcff,0xff3e00,0xffa600,0xfaea27,0x00780f,0x00a720,0x6ccd30,0x002dff,0x5acbff,0xbd95ff,0xc81a7d,0xff3276,0x3c5800,0xc9d464}, .nes_pal={0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x626262,0x001fb2,0x2404c8,0x5200b2,0x730076,0x800024,0x730b00,0x522800, 0x244400,0x005700,0x005c00,0x005324,0x003c76,0x000000,0xababab,0x0d57ff,0x4b30ff,0x8a13ff,0xbc08d6,0xd21269,0xc72e00,0x9d5400,0x607b00,0x209800, 0x00a300,0x009942,0x007db4,0x000000,0xffffff,0x53aeff,0x9085ff,0xd365ff,0xff57ff,0xff5dcf,0xff7757,0xfa9e00,0xbdc700,0x7ae700,0x43f611,0x26ef7e, diff --git a/software/sys_controller/src/userdata.c b/software/sys_controller/src/userdata.c index e2358b0..415cd23 100644 --- a/software/sys_controller/src/userdata.c +++ b/software/sys_controller/src/userdata.c @@ -49,6 +49,7 @@ extern mode_data_t video_modes_plm[]; extern uint8_t update_cur_vm; extern SD_DEV sdcard_dev; extern c_shmask_t c_shmask; +extern c_lc_palette_set_t c_lc_palette_set; char target_profile_name[USERDATA_NAME_LEN+1], cur_profile_name[USERDATA_NAME_LEN+1]; @@ -71,6 +72,7 @@ const ude_item_map ude_initcfg_items[] = { const ude_item_map ude_profile_items[] = { {{0, 120, sizeof(video_modes_plm_default)}, video_modes_plm}, UDE_ITEM(1, 120, c_shmask), + UDE_ITEM(65, 120, c_lc_palette_set), // avconfig_t UDE_ITEM(2, 120, tc.pm_240p), UDE_ITEM(3, 120, tc.pm_384p), @@ -135,6 +137,8 @@ const ude_item_map ude_profile_items[] = { UDE_ITEM(62, 120, tc.full_tx_setup), UDE_ITEM(63, 120, tc.av3_alt_rgb), UDE_ITEM(64, 120, tc.link_av), + // 65 reserved + UDE_ITEM(66, 120, tc.panasonic_hack), }; int write_userdata(uint8_t entry) {