snes.c: fixed a SRAM size problem and moved PPU registers handling to video/snes.c [Fabio Priuli]

the latter change is the first step towards making SNES PPU a device, at last
This commit is contained in:
Fabio Priuli 2010-03-30 22:44:45 +00:00
parent d81beae782
commit bf50c973a2
3 changed files with 871 additions and 828 deletions

View File

@ -394,6 +394,7 @@ public:
UINT16 vram_fgr_high, vram_fgr_increment, vram_fgr_count, vram_fgr_mask, vram_fgr_shift, vram_read_buffer;
UINT32 wram_address;
UINT16 htime, vtime;
UINT16 vmadd;
/* timers */
emu_timer *scanline_timer;
@ -493,6 +494,8 @@ extern DRIVER_INIT( snes_hirom );
extern MACHINE_START( snes );
extern MACHINE_RESET( snes );
READ8_HANDLER( snes_open_bus_r );
extern READ8_HANDLER( snes_r_io );
extern WRITE8_HANDLER( snes_w_io );
@ -651,5 +654,7 @@ extern struct SNES_PPU_STRUCT snes_ppu;
extern VIDEO_START( snes );
extern VIDEO_UPDATE( snes );
extern READ8_HANDLER( snes_ppu_read );
extern WRITE8_HANDLER( snes_ppu_write );
#endif /* _SNES_H_ */

View File

