diff --git a/core/vdp_ctrl.c b/core/vdp_ctrl.c index cc40063..8a1c0e9 100644 --- a/core/vdp_ctrl.c +++ b/core/vdp_ctrl.c @@ -1691,7 +1691,7 @@ static void vdp_reg_w(unsigned int r, unsigned int d, unsigned int cycles) } else { - render_bg = (reg[11] & 0x04) ? render_bg_m5_vs : render_bg_m5; + render_bg = (reg[11] & 0x04) ? (config.enhanced_vscroll ? render_bg_m5_vs_enhanced : render_bg_m5_vs) : render_bg_m5; render_obj = (reg[12] & 0x08) ? render_obj_m5_ste : render_obj_m5; } @@ -1902,7 +1902,7 @@ static void vdp_reg_w(unsigned int r, unsigned int d, unsigned int cycles) /* Vertical Scrolling mode */ if (d & 0x04) { - render_bg = im2_flag ? render_bg_m5_im2_vs : render_bg_m5_vs; + render_bg = im2_flag ? render_bg_m5_im2_vs : (config.enhanced_vscroll ? render_bg_m5_vs_enhanced : render_bg_m5_vs); } else { diff --git a/core/vdp_render.c b/core/vdp_render.c index 6b65e22..070ac18 100644 --- a/core/vdp_render.c +++ b/core/vdp_render.c @@ -6,6 +6,7 @@ * * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) * Copyright (C) 2007-2016 Eke-Eke (Genesis Plus GX) + * Copyright (C) 2022 AlexKiri (enhanced vscroll mode rendering function) * * Redistribution and use of this code or any derivative works are permitted * provided that the following conditions are met: @@ -1847,6 +1848,292 @@ void render_bg_m5_vs(int line) merge(&linebuf[1][0x20], &linebuf[0][0x20], &linebuf[0][0x20], lut[(reg[12] & 0x08) >> 2], bitmap.viewport.w); } +/* Enhanced function that allows each cell to be vscrolled individually, instead of being limited to 2-cell */ +void render_bg_m5_vs_enhanced(int line) +{ + int column, v_offset; + uint32 atex, atbuf, *src, *dst; + uint32 v_line, next_v_line, *nt; + + /* Common data */ + uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; + uint32 yscroll = 0; + uint32 pf_col_mask = playfield_col_mask; + uint32 pf_row_mask = playfield_row_mask; + uint32 pf_shift = playfield_shift; + uint32 *vs = (uint32 *)&vsram[0]; + + /* Window & Plane A */ + int a = (reg[18] & 0x1F) << 3; + int w = (reg[18] >> 7) & 1; + + /* Plane B width */ + int start = 0; + int end = bitmap.viewport.w >> 4; + + /* Plane B horizontal scroll */ +#ifdef LSB_FIRST + uint32 shift = (xscroll >> 16) & 0x0F; + uint32 index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); +#else + uint32 shift = (xscroll & 0x0F); + uint32 index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); +#endif + + /* Left-most column vertical scrolling when partially shown horizontally (verified on PAL MD2) */ + /* TODO: check on Genesis 3 models since it apparently behaves differently */ + /* In H32 mode, vertical scrolling is disabled, in H40 mode, same value is used for both planes */ + /* See Formula One / Kawasaki Superbike Challenge (H32) & Gynoug / Cutie Suzuki no Ringside Angel (H40) */ + if (reg[12] & 1) + { + yscroll = vs[19] & (vs[19] >> 16); + } + + if(shift) + { + /* Plane B vertical scroll */ + v_line = (line + yscroll) & pf_row_mask; + + /* Plane B name table */ + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + /* Plane B line buffer */ + dst = (uint32 *)&linebuf[0][0x10 + shift]; + + atbuf = nt[(index - 1) & pf_col_mask]; + DRAW_COLUMN(atbuf, v_line) + } + else + { + /* Plane B line buffer */ + dst = (uint32 *)&linebuf[0][0x20]; + } + + for(column = 0; column < end; column++, index++) + { + /* Plane B vertical scroll */ +#ifdef LSB_FIRST + v_line = (line + (vs[column] >> 16)) & pf_row_mask; + next_v_line = (line + (vs[column + 1] >> 16)) & pf_row_mask; +#else + v_line = (line + vs[column]) & pf_row_mask; + next_v_line = (line + vs[column + 1]) & pf_row_mask; +#endif + + if (column != end - 1) + { + /* The offset of the intermediary cell is an average of the offsets of the current 2-cell and the next 2-cell. */ + /* For the last column, the previously calculated offset is used */ + v_offset = ((int)next_v_line - (int)v_line) / 2; + v_offset = (abs(v_offset) >= config.enhanced_vscroll_limit) ? 0 : v_offset; + } + + /* Plane B name table */ + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + atbuf = nt[index & pf_col_mask]; +#ifdef LSB_FIRST + GET_LSB_TILE(atbuf, v_line) +#else + GET_MSB_TILE(atbuf, v_line) +#endif + +#ifdef ALIGN_LONG + WRITE_LONG(dst, src[0] | atex); + dst++; + WRITE_LONG(dst, src[1] | atex); + dst++; +#else + *dst++ = (src[0] | atex); + *dst++ = (src[1] | atex); +#endif + +#ifdef LSB_FIRST + v_line = (line + v_offset + (vs[column] >> 16)) & pf_row_mask; +#else + v_line = (line + v_offset + vs[column]) & pf_row_mask; +#endif + + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + v_line = (v_line & 7) << 3; + atbuf = nt[index & pf_col_mask]; + +#ifdef LSB_FIRST + GET_MSB_TILE(atbuf, v_line) +#else + GET_LSB_TILE(atbuf, v_line) +#endif +#ifdef ALIGN_LONG + WRITE_LONG(dst, src[0] | atex); + dst++; + WRITE_LONG(dst, src[1] | atex); + dst++; +#else + *dst++ = (src[0] | atex); + *dst++ = (src[1] | atex); +#endif + } + + if (w == (line >= a)) + { + /* Window takes up entire line */ + a = 0; + w = 1; + } + else + { + /* Window and Plane A share the line */ + a = clip[0].enable; + w = clip[1].enable; + } + + /* Plane A */ + if (a) + { + /* Plane A width */ + start = clip[0].left; + end = clip[0].right; + + /* Plane A horizontal scroll */ +#ifdef LSB_FIRST + shift = (xscroll & 0x0F); + index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); +#else + shift = (xscroll >> 16) & 0x0F; + index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); +#endif + + if(shift) + { + /* Plane A vertical scroll */ + v_line = (line + yscroll) & pf_row_mask; + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x10 + shift + (start << 4)]; + + /* Window bug */ + if (start) + { + atbuf = nt[index & pf_col_mask]; + } + else + { + atbuf = nt[(index - 1) & pf_col_mask]; + } + + DRAW_COLUMN(atbuf, v_line) + } + else + { + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; + } + + for(column = start; column < end; column++, index++) + { + /* Plane A vertical scroll */ +#ifdef LSB_FIRST + v_line = (line + vs[column]) & pf_row_mask; + next_v_line = (line + vs[column + 1]) & pf_row_mask; +#else + v_line = (line + (vs[column] >> 16)) & pf_row_mask; + next_v_line = (line + (vs[column + 1] >> 16)) & pf_row_mask; +#endif + + if (column != end - 1) + { + v_offset = ((int)next_v_line - (int)v_line) / 2; + v_offset = (abs(v_offset) >= config.enhanced_vscroll_limit) ? 0 : v_offset; + } + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + atbuf = nt[index & pf_col_mask]; +#ifdef LSB_FIRST + GET_LSB_TILE(atbuf, v_line) +#else + GET_MSB_TILE(atbuf, v_line) +#endif +#ifdef ALIGN_LONG + WRITE_LONG(dst, src[0] | atex); + dst++; + WRITE_LONG(dst, src[1] | atex); + dst++; +#else + *dst++ = (src[0] | atex); + *dst++ = (src[1] | atex); +#endif + +#ifdef LSB_FIRST + v_line = (line + v_offset + vs[column]) & pf_row_mask; +#else + v_line = (line + v_offset + (vs[column] >> 16)) & pf_row_mask; +#endif + + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + v_line = (v_line & 7) << 3; + atbuf = nt[index & pf_col_mask]; + +#ifdef LSB_FIRST + GET_MSB_TILE(atbuf, v_line) +#else + GET_LSB_TILE(atbuf, v_line) +#endif +#ifdef ALIGN_LONG + WRITE_LONG(dst, src[0] | atex); + dst++; + WRITE_LONG(dst, src[1] | atex); + dst++; +#else + *dst++ = (src[0] | atex); + *dst++ = (src[1] | atex); +#endif + } + + /* Window width */ + start = clip[1].left; + end = clip[1].right; + } + + /* Window */ + if (w) + { + /* Window name table */ + nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; + + /* Pattern row index */ + v_line = (line & 7) << 3; + + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; + + for(column = start; column < end; column++) + { + atbuf = nt[column]; + DRAW_COLUMN(atbuf, v_line) + } + } + + /* Merge background layers */ + merge(&linebuf[1][0x20], &linebuf[0][0x20], &linebuf[0][0x20], lut[(reg[12] & 0x08) >> 2], bitmap.viewport.w); +} + void render_bg_m5_im2(int line) { int column; @@ -2543,6 +2830,342 @@ void render_bg_m5_vs(int line) } } +void render_bg_m5_vs_enhanced(int line) +{ + int column, start, end, v_offset; + uint32 atex, atbuf, *src, *dst; + uint32 shift, index, v_line, next_v_line, *nt; + uint8 *lb; + + /* Scroll Planes common data */ + uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; + uint32 yscroll = 0; + uint32 pf_col_mask = playfield_col_mask; + uint32 pf_row_mask = playfield_row_mask; + uint32 pf_shift = playfield_shift; + uint32 *vs = (uint32 *)&vsram[0]; + + /* Number of columns to draw */ + int width = bitmap.viewport.w >> 4; + + /* Layer priority table */ + uint8 *table = lut[(reg[12] & 8) >> 2]; + + /* Window vertical range (cell 0-31) */ + int a = (reg[18] & 0x1F) << 3; + + /* Window position (0=top, 1=bottom) */ + int w = (reg[18] >> 7) & 1; + + /* Test against current line */ + if (w == (line >= a)) + { + /* Window takes up entire line */ + a = 0; + w = 1; + } + else + { + /* Window and Plane A share the line */ + a = clip[0].enable; + w = clip[1].enable; + } + + /* Left-most column vertical scrolling when partially shown horizontally */ + /* Same value for both planes, only in 40-cell mode, verified on PAL MD2 */ + /* See Gynoug, Cutie Suzuki no Ringside Angel, Formula One, Kawasaki Superbike Challenge */ + if (reg[12] & 1) + { + yscroll = vs[19] & (vs[19] >> 16); + } + + /* Plane A*/ + if (a) + { + /* Plane A width */ + start = clip[0].left; + end = clip[0].right; + + /* Plane A horizontal scroll */ +#ifdef LSB_FIRST + shift = (xscroll & 0x0F); + index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); +#else + shift = (xscroll >> 16) & 0x0F; + index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); +#endif + + /* Background line buffer */ + dst = (uint32 *)&linebuf[0][0x20 + (start << 4) + shift]; + + if(shift) + { + /* Left-most column is partially shown */ + dst -= 4; + + /* Plane A vertical scroll */ + v_line = (line + yscroll) & pf_row_mask; + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + /* Window bug */ + if (start) + { + atbuf = nt[index & pf_col_mask]; + } + else + { + atbuf = nt[(index-1) & pf_col_mask]; + } + + DRAW_COLUMN(atbuf, v_line) + } + + for(column = start; column < end; column++, index++) + { + /* Plane A vertical scroll */ +#ifdef LSB_FIRST + v_line = (line + vs[column]) & pf_row_mask; + next_v_line = (line + vs[column + 1]) & pf_row_mask; +#else + v_line = (line + (vs[column] >> 16)) & pf_row_mask; + next_v_line = (line + (vs[column + 1] >> 16)) & pf_row_mask; +#endif + + if (column != end - 1) + { + v_offset = ((int)next_v_line - (int)v_line) / 2; + v_offset = (abs(v_offset) >= config.enhanced_vscroll_limit) ? 0 : v_offset; + } + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + atbuf = nt[index & pf_col_mask]; +#ifdef LSB_FIRST + GET_LSB_TILE(atbuf, v_line) +#else + GET_MSB_TILE(atbuf, v_line) +#endif +#ifdef ALIGN_LONG + WRITE_LONG(dst, src[0] | atex); + dst++; + WRITE_LONG(dst, src[1] | atex); + dst++; +#else + *dst++ = (src[0] | atex); + *dst++ = (src[1] | atex); +#endif + +#ifdef LSB_FIRST + v_line = (line + v_offset + vs[column]) & pf_row_mask; +#else + v_line = (line + v_offset + (vs[column] >> 16)) & pf_row_mask; +#endif + + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + v_line = (v_line & 7) << 3; + atbuf = nt[index & pf_col_mask]; + +#ifdef LSB_FIRST + GET_MSB_TILE(atbuf, v_line) +#else + GET_LSB_TILE(atbuf, v_line) +#endif +#ifdef ALIGN_LONG + WRITE_LONG(dst, src[0] | atex); + dst++; + WRITE_LONG(dst, src[1] | atex); + dst++; +#else + *dst++ = (src[0] | atex); + *dst++ = (src[1] | atex); +#endif + } + + /* Window width */ + start = clip[1].left; + end = clip[1].right; + } + else + { + /* Window width */ + start = 0; + end = width; + } + + /* Window Plane */ + if (w) + { + /* Background line buffer */ + dst = (uint32 *)&linebuf[0][0x20 + (start << 4)]; + + /* Window name table */ + nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; + + /* Pattern row index */ + v_line = (line & 7) << 3; + + for(column = start; column < end; column++) + { + atbuf = nt[column]; + DRAW_COLUMN(atbuf, v_line) + } + } + + /* Plane B horizontal scroll */ +#ifdef LSB_FIRST + shift = (xscroll >> 16) & 0x0F; + index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); +#else + shift = (xscroll & 0x0F); + index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); +#endif + + /* Background line buffer */ + lb = &linebuf[0][0x20]; + + if(shift) + { + /* Left-most column is partially shown */ + lb -= (0x10 - shift); + + /* Plane B vertical scroll */ + v_line = (line + yscroll) & pf_row_mask; + + /* Plane B name table */ + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + atbuf = nt[(index-1) & pf_col_mask]; + DRAW_BG_COLUMN(atbuf, v_line, xscroll, yscroll) + } + + for(column = 0; column < width; column++, index++) + { + /* Plane B vertical scroll */ +#ifdef LSB_FIRST + v_line = (line + (vs[column] >> 16)) & pf_row_mask; + next_v_line = (line + (vs[column + 1] >> 16)) & pf_row_mask; +#else + v_line = (line + vs[column]) & pf_row_mask; + next_v_line = (line + vs[column + 1]) & pf_row_mask; +#endif + + if (column != width - 1) + { + v_offset = ((int)next_v_line - (int)v_line) / 2; + v_offset = (abs(v_offset) >= config.enhanced_vscroll_limit) ? 0 : v_offset; + } + + /* Plane B name table */ + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + atbuf = nt[index & pf_col_mask]; +#ifdef ALIGN_LONG +#ifdef LSB_FIRST + GET_LSB_TILE(atbuf, v_line) + xscroll = READ_LONG((uint32 *)lb); + yscroll = (src[0] | atex); + DRAW_BG_TILE(xscroll, yscroll) + xscroll = READ_LONG((uint32 *)lb); + yscroll = (src[1] | atex); + DRAW_BG_TILE(xscroll, yscroll) + + v_line = (line + v_offset + (vs[column] >> 16)) & pf_row_mask; + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + v_line = (v_line & 7) << 3; + atbuf = nt[index & pf_col_mask]; + + GET_MSB_TILE(atbuf, v_line) + xscroll = READ_LONG((uint32 *)lb); + yscroll = (src[0] | atex); + DRAW_BG_TILE(xscroll, yscroll) + xscroll = READ_LONG((uint32 *)lb); + yscroll = (src[1] | atex); + DRAW_BG_TILE(xscroll, yscroll) +#else + GET_MSB_TILE(atbuf, v_line) + xscroll = READ_LONG((uint32 *)lb); + yscroll = (src[0] | atex); + DRAW_BG_TILE(xscroll, yscroll) + xscroll = READ_LONG((uint32 *)lb); + yscroll = (src[1] | atex); + DRAW_BG_TILE(xscroll, yscroll) + + v_line = (line + vs[column]) & pf_row_mask; + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + v_line = (v_line & 7) << 3; + atbuf = nt[index & pf_col_mask]; + + GET_LSB_TILE(atbuf, v_line) + xscroll = READ_LONG((uint32 *)lb); + yscroll = (src[0] | atex); + DRAW_BG_TILE(xscroll, yscroll) + xscroll = READ_LONG((uint32 *)lb); + yscroll = (src[1] | atex); + DRAW_BG_TILE(xscroll, yscroll) +#endif +#else /* NOT ALIGNED */ +#ifdef LSB_FIRST + GET_LSB_TILE(atbuf, v_line) + xscroll = *(uint32 *)(lb); + yscroll = (src[0] | atex); + DRAW_BG_TILE(xscroll, yscroll) + xscroll = *(uint32 *)(lb); + yscroll = (src[1] | atex); + DRAW_BG_TILE(xscroll, yscroll) + + v_line = (line + v_offset + (vs[column] >> 16)) & pf_row_mask; + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + v_line = (v_line & 7) << 3; + atbuf = nt[index & pf_col_mask]; + + GET_MSB_TILE(atbuf, v_line) + xscroll = *(uint32 *)(lb); + yscroll = (src[0] | atex); + DRAW_BG_TILE(xscroll, yscroll) + xscroll = *(uint32 *)(lb); + yscroll = (src[1] | atex); + DRAW_BG_TILE(xscroll, yscroll) +#else + GET_MSB_TILE(atbuf, v_line) + xscroll = *(uint32 *)(lb); + yscroll = (src[0] | atex); + DRAW_BG_TILE(xscroll, yscroll) + xscroll = *(uint32 *)(lb); + yscroll = (src[1] | atex); + DRAW_BG_TILE(xscroll, yscroll) + + v_line = (line + vs[column]) & pf_row_mask; + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + v_line = (v_line & 7) << 3; + atbuf = nt[index & pf_col_mask]; + + GET_LSB_TILE(atbuf, v_line) + xscroll = *(uint32 *)(lb); + yscroll = (src[0] | atex); + DRAW_BG_TILE(xscroll, yscroll) + xscroll = *(uint32 *)(lb); + yscroll = (src[1] | atex); + DRAW_BG_TILE(xscroll, yscroll) +#endif +#endif /* ALIGN_LONG */ + } +} + void render_bg_m5_im2(int line) { int column, start, end; diff --git a/core/vdp_render.h b/core/vdp_render.h index f5b2faa..5b341e3 100644 --- a/core/vdp_render.h +++ b/core/vdp_render.h @@ -118,6 +118,7 @@ extern void render_bg_inv(int line); extern void render_bg_m4(int line); extern void render_bg_m5(int line); extern void render_bg_m5_vs(int line); +extern void render_bg_m5_vs_enhanced(int line); extern void render_bg_m5_im2(int line); extern void render_bg_m5_im2_vs(int line); extern void render_obj_tms(int line); diff --git a/gcw0/config.c b/gcw0/config.c index dfa7411..c92e6ba 100644 --- a/gcw0/config.c +++ b/gcw0/config.c @@ -82,6 +82,8 @@ void set_config_defaults(void) config.a_stick = 1; /* 1 = A-Stick on */ config.lightgun_speed = 1; /* 1 = simple speed multiplier for lightgun */ config.gcw0_frameskip = 0; /* 0 = off, 1 = skip alternate frames, 2 = skip 2 in 3 frames, etc. Useful for FMV in MCD. */ + config.enhanced_vscroll = 0; + config.enhanced_vscroll_limit = 8; /* controllers options */ config.cursor = 0; /* different cursor designs */ diff --git a/gcw0/config.h b/gcw0/config.h index 05c1ad9..dea4db7 100644 --- a/gcw0/config.h +++ b/gcw0/config.h @@ -52,6 +52,8 @@ typedef struct uint8 ntsc; uint8 lcd; uint8 render; + uint8 enhanced_vscroll; + uint8 enhanced_vscroll_limit; t_input_config input[MAX_INPUTS]; uint8 gcw0_fullscreen; uint8 gcw0_frameskip; diff --git a/gx/config.c b/gx/config.c index 85d96b1..9364d8a 100644 --- a/gx/config.c +++ b/gx/config.c @@ -141,6 +141,8 @@ void config_default(void) config.vsync = 1; /* AUTO */ config.bilinear = 0; config.vfilter = 1; + config.enhanced_vscroll = 0; + config.enhanced_vscroll_limit = 8; if (VIDEO_HaveComponentCable()) { diff --git a/gx/config.h b/gx/config.h index 1355f5f..a1c1be5 100644 --- a/gx/config.h +++ b/gx/config.h @@ -83,6 +83,8 @@ typedef struct uint8 ntsc; uint8 vsync; uint8 render; + uint8 enhanced_vscroll; + uint8 enhanced_vscroll_limit; uint8 tv_mode; uint8 bilinear; uint8 vfilter; diff --git a/libretro/libretro.c b/libretro/libretro.c index 3d22766..34cc575 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -950,6 +950,8 @@ static void config_default(void) config.overclock = 100; #endif config.no_sprite_limit = 0; + config.enhanced_vscroll = 0; + config.enhanced_vscroll_limit = 8; /* video options */ config.overscan = 0; @@ -1863,6 +1865,19 @@ static void check_variables(bool first_run) config.no_sprite_limit = 1; } + var.key = "genesis_plus_gx_enhanced_vscroll"; + environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var); + { + if (!var.value || !strcmp(var.value, "disabled")) + config.enhanced_vscroll = 0; + else + config.enhanced_vscroll = 1; + } + + var.key = "genesis_plus_gx_enhanced_vscroll_limit"; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + config.enhanced_vscroll_limit = strtol(var.value, NULL, 10); + #ifdef USE_PER_SOUND_CHANNELS_CONFIG var.key = psg_channel_volume_base_str; for (c = 0; c < 4; c++) diff --git a/libretro/libretro_core_options.h b/libretro/libretro_core_options.h index 3dd001b..b01662a 100644 --- a/libretro/libretro_core_options.h +++ b/libretro/libretro_core_options.h @@ -782,6 +782,47 @@ struct retro_core_option_v2_definition option_defs_us[] = { }, "disabled" }, + { + "genesis_plus_gx_enhanced_vscroll", + "Enchanced per-tile vertical scroll", + NULL, + "Allows each individual cell to be scrolled vertically, instead of 16px 2-cell, by averaging out with the vscroll value of the neighbouring cell. This hack only applies to few games that use 2-cell vertical scroll mode.", + NULL, + "hacks", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "genesis_plus_gx_enhanced_vscroll_limit", + "Enchanced per-tile vertical scroll limit", + NULL, + "Only when Enchance per-tile vertical scroll is enabled. Adjusts the limit of the vertical scroll enhancement. When the vscroll difference between neighbouring tiles is bigger than this limit, the enhancement is disabled.", + NULL, + "hacks", + { + { "2", NULL }, + { "3", NULL }, + { "4", NULL }, + { "5", NULL }, + { "6", NULL }, + { "7", NULL }, + { "8", NULL }, + { "9", NULL }, + { "10", NULL }, + { "11", NULL }, + { "12", NULL }, + { "13", NULL }, + { "14", NULL }, + { "15", NULL }, + { "16", NULL }, + { NULL, NULL }, + }, + "8" + }, #ifdef HAVE_OVERCLOCK { "genesis_plus_gx_overclock", diff --git a/libretro/osd.h b/libretro/osd.h index 093b7b1..892028d 100644 --- a/libretro/osd.h +++ b/libretro/osd.h @@ -133,6 +133,8 @@ typedef struct uint8 gun_cursor; uint32 overclock; uint8 no_sprite_limit; + uint8 enhanced_vscroll; + uint8 enhanced_vscroll_limit; uint8 cd_latency; #ifdef USE_PER_SOUND_CHANNELS_CONFIG unsigned int psg_ch_volumes[4]; diff --git a/psp2/config.c b/psp2/config.c index 7eb7196..f80c2c3 100644 --- a/psp2/config.c +++ b/psp2/config.c @@ -43,6 +43,8 @@ void set_config_defaults(void) config.render = 0; /* 1 = double resolution output (only when interlaced mode 2 is enabled) */ config.ntsc = 0; config.lcd = 0; /* 0.8 fixed point */ + config.enhanced_vscroll = 0; + config.enhanced_vscroll_limit = 8; /* controllers options */ input.system[0] = SYSTEM_GAMEPAD; diff --git a/psp2/config.h b/psp2/config.h index b3fbae3..d50b3d1 100644 --- a/psp2/config.h +++ b/psp2/config.h @@ -51,6 +51,8 @@ typedef struct uint8 ntsc; uint8 lcd; uint8 render; + uint8 enhanced_vscroll; + uint8 enhanced_vscroll_limit; t_input_config input[MAX_INPUTS]; } t_config; diff --git a/sdl/config.c b/sdl/config.c index 94cbb21..5e2cf72 100644 --- a/sdl/config.c +++ b/sdl/config.c @@ -46,6 +46,8 @@ void set_config_defaults(void) config.render = 0; /* 1 = double resolution output (only when interlaced mode 2 is enabled) */ config.ntsc = 0; config.lcd = 0; /* 0.8 fixed point */ + config.enhanced_vscroll = 0; + config.enhanced_vscroll_limit = 8; /* controllers options */ input.system[0] = SYSTEM_GAMEPAD; diff --git a/sdl/config.h b/sdl/config.h index fd7dbff..6c01bd3 100644 --- a/sdl/config.h +++ b/sdl/config.h @@ -51,6 +51,8 @@ typedef struct uint8 ntsc; uint8 lcd; uint8 render; + uint8 enhanced_vscroll; + uint8 enhanced_vscroll_limit; t_input_config input[MAX_INPUTS]; } t_config;