in cores/genesis/core/vdp_ctrl.c [1453:2055]
static void vdp_reg_w(unsigned int r, unsigned int d, unsigned int cycles)
{
#ifdef LOGVDP
error("[%d(%d)][%d(%d)] VDP register %d write -> 0x%x (%x)\n", v_counter, (v_counter + (cycles - mcycles_vdp)/MCYCLES_PER_LINE)%lines_per_frame, cycles, cycles%MCYCLES_PER_LINE, r, d, m68k_get_reg(M68K_REG_PC));
#endif
/* VDP registers #11 to #23 cannot be updated in Mode 4 (Captain Planet & Avengers, Bass Master Classic Pro Edition) */
if (!(reg[1] & 4) && (r > 10))
{
return;
}
switch(r)
{
case 0: /* CTRL #1 */
{
/* Look for changed bits */
r = d ^ reg[0];
reg[0] = d;
/* Line Interrupt */
if (r & hint_pending)
{
/* Update IRQ status */
if (reg[1] & vint_pending)
{
set_irq_line(6);
}
else if (d & 0x10)
{
set_irq_line_delay(4);
}
else
{
set_irq_line(0);
}
}
/* Palette selection */
if (r & 0x04)
{
/* Mega Drive VDP only */
if (system_hw & SYSTEM_MD)
{
/* Reset color palette */
int i;
if (reg[1] & 0x04)
{
/* Mode 5 */
color_update_m5(0x00, *(uint16 *)&cram[border << 1]);
for (i = 1; i < 0x40; i++)
{
color_update_m5(i, *(uint16 *)&cram[i << 1]);
}
}
else
{
/* Mode 4 */
for (i = 0; i < 0x20; i++)
{
color_update_m4(i, *(uint16 *)&cram[i << 1]);
}
color_update_m4(0x40, *(uint16 *)&cram[(0x10 | (border & 0x0F)) << 1]);
}
}
}
/* HVC latch (Sunset Riders, Lightgun games) */
if (r & 0x02)
{
/* Mega Drive VDP only */
if (system_hw & SYSTEM_MD)
{
/* Mode 5 only */
if (reg[1] & 0x04)
{
if (d & 0x02)
{
/* Latch current HVC */
hvc_latch = vdp_hvc_r(cycles) | 0x10000;
}
else
{
/* Free-running HVC */
hvc_latch = 0;
}
}
}
}
break;
}
case 1: /* CTRL #2 */
{
/* Look for changed bits */
r = d ^ reg[1];
reg[1] = d;
/* 4K/16K address decoding */
if (r & 0x80)
{
/* original TMS99xx hardware only (fixes Magical Kid Wiz) */
if (system_hw == SYSTEM_SG)
{
int i;
/* make temporary copy of 16KB VRAM */
memcpy(vram + 0x4000, vram, 0x4000);
/* re-arrange 16KB VRAM address decoding */
if (d & 0x80)
{
/* 4K->16K address decoding */
for (i=0; i<0x4000; i+=2)
{
*(uint16 *)(vram + ((i & 0x203F) | ((i << 6) & 0x1000) | ((i >> 1) & 0xFC0))) = *(uint16 *)(vram + 0x4000 + i);
}
}
else
{
/* 16K->4K address decoding */
for (i=0; i<0x4000; i+=2)
{
*(uint16 *)(vram + ((i & 0x203F) | ((i >> 6) & 0x40) | ((i << 1) & 0x1F80))) = *(uint16 *)(vram + 0x4000 + i);
}
}
}
}
/* Display status (modified during active display) */
if ((r & 0x40) && (v_counter < bitmap.viewport.h))
{
/* Cycle offset vs HBLANK */
int offset = cycles - mcycles_vdp;
if (offset <= 860)
{
/* Sprite rendering is limited if display was disabled during HBLANK (Mickey Mania 3d level, Overdrive Demo) */
if (d & 0x40)
{
/* NB: This is not 100% accurate. On real hardware, the maximal number of rendered sprites pixels */
/* for the current line (normally 256 or 320 pixels) but also the maximal number of pre-processed */
/* sprites for the next line (normally 64 or 80 sprites) are both reduced depending on the amount */
/* of cycles spent with display disabled. Here we only reduce them by a fixed amount when display */
/* has been reenabled after a specific point within HBLANK. */
if (offset > 360)
{
max_sprite_pixels = 128;
}
}
/* Redraw entire line (Legend of Galahad, Lemmings 2, Formula One, Kawasaki Super Bike, Deadly Moves,...) */
render_line(v_counter);
/* Restore default */
max_sprite_pixels = 256 + ((reg[12] & 1) << 6);
}
else if (system_hw & SYSTEM_MD)
{
/* Active pixel offset */
if (reg[12] & 1)
{
/* dot clock = MCLK / 8 */
offset = ((offset - 860) / 8) + 16;
}
else
{
/* dot clock = MCLK / 10 */
offset = ((offset - 860) / 10) + 16;
}
/* Line is partially blanked (Nigel Mansell's World Championship Racing , Ren & Stimpy Show, ...) */
if (offset < bitmap.viewport.w)
{
if (d & 0x40)
{
render_line(v_counter);
blank_line(v_counter, 0, offset);
}
else
{
blank_line(v_counter, offset, bitmap.viewport.w - offset);
}
}
}
}
/* Frame Interrupt */
if (r & vint_pending)
{
/* Update IRQ status */
if (d & 0x20)
{
set_irq_line_delay(6);
}
else if (reg[0] & hint_pending)
{
set_irq_line(4);
}
else
{
set_irq_line(0);
}
}
/* Active display height */
if (r & 0x08)
{
/* Mega Drive VDP only */
if (system_hw & SYSTEM_MD)
{
/* Mode 5 only */
if (d & 0x04)
{
/* Changes should be applied on next frame */
bitmap.viewport.changed |= 2;
/* Update vertical counter max value */
vc_max = vc_table[(d >> 2) & 3][vdp_pal];
}
}
}
/* Rendering mode */
if (r & 0x04)
{
/* Mega Drive VDP only */
if (system_hw & SYSTEM_MD)
{
int i;
if (d & 0x04)
{
/* Mode 5 rendering */
parse_satb = parse_satb_m5;
update_bg_pattern_cache = update_bg_pattern_cache_m5;
if (im2_flag)
{
render_bg = (reg[11] & 0x04) ? render_bg_m5_im2_vs : render_bg_m5_im2;
render_obj = (reg[12] & 0x08) ? render_obj_m5_im2_ste : render_obj_m5_im2;
}
else
{
render_bg = (reg[11] & 0x04) ? render_bg_m5_vs : render_bg_m5;
render_obj = (reg[12] & 0x08) ? render_obj_m5_ste : render_obj_m5;
}
/* Reset color palette */
color_update_m5(0x00, *(uint16 *)&cram[border << 1]);
for (i = 1; i < 0x40; i++)
{
color_update_m5(i, *(uint16 *)&cram[i << 1]);
}
/* Mode 5 bus access */
vdp_68k_data_w = vdp_68k_data_w_m5;
vdp_z80_data_w = vdp_z80_data_w_m5;
vdp_68k_data_r = vdp_68k_data_r_m5;
vdp_z80_data_r = vdp_z80_data_r_m5;
/* Clear HVC latched value */
hvc_latch = 0;
/* Check if HVC latch bit is set */
if (reg[0] & 0x02)
{
/* Latch current HVC */
hvc_latch = vdp_hvc_r(cycles) | 0x10000;
}
/* max tiles to invalidate */
bg_list_index = 0x800;
}
else
{
/* Mode 4 rendering */
parse_satb = parse_satb_m4;
update_bg_pattern_cache = update_bg_pattern_cache_m4;
render_bg = render_bg_m4;
render_obj = render_obj_m4;
/* Reset color palette */
for (i = 0; i < 0x20; i++)
{
color_update_m4(i, *(uint16 *)&cram[i << 1]);
}
color_update_m4(0x40, *(uint16 *)&cram[(0x10 | (border & 0x0F)) << 1]);
/* Mode 4 bus access */
vdp_68k_data_w = vdp_68k_data_w_m4;
vdp_z80_data_w = vdp_z80_data_w_m4;
vdp_68k_data_r = vdp_68k_data_r_m4;
vdp_z80_data_r = vdp_z80_data_r_m4;
/* Latch current HVC */
hvc_latch = vdp_hvc_r(cycles) | 0x10000;
/* max tiles to invalidate */
bg_list_index = 0x200;
}
/* Invalidate pattern cache */
for (i=0;i<bg_list_index;i++)
{
bg_name_list[i] = i;
bg_name_dirty[i] = 0xFF;
}
/* Update vertical counter max value */
vc_max = vc_table[(d >> 2) & 3][vdp_pal];
/* Display height change should be applied on next frame */
bitmap.viewport.changed |= 2;
}
else
{
/* No effect (cleared to avoid mode 5 detection elsewhere) */
reg[1] &= ~0x04;
}
}
break;
}
case 2: /* Plane A Name Table Base */
{
reg[2] = d;
ntab = (d << 10) & 0xE000;
/* Plane A Name Table Base changed during HBLANK */
if ((v_counter < bitmap.viewport.h) && (reg[1] & 0x40) && (cycles <= (mcycles_vdp + 860)))
{
/* render entire line */
render_line(v_counter);
}
break;
}
case 3: /* Window Plane Name Table Base */
{
reg[3] = d;
if (reg[12] & 0x01)
{
ntwb = (d << 10) & 0xF000;
}
else
{
ntwb = (d << 10) & 0xF800;
}
/* Window Plane Name Table Base changed during HBLANK */
if ((v_counter < bitmap.viewport.h) && (reg[1] & 0x40) && (cycles <= (mcycles_vdp + 860)))
{
/* render entire line */
render_line(v_counter);
}
break;
}
case 4: /* Plane B Name Table Base */
{
reg[4] = d;
ntbb = (d << 13) & 0xE000;
/* Plane B Name Table Base changed during HBLANK (Adventures of Batman & Robin) */
if ((v_counter < bitmap.viewport.h) && (reg[1] & 0x40) && (cycles <= (mcycles_vdp + 860)))
{
/* render entire line */
render_line(v_counter);
}
break;
}
case 5: /* Sprite Attribute Table Base */
{
reg[5] = d;
satb = (d << 9) & sat_base_mask;
break;
}
case 7: /* Backdrop color */
{
reg[7] = d;
/* Check if backdrop color changed */
d &= 0x3F;
if (d != border)
{
/* Update backdrop color */
border = d;
/* Reset palette entry */
if (reg[1] & 4)
{
/* Mode 5 */
color_update_m5(0x00, *(uint16 *)&cram[d << 1]);
}
else
{
/* Mode 4 */
color_update_m4(0x40, *(uint16 *)&cram[(0x10 | (d & 0x0F)) << 1]);
}
/* Backdrop color modified during HBLANK (Road Rash 1,2,3)*/
if ((v_counter < bitmap.viewport.h) && (cycles <= (mcycles_vdp + 860)))
{
/* remap entire line */
remap_line(v_counter);
}
}
break;
}
case 8: /* Horizontal Scroll (Mode 4 only) */
{
/* H-Scroll is latched at HCount 0xF3, HCount 0xF6 on MD */
/* Line starts at HCount 0xF4, HCount 0xF6 on MD */
if (system_hw < SYSTEM_MD)
{
cycles = cycles + 15;
}
/* Check if H-Scroll has already been latched */
if ((cycles - mcycles_vdp) >= MCYCLES_PER_LINE)
{
/* update line counter */
int line = (v_counter + 1) % lines_per_frame;
/* check if we are within active display range */
if ((line < bitmap.viewport.h) && !(work_ram[0x1ffb] & cart.special & HW_3D_GLASSES))
{
/* update VCounter to indicate next line has already been rendered */
v_counter = line;
/* render next line before updating H-Scroll */
render_line(line);
}
}
reg[8] = d;
break;
}
case 11: /* CTRL #3 */
{
reg[11] = d;
/* Horizontal scrolling mode */
hscroll_mask = hscroll_mask_table[d & 0x03];
/* Vertical Scrolling mode */
if (d & 0x04)
{
render_bg = im2_flag ? render_bg_m5_im2_vs : render_bg_m5_vs;
}
else
{
render_bg = im2_flag ? render_bg_m5_im2 : render_bg_m5;
}
break;
}
case 12: /* CTRL #4 */
{
/* Look for changed bits */
r = d ^ reg[12];
reg[12] = d;
/* Shadow & Highlight mode */
if (r & 0x08)
{
/* Reset color palette */
int i;
color_update_m5(0x00, *(uint16 *)&cram[border << 1]);
for (i = 1; i < 0x40; i++)
{
color_update_m5(i, *(uint16 *)&cram[i << 1]);
}
/* Update sprite rendering function */
if (d & 0x08)
{
render_obj = im2_flag ? render_obj_m5_im2_ste : render_obj_m5_ste;
}
else
{
render_obj = im2_flag ? render_obj_m5_im2 : render_obj_m5;
}
}
/* Interlaced modes */
if (r & 0x06)
{
/* changes should be applied on next frame */
bitmap.viewport.changed |= 2;
}
/* Active display width */
if (r & 0x01)
{
/* FIFO access slots timings depend on active width */
if (fifo_slots)
{
/* Synchronize VDP FIFO */
vdp_fifo_update(cycles);
}
if (d & 0x01)
{
/* Update display-dependant registers */
ntwb = (reg[3] << 10) & 0xF000;
satb = (reg[5] << 9) & 0xFC00;
sat_base_mask = 0xFC00;
sat_addr_mask = 0x03FF;
/* Update HC table */
hctab = cycle2hc40;
/* Update clipping */
window_clip(reg[17], 1);
/* Update max sprite pixels per line*/
max_sprite_pixels = 320;
/* FIFO access slots timings */
fifo_timing = (int *)fifo_timing_h40;
}
else
{
/* Update display-dependant registers */
ntwb = (reg[3] << 10) & 0xF800;
satb = (reg[5] << 9) & 0xFE00;
sat_base_mask = 0xFE00;
sat_addr_mask = 0x01FF;
/* Update HC table */
hctab = cycle2hc32;
/* Update clipping */
window_clip(reg[17], 0);
/* Update max sprite pixels per line*/
max_sprite_pixels = 256;
/* FIFO access slots timings */
fifo_timing = (int *)fifo_timing_h32;
}
/* Update active screen width */
if (v_counter >= bitmap.viewport.h)
{
/* Active screen width modified during VBLANK will be applied on upcoming frame */
bitmap.viewport.w = max_sprite_pixels;
}
else if ((v_counter == 0) && (cycles <= (mcycles_vdp + 860)))
{
/* Active screen width modified during first line HBLANK (Bugs Bunny in Double Trouble) */
bitmap.viewport.w = max_sprite_pixels;
/* Redraw first line */
render_line(0);
}
else
{
/* Screen width changes during active display (Golden Axe 3 intro, Ultraverse Prime) */
/* should be applied on next frame since backend rendered framebuffer width is fixed */
/* and can not be modified mid-frame. This is not 100% accurate but games generally */
/* do this where the screen is blanked so it is likely unnoticeable. */
bitmap.viewport.changed |= 2;
}
}
break;
}
case 13: /* HScroll Base Address */
{
reg[13] = d;
hscb = (d << 10) & 0xFC00;
break;
}
case 16: /* Playfield size */
{
reg[16] = d;
playfield_shift = shift_table[(d & 3)];
playfield_col_mask = col_mask_table[(d & 3)];
playfield_row_mask = row_mask_table[(d >> 4) & 3];
break;
}
case 17: /* Window/Plane A vertical clipping */
{
reg[17] = d;
window_clip(d, reg[12] & 1);
break;
}
default:
{
reg[r] = d;
break;
}
}
}