@ -30,15 +30,9 @@ UINT16 *snes_oam = NULL; /* Object Attribute Memory */
UINT8 snes_has_addon_chip;
UINT32 snes_rom_size;
// full graphic variables
static const UINT16 vram_fgr_inctab[4] = { 1, 32, 128, 128 };
static const UINT16 vram_fgr_inccnts[4] = { 0, 32, 64, 128 };
static const UINT16 vram_fgr_shiftab[4] = { 0, 5, 6, 7 };
static void snes_dma(const address_space *space, UINT8 channels);
static void snes_hdma_init(const address_space *space);
static void snes_hdma(const address_space *space);
static READ8_HANDLER(snes_open_bus_r);
struct snes_cart_info snes_cart;
@ -60,20 +54,6 @@ struct snes_cart_info snes_cart;
*************************************/
// utility function - latches the H/V counters. Used by IRQ, writes to WRIO, etc.
void snes_latch_counters( running_machine *machine )
{
snes_state *state = (snes_state *)machine->driver_data;
snes_ppu.beam.current_horz = video_screen_get_hpos(machine->primary_screen) / state->htmult;
snes_ppu.beam.latch_vert = video_screen_get_vpos(machine->primary_screen);
snes_ppu.beam.latch_horz = snes_ppu.beam.current_horz;
snes_ram[STAT78] |= 0x40; // indicate we latched
// state->read_ophct = state->read_opvct = 0; // clear read flags - 2009-08: I think we must clear these when STAT78 is read...
// printf("latched @ H %d V %d\n", snes_ppu.beam.latch_horz, snes_ppu.beam.latch_vert);
}
static TIMER_CALLBACK( snes_nmi_tick )
{
snes_state *state = (snes_state *)machine->driver_data;
@ -298,35 +278,7 @@ static TIMER_CALLBACK(snes_mult_callback)
*************************************/
static void snes_dynamic_res_change( running_machine *machine )
{
snes_state *state = (snes_state *)machine->driver_data;
rectangle visarea = *video_screen_get_visible_area(machine->primary_screen);
attoseconds_t refresh;
visarea.min_x = visarea.min_y = 0;
visarea.max_y = snes_ppu.beam.last_visible_line*snes_ppu.interlace - 1;
visarea.max_x = (SNES_SCR_WIDTH * 2) - 1;
// fixme: should compensate for SNES_DBG_video
if (snes_ppu.mode == 5 || snes_ppu.mode == 6 || snes_ppu.pseudo_hires)
state->htmult = 2;
else
state->htmult = 1;
/* FIXME: does the timing changes when the gfx mode is equal to 5 or 6? */
if ((snes_ram[STAT78] & 0x10) == SNES_NTSC)
refresh = HZ_TO_ATTOSECONDS(DOTCLK_NTSC) * SNES_HTOTAL * SNES_VTOTAL_NTSC;
else
refresh = HZ_TO_ATTOSECONDS(DOTCLK_PAL) * SNES_HTOTAL * SNES_VTOTAL_PAL;
if ((snes_ram[STAT78] & 0x10) == SNES_NTSC)
video_screen_configure(machine->primary_screen, SNES_HTOTAL*2, SNES_VTOTAL_NTSC*snes_ppu.interlace, &visarea, refresh);
else
video_screen_configure(machine->primary_screen, SNES_HTOTAL*2, SNES_VTOTAL_PAL*snes_ppu.interlace, &visarea, refresh);
}
static READ8_HANDLER( snes_open_bus_r )
READ8_HANDLER( snes_open_bus_r )
{
static UINT8 recurse = 0;
UINT16 result;
@ -341,244 +293,6 @@ static READ8_HANDLER( snes_open_bus_r )
return result;
}
/*************************************************
SNES VRAM accesses:
VRAM accesses during active display are invalid.
Unlike OAM and CGRAM, they will not be written
anywhere at all. Thanks to byuu's researches,
the ranges where writes are invalid have been
validated on hardware, as has the edge case where
the S-CPU open bus can be written if the write
occurs during the very last clock cycle of
vblank.
Our implementation could be not 100% accurate
when interlace is active.
*************************************************/
INLINE UINT32 snes_get_vram_address( running_machine *machine )
{
snes_state *state = (snes_state *)machine->driver_data;
UINT32 addr = (snes_ram[VMADDH] << 8) | snes_ram[VMADDL];
if (state->vram_fgr_count)
{
UINT32 rem = addr & state->vram_fgr_mask;
UINT32 faddr = (addr & ~state->vram_fgr_mask) + (rem >> state->vram_fgr_shift) + ((rem & (state->vram_fgr_count - 1)) << 3);
return faddr;
}
return addr;
}
static READ8_HANDLER( snes_vram_read )
{
UINT8 res = 0;
offset &= 0x1ffff;
if (snes_ppu.screen_disabled)
res = snes_vram[offset];
else
{
UINT16 v = video_screen_get_vpos(space->machine->primary_screen);
UINT16 h = video_screen_get_hpos(space->machine->primary_screen);
UINT16 ls = (((snes_ram[STAT78] & 0x10) == SNES_NTSC ? 525 : 625) >> 1) - 1;
if (snes_ppu.interlace == 2)
ls++;
if (v == ls && h == 1362)
res = 0;
else if (v < snes_ppu.beam.last_visible_line - 1)
res = 0;
else if (v == snes_ppu.beam.last_visible_line - 1)
{
if (h == 1362)
res = snes_vram[offset];
else
res = 0;
}
else
res = snes_vram[offset];
}
return res;
}
static WRITE8_HANDLER( snes_vram_write )
{
offset &= 0x1ffff;
if (snes_ppu.screen_disabled)
snes_vram[offset] = data;
else
{
UINT16 v = video_screen_get_vpos(space->machine->primary_screen);
UINT16 h = video_screen_get_hpos(space->machine->primary_screen);
if (v == 0)
{
if (h <= 4)
snes_vram[offset] = data;
else if (h == 6)
snes_vram[offset] = snes_open_bus_r(space, 0);
else
{
//no write
}
}
else if (v < snes_ppu.beam.last_visible_line)
{
//no write
}
else if (v == snes_ppu.beam.last_visible_line)
{
if (h <= 4)
{
//no write
}
else
snes_vram[offset] = data;
}
else
snes_vram[offset] = data;
}
}
/*************************************************
SNES OAM accesses:
OAM accesses during active display are allowed.
The actual address varies during rendering, as the
PPU reads in data itself for processing.
Unfortunately, no one has been able (yet) to
determine how this works. The only known game to
actually access OAM during active display is
Uniracers and it expects accesses to map to
offset 0x0218. Hence, following byuu's choice
we rerouted OAM accesses during active display
to 0x0218 (0x010c in our snes_oam).
This is a hack, but it is more accurate than
writing to the 'expected' address set by
$2102,$2103.
Notice that, since snes_ram[OAMDATA] is never
read/written directly, we use it as an index
to choose the high/low byte of the snes_oam word.
*************************************************/
static READ8_HANDLER( snes_oam_read )
{
offset &= 0x1ff;
if (offset & 0x100)
offset &= 0x10f;
if (!snes_ppu.screen_disabled)
{
UINT16 v = video_screen_get_vpos(space->machine->primary_screen);
if (v < snes_ppu.beam.last_visible_line)
offset = 0x010c;
}
return (snes_oam[offset] >> (snes_ram[OAMDATA] << 3)) & 0xff;;
}
static WRITE8_HANDLER( snes_oam_write )
{
offset &= 0x1ff;
if (offset & 0x100)
offset &= 0x10f;
if (!snes_ppu.screen_disabled)
{
UINT16 v = video_screen_get_vpos(space->machine->primary_screen);
if (v < snes_ppu.beam.last_visible_line)
offset = 0x010c;
}
if (!(snes_ram[OAMDATA]))
snes_oam[offset] = (snes_oam[offset] & 0xff00) | (data << 0);
else
snes_oam[offset] = (snes_oam[offset] & 0x00ff) | (data << 8);
}
/*************************************************
SNES CGRAM accesses:
CGRAM writes during hblank are valid. During
active display, the actual address the data
is written to varies, as the PPU itself changes
the address. Like OAM, it is not known the exact
algorithm used, but no commercial software seems
to attempt this. While byuu, in his emu, maps
those accesses to 0x01ff, because it is more
accurate to invalidate the 'expected' address
than not, MESS has issues if we don't write to
the expected address (see e.g. Tokimeki Memorial).
This is because writes should work during hblank
(so that the game can produce color fading), but
ends up not working with the conditions below.
Hence, for the moment, we only document the
solution adopted by BSNES without enabling it.
*************************************************/
static READ8_HANDLER( snes_cgram_read )
{
UINT8 res = 0;
offset &= 0x1ff;
#if 0
if (!snes_ppu.screen_disabled)
{
UINT16 v = video_screen_get_vpos(space->machine->primary_screen);
UINT16 h = video_screen_get_hpos(space->machine->primary_screen);
if (v < snes_ppu.beam.last_visible_line && h >= 128 && h < 1096)
offset = 0x1ff;
}
#endif
res = ((UINT8 *)snes_cgram)[offset];
// CGRAM palette data format is 15-bits (0,bbbbb,ggggg,rrrrr).
// Highest bit is simply ignored.
if (offset & 0x01)
res &= 0x7f;
return res;
}
static WRITE8_HANDLER( snes_cgram_write )
{
offset &= 0x1ff;
#if 0
// FIXME: this currently breaks some games (e.g. Tokimeki Memorial),
// even if it's expected to be more accurate than allowing for
// writes to the cgram address
if (!snes_ppu.screen_disabled)
{
UINT16 v = video_screen_get_vpos(space->machine->primary_screen);
UINT16 h = video_screen_get_hpos(space->machine->primary_screen);
if (v < snes_ppu.beam.last_visible_line && h >= 128 && h < 1096)
offset = 0x1ff;
}
#endif
// CGRAM palette data format is 15-bits (0,bbbbb,ggggg,rrrrr).
// Highest bit is simply ignored.
if (offset & 0x01)
data &= 0x7f;
((UINT8 *)snes_cgram)[offset] = data;
}
/*
* DR - Double read : address is read twice to return a 16bit value.
* low - This is the low byte of a 16 or 24 bit value
@ -590,6 +304,12 @@ READ8_HANDLER( snes_r_io )
snes_state *state = (snes_state *)space->machine->driver_data;
UINT8 value = 0;
// PPU accesses are from 2100 to 213f
if (offset >= INIDISP && offset < APU00)
{
return snes_ppu_read(space, offset);
}
// APU is mirrored from 2140 to 217f
if (offset >= APU00 && offset < WMDATA)
{
@ -633,146 +353,6 @@ READ8_HANDLER( snes_r_io )
/* offset is from 0x000000 */
switch (offset)
{
case OAMDATA: /* 21xy for x=0,1,2 and y=4,5,6,8,9,a returns PPU1 open bus*/
case BGMODE:
case MOSAIC:
case BG2SC:
case BG3SC:
case BG4SC:
case BG4VOFS:
case VMAIN:
case VMADDL:
case VMDATAL:
case VMDATAH:
case M7SEL:
case W34SEL:
case WOBJSEL:
case WH0:
case WH2:
case WH3:
case WBGLOG:
return snes_ppu.ppu1_open_bus;
case MPYL: /* Multiplication result (low) */
{
/* Perform 16bit * 8bit multiply */
UINT32 c = (INT16)snes_ppu.mode7.matrix_a * (INT8)(snes_ppu.mode7.matrix_b >> 8);
snes_ppu.ppu1_open_bus = c & 0xff;
return snes_ppu.ppu1_open_bus;
}
case MPYM: /* Multiplication result (mid) */
{
/* Perform 16bit * 8bit multiply */
UINT32 c = (INT16)snes_ppu.mode7.matrix_a * (INT8)(snes_ppu.mode7.matrix_b >> 8);
snes_ppu.ppu1_open_bus = (c >> 8) & 0xff;
return snes_ppu.ppu1_open_bus;
}
case MPYH: /* Multiplication result (high) */
{
/* Perform 16bit * 8bit multiply */
UINT32 c = (INT16)snes_ppu.mode7.matrix_a * (INT8)(snes_ppu.mode7.matrix_b >> 8);
snes_ppu.ppu1_open_bus = (c >> 16) & 0xff;
return snes_ppu.ppu1_open_bus;
}
case SLHV: /* Software latch for H/V counter */
snes_latch_counters(space->machine);
return snes_open_bus_r(space, 0); /* Return value is meaningless */
case ROAMDATA: /* Read data from OAM (DR) */
snes_ppu.ppu1_open_bus = snes_oam_read(space, snes_ppu.oam.address);
snes_ram[OAMDATA] = (snes_ram[OAMDATA] + 1) % 2;
if (!snes_ram[OAMDATA])
{
snes_ppu.oam.address++;
snes_ppu.oam.address &= 0x1ff;
snes_ppu.oam.first_sprite = snes_ppu.oam.priority_rotation ? (snes_ppu.oam.address >> 1) & 127 : 0;
}
return snes_ppu.ppu1_open_bus;
case RVMDATAL: /* Read data from VRAM (low) */
{
UINT32 addr = snes_get_vram_address(space->machine) << 1;
snes_ppu.ppu1_open_bus = state->vram_read_buffer & 0xff;
if (!state->vram_fgr_high)
{
state->vram_read_buffer = snes_vram_read(space, addr);
state->vram_read_buffer |= (snes_vram_read(space, addr + 1) << 8);
addr = ((snes_ram[VMADDH] << 8) | snes_ram[VMADDL]) + state->vram_fgr_increment;
snes_ram[VMADDL] = addr & 0xff;
snes_ram[VMADDH] = (addr >> 8) & 0xff;
}
return snes_ppu.ppu1_open_bus;
}
case RVMDATAH: /* Read data from VRAM (high) */
{
UINT32 addr = snes_get_vram_address(space->machine) << 1;
snes_ppu.ppu1_open_bus = (state->vram_read_buffer >> 8) & 0xff;
if (state->vram_fgr_high)
{
state->vram_read_buffer = snes_vram_read(space, addr);
state->vram_read_buffer |= (snes_vram_read(space, addr + 1) << 8);
addr = ((snes_ram[VMADDH] << 8) | snes_ram[VMADDL]) + state->vram_fgr_increment;
snes_ram[VMADDL] = addr & 0xff;
snes_ram[VMADDH] = (addr >> 8) & 0xff;
}
return snes_ppu.ppu1_open_bus;
}
case RCGDATA: /* Read data from CGRAM */
if (!(state->cgram_address & 0x01))
snes_ppu.ppu2_open_bus = snes_cgram_read(space, state->cgram_address);
else
{
snes_ppu.ppu2_open_bus &= 0x80;
snes_ppu.ppu2_open_bus |= snes_cgram_read(space, state->cgram_address) & 0x7f;
}
state->cgram_address = (state->cgram_address + 1) % (SNES_CGRAM_SIZE - 2);
return snes_ppu.ppu2_open_bus;
case OPHCT: /* Horizontal counter data by ext/soft latch */
if (state->read_ophct)
{
snes_ppu.ppu2_open_bus &= 0xfe;
snes_ppu.ppu2_open_bus |= (snes_ppu.beam.latch_horz >> 8) & 0x01;
}
else
{
snes_ppu.ppu2_open_bus = snes_ppu.beam.latch_horz & 0xff;
}
state->read_ophct ^= 1;
return snes_ppu.ppu2_open_bus;
case OPVCT: /* Vertical counter data by ext/soft latch */
if (state->read_opvct)
{
snes_ppu.ppu2_open_bus &= 0xfe;
snes_ppu.ppu2_open_bus |= (snes_ppu.beam.latch_vert >> 8) & 0x01;
}
else
{
snes_ppu.ppu2_open_bus = snes_ppu.beam.latch_vert & 0xff;
}
state->read_opvct ^= 1;
return snes_ppu.ppu2_open_bus;
case STAT77: /* PPU status flag and version number */
value = snes_ppu.stat77_flags & 0xc0; // 0x80 & 0x40 are Time Over / Range Over Sprite flags, set by the video code
// 0x20 - Master/slave mode select. Little is known about this bit. We always seem to read back 0 here.
value |= (snes_ppu.ppu1_open_bus & 0x10);
value |= (snes_ppu.ppu1_version & 0x0f);
snes_ppu.stat77_flags = value; // not sure if this is needed...
snes_ppu.ppu1_open_bus = value;
return snes_ppu.ppu1_open_bus;
case STAT78: /* PPU status flag and version number */
state->read_ophct = 0;
state->read_opvct = 0;
value = snes_ram[offset];
value |= (snes_ppu.ppu2_open_bus & 0x20);
value |= (snes_ppu.ppu2_version & 0x0f);
snes_ram[offset] = value; // not sure if this is needed...
snes_ppu.ppu2_open_bus = value;
return snes_ppu.ppu2_open_bus;
case WMDATA: /* Data to read from WRAM */
value = memory_read_byte(space, 0x7e0000 + state->wram_address++);
state->wram_address &= 0x1ffff;
@ -894,6 +474,13 @@ WRITE8_HANDLER( snes_w_io )
{
snes_state *state = (snes_state *)space->machine->driver_data;
// PPU accesses are from 2100 to 213f
if (offset >= INIDISP && offset < APU00)
{
snes_ppu_write(space, offset, data);
return;
}
// APU is mirrored from 2140 to 217f
if (offset >= APU00 && offset < WMDATA)
{
@ -945,399 +532,6 @@ WRITE8_HANDLER( snes_w_io )
/* offset is from 0x000000 */
switch (offset)
{
case INIDISP: /* Initial settings for screen */
if ((snes_ppu.screen_disabled & 0x80) && (!(data & 0x80))) //a 1->0 force blank transition causes a reset OAM address
{
memory_write_byte(space, OAMADDL, snes_ppu.oam.saved_address_low);
memory_write_byte(space, OAMADDH, snes_ppu.oam.saved_address_high);
snes_ppu.oam.first_sprite = snes_ppu.oam.priority_rotation ? (snes_ppu.oam.address >> 1) & 127 : 0;
}
snes_ppu.screen_disabled = data & 0x80;
snes_ppu.screen_brightness = (data & 0x0f) + 1;
break;
case OBSEL: /* Object size and data area designation */
snes_ppu.oam.next_charmap = (data & 0x03) << 1;
snes_ppu.oam.next_name_select = (((data & 0x18) >> 3) * 0x1000) << 1;
snes_ppu.oam.next_size = (data & 0xe0) >> 5;
break;
case OAMADDL: /* Address for accessing OAM (low) */
snes_ppu.oam.saved_address_low = data;
snes_ppu.oam.address = (snes_ppu.oam.address & 0xff00) + data;
snes_ppu.oam.first_sprite = snes_ppu.oam.priority_rotation ? (snes_ppu.oam.address >> 1) & 127 : 0;
snes_ram[OAMDATA] = 0;
break;
case OAMADDH: /* Address for accessing OAM (high) */
snes_ppu.oam.saved_address_high = data;
snes_ppu.oam.address = (snes_ppu.oam.address & 0x00ff) | ((data & 0x01) << 8);
snes_ppu.oam.priority_rotation = BIT(data, 7);
snes_ppu.oam.first_sprite = snes_ppu.oam.priority_rotation ? (snes_ppu.oam.address >> 1) & 127 : 0;
snes_ram[OAMDATA] = 0;
break;
case OAMDATA: /* Data for OAM write (DW) */
if (snes_ppu.oam.address >= 0x100)
snes_oam_write(space, snes_ppu.oam.address, data);
else
{
if (!snes_ram[OAMDATA])
snes_ppu.oam.write_latch = data;
else
{
// in this case, we not only write data to the upper byte of the word,
// but also snes_ppu.oam.write_latch to the lower byte (recall that
// snes_ram[OAMDATA] is used to select high/low byte)
snes_oam_write(space, snes_ppu.oam.address, data);
snes_ram[OAMDATA] = 0;
snes_oam_write(space, snes_ppu.oam.address, snes_ppu.oam.write_latch);
snes_ram[OAMDATA] = 1;
}
}
snes_ram[OAMDATA] = (snes_ram[OAMDATA] + 1) % 2;
if (!snes_ram[OAMDATA])
{
snes_ppu.oam.address++;
snes_ppu.oam.address &= 0x1ff;
snes_ppu.oam.first_sprite = snes_ppu.oam.priority_rotation ? (snes_ppu.oam.address >> 1) & 127 : 0;
}
return;
case BGMODE: /* BG mode and character size settings */
snes_ppu.mode = data & 0x07;
snes_dynamic_res_change(space->machine);
snes_ppu.bg3_priority_bit = BIT(data, 3);
snes_ppu.layer[SNES_BG1].tile_size = BIT(data, 4);
snes_ppu.layer[SNES_BG2].tile_size = BIT(data, 5);
snes_ppu.layer[SNES_BG3].tile_size = BIT(data, 6);
snes_ppu.layer[SNES_BG4].tile_size = BIT(data, 7);
snes_ppu.update_offsets = 1;
break;
case MOSAIC: /* Size and screen designation for mosaic */
snes_ppu.mosaic_size = (data & 0xf0) >> 4;
snes_ppu.layer[SNES_BG1].mosaic_enabled = BIT(data, 0);
snes_ppu.layer[SNES_BG2].mosaic_enabled = BIT(data, 1);
snes_ppu.layer[SNES_BG3].mosaic_enabled = BIT(data, 2);
snes_ppu.layer[SNES_BG4].mosaic_enabled = BIT(data, 3);
break;
case BG1SC: /* Address for storing SC data BG1 SC size designation */
case BG2SC: /* Address for storing SC data BG2 SC size designation */
case BG3SC: /* Address for storing SC data BG3 SC size designation */
case BG4SC: /* Address for storing SC data BG4 SC size designation */
snes_ppu.layer[offset - BG1SC].tilemap = data & 0xfc;
snes_ppu.layer[offset - BG1SC].tilemap_size = data & 0x3;
break;
case BG12NBA: /* Address for BG 1 and 2 character data */
snes_ppu.layer[SNES_BG1].charmap = (data & 0x0f);
snes_ppu.layer[SNES_BG2].charmap = (data & 0xf0) >> 4;
break;
case BG34NBA: /* Address for BG 3 and 4 character data */
snes_ppu.layer[SNES_BG3].charmap = (data & 0x0f);
snes_ppu.layer[SNES_BG4].charmap = (data & 0xf0) >> 4;
break;
// Anomie says "H Current = (Byte<<8) | (Prev&~7) | ((Current>>8)&7); V Current = (Current<<8) | Prev;" and Prev is shared by all scrolls but in Mode 7!
case BG1HOFS: /* BG1 - horizontal scroll (DW) */
/* In Mode 0->6 we use ppu_last_scroll as Prev */
snes_ppu.layer[SNES_BG1].hoffs = (data << 8) | (snes_ppu.ppu_last_scroll & ~7) | ((snes_ppu.layer[SNES_BG1].hoffs >> 8) & 7);
snes_ppu.ppu_last_scroll = data;
/* In Mode 7 we use mode7_last_scroll as Prev */
snes_ppu.mode7.hor_offset = (data << 8) | (snes_ppu.mode7_last_scroll & ~7) | ((snes_ppu.mode7.hor_offset >> 8) & 7);
snes_ppu.mode7_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case BG1VOFS: /* BG1 - vertical scroll (DW) */
/* In Mode 0->6 we use ppu_last_scroll as Prev */
snes_ppu.layer[SNES_BG1].voffs = (data << 8) | snes_ppu.ppu_last_scroll;
snes_ppu.ppu_last_scroll = data;
/* In Mode 7 we use mode7_last_scroll as Prev */
snes_ppu.mode7.ver_offset = (data << 8) | snes_ppu.mode7_last_scroll;
snes_ppu.mode7_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case BG2HOFS: /* BG2 - horizontal scroll (DW) */
snes_ppu.layer[SNES_BG2].hoffs = (data << 8) | (snes_ppu.ppu_last_scroll & ~7) | ((snes_ppu.layer[SNES_BG2].hoffs >> 8) & 7);
snes_ppu.ppu_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case BG2VOFS: /* BG2 - vertical scroll (DW) */
snes_ppu.layer[SNES_BG2].voffs = (data << 8) | (snes_ppu.ppu_last_scroll);
snes_ppu.ppu_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case BG3HOFS: /* BG3 - horizontal scroll (DW) */
snes_ppu.layer[SNES_BG3].hoffs = (data << 8) | (snes_ppu.ppu_last_scroll & ~7) | ((snes_ppu.layer[SNES_BG3].hoffs >> 8) & 7);
snes_ppu.ppu_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case BG3VOFS: /* BG3 - vertical scroll (DW) */
snes_ppu.layer[SNES_BG3].voffs = (data << 8) | (snes_ppu.ppu_last_scroll);
snes_ppu.ppu_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case BG4HOFS: /* BG4 - horizontal scroll (DW) */
snes_ppu.layer[SNES_BG4].hoffs = (data << 8) | (snes_ppu.ppu_last_scroll & ~7) | ((snes_ppu.layer[SNES_BG4].hoffs >> 8) & 7);
snes_ppu.ppu_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case BG4VOFS: /* BG4 - vertical scroll (DW) */
snes_ppu.layer[SNES_BG4].voffs = (data << 8) | (snes_ppu.ppu_last_scroll);
snes_ppu.ppu_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case VMAIN: /* VRAM address increment value designation */
state->vram_fgr_high = (data & 0x80);
state->vram_fgr_increment = vram_fgr_inctab[data & 3];
if (data & 0xc)
{
int md = (data & 0xc) >> 2;
state->vram_fgr_count = vram_fgr_inccnts[md]; // 0x20, 0x40, 0x80
state->vram_fgr_mask = (state->vram_fgr_count * 8) - 1; // 0xff, 0x1ff, 0x2ff
state->vram_fgr_shift = vram_fgr_shiftab[md]; // 5, 6, 7
}
else
{
state->vram_fgr_count = 0;
}
// printf("VMAIN: high %x inc %x count %x mask %x shift %x\n", state->vram_fgr_high, state->vram_fgr_increment, state->vram_fgr_count, state->vram_fgr_mask, state->vram_fgr_shift);
break;
case VMADDL: /* Address for VRAM read/write (low) */
case VMADDH: /* Address for VRAM read/write (high) */
{
UINT32 addr;
snes_ram[offset] = data;
addr = snes_get_vram_address(space->machine) << 1;
state->vram_read_buffer = snes_vram_read(space, addr);
state->vram_read_buffer |= (snes_vram_read(space, addr + 1) << 8);
}
break;
case VMDATAL: /* 2118: Data for VRAM write (low) */
{
UINT32 addr = snes_get_vram_address(space->machine) << 1;
snes_vram_write(space, addr, data);
if (!state->vram_fgr_high)
{
addr = ((snes_ram[VMADDH] << 8) | snes_ram[VMADDL]) + state->vram_fgr_increment;
snes_ram[VMADDL] = addr & 0xff;
snes_ram[VMADDH] = (addr >> 8) & 0xff;
}
}
return;
case VMDATAH: /* 2119: Data for VRAM write (high) */
{
UINT32 addr = snes_get_vram_address(space->machine) << 1;
snes_vram_write(space, addr + 1, data);
if (state->vram_fgr_high)
{
addr = ((snes_ram[VMADDH] << 8) | snes_ram[VMADDL]) + state->vram_fgr_increment;
snes_ram[VMADDL] = addr & 0xff;
snes_ram[VMADDH] = (addr >> 8) & 0xff;
}
}
return;
case M7SEL: /* Mode 7 initial settings */
snes_ppu.mode7.repeat = (data >> 6) & 3;
snes_ppu.mode7.vflip = BIT(data, 1);
snes_ppu.mode7.hflip = BIT(data, 0);
break;
/* As per Anomie's doc: Reg = (Current<<8) | Prev; and there is only one Prev, shared by these matrix regs and Mode 7 scroll regs */
case M7A: /* Mode 7 COS angle/x expansion (DW) */
snes_ppu.mode7.matrix_a = snes_ppu.mode7_last_scroll + (data << 8);
snes_ppu.mode7_last_scroll = data;
break;
case M7B: /* Mode 7 SIN angle/ x expansion (DW) */
snes_ppu.mode7.matrix_b = snes_ppu.mode7_last_scroll + (data << 8);
snes_ppu.mode7_last_scroll = data;
break;
case M7C: /* Mode 7 SIN angle/y expansion (DW) */
snes_ppu.mode7.matrix_c = snes_ppu.mode7_last_scroll + (data << 8);
snes_ppu.mode7_last_scroll = data;
break;
case M7D: /* Mode 7 COS angle/y expansion (DW) */
snes_ppu.mode7.matrix_d = snes_ppu.mode7_last_scroll + (data << 8);
snes_ppu.mode7_last_scroll = data;
break;
case M7X: /* Mode 7 x center position (DW) */
snes_ppu.mode7.origin_x = snes_ppu.mode7_last_scroll + (data << 8);
snes_ppu.mode7_last_scroll = data;
break;
case M7Y: /* Mode 7 y center position (DW) */
snes_ppu.mode7.origin_y = snes_ppu.mode7_last_scroll + (data << 8);
snes_ppu.mode7_last_scroll = data;
break;
case CGADD: /* Initial address for colour RAM writing */
/* CGRAM is 16-bit, but when reading/writing we treat it as 8-bit, so we need to double the address */
state->cgram_address = data << 1;
break;
case CGDATA: /* Data for colour RAM */
snes_cgram_write(space, state->cgram_address, data);
state->cgram_address = (state->cgram_address + 1) % (SNES_CGRAM_SIZE - 2);
break;
case W12SEL: /* Window mask settings for BG1-2 */
if (data != snes_ram[offset])
{
snes_ppu.layer[SNES_BG1].window1_invert = BIT(data, 0);
snes_ppu.layer[SNES_BG1].window1_enabled = BIT(data, 1);
snes_ppu.layer[SNES_BG1].window2_invert = BIT(data, 2);
snes_ppu.layer[SNES_BG1].window2_enabled = BIT(data, 3);
snes_ppu.layer[SNES_BG2].window1_invert = BIT(data, 4);
snes_ppu.layer[SNES_BG2].window1_enabled = BIT(data, 5);
snes_ppu.layer[SNES_BG2].window2_invert = BIT(data, 6);
snes_ppu.layer[SNES_BG2].window2_enabled = BIT(data, 7);
snes_ppu.update_windows = 1;
}
break;
case W34SEL: /* Window mask settings for BG3-4 */
if (data != snes_ram[offset])
{
snes_ppu.layer[SNES_BG3].window1_invert = BIT(data, 0);
snes_ppu.layer[SNES_BG3].window1_enabled = BIT(data, 1);
snes_ppu.layer[SNES_BG3].window2_invert = BIT(data, 2);
snes_ppu.layer[SNES_BG3].window2_enabled = BIT(data, 3);
snes_ppu.layer[SNES_BG4].window1_invert = BIT(data, 4);
snes_ppu.layer[SNES_BG4].window1_enabled = BIT(data, 5);
snes_ppu.layer[SNES_BG4].window2_invert = BIT(data, 6);
snes_ppu.layer[SNES_BG4].window2_enabled = BIT(data, 7);
snes_ppu.update_windows = 1;
}
break;
case WOBJSEL: /* Window mask settings for objects */
if (data != snes_ram[offset])
{
snes_ppu.layer[SNES_OAM].window1_invert = BIT(data, 0);
snes_ppu.layer[SNES_OAM].window1_enabled = BIT(data, 1);
snes_ppu.layer[SNES_OAM].window2_invert = BIT(data, 2);
snes_ppu.layer[SNES_OAM].window2_enabled = BIT(data, 3);
snes_ppu.layer[SNES_COLOR].window1_invert = BIT(data, 4);
snes_ppu.layer[SNES_COLOR].window1_enabled = BIT(data, 5);
snes_ppu.layer[SNES_COLOR].window2_invert = BIT(data, 6);
snes_ppu.layer[SNES_COLOR].window2_enabled = BIT(data, 7);
snes_ppu.update_windows = 1;
}
break;
case WH0: /* Window 1 left position */
if (data != snes_ram[offset])
{
snes_ppu.window1_left = data;
snes_ppu.update_windows = 1;
}
break;
case WH1: /* Window 1 right position */
if (data != snes_ram[offset])
{
snes_ppu.window1_right = data;
snes_ppu.update_windows = 1;
}
break;
case WH2: /* Window 2 left position */
if (data != snes_ram[offset])
{
snes_ppu.window2_left = data;
snes_ppu.update_windows = 1;
}
break;
case WH3: /* Window 2 right position */
if (data != snes_ram[offset])
{
snes_ppu.window2_right = data;
snes_ppu.update_windows = 1;
}
break;
case WBGLOG: /* Window mask logic for BG's */
if (data != snes_ram[offset])
{
snes_ppu.layer[SNES_BG1].wlog_mask = data & 0x03;
snes_ppu.layer[SNES_BG2].wlog_mask = (data & 0x0c) >> 2;
snes_ppu.layer[SNES_BG3].wlog_mask = (data & 0x30) >> 4;
snes_ppu.layer[SNES_BG4].wlog_mask = (data & 0xc0) >> 6;
snes_ppu.update_windows = 1;
}
break;
case WOBJLOG: /* Window mask logic for objects */
if (data != snes_ram[offset])
{
snes_ppu.layer[SNES_OAM].wlog_mask = data & 0x03;
snes_ppu.layer[SNES_COLOR].wlog_mask = (data & 0x0c) >> 2;
snes_ppu.update_windows = 1;
}
break;
case TM: /* Main screen designation */
snes_ppu.layer[SNES_BG1].main_bg_enabled = BIT(data, 0);
snes_ppu.layer[SNES_BG2].main_bg_enabled = BIT(data, 1);
snes_ppu.layer[SNES_BG3].main_bg_enabled = BIT(data, 2);
snes_ppu.layer[SNES_BG4].main_bg_enabled = BIT(data, 3);
snes_ppu.layer[SNES_OAM].main_bg_enabled = BIT(data, 4);
break;
case TS: /* Subscreen designation */
snes_ppu.layer[SNES_BG1].sub_bg_enabled = BIT(data, 0);
snes_ppu.layer[SNES_BG2].sub_bg_enabled = BIT(data, 1);
snes_ppu.layer[SNES_BG3].sub_bg_enabled = BIT(data, 2);
snes_ppu.layer[SNES_BG4].sub_bg_enabled = BIT(data, 3);
snes_ppu.layer[SNES_OAM].sub_bg_enabled = BIT(data, 4);
break;
case TMW: /* Window mask for main screen designation */
snes_ppu.layer[SNES_BG1].main_window_enabled = BIT(data, 0);
snes_ppu.layer[SNES_BG2].main_window_enabled = BIT(data, 1);
snes_ppu.layer[SNES_BG3].main_window_enabled = BIT(data, 2);
snes_ppu.layer[SNES_BG4].main_window_enabled = BIT(data, 3);
snes_ppu.layer[SNES_OAM].main_window_enabled = BIT(data, 4);
break;
case TSW: /* Window mask for subscreen designation */
snes_ppu.layer[SNES_BG1].sub_window_enabled = BIT(data, 0);
snes_ppu.layer[SNES_BG2].sub_window_enabled = BIT(data, 1);
snes_ppu.layer[SNES_BG3].sub_window_enabled = BIT(data, 2);
snes_ppu.layer[SNES_BG4].sub_window_enabled = BIT(data, 3);
snes_ppu.layer[SNES_OAM].sub_window_enabled = BIT(data, 4);
break;
case CGWSEL: /* Initial settings for Fixed colour addition or screen addition */
snes_ppu.clip_to_black = (data >> 6) & 0x03;
snes_ppu.prevent_color_math = (data >> 4) & 0x03;
snes_ppu.sub_add_mode = BIT(data, 1);
snes_ppu.direct_color = BIT(data, 0);
#ifdef SNES_DBG_REG_W
if ((data & 0x2) != (snes_ram[CGWSEL] & 0x2))
mame_printf_debug( "Add/Sub Layer: %s\n", ((data & 0x2) >> 1) ? "Subscreen" : "Fixed colour" );
#endif
break;
case CGADSUB: /* Addition/Subtraction designation for each screen */
snes_ppu.color_modes = data & 0xc0;
snes_ppu.layer[SNES_BG1].color_math = BIT(data, 0);
snes_ppu.layer[SNES_BG2].color_math = BIT(data, 1);
snes_ppu.layer[SNES_BG3].color_math = BIT(data, 2);
snes_ppu.layer[SNES_BG4].color_math = BIT(data, 3);
snes_ppu.layer[SNES_OAM].color_math = BIT(data, 4);
snes_ppu.layer[SNES_COLOR].color_math = BIT(data, 5);
break;
case COLDATA: /* Fixed colour data for fixed colour addition/subtraction */
{
/* Store it in the extra space we made in the CGRAM. It doesn't really go there, but it's as good a place as any. */
UINT8 r, g, b;
/* Get existing value. */
r = snes_cgram[FIXED_COLOUR] & 0x1f;
g = (snes_cgram[FIXED_COLOUR] & 0x3e0) >> 5;
b = (snes_cgram[FIXED_COLOUR] & 0x7c00) >> 10;
/* Set new value */
if (data & 0x20)
r = data & 0x1f;
if (data & 0x40)
g = data & 0x1f;
if (data & 0x80)
b = data & 0x1f;
snes_cgram[FIXED_COLOUR] = (r | (g << 5) | (b << 10));
} break;
case SETINI: /* Screen mode/video select */
snes_ppu.interlace = (data & 0x01) ? 2 : 1;
snes_ppu.obj_interlace = (data & 0x02) ? 2 : 1;
snes_ppu.beam.last_visible_line = (data & 0x04) ? 240 : 225;
snes_ppu.pseudo_hires = BIT(data, 3);
snes_ppu.mode7.extbg = BIT(data, 6);
snes_dynamic_res_change(space->machine);
#ifdef SNES_DBG_REG_W
if ((data & 0x8) != (snes_ram[SETINI] & 0x8))
mame_printf_debug( "Pseudo 512 mode: %s\n", (data & 0x8) ? "on" : "off" );
#endif
break;
case WMDATA: /* Data to write to WRAM */
memory_write_byte(space, 0x7e0000 + state->wram_address++, data );
state->wram_address &= 0x1ffff;
@ -1681,7 +875,7 @@ READ8_HANDLER( snes_r_bank2 )
}
else if ((snes_cart.mode == SNES_MODE_21) && (snes_cart.sram > 0))
{
int mask = ((snes_cart.sram * 1024) - 1); /* Limit SRAM size to what's actually present */
int mask = (snes_cart.sram - 1); /* Limit SRAM size to what's actually present */
offset -= 0x6000;
value = snes_ram[0x306000 + (offset & mask)];
}
@ -1797,7 +991,7 @@ READ8_HANDLER( snes_r_bank5 )
{
if (snes_cart.sram > 0)
{
int mask = ((snes_cart.sram * 1024) - 1) | 0xff0000; /* Limit SRAM size to what's actually present */
int mask = (snes_cart.sram - 1) | 0xff0000; /* Limit SRAM size to what's actually present */
value = snes_ram[0x700000 + (offset & mask)];
}
else
@ -1832,7 +1026,7 @@ READ8_HANDLER( snes_r_bank6 )
value = memory_read_byte(space, offset);
else if ((offset >= 0x300000) && (snes_cart.sram > 0))
{
int mask = ((snes_cart.sram * 1024) - 1); /* Limit SRAM size to what's actually present */
int mask = (snes_cart.sram - 1); /* Limit SRAM size to what's actually present */
offset -= 0x6000;
value = snes_ram[0x806000 + (offset & mask)]; /* SRAM */
}
@ -1974,7 +1168,7 @@ WRITE8_HANDLER( snes_w_bank2 )
}
else if ((snes_cart.mode == SNES_MODE_21) && (snes_cart.sram > 0))
{
int mask = ((snes_cart.sram * 1024) - 1); /* Limit SRAM size to what's actually present */
int mask = (snes_cart.sram - 1); /* Limit SRAM size to what's actually present */
offset -= 0x6000;
snes_ram[0x306000 + (offset & mask)] = data;
}
@ -2032,7 +1226,7 @@ WRITE8_HANDLER( snes_w_bank5 )
{
if (snes_cart.sram > 0)
{
int mask = ((snes_cart.sram * 1024) - 1) | 0xff0000; /* Limit SRAM size to what's actually present */
int mask = (snes_cart.sram - 1) | 0xff0000; /* Limit SRAM size to what's actually present */
snes_ram[0x700000 + (offset & mask)] = data;
}
else
@ -2062,7 +1256,7 @@ WRITE8_HANDLER( snes_w_bank6 )
memory_write_byte(space, offset, data);
else if ((offset >= 0x300000) && (snes_cart.sram > 0))
{
int mask = ((snes_cart.sram * 1024) - 1); /* Limit SRAM size to what's actually present */
int mask = (snes_cart.sram - 1); /* Limit SRAM size to what's actually present */
offset -= 0x6000;
snes_ram[0xb06000 + (offset & mask)] = data;
}
@ -2475,7 +1669,7 @@ DRIVER_INIT( snes )
snes_cart.sram = snes_r_bank1(space, 0x00ffd8);
if (snes_cart.sram > 0)
{
snes_cart.sram = ((1 << (snes_cart.sram + 3)) / 8);
snes_cart.sram = (1024 << snes_cart.sram);
if (snes_cart.sram > snes_cart.sram_max)
snes_cart.sram = snes_cart.sram_max;
}
@ -2534,7 +1728,7 @@ DRIVER_INIT( snes_hirom )
snes_cart.sram = snes_r_bank1(space, 0x00ffd8);
if (snes_cart.sram > 0)
{
snes_cart.sram = ((1 << (snes_cart.sram + 3)) / 8);
snes_cart.sram = (1024 << snes_cart.sram);
if (snes_cart.sram > snes_cart.sram_max)
snes_cart.sram = snes_cart.sram_max;
}

View File

@ -1669,6 +1669,850 @@ VIDEO_UPDATE( snes )
}
/* CPU <-> PPU comms */
// full graphic variables
static const UINT16 vram_fgr_inctab[4] = { 1, 32, 128, 128 };
static const UINT16 vram_fgr_inccnts[4] = { 0, 32, 64, 128 };
static const UINT16 vram_fgr_shiftab[4] = { 0, 5, 6, 7 };
// utility function - latches the H/V counters. Used by IRQ, writes to WRIO, etc.
void snes_latch_counters( running_machine *machine )
{
snes_state *state = (snes_state *)machine->driver_data;
snes_ppu.beam.current_horz = video_screen_get_hpos(machine->primary_screen) / state->htmult;
snes_ppu.beam.latch_vert = video_screen_get_vpos(machine->primary_screen);
snes_ppu.beam.latch_horz = snes_ppu.beam.current_horz;
snes_ram[STAT78] |= 0x40; // indicate we latched
// state->read_ophct = state->read_opvct = 0; // clear read flags - 2009-08: I think we must clear these when STAT78 is read...
// printf("latched @ H %d V %d\n", snes_ppu.beam.latch_horz, snes_ppu.beam.latch_vert);
}
static void snes_dynamic_res_change( running_machine *machine )
{
snes_state *state = (snes_state *)machine->driver_data;
rectangle visarea = *video_screen_get_visible_area(machine->primary_screen);
attoseconds_t refresh;
visarea.min_x = visarea.min_y = 0;
visarea.max_y = snes_ppu.beam.last_visible_line * snes_ppu.interlace - 1;
visarea.max_x = (SNES_SCR_WIDTH * 2) - 1;
// fixme: should compensate for SNES_DBG_video
if (snes_ppu.mode == 5 || snes_ppu.mode == 6 || snes_ppu.pseudo_hires)
state->htmult = 2;
else
state->htmult = 1;
/* FIXME: does the timing changes when the gfx mode is equal to 5 or 6? */
if ((snes_ram[STAT78] & 0x10) == SNES_NTSC)
refresh = HZ_TO_ATTOSECONDS(DOTCLK_NTSC) * SNES_HTOTAL * SNES_VTOTAL_NTSC;
else
refresh = HZ_TO_ATTOSECONDS(DOTCLK_PAL) * SNES_HTOTAL * SNES_VTOTAL_PAL;
if ((snes_ram[STAT78] & 0x10) == SNES_NTSC)
video_screen_configure(machine->primary_screen, SNES_HTOTAL * 2, SNES_VTOTAL_NTSC * snes_ppu.interlace, &visarea, refresh);
else
video_screen_configure(machine->primary_screen, SNES_HTOTAL * 2, SNES_VTOTAL_PAL * snes_ppu.interlace, &visarea, refresh);
}
/*************************************************
SNES VRAM accesses:
VRAM accesses during active display are invalid.
Unlike OAM and CGRAM, they will not be written
anywhere at all. Thanks to byuu's researches,
the ranges where writes are invalid have been
validated on hardware, as has the edge case where
the S-CPU open bus can be written if the write
occurs during the very last clock cycle of
vblank.
Our implementation could be not 100% accurate
when interlace is active.
*************************************************/
INLINE UINT32 snes_get_vram_address( running_machine *machine )
{
snes_state *state = (snes_state *)machine->driver_data;
UINT32 addr = (snes_ram[VMADDH] << 8) | snes_ram[VMADDL];
if (state->vram_fgr_count)
{
UINT32 rem = addr & state->vram_fgr_mask;
UINT32 faddr = (addr & ~state->vram_fgr_mask) + (rem >> state->vram_fgr_shift) + ((rem & (state->vram_fgr_count - 1)) << 3);
return faddr;
}
return addr;
}
static READ8_HANDLER( snes_vram_read )
{
UINT8 res = 0;
offset &= 0x1ffff;
if (snes_ppu.screen_disabled)
res = snes_vram[offset];
else
{
UINT16 v = video_screen_get_vpos(space->machine->primary_screen);
UINT16 h = video_screen_get_hpos(space->machine->primary_screen);
UINT16 ls = (((snes_ram[STAT78] & 0x10) == SNES_NTSC ? 525 : 625) >> 1) - 1;
if (snes_ppu.interlace == 2)
ls++;
if (v == ls && h == 1362)
res = 0;
else if (v < snes_ppu.beam.last_visible_line - 1)
res = 0;
else if (v == snes_ppu.beam.last_visible_line - 1)
{
if (h == 1362)
res = snes_vram[offset];
else
res = 0;
}
else
res = snes_vram[offset];
}
return res;
}
static WRITE8_HANDLER( snes_vram_write )
{
offset &= 0x1ffff;
if (snes_ppu.screen_disabled)
snes_vram[offset] = data;
else
{
UINT16 v = video_screen_get_vpos(space->machine->primary_screen);
UINT16 h = video_screen_get_hpos(space->machine->primary_screen);
if (v == 0)
{
if (h <= 4)
snes_vram[offset] = data;
else if (h == 6)
snes_vram[offset] = snes_open_bus_r(space, 0);
else
{
//no write
}
}
else if (v < snes_ppu.beam.last_visible_line)
{
//no write
}
else if (v == snes_ppu.beam.last_visible_line)
{
if (h <= 4)
{
//no write
}
else
snes_vram[offset] = data;
}
else
snes_vram[offset] = data;
}
}
/*************************************************
SNES OAM accesses:
OAM accesses during active display are allowed.
The actual address varies during rendering, as the
PPU reads in data itself for processing.
Unfortunately, no one has been able (yet) to
determine how this works. The only known game to
actually access OAM during active display is
Uniracers and it expects accesses to map to
offset 0x0218. Hence, following byuu's choice
we rerouted OAM accesses during active display
to 0x0218 (0x010c in our snes_oam).
This is a hack, but it is more accurate than
writing to the 'expected' address set by
$2102,$2103.
Notice that, since snes_ram[OAMDATA] is never
read/written directly, we use it as an index
to choose the high/low byte of the snes_oam word.
*************************************************/
static READ8_HANDLER( snes_oam_read )
{
offset &= 0x1ff;
if (offset & 0x100)
offset &= 0x10f;
if (!snes_ppu.screen_disabled)
{
UINT16 v = video_screen_get_vpos(space->machine->primary_screen);
if (v < snes_ppu.beam.last_visible_line)
offset = 0x010c;
}
return (snes_oam[offset] >> (snes_ram[OAMDATA] << 3)) & 0xff;;
}
static WRITE8_HANDLER( snes_oam_write )
{
offset &= 0x1ff;
if (offset & 0x100)
offset &= 0x10f;
if (!snes_ppu.screen_disabled)
{
UINT16 v = video_screen_get_vpos(space->machine->primary_screen);
if (v < snes_ppu.beam.last_visible_line)
offset = 0x010c;
}
if (!(snes_ram[OAMDATA]))
snes_oam[offset] = (snes_oam[offset] & 0xff00) | (data << 0);
else
snes_oam[offset] = (snes_oam[offset] & 0x00ff) | (data << 8);
}
/*************************************************
SNES CGRAM accesses:
CGRAM writes during hblank are valid. During
active display, the actual address the data
is written to varies, as the PPU itself changes
the address. Like OAM, it is not known the exact
algorithm used, but no commercial software seems
to attempt this. While byuu, in his emu, maps
those accesses to 0x01ff, because it is more
accurate to invalidate the 'expected' address
than not, MESS has issues if we don't write to
the expected address (see e.g. Tokimeki Memorial).
This is because writes should work during hblank
(so that the game can produce color fading), but
ends up not working with the conditions below.
Hence, for the moment, we only document the
solution adopted by BSNES without enabling it.
*************************************************/
static READ8_HANDLER( snes_cgram_read )
{
UINT8 res = 0;
offset &= 0x1ff;
#if 0
if (!snes_ppu.screen_disabled)
{
UINT16 v = video_screen_get_vpos(space->machine->primary_screen);
UINT16 h = video_screen_get_hpos(space->machine->primary_screen);
if (v < snes_ppu.beam.last_visible_line && h >= 128 && h < 1096)
offset = 0x1ff;
}
#endif
res = ((UINT8 *)snes_cgram)[offset];
// CGRAM palette data format is 15-bits (0,bbbbb,ggggg,rrrrr).
// Highest bit is simply ignored.
if (offset & 0x01)
res &= 0x7f;
return res;
}
static WRITE8_HANDLER( snes_cgram_write )
{
offset &= 0x1ff;
#if 0
// FIXME: this currently breaks some games (e.g. Tokimeki Memorial),
// even if it's expected to be more accurate than allowing for
// writes to the cgram address
if (!snes_ppu.screen_disabled)
{
UINT16 v = video_screen_get_vpos(space->machine->primary_screen);
UINT16 h = video_screen_get_hpos(space->machine->primary_screen);
if (v < snes_ppu.beam.last_visible_line && h >= 128 && h < 1096)
offset = 0x1ff;
}
#endif
// CGRAM palette data format is 15-bits (0,bbbbb,ggggg,rrrrr).
// Highest bit is simply ignored.
if (offset & 0x01)
data &= 0x7f;
((UINT8 *)snes_cgram)[offset] = data;
}
READ8_HANDLER( snes_ppu_read )
{
snes_state *state = (snes_state *)space->machine->driver_data;
UINT8 value;
switch (offset)
{
case OAMDATA: /* 21xy for x=0,1,2 and y=4,5,6,8,9,a returns PPU1 open bus*/
case BGMODE:
case MOSAIC:
case BG2SC:
case BG3SC:
case BG4SC:
case BG4VOFS:
case VMAIN:
case VMADDL:
case VMDATAL:
case VMDATAH:
case M7SEL:
case W34SEL:
case WOBJSEL:
case WH0:
case WH2:
case WH3:
case WBGLOG:
return snes_ppu.ppu1_open_bus;
case MPYL: /* Multiplication result (low) */
{
/* Perform 16bit * 8bit multiply */
UINT32 c = (INT16)snes_ppu.mode7.matrix_a * (INT8)(snes_ppu.mode7.matrix_b >> 8);
snes_ppu.ppu1_open_bus = c & 0xff;
return snes_ppu.ppu1_open_bus;
}
case MPYM: /* Multiplication result (mid) */
{
/* Perform 16bit * 8bit multiply */
UINT32 c = (INT16)snes_ppu.mode7.matrix_a * (INT8)(snes_ppu.mode7.matrix_b >> 8);
snes_ppu.ppu1_open_bus = (c >> 8) & 0xff;
return snes_ppu.ppu1_open_bus;
}
case MPYH: /* Multiplication result (high) */
{
/* Perform 16bit * 8bit multiply */
UINT32 c = (INT16)snes_ppu.mode7.matrix_a * (INT8)(snes_ppu.mode7.matrix_b >> 8);
snes_ppu.ppu1_open_bus = (c >> 16) & 0xff;
return snes_ppu.ppu1_open_bus;
}
case SLHV: /* Software latch for H/V counter */
snes_latch_counters(space->machine);
return snes_open_bus_r(space, 0); /* Return value is meaningless */
case ROAMDATA: /* Read data from OAM (DR) */
snes_ppu.ppu1_open_bus = snes_oam_read(space, snes_ppu.oam.address);
snes_ram[OAMDATA] = (snes_ram[OAMDATA] + 1) % 2;
if (!snes_ram[OAMDATA])
{
snes_ppu.oam.address++;
snes_ppu.oam.address &= 0x1ff;
snes_ppu.oam.first_sprite = snes_ppu.oam.priority_rotation ? (snes_ppu.oam.address >> 1) & 127 : 0;
}
return snes_ppu.ppu1_open_bus;
case RVMDATAL: /* Read data from VRAM (low) */
{
UINT32 addr = snes_get_vram_address(space->machine) << 1;
snes_ppu.ppu1_open_bus = state->vram_read_buffer & 0xff;
if (!state->vram_fgr_high)
{
state->vram_read_buffer = snes_vram_read(space, addr);
state->vram_read_buffer |= (snes_vram_read(space, addr + 1) << 8);
addr = ((snes_ram[VMADDH] << 8) | snes_ram[VMADDL]) + state->vram_fgr_increment;
snes_ram[VMADDL] = addr & 0xff;
snes_ram[VMADDH] = (addr >> 8) & 0xff;
}
return snes_ppu.ppu1_open_bus;
}
case RVMDATAH: /* Read data from VRAM (high) */
{
UINT32 addr = snes_get_vram_address(space->machine) << 1;
snes_ppu.ppu1_open_bus = (state->vram_read_buffer >> 8) & 0xff;
if (state->vram_fgr_high)
{
state->vram_read_buffer = snes_vram_read(space, addr);
state->vram_read_buffer |= (snes_vram_read(space, addr + 1) << 8);
addr = ((snes_ram[VMADDH] << 8) | snes_ram[VMADDL]) + state->vram_fgr_increment;
snes_ram[VMADDL] = addr & 0xff;
snes_ram[VMADDH] = (addr >> 8) & 0xff;
}
return snes_ppu.ppu1_open_bus;
}
case RCGDATA: /* Read data from CGRAM */
if (!(state->cgram_address & 0x01))
snes_ppu.ppu2_open_bus = snes_cgram_read(space, state->cgram_address);
else
{
snes_ppu.ppu2_open_bus &= 0x80;
snes_ppu.ppu2_open_bus |= snes_cgram_read(space, state->cgram_address) & 0x7f;
}
state->cgram_address = (state->cgram_address + 1) % (SNES_CGRAM_SIZE - 2);
return snes_ppu.ppu2_open_bus;
case OPHCT: /* Horizontal counter data by ext/soft latch */
if (state->read_ophct)
{
snes_ppu.ppu2_open_bus &= 0xfe;
snes_ppu.ppu2_open_bus |= (snes_ppu.beam.latch_horz >> 8) & 0x01;
}
else
{
snes_ppu.ppu2_open_bus = snes_ppu.beam.latch_horz & 0xff;
}
state->read_ophct ^= 1;
return snes_ppu.ppu2_open_bus;
case OPVCT: /* Vertical counter data by ext/soft latch */
if (state->read_opvct)
{
snes_ppu.ppu2_open_bus &= 0xfe;
snes_ppu.ppu2_open_bus |= (snes_ppu.beam.latch_vert >> 8) & 0x01;
}
else
{
snes_ppu.ppu2_open_bus = snes_ppu.beam.latch_vert & 0xff;
}
state->read_opvct ^= 1;
return snes_ppu.ppu2_open_bus;
case STAT77: /* PPU status flag and version number */
value = snes_ppu.stat77_flags & 0xc0; // 0x80 & 0x40 are Time Over / Range Over Sprite flags, set by the video code
// 0x20 - Master/slave mode select. Little is known about this bit. We always seem to read back 0 here.
value |= (snes_ppu.ppu1_open_bus & 0x10);
value |= (snes_ppu.ppu1_version & 0x0f);
snes_ppu.stat77_flags = value; // not sure if this is needed...
snes_ppu.ppu1_open_bus = value;
return snes_ppu.ppu1_open_bus;
case STAT78: /* PPU status flag and version number */
state->read_ophct = 0;
state->read_opvct = 0;
value = snes_ram[offset];
value |= (snes_ppu.ppu2_open_bus & 0x20);
value |= (snes_ppu.ppu2_version & 0x0f);
snes_ram[offset] = value; // not sure if this is needed...
snes_ppu.ppu2_open_bus = value;
return snes_ppu.ppu2_open_bus;
}
return snes_ppu.ppu1_open_bus;
}
WRITE8_HANDLER( snes_ppu_write )
{
snes_state *state = (snes_state *)space->machine->driver_data;
switch (offset)
{
case INIDISP: /* Initial settings for screen */
if ((snes_ppu.screen_disabled & 0x80) && (!(data & 0x80))) //a 1->0 force blank transition causes a reset OAM address
{
memory_write_byte(space, OAMADDL, snes_ppu.oam.saved_address_low);
memory_write_byte(space, OAMADDH, snes_ppu.oam.saved_address_high);
snes_ppu.oam.first_sprite = snes_ppu.oam.priority_rotation ? (snes_ppu.oam.address >> 1) & 127 : 0;
}
snes_ppu.screen_disabled = data & 0x80;
snes_ppu.screen_brightness = (data & 0x0f) + 1;
break;
case OBSEL: /* Object size and data area designation */
snes_ppu.oam.next_charmap = (data & 0x03) << 1;
snes_ppu.oam.next_name_select = (((data & 0x18) >> 3) * 0x1000) << 1;
snes_ppu.oam.next_size = (data & 0xe0) >> 5;
break;
case OAMADDL: /* Address for accessing OAM (low) */
snes_ppu.oam.saved_address_low = data;
snes_ppu.oam.address = (snes_ppu.oam.address & 0xff00) + data;
snes_ppu.oam.first_sprite = snes_ppu.oam.priority_rotation ? (snes_ppu.oam.address >> 1) & 127 : 0;
snes_ram[OAMDATA] = 0;
break;
case OAMADDH: /* Address for accessing OAM (high) */
snes_ppu.oam.saved_address_high = data;
snes_ppu.oam.address = (snes_ppu.oam.address & 0x00ff) | ((data & 0x01) << 8);
snes_ppu.oam.priority_rotation = BIT(data, 7);
snes_ppu.oam.first_sprite = snes_ppu.oam.priority_rotation ? (snes_ppu.oam.address >> 1) & 127 : 0;
snes_ram[OAMDATA] = 0;
break;
case OAMDATA: /* Data for OAM write (DW) */
if (snes_ppu.oam.address >= 0x100)
snes_oam_write(space, snes_ppu.oam.address, data);
else
{
if (!snes_ram[OAMDATA])
snes_ppu.oam.write_latch = data;
else
{
// in this case, we not only write data to the upper byte of the word,
// but also snes_ppu.oam.write_latch to the lower byte (recall that
// snes_ram[OAMDATA] is used to select high/low byte)
snes_oam_write(space, snes_ppu.oam.address, data);
snes_ram[OAMDATA] = 0;
snes_oam_write(space, snes_ppu.oam.address, snes_ppu.oam.write_latch);
snes_ram[OAMDATA] = 1;
}
}
snes_ram[OAMDATA] = (snes_ram[OAMDATA] + 1) % 2;
if (!snes_ram[OAMDATA])
{
snes_ppu.oam.address++;
snes_ppu.oam.address &= 0x1ff;
snes_ppu.oam.first_sprite = snes_ppu.oam.priority_rotation ? (snes_ppu.oam.address >> 1) & 127 : 0;
}
return;
case BGMODE: /* BG mode and character size settings */
snes_ppu.mode = data & 0x07;
snes_dynamic_res_change(space->machine);
snes_ppu.bg3_priority_bit = BIT(data, 3);
snes_ppu.layer[SNES_BG1].tile_size = BIT(data, 4);
snes_ppu.layer[SNES_BG2].tile_size = BIT(data, 5);
snes_ppu.layer[SNES_BG3].tile_size = BIT(data, 6);
snes_ppu.layer[SNES_BG4].tile_size = BIT(data, 7);
snes_ppu.update_offsets = 1;
break;
case MOSAIC: /* Size and screen designation for mosaic */
snes_ppu.mosaic_size = (data & 0xf0) >> 4;
snes_ppu.layer[SNES_BG1].mosaic_enabled = BIT(data, 0);
snes_ppu.layer[SNES_BG2].mosaic_enabled = BIT(data, 1);
snes_ppu.layer[SNES_BG3].mosaic_enabled = BIT(data, 2);
snes_ppu.layer[SNES_BG4].mosaic_enabled = BIT(data, 3);
break;
case BG1SC: /* Address for storing SC data BG1 SC size designation */
case BG2SC: /* Address for storing SC data BG2 SC size designation */
case BG3SC: /* Address for storing SC data BG3 SC size designation */
case BG4SC: /* Address for storing SC data BG4 SC size designation */
snes_ppu.layer[offset - BG1SC].tilemap = data & 0xfc;
snes_ppu.layer[offset - BG1SC].tilemap_size = data & 0x3;
break;
case BG12NBA: /* Address for BG 1 and 2 character data */
snes_ppu.layer[SNES_BG1].charmap = (data & 0x0f);
snes_ppu.layer[SNES_BG2].charmap = (data & 0xf0) >> 4;
break;
case BG34NBA: /* Address for BG 3 and 4 character data */
snes_ppu.layer[SNES_BG3].charmap = (data & 0x0f);
snes_ppu.layer[SNES_BG4].charmap = (data & 0xf0) >> 4;
break;
// Anomie says "H Current = (Byte<<8) | (Prev&~7) | ((Current>>8)&7); V Current = (Current<<8) | Prev;" and Prev is shared by all scrolls but in Mode 7!
case BG1HOFS: /* BG1 - horizontal scroll (DW) */
/* In Mode 0->6 we use ppu_last_scroll as Prev */
snes_ppu.layer[SNES_BG1].hoffs = (data << 8) | (snes_ppu.ppu_last_scroll & ~7) | ((snes_ppu.layer[SNES_BG1].hoffs >> 8) & 7);
snes_ppu.ppu_last_scroll = data;
/* In Mode 7 we use mode7_last_scroll as Prev */
snes_ppu.mode7.hor_offset = (data << 8) | (snes_ppu.mode7_last_scroll & ~7) | ((snes_ppu.mode7.hor_offset >> 8) & 7);
snes_ppu.mode7_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case BG1VOFS: /* BG1 - vertical scroll (DW) */
/* In Mode 0->6 we use ppu_last_scroll as Prev */
snes_ppu.layer[SNES_BG1].voffs = (data << 8) | snes_ppu.ppu_last_scroll;
snes_ppu.ppu_last_scroll = data;
/* In Mode 7 we use mode7_last_scroll as Prev */
snes_ppu.mode7.ver_offset = (data << 8) | snes_ppu.mode7_last_scroll;
snes_ppu.mode7_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case BG2HOFS: /* BG2 - horizontal scroll (DW) */
snes_ppu.layer[SNES_BG2].hoffs = (data << 8) | (snes_ppu.ppu_last_scroll & ~7) | ((snes_ppu.layer[SNES_BG2].hoffs >> 8) & 7);
snes_ppu.ppu_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case BG2VOFS: /* BG2 - vertical scroll (DW) */
snes_ppu.layer[SNES_BG2].voffs = (data << 8) | (snes_ppu.ppu_last_scroll);
snes_ppu.ppu_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case BG3HOFS: /* BG3 - horizontal scroll (DW) */
snes_ppu.layer[SNES_BG3].hoffs = (data << 8) | (snes_ppu.ppu_last_scroll & ~7) | ((snes_ppu.layer[SNES_BG3].hoffs >> 8) & 7);
snes_ppu.ppu_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case BG3VOFS: /* BG3 - vertical scroll (DW) */
snes_ppu.layer[SNES_BG3].voffs = (data << 8) | (snes_ppu.ppu_last_scroll);
snes_ppu.ppu_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case BG4HOFS: /* BG4 - horizontal scroll (DW) */
snes_ppu.layer[SNES_BG4].hoffs = (data << 8) | (snes_ppu.ppu_last_scroll & ~7) | ((snes_ppu.layer[SNES_BG4].hoffs >> 8) & 7);
snes_ppu.ppu_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case BG4VOFS: /* BG4 - vertical scroll (DW) */
snes_ppu.layer[SNES_BG4].voffs = (data << 8) | (snes_ppu.ppu_last_scroll);
snes_ppu.ppu_last_scroll = data;
snes_ppu.update_offsets = 1;
return;
case VMAIN: /* VRAM address increment value designation */
state->vram_fgr_high = (data & 0x80);
state->vram_fgr_increment = vram_fgr_inctab[data & 3];
if (data & 0xc)
{
int md = (data & 0xc) >> 2;
state->vram_fgr_count = vram_fgr_inccnts[md]; // 0x20, 0x40, 0x80
state->vram_fgr_mask = (state->vram_fgr_count * 8) - 1; // 0xff, 0x1ff, 0x2ff
state->vram_fgr_shift = vram_fgr_shiftab[md]; // 5, 6, 7
}
else
{
state->vram_fgr_count = 0;
}
// printf("VMAIN: high %x inc %x count %x mask %x shift %x\n", state->vram_fgr_high, state->vram_fgr_increment, state->vram_fgr_count, state->vram_fgr_mask, state->vram_fgr_shift);
break;
case VMADDL: /* Address for VRAM read/write (low) */
case VMADDH: /* Address for VRAM read/write (high) */
{
UINT32 addr;
snes_ram[offset] = data;
addr = snes_get_vram_address(space->machine) << 1;
state->vram_read_buffer = snes_vram_read(space, addr);
state->vram_read_buffer |= (snes_vram_read(space, addr + 1) << 8);
}
break;
case VMDATAL: /* 2118: Data for VRAM write (low) */
{
UINT32 addr = snes_get_vram_address(space->machine) << 1;
snes_vram_write(space, addr, data);
if (!state->vram_fgr_high)
{
addr = ((snes_ram[VMADDH] << 8) | snes_ram[VMADDL]) + state->vram_fgr_increment;
snes_ram[VMADDL] = addr & 0xff;
snes_ram[VMADDH] = (addr >> 8) & 0xff;
}
}
return;
case VMDATAH: /* 2119: Data for VRAM write (high) */
{
UINT32 addr = snes_get_vram_address(space->machine) << 1;
snes_vram_write(space, addr + 1, data);
if (state->vram_fgr_high)
{
addr = ((snes_ram[VMADDH] << 8) | snes_ram[VMADDL]) + state->vram_fgr_increment;
snes_ram[VMADDL] = addr & 0xff;
snes_ram[VMADDH] = (addr >> 8) & 0xff;
}
}
return;
case M7SEL: /* Mode 7 initial settings */
snes_ppu.mode7.repeat = (data >> 6) & 3;
snes_ppu.mode7.vflip = BIT(data, 1);
snes_ppu.mode7.hflip = BIT(data, 0);
break;
/* As per Anomie's doc: Reg = (Current<<8) | Prev; and there is only one Prev, shared by these matrix regs and Mode 7 scroll regs */
case M7A: /* Mode 7 COS angle/x expansion (DW) */
snes_ppu.mode7.matrix_a = snes_ppu.mode7_last_scroll + (data << 8);
snes_ppu.mode7_last_scroll = data;
break;
case M7B: /* Mode 7 SIN angle/ x expansion (DW) */
snes_ppu.mode7.matrix_b = snes_ppu.mode7_last_scroll + (data << 8);
snes_ppu.mode7_last_scroll = data;
break;
case M7C: /* Mode 7 SIN angle/y expansion (DW) */
snes_ppu.mode7.matrix_c = snes_ppu.mode7_last_scroll + (data << 8);
snes_ppu.mode7_last_scroll = data;
break;
case M7D: /* Mode 7 COS angle/y expansion (DW) */
snes_ppu.mode7.matrix_d = snes_ppu.mode7_last_scroll + (data << 8);
snes_ppu.mode7_last_scroll = data;
break;
case M7X: /* Mode 7 x center position (DW) */
snes_ppu.mode7.origin_x = snes_ppu.mode7_last_scroll + (data << 8);
snes_ppu.mode7_last_scroll = data;
break;
case M7Y: /* Mode 7 y center position (DW) */
snes_ppu.mode7.origin_y = snes_ppu.mode7_last_scroll + (data << 8);
snes_ppu.mode7_last_scroll = data;
break;
case CGADD: /* Initial address for colour RAM writing */
/* CGRAM is 16-bit, but when reading/writing we treat it as 8-bit, so we need to double the address */
state->cgram_address = data << 1;
break;
case CGDATA: /* Data for colour RAM */
snes_cgram_write(space, state->cgram_address, data);
state->cgram_address = (state->cgram_address + 1) % (SNES_CGRAM_SIZE - 2);
break;
case W12SEL: /* Window mask settings for BG1-2 */
if (data != snes_ram[offset])
{
snes_ppu.layer[SNES_BG1].window1_invert = BIT(data, 0);
snes_ppu.layer[SNES_BG1].window1_enabled = BIT(data, 1);
snes_ppu.layer[SNES_BG1].window2_invert = BIT(data, 2);
snes_ppu.layer[SNES_BG1].window2_enabled = BIT(data, 3);
snes_ppu.layer[SNES_BG2].window1_invert = BIT(data, 4);
snes_ppu.layer[SNES_BG2].window1_enabled = BIT(data, 5);
snes_ppu.layer[SNES_BG2].window2_invert = BIT(data, 6);
snes_ppu.layer[SNES_BG2].window2_enabled = BIT(data, 7);
snes_ppu.update_windows = 1;
}
break;
case W34SEL: /* Window mask settings for BG3-4 */
if (data != snes_ram[offset])
{
snes_ppu.layer[SNES_BG3].window1_invert = BIT(data, 0);
snes_ppu.layer[SNES_BG3].window1_enabled = BIT(data, 1);
snes_ppu.layer[SNES_BG3].window2_invert = BIT(data, 2);
snes_ppu.layer[SNES_BG3].window2_enabled = BIT(data, 3);
snes_ppu.layer[SNES_BG4].window1_invert = BIT(data, 4);
snes_ppu.layer[SNES_BG4].window1_enabled = BIT(data, 5);
snes_ppu.layer[SNES_BG4].window2_invert = BIT(data, 6);
snes_ppu.layer[SNES_BG4].window2_enabled = BIT(data, 7);
snes_ppu.update_windows = 1;
}
break;
case WOBJSEL: /* Window mask settings for objects */
if (data != snes_ram[offset])
{
snes_ppu.layer[SNES_OAM].window1_invert = BIT(data, 0);
snes_ppu.layer[SNES_OAM].window1_enabled = BIT(data, 1);
snes_ppu.layer[SNES_OAM].window2_invert = BIT(data, 2);
snes_ppu.layer[SNES_OAM].window2_enabled = BIT(data, 3);
snes_ppu.layer[SNES_COLOR].window1_invert = BIT(data, 4);
snes_ppu.layer[SNES_COLOR].window1_enabled = BIT(data, 5);
snes_ppu.layer[SNES_COLOR].window2_invert = BIT(data, 6);
snes_ppu.layer[SNES_COLOR].window2_enabled = BIT(data, 7);
snes_ppu.update_windows = 1;
}
break;
case WH0: /* Window 1 left position */
if (data != snes_ram[offset])
{
snes_ppu.window1_left = data;
snes_ppu.update_windows = 1;
}
break;
case WH1: /* Window 1 right position */
if (data != snes_ram[offset])
{
snes_ppu.window1_right = data;
snes_ppu.update_windows = 1;
}
break;
case WH2: /* Window 2 left position */
if (data != snes_ram[offset])
{
snes_ppu.window2_left = data;
snes_ppu.update_windows = 1;
}
break;
case WH3: /* Window 2 right position */
if (data != snes_ram[offset])
{
snes_ppu.window2_right = data;
snes_ppu.update_windows = 1;
}
break;
case WBGLOG: /* Window mask logic for BG's */
if (data != snes_ram[offset])
{
snes_ppu.layer[SNES_BG1].wlog_mask = data & 0x03;
snes_ppu.layer[SNES_BG2].wlog_mask = (data & 0x0c) >> 2;
snes_ppu.layer[SNES_BG3].wlog_mask = (data & 0x30) >> 4;
snes_ppu.layer[SNES_BG4].wlog_mask = (data & 0xc0) >> 6;
snes_ppu.update_windows = 1;
}
break;
case WOBJLOG: /* Window mask logic for objects */
if (data != snes_ram[offset])
{
snes_ppu.layer[SNES_OAM].wlog_mask = data & 0x03;
snes_ppu.layer[SNES_COLOR].wlog_mask = (data & 0x0c) >> 2;
snes_ppu.update_windows = 1;
}
break;
case TM: /* Main screen designation */
snes_ppu.layer[SNES_BG1].main_bg_enabled = BIT(data, 0);
snes_ppu.layer[SNES_BG2].main_bg_enabled = BIT(data, 1);
snes_ppu.layer[SNES_BG3].main_bg_enabled = BIT(data, 2);
snes_ppu.layer[SNES_BG4].main_bg_enabled = BIT(data, 3);
snes_ppu.layer[SNES_OAM].main_bg_enabled = BIT(data, 4);
break;
case TS: /* Subscreen designation */
snes_ppu.layer[SNES_BG1].sub_bg_enabled = BIT(data, 0);
snes_ppu.layer[SNES_BG2].sub_bg_enabled = BIT(data, 1);
snes_ppu.layer[SNES_BG3].sub_bg_enabled = BIT(data, 2);
snes_ppu.layer[SNES_BG4].sub_bg_enabled = BIT(data, 3);
snes_ppu.layer[SNES_OAM].sub_bg_enabled = BIT(data, 4);
break;
case TMW: /* Window mask for main screen designation */
snes_ppu.layer[SNES_BG1].main_window_enabled = BIT(data, 0);
snes_ppu.layer[SNES_BG2].main_window_enabled = BIT(data, 1);
snes_ppu.layer[SNES_BG3].main_window_enabled = BIT(data, 2);
snes_ppu.layer[SNES_BG4].main_window_enabled = BIT(data, 3);
snes_ppu.layer[SNES_OAM].main_window_enabled = BIT(data, 4);
break;
case TSW: /* Window mask for subscreen designation */
snes_ppu.layer[SNES_BG1].sub_window_enabled = BIT(data, 0);
snes_ppu.layer[SNES_BG2].sub_window_enabled = BIT(data, 1);
snes_ppu.layer[SNES_BG3].sub_window_enabled = BIT(data, 2);
snes_ppu.layer[SNES_BG4].sub_window_enabled = BIT(data, 3);
snes_ppu.layer[SNES_OAM].sub_window_enabled = BIT(data, 4);
break;
case CGWSEL: /* Initial settings for Fixed colour addition or screen addition */
snes_ppu.clip_to_black = (data >> 6) & 0x03;
snes_ppu.prevent_color_math = (data >> 4) & 0x03;
snes_ppu.sub_add_mode = BIT(data, 1);
snes_ppu.direct_color = BIT(data, 0);
#ifdef SNES_DBG_REG_W
if ((data & 0x2) != (snes_ram[CGWSEL] & 0x2))
mame_printf_debug( "Add/Sub Layer: %s\n", ((data & 0x2) >> 1) ? "Subscreen" : "Fixed colour" );
#endif
break;
case CGADSUB: /* Addition/Subtraction designation for each screen */
snes_ppu.color_modes = data & 0xc0;
snes_ppu.layer[SNES_BG1].color_math = BIT(data, 0);
snes_ppu.layer[SNES_BG2].color_math = BIT(data, 1);
snes_ppu.layer[SNES_BG3].color_math = BIT(data, 2);
snes_ppu.layer[SNES_BG4].color_math = BIT(data, 3);
snes_ppu.layer[SNES_OAM].color_math = BIT(data, 4);
snes_ppu.layer[SNES_COLOR].color_math = BIT(data, 5);
break;
case COLDATA: /* Fixed colour data for fixed colour addition/subtraction */
{
/* Store it in the extra space we made in the CGRAM. It doesn't really go there, but it's as good a place as any. */
UINT8 r, g, b;
/* Get existing value. */
r = snes_cgram[FIXED_COLOUR] & 0x1f;
g = (snes_cgram[FIXED_COLOUR] & 0x3e0) >> 5;
b = (snes_cgram[FIXED_COLOUR] & 0x7c00) >> 10;
/* Set new value */
if (data & 0x20)
r = data & 0x1f;
if (data & 0x40)
g = data & 0x1f;
if (data & 0x80)
b = data & 0x1f;
snes_cgram[FIXED_COLOUR] = (r | (g << 5) | (b << 10));
} break;
case SETINI: /* Screen mode/video select */
snes_ppu.interlace = (data & 0x01) ? 2 : 1;
snes_ppu.obj_interlace = (data & 0x02) ? 2 : 1;
snes_ppu.beam.last_visible_line = (data & 0x04) ? 240 : 225;
snes_ppu.pseudo_hires = BIT(data, 3);
snes_ppu.mode7.extbg = BIT(data, 6);
snes_dynamic_res_change(space->machine);
#ifdef SNES_DBG_REG_W
if ((data & 0x8) != (snes_ram[SETINI] & 0x8))
mame_printf_debug( "Pseudo 512 mode: %s\n", (data & 0x8) ? "on" : "off" );
#endif
break;
}
snes_ram[offset] = data;
}
/***** Debug Functions *****/
#ifdef SNES_LAYER_DEBUG