summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/video
diff options
context:
space:
mode:
author Devin Acker <d@revenant1.net>2020-10-22 08:56:02 -0400
committer GitHub <noreply@github.com>2020-10-22 23:56:02 +1100
commit58ef849d1864ef55d8a32b0cfef97ee370eeeb18 (patch)
tree4c09af38ccf6fc8aa8057385ab8cea3896cf1a13 /src/devices/video
parent0ff27cf174c609495eeddad051a43e82d098dc4d (diff)
vsnes.cpp: working sound + "improved" graphics in Vs. SMB bootleg sets (#7360)
* vsnes.cpp: Improvements to Vs SMB bootleg sets based on kevtris' schematics/video. * video/ppu2c0x.cpp: Add 2C04 PPU clone device and use the real palette ROMs for the bootlegs. * video/ppu2c0x.cpp: Implement most PPU behavior differences for VS System bootlegs. * video/ppu2c0x.cpp: add the 2c04 clone's delayed sprite rendering.
Diffstat (limited to 'src/devices/video')
-rw-r--r--src/devices/video/ppu2c0x.cpp163
-rw-r--r--src/devices/video/ppu2c0x.h61
2 files changed, 196 insertions, 28 deletions
diff --git a/src/devices/video/ppu2c0x.cpp b/src/devices/video/ppu2c0x.cpp
index d769ff54162..ebc65b79f46 100644
--- a/src/devices/video/ppu2c0x.cpp
+++ b/src/devices/video/ppu2c0x.cpp
@@ -35,15 +35,16 @@
//**************************************************************************
// devices
-DEFINE_DEVICE_TYPE(PPU_2C02, ppu2c02_device, "ppu2c02", "2C02 PPU")
-DEFINE_DEVICE_TYPE(PPU_2C03B, ppu2c03b_device, "ppu2c03b", "2C03B PPC")
-DEFINE_DEVICE_TYPE(PPU_2C04, ppu2c04_device, "ppu2c04", "2C04 PPU")
-DEFINE_DEVICE_TYPE(PPU_2C07, ppu2c07_device, "ppu2c07", "2C07 PPU")
-DEFINE_DEVICE_TYPE(PPU_PALC, ppupalc_device, "ppupalc", "Generic PAL Clone PPU")
-DEFINE_DEVICE_TYPE(PPU_2C05_01, ppu2c05_01_device, "ppu2c05_01", "2C05_01 PPU")
-DEFINE_DEVICE_TYPE(PPU_2C05_02, ppu2c05_02_device, "ppu2c05_02", "2C05_02 PPU")
-DEFINE_DEVICE_TYPE(PPU_2C05_03, ppu2c05_03_device, "ppu2c05_03", "2C05_03 PPU")
-DEFINE_DEVICE_TYPE(PPU_2C05_04, ppu2c05_04_device, "ppu2c05_04", "2C05_04 PPU")
+DEFINE_DEVICE_TYPE(PPU_2C02, ppu2c02_device, "ppu2c02", "2C02 PPU")
+DEFINE_DEVICE_TYPE(PPU_2C03B, ppu2c03b_device, "ppu2c03b", "2C03B PPC")
+DEFINE_DEVICE_TYPE(PPU_2C04, ppu2c04_device, "ppu2c04", "2C04 PPU")
+DEFINE_DEVICE_TYPE(PPU_2C07, ppu2c07_device, "ppu2c07", "2C07 PPU")
+DEFINE_DEVICE_TYPE(PPU_PALC, ppupalc_device, "ppupalc", "Generic PAL Clone PPU")
+DEFINE_DEVICE_TYPE(PPU_2C05_01, ppu2c05_01_device, "ppu2c05_01", "2C05_01 PPU")
+DEFINE_DEVICE_TYPE(PPU_2C05_02, ppu2c05_02_device, "ppu2c05_02", "2C05_02 PPU")
+DEFINE_DEVICE_TYPE(PPU_2C05_03, ppu2c05_03_device, "ppu2c05_03", "2C05_03 PPU")
+DEFINE_DEVICE_TYPE(PPU_2C05_04, ppu2c05_04_device, "ppu2c05_04", "2C05_04 PPU")
+DEFINE_DEVICE_TYPE(PPU_2C04C, ppu2c04_clone_device, "ppu2c04c", "2C04 Clone PPU")
// default address map
@@ -99,6 +100,7 @@ ppu2c0x_device::ppu2c0x_device(const machine_config& mconfig, device_type type,
m_back_color(0),
m_refresh_data(0),
m_x_fine(0),
+ m_toggle(0),
m_tilecount(0),
m_latch(*this),
m_scanline_callback_proc(*this),
@@ -106,7 +108,6 @@ ppu2c0x_device::ppu2c0x_device(const machine_config& mconfig, device_type type,
m_vidaccess_callback_proc(*this),
m_int_callback(*this),
m_refresh_latch(0),
- m_toggle(0),
m_add(1),
m_videomem_addr(0),
m_data_latch(0),
@@ -197,6 +198,17 @@ ppu2c05_04_device::ppu2c05_04_device(const machine_config& mconfig, const char*
m_security_value = 0x1b;
}
+// Vs. Unisystem (Super Mario Bros. bootlegs)
+ppu2c04_clone_device::ppu2c04_clone_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
+ ppu2c0x_device(mconfig, PPU_2C04C, tag, owner, clock),
+ m_palette_data(*this, "palette", 0x100)
+{
+ m_scanlines_per_frame = VS_CLONE_SCANLINES_PER_FRAME;
+ m_vblank_first_scanline = VBLANK_FIRST_SCANLINE_VS_CLONE;
+
+ // background and sprites are always enabled; monochrome and color emphasis aren't supported
+ m_regs[PPU_CONTROL1] = ~(PPU_CONTROL1_COLOR_EMPHASIS | PPU_CONTROL1_DISPLAY_MONO);
+}
//-------------------------------------------------
// device_start - device-specific startup
@@ -259,6 +271,26 @@ void ppu2c0x_device::device_start()
save_item(NAME(m_palette_ram));
}
+void ppu2c04_clone_device::device_start()
+{
+ ppu2c0x_device::device_start();
+
+ /* this PPU clone draws sprites into a frame buffer before displaying them,
+ causing sprite rendering to be one frame behind tile/background rendering
+ (mainly noticeable during scrolling)
+ to simulate that, we can just have a secondary OAM buffer and swap them
+ at the end of each frame.
+
+ (theoretically this can cause the wrong sprite tiles to be drawn for
+ one frame after changing CHR banks, but the Vs. SMB bootlegs that use
+ this clone hardware don't actually have CHR bank switching anyway.
+ also generally affects PPU-side read timings involving the OAM, but
+ this still doesn't seem to matter for Vs. SMB specifically)
+ */
+ m_spritebuf = make_unique_clear<uint8_t[]>(SPRITERAM_SIZE);
+ save_pointer(NAME(m_spritebuf), SPRITERAM_SIZE);
+}
+
//**************************************************************************
// INLINE HELPERS
//**************************************************************************
@@ -445,6 +477,24 @@ void ppu2c0x_rgb_device::init_palette_tables()
}
}
+void ppu2c04_clone_device::init_palette_tables()
+{
+ /* clone HW doesn't use color emphasis bits.
+ however, it does have two separate palettes: colors 0-63 for background, and 64-127 for sprites
+ (although the tile and sprite colors are identical in the Vs. SMB bootleg ROMs)
+ */
+ for (int color_num = 0; color_num < 64*2; color_num++)
+ {
+ /* A7 line on palette ROMs is always high, color bits are in reverse order */
+ u8 color = m_palette_data[color_num | 0x80];
+ int R = bitswap<3>(color, 0, 1, 2);
+ int G = bitswap<3>(color, 3, 4, 5);
+ int B = bitswap<2>(color, 6, 7);
+
+ m_nespens[color_num] = (pal3bit(R) << 16) | (pal3bit(G) << 8) | pal2bit(B);
+ }
+}
+
/*************************************
*
* PPU Initialization and Disposal
@@ -704,6 +754,15 @@ void ppu2c0x_device::draw_background(uint8_t* line_priority)
}
}
+void ppu2c04_clone_device::draw_background(uint8_t* line_priority)
+{
+ // nametable selection is ignored below the hardwired scroll split position
+ if (m_scanline < 31)
+ m_refresh_data &= ~0x0c00;
+
+ ppu2c0x_device::draw_background(line_priority);
+}
+
void ppu2c0x_device::draw_back_pen(uint32_t* dst, int back_pen)
{
*dst = m_nespens[back_pen];
@@ -757,6 +816,17 @@ void ppu2c0x_device::draw_sprite_pixel(int sprite_xpos, int color, int pixel, ui
bitmap.pix(m_scanline, sprite_xpos + pixel) = pix;
}
+void ppu2c04_clone_device::draw_sprite_pixel(int sprite_xpos, int color, int pixel, uint8_t pixel_data, bitmap_rgb32 &bitmap)
+{
+ /* clone PPU clips sprites at the screen edges */
+ if ((sprite_xpos + pixel < 8) || (sprite_xpos + pixel) >= (VISIBLE_SCREEN_WIDTH - 6))
+ return;
+
+ uint16_t palval = m_palette_ram[((4 * color) | pixel_data) & 0x1f];
+ uint32_t pix = m_nespens[palval | 0x40];
+ bitmap.pix(m_scanline, sprite_xpos + pixel) = pix;
+}
+
void ppu2c0x_device::read_extra_sprite_bits(int sprite_index)
{
// needed for some clones
@@ -953,6 +1023,19 @@ void ppu2c0x_device::draw_sprites(uint8_t* line_priority)
}
}
+void ppu2c04_clone_device::draw_sprites(uint8_t *line_priority)
+{
+ ppu2c0x_device::draw_sprites(line_priority);
+
+ if (m_scanline == BOTTOM_VISIBLE_SCANLINE)
+ {
+ /* this frame's sprite buffer is cleared after being displayed
+ and the other one that was filled this frame will be displayed next frame */
+ m_spriteram.swap(m_spritebuf);
+ memset(m_spritebuf.get(), 0, SPRITERAM_SIZE);
+ }
+}
+
/*************************************
*
* Scanline Rendering and Update
@@ -1185,6 +1268,23 @@ uint8_t ppu2c0x_device::read(offs_t offset)
return m_data_latch;
}
+uint8_t ppu2c04_clone_device::read(offs_t offset)
+{
+ switch (offset & 7)
+ {
+ case PPU_STATUS: /* 2 */
+ // $2002 on this clone only contains the sprite 0 hit flag,
+ // and it's hardwired to trigger after a specific scanline, not based on actual sprite positions
+ if (m_scanline < 31 || m_scanline >= (m_vblank_first_scanline - 1))
+ return ~PPU_STATUS_SPRITE0_HIT;
+ return 0xff;
+
+ case PPU_SPRITE_DATA: /* 4 */
+ return m_spritebuf[m_regs[PPU_SPRITE_ADDRESS]];
+ }
+
+ return ppu2c0x_device::read(offset);
+}
/*************************************
*
@@ -1324,6 +1424,49 @@ void ppu2c0x_device::write(offs_t offset, uint8_t data)
m_data_latch = data;
}
+void ppu2c04_clone_device::write(offs_t offset, uint8_t data)
+{
+ switch (offset & 7)
+ {
+ case PPU_CONTROL0: /* 0 */
+ data &= (0x01 | PPU_CONTROL0_INC | PPU_CONTROL0_NMI); /* other bits of $2000 are ignored by this clone */
+ data |= PPU_CONTROL0_CHR_SELECT;
+ break;
+
+ case PPU_CONTROL1: /* 1 */
+ case PPU_SPRITE_ADDRESS: /* 3 */
+ return; /* $2001 and $2003 do nothing on this clone */
+
+ case PPU_SPRITE_DATA: /* 4 */
+ m_spritebuf[m_regs[PPU_SPRITE_ADDRESS]] = data;
+ m_regs[PPU_SPRITE_ADDRESS] = (m_regs[PPU_SPRITE_ADDRESS] + 1) & 0xff;
+ return;
+
+ case PPU_SCROLL: /* 5 */
+ if (m_toggle)
+ data = 0; /* no vertical scroll */
+ break;
+
+ case PPU_ADDRESS: /* 6 */
+ /* $2006 doesn't affect scroll latching */
+ if (m_toggle)
+ {
+ /* second write */
+ set_vram_dest((get_vram_dest() & 0xff00) | data);
+ }
+ else
+ {
+ /* first write */
+ set_vram_dest((get_vram_dest() & 0x00ff) | (data << 8));
+ }
+
+ m_toggle ^= 1;
+ return;
+ }
+
+ ppu2c0x_device::write(offset, data);
+}
+
uint16_t ppu2c0x_device::get_vram_dest()
{
return m_videomem_addr;
diff --git a/src/devices/video/ppu2c0x.h b/src/devices/video/ppu2c0x.h
index 9d8fa0f5351..532eec0e7f1 100644
--- a/src/devices/video/ppu2c0x.h
+++ b/src/devices/video/ppu2c0x.h
@@ -53,14 +53,17 @@ public:
enum
{
- NTSC_SCANLINES_PER_FRAME = 262,
- PAL_SCANLINES_PER_FRAME = 312,
-
- BOTTOM_VISIBLE_SCANLINE = 239,
- VBLANK_FIRST_SCANLINE = 241,
- VBLANK_FIRST_SCANLINE_PALC = 291,
- VBLANK_LAST_SCANLINE_NTSC = 260,
- VBLANK_LAST_SCANLINE_PAL = 310
+ NTSC_SCANLINES_PER_FRAME = 262,
+ PAL_SCANLINES_PER_FRAME = 312,
+ VS_CLONE_SCANLINES_PER_FRAME = 280,
+
+ BOTTOM_VISIBLE_SCANLINE = 239,
+ VBLANK_FIRST_SCANLINE = 241,
+ VBLANK_FIRST_SCANLINE_PALC = 291,
+ VBLANK_FIRST_SCANLINE_VS_CLONE = 240,
+ VBLANK_LAST_SCANLINE_NTSC = 260,
+ VBLANK_LAST_SCANLINE_PAL = 310,
+ VBLANK_LAST_SCANLINE_VS_CLONE = 279
// Both the scanline immediately before and immediately after VBLANK
// are non-rendering and non-vblank.
@@ -207,6 +210,7 @@ protected:
int m_back_color; /* background color */
int m_refresh_data; /* refresh-related */
int m_x_fine; /* refresh-related */
+ int m_toggle; /* used to latch hi-lo scroll */
int m_tilecount; /* MMC5 can change attributes to subsets of the 34 visible tiles */
latch_delegate m_latch;
@@ -228,7 +232,6 @@ private:
devcb_write_line m_int_callback; /* nmi access callback from interface */
int m_refresh_latch; /* refresh-related */
- int m_toggle; /* used to latch hi-lo scroll */
int m_add; /* vram increment amount */
int m_videomem_addr; /* videomem address pointer */
int m_data_latch; /* latched videomem data */
@@ -300,17 +303,39 @@ public:
ppu2c05_04_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
};
+class ppu2c04_clone_device : public ppu2c0x_device {
+public:
+ ppu2c04_clone_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+
+ virtual uint8_t read(offs_t offset) override;
+ virtual void write(offs_t offset, uint8_t data) override;
+
+ virtual void draw_background(uint8_t *line_priority) override;
+ virtual void draw_sprite_pixel(int sprite_xpos, int color, int pixel, uint8_t pixel_data, bitmap_rgb32 &bitmap) override;
+ virtual void draw_sprites(uint8_t *line_priority) override;
+
+ virtual void init_palette_tables() override;
+
+protected:
+ virtual void device_start() override;
+
+private:
+ required_region_ptr<uint8_t> m_palette_data;
+
+ std::unique_ptr<uint8_t[]> m_spritebuf; /* buffered sprite ram for next frame */
+};
// device type definition
//extern const device_type PPU_2C0X;
-DECLARE_DEVICE_TYPE(PPU_2C02, ppu2c02_device) // NTSC NES
-DECLARE_DEVICE_TYPE(PPU_2C03B, ppu2c03b_device) // Playchoice 10
-DECLARE_DEVICE_TYPE(PPU_2C04, ppu2c04_device) // Vs. Unisystem
-DECLARE_DEVICE_TYPE(PPU_2C07, ppu2c07_device) // PAL NES
-DECLARE_DEVICE_TYPE(PPU_PALC, ppupalc_device) // PAL Clones
-DECLARE_DEVICE_TYPE(PPU_2C05_01, ppu2c05_01_device) // Vs. Unisystem (Ninja Jajamaru Kun)
-DECLARE_DEVICE_TYPE(PPU_2C05_02, ppu2c05_02_device) // Vs. Unisystem (Mighty Bomb Jack)
-DECLARE_DEVICE_TYPE(PPU_2C05_03, ppu2c05_03_device) // Vs. Unisystem (Gumshoe)
-DECLARE_DEVICE_TYPE(PPU_2C05_04, ppu2c05_04_device) // Vs. Unisystem (Top Gun)
+DECLARE_DEVICE_TYPE(PPU_2C02, ppu2c02_device) // NTSC NES
+DECLARE_DEVICE_TYPE(PPU_2C03B, ppu2c03b_device) // Playchoice 10
+DECLARE_DEVICE_TYPE(PPU_2C04, ppu2c04_device) // Vs. Unisystem
+DECLARE_DEVICE_TYPE(PPU_2C07, ppu2c07_device) // PAL NES
+DECLARE_DEVICE_TYPE(PPU_PALC, ppupalc_device) // PAL Clones
+DECLARE_DEVICE_TYPE(PPU_2C05_01, ppu2c05_01_device) // Vs. Unisystem (Ninja Jajamaru Kun)
+DECLARE_DEVICE_TYPE(PPU_2C05_02, ppu2c05_02_device) // Vs. Unisystem (Mighty Bomb Jack)
+DECLARE_DEVICE_TYPE(PPU_2C05_03, ppu2c05_03_device) // Vs. Unisystem (Gumshoe)
+DECLARE_DEVICE_TYPE(PPU_2C05_04, ppu2c05_04_device) // Vs. Unisystem (Top Gun)
+DECLARE_DEVICE_TYPE(PPU_2C04C, ppu2c04_clone_device) // Vs. Unisystem (Super Mario Bros. bootlegs)
#endif // MAME_VIDEO_PPU2C0X_H