diff options
Diffstat (limited to 'src/devices/video/ppu2c0x.cpp')
-rw-r--r-- | src/devices/video/ppu2c0x.cpp | 787 |
1 files changed, 417 insertions, 370 deletions
diff --git a/src/devices/video/ppu2c0x.cpp b/src/devices/video/ppu2c0x.cpp index fe220ed1e6e..dd895b469f1 100644 --- a/src/devices/video/ppu2c0x.cpp +++ b/src/devices/video/ppu2c0x.cpp @@ -32,18 +32,9 @@ /*************************************************************************** - CONSTANTS + CONSTANTS ***************************************************************************/ -/* constant definitions */ -#define VISIBLE_SCREEN_WIDTH (32*8) /* Visible screen width */ -#define VISIBLE_SCREEN_HEIGHT (30*8) /* Visible screen height */ -#define VIDEOMEM_SIZE 0x1000 /* videomem size */ -#define VIDEOMEM_PAGE_SIZE 0x400 /* videomem page size */ -#define SPRITERAM_SIZE 0x100 /* spriteram size */ -#define SPRITERAM_MASK (0x100-1) /* spriteram size */ -#define CHARGEN_NUM_CHARS 512 /* max number of characters handled by the chargen */ - /* default monochromatic colortable */ static const pen_t default_colortable_mono[] = { @@ -87,13 +78,13 @@ DEFINE_DEVICE_TYPE(PPU_2C05_04, ppu2c05_04_device, "ppu2c05_04", "2C05_04 PPU") // default address map -void ppu2c0x_device::ppu2c0x(address_map &map) +void ppu2c0x_device::ppu2c0x(address_map& map) { if (!has_configured_map(0)) { map(0x0000, 0x3eff).ram(); map(0x3f00, 0x3fff).rw(FUNC(ppu2c0x_device::palette_read), FUNC(ppu2c0x_device::palette_write)); -// map(0x0000, 0x3fff).ram(); + //map(0x0000, 0x3fff).ram(); } } @@ -104,7 +95,8 @@ void ppu2c0x_device::ppu2c0x(address_map &map) device_memory_interface::space_config_vector ppu2c0x_device::memory_space_config() const { - return space_config_vector { + return space_config_vector + { std::make_pair(0, &m_space_config) }; } @@ -123,40 +115,40 @@ void ppu2c0x_device::device_config_complete() m_latch.set(nullptr); } -ppu2c0x_device::ppu2c0x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, type, tag, owner, clock) - , device_memory_interface(mconfig, *this) - , device_video_interface(mconfig, *this) - , device_palette_interface(mconfig, *this) - , m_space_config("videoram", ENDIANNESS_LITTLE, 8, 17, 0, address_map_constructor(FUNC(ppu2c0x_device::ppu2c0x), this)) - , m_cpu(*this, finder_base::DUMMY_TAG) - , m_scanline(0) // reset the scanline count - , m_scanline_callback_proc(*this) - , m_hblank_callback_proc(*this) - , m_vidaccess_callback_proc(*this) - , m_int_callback(*this) - , m_refresh_data(0) - , m_refresh_latch(0) - , m_x_fine(0) - , m_toggle(0) - , m_add(1) - , m_videomem_addr(0) - , m_data_latch(0) - , m_buffered_data(0) - , m_tile_page(0) - , m_sprite_page(0) - , m_back_color(0) - , m_scan_scale(1) // set the scan scale (this is for dual monitor vertical setups) - , m_tilecount(0) - , m_draw_phase(0) - , m_latch(*this) - , m_use_sprite_write_limitation(true) +ppu2c0x_device::ppu2c0x_device(const machine_config& mconfig, device_type type, const char* tag, device_t* owner, uint32_t clock, address_map_constructor internal_map) : + device_t(mconfig, type, tag, owner, clock), + device_memory_interface(mconfig, *this), + device_video_interface(mconfig, *this), + device_palette_interface(mconfig, *this), + m_space_config("videoram", ENDIANNESS_LITTLE, 8, 17, 0, internal_map), + m_cpu(*this, finder_base::DUMMY_TAG), + m_scanline(0), // reset the scanline count + m_line_write_increment_large(32), + m_paletteram_in_ppuspace(false), + m_tile_page(0), + m_back_color(0), + m_refresh_data(0), + m_x_fine(0), + m_tilecount(0), + m_latch(*this), + m_scanline_callback_proc(*this), + m_hblank_callback_proc(*this), + 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), + m_buffered_data(0), + m_sprite_page(0), + m_scan_scale(1), // set the scan scale (this is for dual monitor vertical setups) + m_draw_phase(0), + m_use_sprite_write_limitation(true) { - for (auto & elem : m_regs) + for (auto& elem : m_regs) elem = 0; - memset(m_palette_ram, 0, ARRAY_LENGTH(m_palette_ram)); - m_scanlines_per_frame = NTSC_SCANLINES_PER_FRAME; m_vblank_first_scanline = VBLANK_FIRST_SCANLINE; @@ -164,34 +156,46 @@ ppu2c0x_device::ppu2c0x_device(const machine_config &mconfig, device_type type, m_security_value = 0; } -ppu2c0x_rgb_device::ppu2c0x_rgb_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : ppu2c0x_device(mconfig, type, tag, owner, clock) - , m_palette_data(*this, "palette", 0xc0) +ppu2c0x_device::ppu2c0x_device(const machine_config& mconfig, device_type type, const char* tag, device_t* owner, uint32_t clock) : + ppu2c0x_device(mconfig, type, tag, owner, clock, address_map_constructor(FUNC(ppu2c0x_device::ppu2c0x), this)) +{ + m_paletteram_in_ppuspace = true; +} + +ppu2c0x_rgb_device::ppu2c0x_rgb_device(const machine_config& mconfig, device_type type, const char* tag, device_t* owner, uint32_t clock) : + ppu2c0x_device(mconfig, type, tag, owner, clock), + m_palette_data(*this, "palette", 0xc0) { } // NTSC NES -ppu2c02_device::ppu2c02_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ppu2c0x_device(mconfig, PPU_2C02, tag, owner, clock) +ppu2c02_device::ppu2c02_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) : + ppu2c0x_device(mconfig, PPU_2C02, tag, owner, clock) { } // Playchoice 10 -ppu2c03b_device::ppu2c03b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ppu2c0x_rgb_device(mconfig, PPU_2C03B, tag, owner, clock) +ppu2c03b_device::ppu2c03b_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) : + ppu2c0x_rgb_device(mconfig, PPU_2C03B, tag, owner, clock) { } // Vs. Unisystem -ppu2c04_device::ppu2c04_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ppu2c0x_rgb_device(mconfig, PPU_2C04, tag, owner, clock) +ppu2c04_device::ppu2c04_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) : + ppu2c0x_rgb_device(mconfig, PPU_2C04, tag, owner, clock) { } // PAL NES -ppu2c07_device::ppu2c07_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ppu2c0x_device(mconfig, PPU_2C07, tag, owner, clock) +ppu2c07_device::ppu2c07_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) : + ppu2c0x_device(mconfig, PPU_2C07, tag, owner, clock) { m_scanlines_per_frame = PAL_SCANLINES_PER_FRAME; } // PAL clones -ppupalc_device::ppupalc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ppu2c0x_device(mconfig, PPU_PALC, tag, owner, clock) +ppupalc_device::ppupalc_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) : + ppu2c0x_device(mconfig, PPU_PALC, tag, owner, clock) { m_scanlines_per_frame = PAL_SCANLINES_PER_FRAME; m_vblank_first_scanline = VBLANK_FIRST_SCANLINE_PALC; @@ -199,22 +203,26 @@ ppupalc_device::ppupalc_device(const machine_config &mconfig, const char *tag, d // The PPU_2C05 variants have different protection value, set at device start, but otherwise are all the same... // Vs. Unisystem (Ninja Jajamaru Kun) -ppu2c05_01_device::ppu2c05_01_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ppu2c0x_rgb_device(mconfig, PPU_2C05_01, tag, owner, clock) +ppu2c05_01_device::ppu2c05_01_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) : + ppu2c0x_rgb_device(mconfig, PPU_2C05_01, tag, owner, clock) { m_security_value = 0x1b; // game (jajamaru) doesn't seem to ever actually check it } // Vs. Unisystem (Mighty Bomb Jack) -ppu2c05_02_device::ppu2c05_02_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ppu2c0x_rgb_device(mconfig, PPU_2C05_02, tag, owner, clock) +ppu2c05_02_device::ppu2c05_02_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) : + ppu2c0x_rgb_device(mconfig, PPU_2C05_02, tag, owner, clock) { m_security_value = 0x3d; } // Vs. Unisystem (Gumshoe) -ppu2c05_03_device::ppu2c05_03_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ppu2c0x_rgb_device(mconfig, PPU_2C05_03, tag, owner, clock) +ppu2c05_03_device::ppu2c05_03_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) : + ppu2c0x_rgb_device(mconfig, PPU_2C05_03, tag, owner, clock) { m_security_value = 0x1c; } // Vs. Unisystem (Top Gun) -ppu2c05_04_device::ppu2c05_04_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ppu2c0x_rgb_device(mconfig, PPU_2C05_04, tag, owner, clock) +ppu2c05_04_device::ppu2c05_04_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) : + ppu2c0x_rgb_device(mconfig, PPU_2C05_04, tag, owner, clock) { m_security_value = 0x1b; } @@ -245,6 +253,11 @@ void ppu2c0x_device::device_start() m_colortable = std::make_unique<pen_t[]>(ARRAY_LENGTH(default_colortable)); m_colortable_mono = std::make_unique<pen_t[]>(ARRAY_LENGTH(default_colortable_mono)); + m_palette_ram.resize(0x20); + + for (int i = 0; i < 0x20; i++) + m_palette_ram[i] = 0x00; + /* initialize the color tables */ for (int i = 0; i < ARRAY_LENGTH(default_colortable_mono); i++) { @@ -291,7 +304,7 @@ void ppu2c0x_device::device_start() // readbyte - read a byte at the given address //------------------------------------------------- -inline uint8_t ppu2c0x_device::readbyte(offs_t address) +uint8_t ppu2c0x_device::readbyte(offs_t address) { return space().read_byte(address); } @@ -413,14 +426,14 @@ void ppu2c0x_device::init_palette(bool indirect) switch (color_emphasis) { - case 0: r_mod = 1.0; g_mod = 1.0; b_mod = 1.0; break; - case 1: r_mod = 1.24; g_mod = .915; b_mod = .743; break; - case 2: r_mod = .794; g_mod = 1.09; b_mod = .882; break; - case 3: r_mod = .905; g_mod = 1.03; b_mod = 1.28; break; - case 4: r_mod = .741; g_mod = .987; b_mod = 1.0; break; - case 5: r_mod = 1.02; g_mod = .908; b_mod = .979; break; - case 6: r_mod = 1.02; g_mod = .98; b_mod = .653; break; - case 7: r_mod = .75; g_mod = .75; b_mod = .75; break; + case 0: r_mod = 1.0; g_mod = 1.0; b_mod = 1.0; break; + case 1: r_mod = 1.24; g_mod = .915; b_mod = .743; break; + case 2: r_mod = .794; g_mod = 1.09; b_mod = .882; break; + case 3: r_mod = .905; g_mod = 1.03; b_mod = 1.28; break; + case 4: r_mod = .741; g_mod = .987; b_mod = 1.0; break; + case 5: r_mod = 1.02; g_mod = .908; b_mod = .979; break; + case 6: r_mod = 1.02; g_mod = .98; b_mod = .653; break; + case 7: r_mod = .75; g_mod = .75; b_mod = .75; break; } */ @@ -451,13 +464,13 @@ void ppu2c0x_rgb_device::init_palette() for (int color_emphasis = 0; color_emphasis < 8; color_emphasis++) { for (int color_num = 0; color_num < 64; color_num++) - { - int R = ((color_emphasis & 1) ? 7 : m_palette_data[color_num * 3]); - int G = ((color_emphasis & 2) ? 7 : m_palette_data[color_num * 3 + 1]); - int B = ((color_emphasis & 4) ? 7 : m_palette_data[color_num * 3 + 2]); + { + int R = ((color_emphasis & 1) ? 7 : m_palette_data[color_num * 3]); + int G = ((color_emphasis & 2) ? 7 : m_palette_data[color_num * 3 + 1]); + int B = ((color_emphasis & 4) ? 7 : m_palette_data[color_num * 3 + 2]); - set_pen_color(entry++, pal3bit(R), pal3bit(G), pal3bit(B)); - } + set_pen_color(entry++, pal3bit(R), pal3bit(G), pal3bit(B)); + } } /* color tables are modified at run-time, and are initialized on 'ppu2c0x_reset' */ @@ -487,96 +500,96 @@ static const gfx_layout ppu_charlayout = // device_timer - handle timer events //------------------------------------------------- -void ppu2c0x_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +void ppu2c0x_device::device_timer(emu_timer& timer, device_timer_id id, int param, void* ptr) { int blanked, vblank; switch (id) { - case TIMER_HBLANK: - blanked = (m_regs[PPU_CONTROL1] & (PPU_CONTROL1_BACKGROUND | PPU_CONTROL1_SPRITES)) == 0; - vblank = ((m_scanline >= m_vblank_first_scanline - 1) && (m_scanline < m_scanlines_per_frame - 1)) ? 1 : 0; + case TIMER_HBLANK: + blanked = (m_regs[PPU_CONTROL1] & (PPU_CONTROL1_BACKGROUND | PPU_CONTROL1_SPRITES)) == 0; + vblank = ((m_scanline >= m_vblank_first_scanline - 1) && (m_scanline < m_scanlines_per_frame - 1)) ? 1 : 0; - //update_scanline(); + //update_scanline(); - if (!m_hblank_callback_proc.isnull()) - m_hblank_callback_proc(m_scanline, vblank, blanked); + if (!m_hblank_callback_proc.isnull()) + m_hblank_callback_proc(m_scanline, vblank, blanked); - m_hblank_timer->adjust(attotime::never); - break; + m_hblank_timer->adjust(attotime::never); + break; - case TIMER_NMI: - // Actually fire the VMI - m_int_callback(ASSERT_LINE); - m_int_callback(CLEAR_LINE); + case TIMER_NMI: + // Actually fire the VMI + m_int_callback(ASSERT_LINE); + m_int_callback(CLEAR_LINE); - m_nmi_timer->adjust(attotime::never); - break; + m_nmi_timer->adjust(attotime::never); + break; - case TIMER_SCANLINE: - blanked = (m_regs[PPU_CONTROL1] & (PPU_CONTROL1_BACKGROUND | PPU_CONTROL1_SPRITES)) == 0; - vblank = ((m_scanline >= m_vblank_first_scanline - 1) && (m_scanline < m_scanlines_per_frame - 1)) ? 1 : 0; - int next_scanline; + case TIMER_SCANLINE: + blanked = (m_regs[PPU_CONTROL1] & (PPU_CONTROL1_BACKGROUND | PPU_CONTROL1_SPRITES)) == 0; + vblank = ((m_scanline >= m_vblank_first_scanline - 1) && (m_scanline < m_scanlines_per_frame - 1)) ? 1 : 0; + int next_scanline; - /* if a callback is available, call it */ - if (!m_scanline_callback_proc.isnull()) - m_scanline_callback_proc(m_scanline, vblank, blanked); + /* if a callback is available, call it */ + if (!m_scanline_callback_proc.isnull()) + m_scanline_callback_proc(m_scanline, vblank, blanked); - /* update the scanline that just went by */ - update_scanline(); + /* update the scanline that just went by */ + update_scanline(); - /* increment our scanline count */ - m_scanline++; + /* increment our scanline count */ + m_scanline++; - // logerror("starting scanline %d (MAME %d, beam %d)\n", m_scanline, device->screen().vpos(), device->screen().hpos()); + //logerror("starting scanline %d (MAME %d, beam %d)\n", m_scanline, device->screen().vpos(), device->screen().hpos()); - /* Note: this is called at the _end_ of each scanline */ - if (m_scanline == m_vblank_first_scanline) - { - // logerror("vblank starting\n"); - /* We just entered VBLANK */ - m_regs[PPU_STATUS] |= PPU_STATUS_VBLANK; - - /* If NMI's are set to be triggered, go for it */ - if (m_regs[PPU_CONTROL0] & PPU_CONTROL0_NMI) - { - // We need an ever-so-slight delay between entering vblank and firing an NMI - enough so that - // a game can read the high bit of $2002 before the NMI is called (potentially resetting the bit - // via a read from $2002 in the NMI handler). - // B-Wings is an example game that needs this. - m_nmi_timer->adjust(m_cpu->cycles_to_attotime(4)); - } - } + /* Note: this is called at the _end_ of each scanline */ + if (m_scanline == m_vblank_first_scanline) + { + // logerror("vblank starting\n"); + /* We just entered VBLANK */ + m_regs[PPU_STATUS] |= PPU_STATUS_VBLANK; - if (m_scanline == m_scanlines_per_frame - 1) + /* If NMI's are set to be triggered, go for it */ + if (m_regs[PPU_CONTROL0] & PPU_CONTROL0_NMI) { - // logerror("vblank ending\n"); - /* clear the vblank & sprite hit flag */ - m_regs[PPU_STATUS] &= ~(PPU_STATUS_VBLANK | PPU_STATUS_SPRITE0_HIT | PPU_STATUS_8SPRITES); + // We need an ever-so-slight delay between entering vblank and firing an NMI - enough so that + // a game can read the high bit of $2002 before the NMI is called (potentially resetting the bit + // via a read from $2002 in the NMI handler). + // B-Wings is an example game that needs this. + m_nmi_timer->adjust(m_cpu->cycles_to_attotime(4)); } + } - /* see if we rolled */ - else if (m_scanline == m_scanlines_per_frame) - { - /* if background or sprites are enabled, copy the ppu address latch */ - if (!blanked) - m_refresh_data = m_refresh_latch; + if (m_scanline == m_scanlines_per_frame - 1) + { + //logerror("vblank ending\n"); + /* clear the vblank & sprite hit flag */ + m_regs[PPU_STATUS] &= ~(PPU_STATUS_VBLANK | PPU_STATUS_SPRITE0_HIT | PPU_STATUS_8SPRITES); + } - /* reset the scanline count */ - m_scanline = 0; - //logerror("sprite 0 x: %d y: %d num: %d\n", m_spriteram[3], m_spriteram[0] + 1, m_spriteram[1]); - } + /* see if we rolled */ + else if (m_scanline == m_scanlines_per_frame) + { + /* if background or sprites are enabled, copy the ppu address latch */ + if (!blanked) + m_refresh_data = m_refresh_latch; - next_scanline = m_scanline + 1; - if (next_scanline == m_scanlines_per_frame) - next_scanline = 0; + /* reset the scanline count */ + m_scanline = 0; + //logerror("sprite 0 x: %d y: %d num: %d\n", m_spriteram[3], m_spriteram[0] + 1, m_spriteram[1]); + } - // Call us back when the hblank starts for this scanline - m_hblank_timer->adjust(m_cpu->cycles_to_attotime(260) / 3); // ??? FIXME - hardcoding NTSC, need better calculation + next_scanline = m_scanline + 1; + if (next_scanline == m_scanlines_per_frame) + next_scanline = 0; - // trigger again at the start of the next scanline - m_scanline_timer->adjust(screen().time_until_pos(next_scanline * m_scan_scale)); - break; + // Call us back when the hblank starts for this scanline + m_hblank_timer->adjust(m_cpu->cycles_to_attotime(260) / 3); // ??? FIXME - hardcoding NTSC, need better calculation + + // trigger again at the start of the next scanline + m_scanline_timer->adjust(screen().time_until_pos(next_scanline * m_scan_scale)); + break; } } @@ -587,20 +600,20 @@ void ppu2c0x_device::read_tile_plane_data(int address, int color) m_planebuf[1] = readbyte((address + 8) & 0x1fff); } -void ppu2c0x_device::shift_tile_plane_data(uint8_t &pix) +void ppu2c0x_device::shift_tile_plane_data(uint8_t& pix) { pix = ((m_planebuf[0] >> 7) & 1) | (((m_planebuf[1] >> 7) & 1) << 1); m_planebuf[0] = m_planebuf[0] << 1; m_planebuf[1] = m_planebuf[1] << 1; } -void ppu2c0x_device::draw_tile_pixel(uint8_t pix, int color, pen_t back_pen, uint32_t *&dest, const pen_t *color_table) +void ppu2c0x_device::draw_tile_pixel(uint8_t pix, int color, pen_t back_pen, uint32_t*& dest, const pen_t* color_table) { pen_t pen; if (pix) { - const pen_t *paldata = &color_table[4 * color]; + const pen_t* paldata = &color_table[4 * color]; pen = this->pen(paldata[pix]); } else @@ -611,7 +624,7 @@ void ppu2c0x_device::draw_tile_pixel(uint8_t pix, int color, pen_t back_pen, uin *dest = pen; } -void ppu2c0x_device::draw_tile(uint8_t *line_priority, int color_byte, int color_bits, int address, int start_x, pen_t back_pen, uint32_t *&dest, const pen_t *color_table) +void ppu2c0x_device::draw_tile(uint8_t* line_priority, int color_byte, int color_bits, int address, int start_x, pen_t back_pen, uint32_t*& dest, const pen_t* color_table) { int color = (((color_byte >> color_bits) & 0x03)); @@ -635,17 +648,17 @@ void ppu2c0x_device::draw_tile(uint8_t *line_priority, int color_byte, int color } } -void ppu2c0x_device::draw_background(uint8_t *line_priority) -{ - bitmap_rgb32 &bitmap = *m_bitmap; - int start_x = (m_x_fine ^ 0x07) - 7; - uint8_t scroll_x_coarse, scroll_y_coarse, scroll_y_fine, color_mask; - int x, tile_index, i; +// m_refresh_data is important as it is updated during rendering, and overwritten when you write new scroll values +// making raster effects more complex than on other systems +// https://retrocomputing.stackexchange.com/questions/1898/how-can-i-create-a-split-scroll-effect-in-an-nes-game - const pen_t *color_table; +void ppu2c0x_device::draw_background(uint8_t* line_priority) +{ + bitmap_rgb32& bitmap = *m_bitmap; - m_tilecount = 0; + uint8_t color_mask; + const pen_t* color_table; /* setup the color mask and colortable to use */ if (m_regs[PPU_CONTROL1] & PPU_CONTROL1_DISPLAY_MONO) @@ -664,17 +677,21 @@ void ppu2c0x_device::draw_background(uint8_t *line_priority) /* determine where in the nametable to start drawing from */ /* based on the current scanline and scroll regs */ - scroll_x_coarse = m_refresh_data & 0x1f; - scroll_y_coarse = (m_refresh_data & 0x3e0) >> 5; - scroll_y_fine = (m_refresh_data & 0x7000) >> 12; + uint8_t scroll_x_coarse = m_refresh_data & 0x001f; + uint8_t scroll_y_coarse = (m_refresh_data & 0x03e0) >> 5; + uint16_t nametable = (m_refresh_data & 0x0c00); + uint8_t scroll_y_fine = (m_refresh_data & 0x7000) >> 12; - x = scroll_x_coarse; + int x = scroll_x_coarse; /* get the tile index */ - tile_index = ((m_refresh_data & 0xc00) | 0x2000) + scroll_y_coarse * 32; + int tile_index = (nametable | 0x2000) + scroll_y_coarse * 32; /* set up dest */ - uint32_t *dest = &bitmap.pix32(m_scanline, start_x); + int start_x = (m_x_fine ^ 0x07) - 7; + uint32_t* dest = &bitmap.pix32(m_scanline, start_x); + + m_tilecount = 0; /* draw the 32 or 33 tiles that make up a line */ while (m_tilecount < 34) @@ -730,7 +747,7 @@ void ppu2c0x_device::draw_background(uint8_t *line_priority) if (!(m_regs[PPU_CONTROL1] & PPU_CONTROL1_BACKGROUND_L8)) { dest = &bitmap.pix32(m_scanline); - for (i = 0; i < 8; i++) + for (int i = 0; i < 8; i++) { *(dest++) = back_pen; line_priority[i] ^= 0x02; @@ -738,13 +755,28 @@ void ppu2c0x_device::draw_background(uint8_t *line_priority) } } +void ppu2c0x_device::draw_background_pen() +{ + bitmap_rgb32& bitmap = *m_bitmap; + + /* setup the color mask and colortable to use */ + uint8_t color_mask = (m_regs[PPU_CONTROL1] & PPU_CONTROL1_DISPLAY_MONO) ? 0xf0 : 0xff; + + /* cache the background pen */ + pen_t back_pen = pen(m_back_color & color_mask); + + // Fill this scanline with the background pen. + for (int i = 0; i < bitmap.width(); i++) + bitmap.pix32(m_scanline, i) = back_pen; +} + void ppu2c0x_device::read_sprite_plane_data(int address) { m_planebuf[0] = readbyte((address + 0) & 0x1fff); m_planebuf[1] = readbyte((address + 8) & 0x1fff); } -void ppu2c0x_device::make_sprite_pixel_data(uint8_t &pixel_data, int flipx) +void ppu2c0x_device::make_sprite_pixel_data(uint8_t& pixel_data, int flipx) { if (flipx) { @@ -760,9 +792,9 @@ void ppu2c0x_device::make_sprite_pixel_data(uint8_t &pixel_data, int flipx) } } -void ppu2c0x_device::draw_sprite_pixel(int sprite_xpos, int color, int pixel, uint8_t pixel_data, bitmap_rgb32 &bitmap) +void ppu2c0x_device::draw_sprite_pixel(int sprite_xpos, int color, int pixel, uint8_t pixel_data, bitmap_rgb32& bitmap) { - const pen_t *paldata = &m_colortable[4 * color]; + const pen_t* paldata = &m_colortable[4 * color]; bitmap.pix32(m_scanline, sprite_xpos + pixel) = pen(paldata[pixel_data]); } @@ -771,9 +803,9 @@ void ppu2c0x_device::read_extra_sprite_bits(int sprite_index) // needed for some clones } -void ppu2c0x_device::draw_sprites(uint8_t *line_priority) +void ppu2c0x_device::draw_sprites(uint8_t* line_priority) { - bitmap_rgb32 &bitmap = *m_bitmap; + bitmap_rgb32& bitmap = *m_bitmap; int sprite_xpos, sprite_ypos, sprite_index; int tile, index1; @@ -790,7 +822,7 @@ void ppu2c0x_device::draw_sprites(uint8_t *line_priority) /* determine if the sprites are 8x8 or 8x16 */ size = (m_regs[PPU_CONTROL0] & PPU_CONTROL0_SPRITE_SIZE) ? 16 : 8; - first_pixel = (m_regs[PPU_CONTROL1] & PPU_CONTROL1_SPRITES_L8)? 0: 8; + first_pixel = (m_regs[PPU_CONTROL1] & PPU_CONTROL1_SPRITES_L8) ? 0 : 8; for (sprite_index = 0; sprite_index < SPRITERAM_SIZE; sprite_index += 4) { @@ -816,11 +848,11 @@ void ppu2c0x_device::draw_sprites(uint8_t *line_priority) if ((sprite_ypos + size <= m_scanline) || (sprite_ypos > m_scanline)) continue; - tile = m_spriteram[sprite_index + 1]; + tile = m_spriteram[sprite_index + 1]; color = (m_spriteram[sprite_index + 2] & 0x03) + 4; - pri = m_spriteram[sprite_index + 2] & 0x20; - flipx = m_spriteram[sprite_index + 2] & 0x40; - flipy = m_spriteram[sprite_index + 2] & 0x80; + pri = m_spriteram[sprite_index + 2] & 0x20; + flipx = m_spriteram[sprite_index + 2] & 0x40; + flipy = m_spriteram[sprite_index + 2] & 0x80; read_extra_sprite_bits(sprite_index); if (size == 16) @@ -852,13 +884,13 @@ void ppu2c0x_device::draw_sprites(uint8_t *line_priority) if (size == 8) index1 += ((m_sprite_page == 0) ? 0 : 0x1000); - read_sprite_plane_data(index1+sprite_line); + read_sprite_plane_data(index1 + sprite_line); /* if there are more than 8 sprites on this line, set the flag */ if (sprite_count == 8) { m_regs[PPU_STATUS] |= PPU_STATUS_8SPRITES; -// logerror ("> 8 sprites, scanline: %d\n", m_scanline); + //logerror ("> 8 sprites, scanline: %d\n", m_scanline); /* the real NES only draws up to 8 sprites - the rest should be invisible */ break; @@ -956,20 +988,12 @@ void ppu2c0x_device::render_scanline() /* see if we need to render the background */ if (m_regs[PPU_CONTROL1] & PPU_CONTROL1_BACKGROUND) + { draw_background(line_priority); + } else { - bitmap_rgb32 &bitmap = *m_bitmap; - - /* setup the color mask and colortable to use */ - uint8_t color_mask = (m_regs[PPU_CONTROL1] & PPU_CONTROL1_DISPLAY_MONO) ? 0xf0 : 0xff; - - /* cache the background pen */ - pen_t back_pen = pen(m_back_color & color_mask); - - // Fill this scanline with the background pen. - for (int i = 0; i < bitmap.width(); i++) - bitmap.pix32(m_scanline, i) = back_pen; + draw_background_pen(); } m_draw_phase = PPU_DRAW_OAM; @@ -983,74 +1007,96 @@ void ppu2c0x_device::render_scanline() g_profiler.stop(); } -void ppu2c0x_device::update_scanline() +void ppu2c0x_device::scanline_increment_fine_ycounter() { - if (m_scanline <= BOTTOM_VISIBLE_SCANLINE) + /* increment the fine y-scroll */ + m_refresh_data += 0x1000; + + /* if it's rolled, increment the coarse y-scroll */ + if (m_refresh_data & 0x8000) { - /* Render this scanline if appropriate */ - if (m_regs[PPU_CONTROL1] & (PPU_CONTROL1_BACKGROUND | PPU_CONTROL1_SPRITES)) - { - if (m_scanline_timer->remaining() == attotime::zero) - { - /* If background or sprites are enabled, copy the ppu address latch */ - /* Copy only the scroll x-coarse and the x-overflow bit */ - m_refresh_data &= ~0x041f; - m_refresh_data |= (m_refresh_latch & 0x041f); - } + uint16_t tmp; + tmp = (m_refresh_data & 0x03e0) + 0x20; + m_refresh_data &= 0x7c1f; -// logerror("updating refresh_data: %04x (scanline: %d)\n", m_refresh_data, m_scanline); - render_scanline(); - } + /* handle bizarro scrolling rollover at the 30th (not 32nd) vertical tile */ + if (tmp == 0x03c0) + m_refresh_data ^= 0x0800; else - { - bitmap_rgb32 &bitmap = *m_bitmap; - pen_t back_pen; + m_refresh_data |= (tmp & 0x03e0); - /* setup the color mask and colortable to use */ - uint8_t color_mask = (m_regs[PPU_CONTROL1] & PPU_CONTROL1_DISPLAY_MONO) ? 0xf0 : 0xff; + //logerror("updating refresh_data: %04x\n", m_refresh_data); + } +} - /* cache the background pen */ - if (m_videomem_addr >= 0x3f00) - { - // If the PPU's VRAM address happens to point into palette ram space while - // both the sprites and background are disabled, the PPU paints the scanline - // with the palette entry at the VRAM address instead of the usual background - // pen. Micro Machines makes use of this feature. - int pen_num = m_palette_ram[(m_videomem_addr & 0x03) ? (m_videomem_addr & 0x1f) : 0]; +void ppu2c0x_device::update_visible_enabled_scanline() +{ + if (m_scanline_timer->remaining() == attotime::zero) + { + /* If background or sprites are enabled, copy the ppu address latch */ + /* Copy only the scroll x-coarse and the x-overflow bit */ + m_refresh_data &= ~0x041f; + m_refresh_data |= (m_refresh_latch & 0x041f); + } - back_pen = pen(pen_num); - } - else - back_pen = pen(m_back_color & color_mask); + //logerror("updating refresh_data: %04x (scanline: %d)\n", m_refresh_data, m_scanline); + render_scanline(); +} - // Fill this scanline with the background pen. - for (int i = 0; i < bitmap.width(); i++) - bitmap.pix32(m_scanline, i) = back_pen; - } +void ppu2c0x_device::update_visible_disabled_scanline() +{ + bitmap_rgb32& bitmap = *m_bitmap; + pen_t back_pen; - if (m_scanline_timer->remaining() == attotime::zero) - { - /* increment the fine y-scroll */ - m_refresh_data += 0x1000; + /* setup the color mask and colortable to use */ + uint8_t color_mask = (m_regs[PPU_CONTROL1] & PPU_CONTROL1_DISPLAY_MONO) ? 0xf0 : 0xff; - /* if it's rolled, increment the coarse y-scroll */ - if (m_refresh_data & 0x8000) - { - uint16_t tmp; - tmp = (m_refresh_data & 0x03e0) + 0x20; - m_refresh_data &= 0x7c1f; + back_pen = pen(m_back_color & color_mask); - /* handle bizarro scrolling rollover at the 30th (not 32nd) vertical tile */ - if (tmp == 0x03c0) - m_refresh_data ^= 0x0800; - else - m_refresh_data |= (tmp & 0x03e0); + if (m_paletteram_in_ppuspace) + { + /* cache the background pen */ + if (m_videomem_addr >= 0x3f00) + { + // If the PPU's VRAM address happens to point into palette ram space while + // both the sprites and background are disabled, the PPU paints the scanline + // with the palette entry at the VRAM address instead of the usual background + // pen. Micro Machines makes use of this feature. + int pen_num = m_palette_ram[(m_videomem_addr & 0x03) ? (m_videomem_addr & 0x1f) : 0]; - //logerror("updating refresh_data: %04x\n", m_refresh_data); - } + back_pen = pen(pen_num); } } + // Fill this scanline with the background pen. + for (int i = 0; i < bitmap.width(); i++) + bitmap.pix32(m_scanline, i) = back_pen; +} + +void ppu2c0x_device::update_visible_scanline() +{ + /* Render this scanline if appropriate */ + if (m_regs[PPU_CONTROL1] & (PPU_CONTROL1_BACKGROUND | PPU_CONTROL1_SPRITES)) + { + update_visible_enabled_scanline(); + } + else + { + update_visible_disabled_scanline(); + } + + if (m_scanline_timer->remaining() == attotime::zero) + { + scanline_increment_fine_ycounter(); + } +} + +void ppu2c0x_device::update_scanline() +{ + if (m_scanline <= BOTTOM_VISIBLE_SCANLINE) + { + update_visible_scanline(); + } } /************************************* @@ -1115,47 +1161,47 @@ uint8_t ppu2c0x_device::read(offs_t offset) // see which register to read switch (offset & 7) { - case PPU_STATUS: /* 2 */ - // The top 3 bits of the status register are the only ones that report data. The - // remainder contain whatever was last in the PPU data latch, except on the RC2C05 (protection) - if (m_security_value) - m_data_latch = (m_regs[PPU_STATUS] & 0xc0) | m_security_value; - else - m_data_latch = m_regs[PPU_STATUS] | (m_data_latch & 0x1f); - - // Reset hi/lo scroll toggle - m_toggle = 0; - - // If the vblank bit is set, clear all status bits but the 2 sprite flags - if (m_data_latch & PPU_STATUS_VBLANK) - m_regs[PPU_STATUS] &= 0x60; - break; + case PPU_STATUS: /* 2 */ + // The top 3 bits of the status register are the only ones that report data. The + // remainder contain whatever was last in the PPU data latch, except on the RC2C05 (protection) + if (m_security_value) + m_data_latch = (m_regs[PPU_STATUS] & 0xc0) | m_security_value; + else + m_data_latch = m_regs[PPU_STATUS] | (m_data_latch & 0x1f); - case PPU_SPRITE_DATA: /* 4 */ - m_data_latch = m_spriteram[m_regs[PPU_SPRITE_ADDRESS]]; - break; + // Reset hi/lo scroll toggle + m_toggle = 0; - case PPU_DATA: /* 7 */ - if (!m_latch.isnull()) - m_latch( m_videomem_addr & 0x3fff); + // If the vblank bit is set, clear all status bits but the 2 sprite flags + if (m_data_latch & PPU_STATUS_VBLANK) + m_regs[PPU_STATUS] &= 0x60; + break; - if (m_videomem_addr >= 0x3f00) - { - m_data_latch = readbyte(m_videomem_addr); - // buffer the mirrored NT data - m_buffered_data = readbyte(m_videomem_addr & 0x2fff); - } - else - { - m_data_latch = m_buffered_data; - m_buffered_data = readbyte(m_videomem_addr); - } + case PPU_SPRITE_DATA: /* 4 */ + m_data_latch = m_spriteram[m_regs[PPU_SPRITE_ADDRESS]]; + break; - m_videomem_addr += m_add; - break; + case PPU_DATA: /* 7 */ + if (!m_latch.isnull()) + m_latch(m_videomem_addr & 0x3fff); - default: - break; + if ((m_videomem_addr >= 0x3f00) && m_paletteram_in_ppuspace) + { + m_data_latch = readbyte(m_videomem_addr); + // buffer the mirrored NT data + m_buffered_data = readbyte(m_videomem_addr & 0x2fff); + } + else + { + m_data_latch = m_buffered_data; + m_buffered_data = readbyte(m_videomem_addr); + } + + m_videomem_addr += m_add; + break; + + default: + break; } return m_data_latch; @@ -1189,135 +1235,136 @@ void ppu2c0x_device::write(offs_t offset, uint8_t data) switch (offset & 7) { - case PPU_CONTROL0: /* 0 */ - m_regs[PPU_CONTROL0] = data; + case PPU_CONTROL0: /* 0 */ + m_regs[PPU_CONTROL0] = data; - /* update the name table number on our refresh latches */ - m_refresh_latch &= 0x73ff; - m_refresh_latch |= (data & 3) << 10; + /* update the name table number on our refresh latches */ + m_refresh_latch &= 0x73ff; + m_refresh_latch |= (data & 3) << 10; - /* the char ram bank points either 0x0000 or 0x1000 (page 0 or page 4) */ - m_tile_page = (data & PPU_CONTROL0_CHR_SELECT) >> 2; - m_sprite_page = (data & PPU_CONTROL0_SPR_SELECT) >> 1; + /* the char ram bank points either 0x0000 or 0x1000 (page 0 or page 4) */ + m_tile_page = (data & PPU_CONTROL0_CHR_SELECT) >> 2; + m_sprite_page = (data & PPU_CONTROL0_SPR_SELECT) >> 1; - m_add = (data & PPU_CONTROL0_INC) ? 32 : 1; -// logerror("control0 write: %02x (scanline: %d)\n", data, m_scanline); - break; + m_add = (data & PPU_CONTROL0_INC) ? m_line_write_increment_large : 1; + //logerror("control0 write: %02x (scanline: %d)\n", data, m_scanline); + break; - case PPU_CONTROL1: /* 1 */ - /* if color intensity has changed, change all the color tables to reflect them */ - if ((data & PPU_CONTROL1_COLOR_EMPHASIS) != (m_regs[PPU_CONTROL1] & PPU_CONTROL1_COLOR_EMPHASIS)) + case PPU_CONTROL1: /* 1 */ + /* if color intensity has changed, change all the color tables to reflect them */ + if ((data & PPU_CONTROL1_COLOR_EMPHASIS) != (m_regs[PPU_CONTROL1] & PPU_CONTROL1_COLOR_EMPHASIS)) + { + int i; + for (i = 0; i <= 0x1f; i++) { - int i; - for (i = 0; i <= 0x1f; i++) - { - uint8_t oldColor = m_palette_ram[i]; + uint8_t oldColor = m_palette_ram[i]; - m_colortable[i] = oldColor + (data & PPU_CONTROL1_COLOR_EMPHASIS) * 2; - } + m_colortable[i] = oldColor + (data & PPU_CONTROL1_COLOR_EMPHASIS) * 2; } + } -// logerror("control1 write: %02x (scanline: %d)\n", data, m_scanline); - m_regs[PPU_CONTROL1] = data; - break; - - case PPU_SPRITE_ADDRESS: /* 3 */ - m_regs[PPU_SPRITE_ADDRESS] = data; - break; + //logerror("control1 write: %02x (scanline: %d)\n", data, m_scanline); + m_regs[PPU_CONTROL1] = data; + break; - case PPU_SPRITE_DATA: /* 4 */ - // If the PPU is currently rendering the screen, 0xff is written instead of the desired data. - if (m_use_sprite_write_limitation) - if (m_scanline <= BOTTOM_VISIBLE_SCANLINE) - data = 0xff; - m_spriteram[m_regs[PPU_SPRITE_ADDRESS]] = data; - m_regs[PPU_SPRITE_ADDRESS] = (m_regs[PPU_SPRITE_ADDRESS] + 1) & 0xff; - break; + case PPU_SPRITE_ADDRESS: /* 3 */ + m_regs[PPU_SPRITE_ADDRESS] = data; + break; - case PPU_SCROLL: /* 5 */ - if (m_toggle) - { - /* second write */ - m_refresh_latch &= 0x0c1f; - m_refresh_latch |= (data & 0xf8) << 2; - m_refresh_latch |= (data & 0x07) << 12; -// logerror("scroll write 2: %d, %04x (scanline: %d)\n", data, m_refresh_latch, m_scanline); - } - else - { - /* first write */ - m_refresh_latch &= 0x7fe0; - m_refresh_latch |= (data & 0xf8) >> 3; + case PPU_SPRITE_DATA: /* 4 */ + // If the PPU is currently rendering the screen, 0xff is written instead of the desired data. + if (m_use_sprite_write_limitation) + if (m_scanline <= BOTTOM_VISIBLE_SCANLINE) + data = 0xff; + m_spriteram[m_regs[PPU_SPRITE_ADDRESS]] = data; + m_regs[PPU_SPRITE_ADDRESS] = (m_regs[PPU_SPRITE_ADDRESS] + 1) & 0xff; + break; - m_x_fine = data & 7; -// logerror("scroll write 1: %d, %04x (scanline: %d)\n", data, m_refresh_latch, m_scanline); - } + case PPU_SCROLL: /* 5 */ + if (m_toggle) + { + /* second write */ + m_refresh_latch &= 0x0c1f; + m_refresh_latch |= (data & 0xf8) << 2; + m_refresh_latch |= (data & 0x07) << 12; + //logerror("scroll write 2: %d, %04x (scanline: %d)\n", data, m_refresh_latch, m_scanline); + } + else + { + /* first write */ + m_refresh_latch &= 0x7fe0; + m_refresh_latch |= (data & 0xf8) >> 3; - m_toggle ^= 1; - break; + m_x_fine = data & 7; + //logerror("scroll write 1: %d, %04x (scanline: %d)\n", data, m_refresh_latch, m_scanline); + } - case PPU_ADDRESS: /* 6 */ - if (m_toggle) - { - /* second write */ - m_refresh_latch &= 0x7f00; - m_refresh_latch |= data; - m_refresh_data = m_refresh_latch; + m_toggle ^= 1; + break; - m_videomem_addr = m_refresh_latch; -// logerror("vram addr write 2: %02x, %04x (scanline: %d)\n", data, m_refresh_latch, m_scanline); - } - else - { - /* first write */ - m_refresh_latch &= 0x00ff; - m_refresh_latch |= (data & 0x3f) << 8; -// logerror("vram addr write 1: %02x, %04x (scanline: %d)\n", data, m_refresh_latch, m_scanline); - } + case PPU_ADDRESS: /* 6 */ + if (m_toggle) + { + /* second write */ + m_refresh_latch &= 0x7f00; + m_refresh_latch |= data; + m_refresh_data = m_refresh_latch; - m_toggle ^= 1; - break; + m_videomem_addr = m_refresh_latch; + //logerror("vram addr write 2: %02x, %04x (scanline: %d)\n", data, m_refresh_latch, m_scanline); + } + else + { + /* first write */ + m_refresh_latch &= 0x00ff; + m_refresh_latch |= (data & 0x3f) << 8; + //logerror("vram addr write 1: %02x, %04x (scanline: %d)\n", data, m_refresh_latch, m_scanline); + } - case PPU_DATA: /* 7 */ - { - int tempAddr = m_videomem_addr & 0x3fff; + m_toggle ^= 1; + break; - if (!m_latch.isnull()) - m_latch(tempAddr); + case PPU_DATA: /* 7 */ + { + int tempAddr = m_videomem_addr & 0x3fff; - /* if there's a callback, call it now */ - if (!m_vidaccess_callback_proc.isnull()) - data = m_vidaccess_callback_proc(tempAddr, data); + if (!m_latch.isnull()) + m_latch(tempAddr); - /* see if it's on the chargen portion */ - if (tempAddr < 0x2000) - { - /* store the data */ - writebyte(tempAddr, data); - } + /* if there's a callback, call it now */ + if (!m_vidaccess_callback_proc.isnull()) + data = m_vidaccess_callback_proc(tempAddr, data); - else - { - writebyte(tempAddr, data); - } - /* increment the address */ - m_videomem_addr += m_add; - } - break; + /* see if it's on the chargen portion */ + if (tempAddr < 0x2000) + { + /* store the data */ + writebyte(tempAddr, data); + } + else // this codepath is identical? + { + writebyte(tempAddr, data); + } + /* increment the address */ + m_videomem_addr += m_add; + } + break; - default: - /* ignore other registers writes */ - break; + default: + /* ignore other registers writes */ + break; } m_data_latch = data; } -uint16_t ppu2c0x_device::get_vram_dest() { +uint16_t ppu2c0x_device::get_vram_dest() +{ return m_videomem_addr; } -void ppu2c0x_device::set_vram_dest(uint16_t dest) { +void ppu2c0x_device::set_vram_dest(uint16_t dest) +{ m_videomem_addr = dest; } @@ -1327,7 +1374,7 @@ void ppu2c0x_device::set_vram_dest(uint16_t dest) { * *************************************/ -void ppu2c0x_device::spriteram_dma( address_space &space, const uint8_t page ) +void ppu2c0x_device::spriteram_dma(address_space& space, const uint8_t page) { int i; int address = page << 8; @@ -1348,7 +1395,7 @@ void ppu2c0x_device::spriteram_dma( address_space &space, const uint8_t page ) * *************************************/ -void ppu2c0x_device::render(bitmap_rgb32 &bitmap, int flipx, int flipy, int sx, int sy, const rectangle &cliprect) +void ppu2c0x_device::render(bitmap_rgb32& bitmap, int flipx, int flipy, int sx, int sy, const rectangle& cliprect) { if (m_scanline_timer->remaining() != attotime::zero) { @@ -1358,7 +1405,7 @@ void ppu2c0x_device::render(bitmap_rgb32 &bitmap, int flipx, int flipy, int sx, copybitmap(bitmap, *m_bitmap, flipx, flipy, sx, sy, cliprect); } -uint32_t ppu2c0x_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +uint32_t ppu2c0x_device::screen_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect) { render(bitmap, 0, 0, 0, 0, cliprect); return 0; |