diff options
Diffstat (limited to 'src/devices/video/315_5124.cpp')
-rw-r--r-- | src/devices/video/315_5124.cpp | 996 |
1 files changed, 665 insertions, 331 deletions
diff --git a/src/devices/video/315_5124.cpp b/src/devices/video/315_5124.cpp index 75e27e60b54..015ee73040f 100644 --- a/src/devices/video/315_5124.cpp +++ b/src/devices/video/315_5124.cpp @@ -7,16 +7,17 @@ Implementation of video hardware chips used by Sega System E, Master System, and Game Gear. + Some specific behavior of the chip used by Sega Genesis/Mega + Drive is also implemented for mode 4 only. + **********************************************************************/ /* To do: - - Display mode 1 (text) - - Display mode 3 (multicolor) - - Sprite doubling bug of the 315-5124 chip - - Verify timing on the Game Gear (315-5378 chip) + - Register 3 behaviour in mode 4 + - VRAM/CRAM access constraints SMS Display Timing @@ -69,28 +70,13 @@ PAL frame timing #define SEGA315_5124_PALETTE_SIZE (64 + 16) -#define SEGA315_5378_PALETTE_SIZE 4096 +#define SEGA315_5377_PALETTE_SIZE 4096 #define VRAM_SIZE 0x4000 #define STATUS_VINT 0x80 /* Pending vertical interrupt flag */ #define STATUS_SPROVR 0x40 /* Sprite overflow flag */ #define STATUS_SPRCOL 0x20 /* Object collision flag */ -#define STATUS_HINT 0x02 /* Pending horizontal interrupt flag */ - -#define VINT_HPOS 24 -#define VINT_FLAG_HPOS 24 -#define HINT_HPOS 26 -#define NMI_HPOS 28 /* not verified */ -#define VCOUNT_CHANGE_HPOS 23 -#define SPROVR_HPOS 24 -#define SPRCOL_BASEHPOS 59 -#define XSCROLL_HPOS 21 -#define DISPLAY_DISABLED_HPOS 24 /* not verified, works if above 18 (for 'pstrike2') and below 25 (for 'fantdizzy') */ -#define DISPLAY_CB_HPOS 2 /* fixes 'roadrash' (SMS game) title scrolling, due to line counter reload timing */ - -#define DRAW_TIME_GG 94 /* 9 + 2 + 14 + 8 + 13 + 96/2 */ -#define DRAW_TIME_SMS 46 /* 9 + 2 + 14 + 8 + 13 */ #define PRIORITY_BIT 0x1000 #define BACKDROP_COLOR ((m_vdp_mode == 4 ? 0x10 : 0x00) + (m_reg[0x07] & 0x0f)) @@ -109,20 +95,37 @@ static constexpr uint8_t pal_192[6] = { 3, 13, 54, 192, 48, 3 }; static constexpr uint8_t pal_224[6] = { 3, 13, 38, 224, 32, 3 }; static constexpr uint8_t pal_240[6] = { 3, 13, 30, 240, 24, 3 }; +#define VINT_HPOS 0 +#define VINT_FLAG_HPOS 1 +#define HINT_HPOS 2 +#define NMI_HPOS 3 +#define XSCROLL_HPOS 4 +#define VCOUNT_CHANGE_HPOS 5 +#define SPROVR_HPOS 6 +#define SPRCOL_BASEHPOS 7 + +static constexpr uint8_t line_315_5124[8] = { 24, 24, 26, 28 /* not verified */, 21, 23, 24, 59 }; +static constexpr uint8_t line_315_5377[8] = { 26, 26, 27, 28 /* not verified */, 24, 28, 26, 62 }; + +#define DISPLAY_DISABLED_HPOS 24 /* not verified, works if above 18 (for 'pstrike2') and below 25 (for 'fantdizzy') */ +#define DISPLAY_CB_HPOS 2 /* fixes 'roadrash' (SMS game) title scrolling, due to line counter reload timing */ + +#define DRAW_TIME_GG 94 /* 9 + 2 + 14 + 8 + 13 + 96/2 */ +#define DRAW_TIME_SMS 46 /* 9 + 2 + 14 + 8 + 13 */ + DEFINE_DEVICE_TYPE(SEGA315_5124, sega315_5124_device, "sega315_5124", "Sega 315-5124 SMS1 VDP") DEFINE_DEVICE_TYPE(SEGA315_5246, sega315_5246_device, "sega315_5246", "Sega 315-5246 SMS2 VDP") -DEFINE_DEVICE_TYPE(SEGA315_5378, sega315_5378_device, "sega315_5378", "Sega 315-5378 Gamegear VDP") +DEFINE_DEVICE_TYPE(SEGA315_5377, sega315_5377_device, "sega315_5377", "Sega 315-5377 Gamegear VDP") PALETTE_INIT_MEMBER(sega315_5124_device, sega315_5124) { - int i; - for (i = 0; i < 64; i++) + for (int i = 0; i < 64; i++) { - int r = i & 0x03; - int g = (i & 0x0c) >> 2; - int b = (i & 0x30) >> 4; + const int r = i & 0x03; + const int g = (i & 0x0c) >> 2; + const int b = (i & 0x30) >> 4; palette.set_pen_color(i, pal2bit(r), pal2bit(g), pal2bit(b)); } /* sms and sg1000-mark3 uses a different palette for modes 0 to 3 - see http://www.smspower.org/Development/Palette */ @@ -146,14 +149,13 @@ PALETTE_INIT_MEMBER(sega315_5124_device, sega315_5124) } -PALETTE_INIT_MEMBER(sega315_5378_device, sega315_5378) +PALETTE_INIT_MEMBER(sega315_5377_device, sega315_5377) { - int i; - for (i = 0; i < 4096; i++) + for (int i = 0; i < 4096; i++) { - int r = i & 0x000f; - int g = (i & 0x00f0) >> 4; - int b = (i & 0x0f00) >> 8; + const int r = i & 0x000f; + const int g = (i & 0x00f0) >> 4; + const int b = (i & 0x0f00) >> 8; palette.set_pen_color(i, pal4bit(r), pal4bit(g), pal4bit(b)); } } @@ -167,18 +169,21 @@ void sega315_5124_device::sega315_5124(address_map &map) sega315_5124_device::sega315_5124_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : sega315_5124_device(mconfig, SEGA315_5124, tag, owner, clock, SEGA315_5124_CRAM_SIZE, 0, false) + : sega315_5124_device(mconfig, SEGA315_5124, tag, owner, clock, SEGA315_5124_CRAM_SIZE, 0x00, 0x0f, 4, 8, line_315_5124) { } -sega315_5124_device::sega315_5124_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint8_t cram_size, uint8_t palette_offset, bool supports_224_240) +sega315_5124_device::sega315_5124_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint8_t cram_size, uint8_t palette_offset, uint8_t reg_num_mask, int max_sprite_zoom_hcount, int max_sprite_zoom_vcount, const uint8_t *line_timing) : device_t(mconfig, type, tag, owner, clock) , device_memory_interface(mconfig, *this) , device_video_interface(mconfig, *this) , m_cram_size( cram_size ) + , m_line_timing( line_timing ) , m_palette_offset( palette_offset ) - , m_supports_224_240( supports_224_240 ) + , m_reg_num_mask( reg_num_mask ) + , m_max_sprite_zoom_hcount( max_sprite_zoom_hcount ) + , m_max_sprite_zoom_vcount( max_sprite_zoom_vcount ) , m_is_pal(false) , m_int_cb(*this) , m_csync_cb(*this) @@ -190,13 +195,26 @@ sega315_5124_device::sega315_5124_device(const machine_config &mconfig, device_t sega315_5246_device::sega315_5246_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : sega315_5124_device(mconfig, SEGA315_5246, tag, owner, clock, SEGA315_5124_CRAM_SIZE, 0, true) + : sega315_5124_device(mconfig, SEGA315_5246, tag, owner, clock, SEGA315_5124_CRAM_SIZE, 0x00, 0x0f, 8, 8, line_315_5124) +{ +} + + +sega315_5246_device::sega315_5246_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint8_t cram_size, uint8_t palette_offset, uint8_t reg_num_mask, int max_sprite_zoom_hcount, int max_sprite_zoom_vcount, const uint8_t *line_timing) + : sega315_5124_device(mconfig, SEGA315_5246, tag, owner, clock, cram_size, palette_offset, reg_num_mask, max_sprite_zoom_hcount, max_sprite_zoom_vcount, line_timing) { } -sega315_5378_device::sega315_5378_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : sega315_5124_device(mconfig, SEGA315_5378, tag, owner, clock, SEGA315_5378_CRAM_SIZE, 0x10, true) +// Embedded mode 4 support of the 315-5313 VDP (see 315_5313.cpp), used by Sega Genesis/Mega Drive +sega315_5313_mode4_device::sega315_5313_mode4_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint8_t cram_size, uint8_t palette_offset, uint8_t reg_num_mask, int max_sprite_zoom_hcount, int max_sprite_zoom_vcount, const uint8_t *line_timing) + : sega315_5246_device(mconfig, SEGA315_5246, tag, owner, clock, cram_size, palette_offset, reg_num_mask, max_sprite_zoom_hcount, max_sprite_zoom_vcount, line_timing) +{ +} + + +sega315_5377_device::sega315_5377_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : sega315_5246_device(mconfig, SEGA315_5377, tag, owner, clock, SEGA315_5377_CRAM_SIZE, 0x10, 0x0f, 8, 8, line_315_5377) { } @@ -209,67 +227,104 @@ device_memory_interface::space_config_vector sega315_5124_device::memory_space_c } -void sega315_5124_device::set_display_settings() +void sega315_5124_device::select_extended_res_mode4(bool M1, bool M2, bool M3) { - const bool M1 = m_reg[0x01] & 0x10; - const bool M2 = m_reg[0x00] & 0x02; - const bool M3 = m_reg[0x01] & 0x08; - const bool M4 = m_reg[0x00] & 0x04; + // no extended resolution supported. + return; +} - m_y_pixels = 192; + +void sega315_5246_device::select_extended_res_mode4(bool M1, bool M2, bool M3) +{ + if (M2) + { + if (M1 && !M3) + { + m_y_pixels = 224; /* 224-line display */ + } + else if (!M1 && M3) + { + m_y_pixels = 240; /* 240-line display */ + } + } +} + +void sega315_5313_mode4_device::select_extended_res_mode4(bool M1, bool M2, bool M3) +{ + // no extended resolution supported. + return; +} + + +void sega315_5124_device::select_display_mode() +{ + const bool M1 = BIT(m_reg[0x01], 4); + const bool M2 = BIT(m_reg[0x00], 1); + const bool M3 = BIT(m_reg[0x01], 3); + const bool M4 = BIT(m_reg[0x00], 2); if (M4) { /* mode 4 */ m_vdp_mode = 4; - if ( m_supports_224_240 ) - { - if (M2) - { - if (M1 && !M3) - { - m_y_pixels = 224; /* 224-line display */ - } - else if (!M1 && M3) - { - m_y_pixels = 240; /* 240-line display */ - } - } - } + select_extended_res_mode4(M1, M2, M3); } - else + else /* original TMS9918 mode */ { - /* original TMS9918 mode */ + /* Mode 0 (Graphics I Mode) */ if (!M1 && !M2 && !M3) { m_vdp_mode = 0; } -#if 0 - /* Mode 1, not implemented */ + /* Mode 1 (Text Mode) */ else if (M1 && !M2 && !M3) { m_vdp_mode = 1; } -#endif + /* Mode 2 (Graphics II Mode) */ else if (!M1 && M2 && !M3) { m_vdp_mode = 2; } -#if 0 - /* Mode 3, not implemented */ + /* Mode 3 (Multicolor Mode) */ else if (!M1 && !M2 && M3) { m_vdp_mode = 3; } -#endif else { logerror("Unknown video mode detected (M1 = %c, M2 = %c, M3 = %c, M4 = %c)\n", M1 ? '1' : '0', M2 ? '1' : '0', M3 ? '1' : '0', M4 ? '1' : '0'); } } +} + + +void sega315_5313_mode4_device::select_display_mode() +{ + const bool M5 = BIT(m_reg[0x01], 2); + + if (M5) + { + /* mode 5, not implemented */ + m_vdp_mode = 5; + logerror("Switched to unimplemented video mode 5 !\n"); + } + else + { + /* mode 4, SMS compatibility */ + m_vdp_mode = 4; + } +} + + +void sega315_5124_device::set_display_settings() +{ + m_y_pixels = 192; + + select_display_mode(); set_frame_timing(); - m_cram_dirty = 1; + m_cram_dirty = true; } @@ -297,7 +352,7 @@ READ8_MEMBER( sega315_5124_device::vcount_read ) const int active_scr_start = m_frame_timing[VERTICAL_SYNC] + m_frame_timing[TOP_BLANKING] + m_frame_timing[TOP_BORDER]; int vpos = screen().vpos(); - if (screen().hpos() < VCOUNT_CHANGE_HPOS) + if (screen().hpos() < m_line_timing[VCOUNT_CHANGE_HPOS]) { vpos--; if (vpos < 0) @@ -327,10 +382,10 @@ void sega315_5124_device::hcount_latch_at_hpos( int hpos ) } -void sega315_5378_device::set_sega315_5124_compatibility_mode( bool sega315_5124_compatibility_mode ) +void sega315_5377_device::set_sega315_5124_compatibility_mode( bool sega315_5124_compatibility_mode ) { m_sega315_5124_compatibility_mode = sega315_5124_compatibility_mode; - m_cram_mask = (!m_sega315_5124_compatibility_mode) ? (SEGA315_5378_CRAM_SIZE - 1) : (SEGA315_5124_CRAM_SIZE - 1); + m_cram_mask = (!m_sega315_5124_compatibility_mode) ? (SEGA315_5377_CRAM_SIZE - 1) : (SEGA315_5124_CRAM_SIZE - 1); m_draw_time = m_sega315_5124_compatibility_mode ? DRAW_TIME_SMS : DRAW_TIME_GG; } @@ -384,9 +439,9 @@ void sega315_5124_device::device_timer(emu_timer &timer, device_timer_id id, int break; case TIMER_HINT: - if ((m_pending_status & STATUS_HINT) || (m_status & STATUS_HINT)) + if (m_pending_hint || m_hint_occurred) { - if ((m_reg[0x00] & 0x10)) + if (BIT(m_reg[0x00], 4)) { m_irq_state = 1; @@ -399,7 +454,7 @@ void sega315_5124_device::device_timer(emu_timer &timer, device_timer_id id, int case TIMER_VINT: if ((m_pending_status & STATUS_VINT) || (m_status & STATUS_VINT)) { - if ((m_reg[0x01] & 0x20)) + if (BIT(m_reg[0x01], 5)) { m_irq_state = 1; @@ -425,7 +480,7 @@ void sega315_5124_device::process_line_timer() + m_frame_timing[BOTTOM_BORDER] + m_frame_timing[BOTTOM_BLANKING]; /* copy current values in case they are not changed until latch time */ - m_display_disabled = !(m_reg[0x01] & 0x40); + m_display_disabled = !BIT(m_reg[0x01], 6); m_reg8copy = m_reg[0x08]; /* Check if the /CSYNC signal must be active (low) */ @@ -463,8 +518,8 @@ void sega315_5124_device::process_line_timer() if (m_line_counter == 0x00) { m_line_counter = m_reg[0x0a]; - m_hint_timer->adjust( screen().time_until_pos( vpos, HINT_HPOS ) ); - m_pending_status |= STATUS_HINT; + m_hint_timer->adjust( screen().time_until_pos( vpos, m_line_timing[HINT_HPOS] ) ); + m_pending_hint = true; } else { @@ -476,9 +531,10 @@ void sega315_5124_device::process_line_timer() m_line_counter = m_reg[0x0a]; } + // vpos_limit + 1 because VINT fires at the end of the first logical line of the bottom border. if (vpos == vpos_limit + 1) { - m_vint_timer->adjust( screen().time_until_pos( vpos, VINT_HPOS ) ); + m_vint_timer->adjust( screen().time_until_pos( vpos, m_line_timing[VINT_HPOS] ) ); m_pending_status |= STATUS_VINT; } @@ -507,8 +563,8 @@ void sega315_5124_device::process_line_timer() if (m_line_counter == 0x00) { m_line_counter = m_reg[0x0a]; - m_hint_timer->adjust( screen().time_until_pos( vpos, HINT_HPOS ) ); - m_pending_status |= STATUS_HINT; + m_hint_timer->adjust( screen().time_until_pos( vpos, m_line_timing[HINT_HPOS] ) ); + m_pending_hint = true; } else { @@ -535,7 +591,7 @@ void sega315_5124_device::process_line_timer() /* Check if we're on the last line of the top border */ if (vpos == vpos_limit + m_frame_timing[TOP_BORDER] - 1) { - m_nmi_timer->adjust( screen().time_until_pos( vpos, NMI_HPOS ) ); + m_nmi_timer->adjust( screen().time_until_pos( vpos, m_line_timing[NMI_HPOS] ) ); } /* Draw borders */ @@ -555,18 +611,13 @@ void sega315_5124_device::process_line_timer() } -READ8_MEMBER( sega315_5124_device::vram_read ) +READ8_MEMBER( sega315_5124_device::data_read ) { - uint8_t temp; + /* Return data buffer contents */ + const uint8_t temp = m_buffer; - /* SMS 2 & GG behaviour. Seems like the latched data is passed straight through */ - /* to the address register when in the middle of doing a command. */ - /* Cosmic Spacehead needs this, among others */ /* Clear pending write flag */ - m_pending_reg_write = 0; - - /* Return read buffer contents */ - temp = m_buffer; + m_pending_control_write = false; if ( !machine().side_effects_disabled() ) { @@ -584,7 +635,7 @@ void sega315_5124_device::check_pending_flags() { int hpos; - if (!m_pending_status) + if (!(m_pending_status & (STATUS_VINT | STATUS_SPROVR | STATUS_SPRCOL)) && !m_pending_hint) { return; } @@ -604,20 +655,24 @@ void sega315_5124_device::check_pending_flags() hpos = screen().hpos(); } - if ((m_pending_status & STATUS_HINT) && hpos >= HINT_HPOS) + if ((m_pending_hint) && hpos >= m_line_timing[HINT_HPOS]) { - m_pending_status &= ~STATUS_HINT; - m_status |= STATUS_HINT; // fake flag, it is overridden on register read. + m_pending_hint = false; + m_hint_occurred = true; } - if ((m_pending_status & STATUS_VINT) && hpos >= VINT_FLAG_HPOS) + if ((m_pending_status & STATUS_VINT) && hpos >= m_line_timing[VINT_FLAG_HPOS]) { m_pending_status &= ~STATUS_VINT; m_status |= STATUS_VINT; } - if ((m_pending_status & STATUS_SPROVR) && hpos >= SPROVR_HPOS) + if ((m_pending_status & STATUS_SPROVR) && hpos >= m_line_timing[SPROVR_HPOS]) { m_pending_status &= ~STATUS_SPROVR; m_status |= STATUS_SPROVR; + // copy and reset the pending bits that were based on the number + // of the first sprite that overflowed. + m_status &= m_pending_status | (STATUS_VINT | STATUS_SPROVR | STATUS_SPRCOL); + m_pending_status |= ~(STATUS_VINT | STATUS_SPROVR | STATUS_SPRCOL); } if ((m_pending_status & STATUS_SPRCOL) && hpos >= m_pending_sprcol_x) { @@ -628,7 +683,7 @@ void sega315_5124_device::check_pending_flags() } -READ8_MEMBER( sega315_5124_device::register_read ) +READ8_MEMBER( sega315_5124_device::control_read ) { uint8_t temp; @@ -638,9 +693,11 @@ READ8_MEMBER( sega315_5124_device::register_read ) if ( !machine().side_effects_disabled() ) { /* Clear pending write flag */ - m_pending_reg_write = 0; + m_pending_control_write = false; - m_status &= ~(STATUS_VINT | STATUS_SPROVR | STATUS_SPRCOL | STATUS_HINT); + /* Clear status flags */ + m_hint_occurred = false; + m_status = (uint8_t) ~(STATUS_VINT | STATUS_SPROVR | STATUS_SPRCOL); if (m_irq_state == 1) { @@ -651,19 +708,12 @@ READ8_MEMBER( sega315_5124_device::register_read ) } } - /* low 5 bits return non-zero data (it fixes PGA Tour Golf course map introduction) */ - return temp | 0x1f; + return temp; } -WRITE8_MEMBER( sega315_5124_device::vram_write ) +void sega315_5124_device::write_memory(uint8_t data) { - /* SMS 2 & GG behaviour. Seems like the latched data is passed straight through */ - /* to the address register when in the middle of doing a command. */ - /* Cosmic Spacehead needs this, among others */ - /* Clear pending write flag */ - m_pending_reg_write = 0; - switch(m_addrmode) { case 0x00: @@ -677,27 +727,84 @@ WRITE8_MEMBER( sega315_5124_device::vram_write ) break; } + // data written to data port loads the data buffer. m_buffer = data; - m_addr += 1; } -WRITE8_MEMBER( sega315_5124_device::register_write ) +void sega315_5313_mode4_device::write_memory(uint8_t data) { - int reg_num; + switch(m_addrmode) + { + case 0x00: + case 0x01: + this->space().write_byte(m_addr & 0x3fff, data); + break; + + case 0x02: + case 0x03: + cram_write(data); + break; + } + // data buffer isn't loaded on data port write. +} + + +WRITE8_MEMBER( sega315_5124_device::data_write ) +{ + /* Clear pending write flag */ + m_pending_control_write = false; + + write_memory(data); + m_addr += 1; +} + - if (m_pending_reg_write == 0) +void sega315_5124_device::load_vram_addr(uint8_t data) +{ + /* Seems like the latched data is passed straight through */ + /* to the address register when in the middle of doing a command. */ + /* Cosmic Spacehead needs this, among others */ + if (m_pending_control_write) { m_addr = (m_addr & 0xff00) | data; - m_pending_reg_write = 1; } else { + m_addr = (data << 8) | (m_addr & 0xff); + } +} + + +void sega315_5313_mode4_device::load_vram_addr(uint8_t data) +{ + if (m_pending_control_write) + { + m_control_write_data_latch = data; + } + else + { + m_addr = (data << 8) | m_control_write_data_latch; + } +} + + +WRITE8_MEMBER( sega315_5124_device::control_write ) +{ + if (!m_pending_control_write) + { + m_pending_control_write = true; + load_vram_addr(data); + } + else + { + int reg_num; + /* Clear pending write flag */ - m_pending_reg_write = 0; + m_pending_control_write = false; m_addrmode = (data >> 6) & 0x03; - m_addr = (data << 8) | (m_addr & 0xff); + load_vram_addr(data); switch (m_addrmode) { case 0: /* VRAM reading mode */ @@ -709,7 +816,15 @@ WRITE8_MEMBER( sega315_5124_device::register_write ) break; case 2: /* VDP register write */ - reg_num = data & 0x0f; + reg_num = data & m_reg_num_mask; + // for the 315-5313, the proper bit count for register + // numbers is emulated, but because it allows for more + // than 16 registers, what is not implemented, there is + // a break here if the limit is surpassed. + if (reg_num >= 16) + { + break; + } m_reg[reg_num] = m_addr & 0xff; //logerror("%s: %s: setting register %x to %02x\n", machine().describe_context(), tag(), reg_num, m_addr & 0xff); @@ -717,22 +832,22 @@ WRITE8_MEMBER( sega315_5124_device::register_write ) { case 0: set_display_settings(); - if (m_addr & 0x02) + if (BIT(m_addr, 1)) logerror("overscan enabled.\n"); break; case 1: set_display_settings(); if (screen().hpos() <= DISPLAY_DISABLED_HPOS) - m_display_disabled = !(m_reg[0x01] & 0x40); + m_display_disabled = !BIT(m_reg[0x01], 6); break; case 8: - if (screen().hpos() <= XSCROLL_HPOS) + if (screen().hpos() <= m_line_timing[XSCROLL_HPOS]) m_reg8copy = m_reg[0x08]; } check_pending_flags(); - if ( ( reg_num == 0 && (m_status & STATUS_HINT) ) || + if ( ( reg_num == 0 && (m_hint_occurred) ) || ( reg_num == 1 && (m_status & STATUS_VINT) ) ) { // For HINT disabling through register 00: @@ -742,12 +857,12 @@ WRITE8_MEMBER( sega315_5124_device::register_write ) // // For VINT disabling through register 01: // When running eagles5 on the smskr driver the irq_state is 1 because of some - // previos HINTs that occurred. eagles5 sets register 01 to 0x02 and expects + // previous HINTs that occurred. eagles5 sets register 01 to 0x02 and expects // the irq state to be cleared after that. // The following bit of code takes care of that. // - if ( ( reg_num == 0 && !(m_reg[0x00] & 0x10) ) || - ( reg_num == 1 && !(m_reg[0x01] & 0x20) ) ) + if ( ( reg_num == 0 && !BIT(m_reg[0x00], 4) ) || + ( reg_num == 1 && !BIT(m_reg[0x01], 5) ) ) { if (m_irq_state == 1) { @@ -783,37 +898,74 @@ WRITE8_MEMBER( sega315_5124_device::register_write ) } -uint16_t sega315_5124_device::get_name_table_row(int row) +uint16_t sega315_5124_device::name_table_row_mode4(int row) { - return ((row >> 3) << 6) & (((m_reg[0x02] & 0x01) << 10) | 0x3bff); + return ((row >> 3) << 6) & (((m_reg[0x02] & 0x01) << 10) | ((m_reg[0x04] & 0x03) << 11) | 0x23ff); } -uint16_t sega315_5246_device::get_name_table_row(int row) +uint16_t sega315_5246_device::name_table_row_mode4(int row) { return (row >> 3) << 6; } -uint16_t sega315_5378_device::get_name_table_row(int row) +void sega315_5124_device::draw_column0_x_scroll_mode4(int *line_buffer, int *priority_selected, int x_scroll_fine_adjust, int palette_selected, int tile_line) { - return (row >> 3) << 6; + // To draw the leftmost column when it is incomplete on screen due to + // scrolling, Sega Master System has a weird behaviour to select which + // palette will be used to obtain the color in entry 0, that depends + // on the content of sprite 0. + // This implementation mimics the behaviour of the Emulicious emulator, + // seen with the test ROM provided by sverx here: + // + // http://www.smspower.org/forums/15653-CommunityEffortRequestHelpDiscoverHowTheVDPHandlesTheLeftmostPixelsWhenScrolling + // + // From the sprite 0 tile, it uses bit 1 of the plane that would + // select the color for the pixel 4 at the current line. + + const int pixel_x = 4; + + // Locate the tile number for sprite 0. + const int sprite_tile_selected = select_sprite_tile_mode4(0, tile_line & 0x07); + + // Load data of bit plane 1 for the sprite tile. + const int tmp_bit_plane_1 = space().read_byte((sprite_tile_selected << 5) + ((tile_line & 0x07) << 2) + 0x01); + const uint8_t pen_bit_1 = BIT(tmp_bit_plane_1, 7 - pixel_x); + + for (int pixel_plot_x = 0; pixel_plot_x < x_scroll_fine_adjust; pixel_plot_x++) + { + line_buffer[pixel_plot_x] = m_current_palette[pen_bit_1 ? 0x10 : 0x00]; + priority_selected[pixel_plot_x] = 0; + } +} + + +void sega315_5313_mode4_device::draw_column0_x_scroll_mode4(int *line_buffer, int *priority_selected, int x_scroll_fine_adjust, int palette_selected, int tile_line) +{ + // To draw the leftmost column when it is incomplete on screen due to + // scrolling, Sega Genesis/Mega Drive seems to use entry #0 of the + // palette selected by the next background tile to be drawn on screen. + + for (int pixel_plot_x = 0; pixel_plot_x < x_scroll_fine_adjust; pixel_plot_x++) + { + line_buffer[pixel_plot_x] = m_current_palette[palette_selected ? 0x10 : 0x00]; + priority_selected[pixel_plot_x] = 0; + } } void sega315_5124_device::draw_scanline_mode4( int *line_buffer, int *priority_selected, int line ) { - int tile_column; - int y_scroll, scroll_mod; - int pixel_x, pixel_plot_x; - int bit_plane_0, bit_plane_1, bit_plane_2, bit_plane_3; + int scroll_mod; uint16_t name_table_address; /* if top 2 rows of screen not affected by horizontal scrolling, then x_scroll = 0 */ /* else x_scroll = m_reg8copy */ - const int x_scroll = (((m_reg[0x00] & 0x40) && (line < 16)) ? 0 : 0x0100 - m_reg8copy); + const int x_scroll = ((BIT(m_reg[0x00], 6) && (line < 16)) ? 0 : m_reg8copy); - const int x_scroll_start_column = (x_scroll >> 3); /* x starting column tile */ + const int x_scroll_start_column = 32 - (x_scroll >> 3); /* x starting column tile */ + const int x_scroll_fine_adjust = (x_scroll & 0x07); if ( m_y_pixels != 192 ) { @@ -827,43 +979,49 @@ void sega315_5124_device::draw_scanline_mode4( int *line_buffer, int *priority_s } /* Draw background layer */ - for (tile_column = 0; tile_column < 33; tile_column++) + for (int tile_column = 0; tile_column < 32; tile_column++) { - uint16_t tile_data; - int tile_selected, palette_selected, horiz_selected, vert_selected, priority_select; int tile_line; + const int table_column = ((tile_column + x_scroll_start_column) & 0x1f) << 1; /* Rightmost 8 columns for SMS (or 2 columns for GG) not affected by */ /* vertical scrolling when bit 7 of reg[0x00] is set */ - y_scroll = ((m_reg[0x00] & 0x80) && (tile_column > 23)) ? 0 : m_reg9copy; + const int y_scroll = (BIT(m_reg[0x00], 7) && (tile_column > 23)) ? 0 : m_reg9copy; - tile_line = ((tile_column + x_scroll_start_column) & 0x1f) << 1; - tile_data = space().read_word(name_table_address + get_name_table_row((line + y_scroll) % scroll_mod) + tile_line); + const uint16_t tile_data = space().read_word(name_table_address + name_table_row_mode4((line + y_scroll) % scroll_mod) + table_column); - tile_selected = (tile_data & 0x01ff); - priority_select = tile_data & PRIORITY_BIT; - palette_selected = (tile_data >> 11) & 0x01; - vert_selected = (tile_data >> 10) & 0x01; - horiz_selected = (tile_data >> 9) & 0x01; + const int tile_selected = (tile_data & 0x01ff); + const int priority_select = tile_data & PRIORITY_BIT; + const int palette_selected = BIT(tile_data, 11); + const int vert_selected = BIT(tile_data, 10); + const int horiz_selected = BIT(tile_data, 9); tile_line = line - ((0x07 - (y_scroll & 0x07)) + 1); if (vert_selected) tile_line = 0x07 - tile_line; - bit_plane_0 = space().read_byte(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x00); - bit_plane_1 = space().read_byte(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x01); - bit_plane_2 = space().read_byte(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x02); - bit_plane_3 = space().read_byte(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x03); + const int bit_plane_0 = space().read_byte(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x00); + const int bit_plane_1 = space().read_byte(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x01); + const int bit_plane_2 = space().read_byte(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x02); + const int bit_plane_3 = space().read_byte(((tile_selected << 5) + ((tile_line & 0x07) << 2)) + 0x03); + + /* Column 0 is the leftmost tile column that completely entered in the screen. + If the leftmost pixels aren't part of a complete tile, due to horizontal + scrolling, they are drawn only with color #0 of the selected palette. */ + if (tile_column == 0 && x_scroll_fine_adjust > 0) + { + draw_column0_x_scroll_mode4(line_buffer, priority_selected, x_scroll_fine_adjust, palette_selected, tile_line); + } - for (pixel_x = 0; pixel_x < 8; pixel_x++) + for (int pixel_x = 0; pixel_x < 8; pixel_x++) { - uint8_t pen_bit_0, pen_bit_1, pen_bit_2, pen_bit_3; uint8_t pen_selected; + int pixel_plot_x; - pen_bit_0 = (bit_plane_0 >> (7 - pixel_x)) & 0x01; - pen_bit_1 = (bit_plane_1 >> (7 - pixel_x)) & 0x01; - pen_bit_2 = (bit_plane_2 >> (7 - pixel_x)) & 0x01; - pen_bit_3 = (bit_plane_3 >> (7 - pixel_x)) & 0x01; + const uint8_t pen_bit_0 = BIT(bit_plane_0, 7 - pixel_x); + const uint8_t pen_bit_1 = BIT(bit_plane_1, 7 - pixel_x); + const uint8_t pen_bit_2 = BIT(bit_plane_2, 7 - pixel_x); + const uint8_t pen_bit_3 = BIT(bit_plane_3, 7 - pixel_x); pen_selected = (pen_bit_3 << 3 | pen_bit_2 << 2 | pen_bit_1 << 1 | pen_bit_0); if (palette_selected) @@ -878,60 +1036,115 @@ void sega315_5124_device::draw_scanline_mode4( int *line_buffer, int *priority_s pixel_plot_x = 7 - pixel_x; } - pixel_plot_x = (0 - (x_scroll & 0x07) + (tile_column << 3) + pixel_plot_x); - if (pixel_plot_x >= 0 && pixel_plot_x < 256) + pixel_plot_x = x_scroll_fine_adjust + (tile_column << 3) + pixel_plot_x; + if (pixel_plot_x < 256) { - //logerror("%x %x\n", pixel_plot_x, line); - if (tile_column == 0 && (x_scroll & 0x07)) - { - /* when the first column hasn't completely entered in the screen, its - background is filled only with color #0 of the selected palette */ - line_buffer[pixel_plot_x] = m_current_palette[palette_selected ? 0x10 : 0x00]; - priority_selected[pixel_plot_x] = priority_select; - } - else - { - line_buffer[pixel_plot_x] = m_current_palette[pen_selected]; - priority_selected[pixel_plot_x] = priority_select | (pen_selected & 0x0f); - } + line_buffer[pixel_plot_x] = m_current_palette[pen_selected]; + priority_selected[pixel_plot_x] = priority_select | (pen_selected & 0x0f); } } } } -void sega315_5124_device::select_sprites( int line ) +uint16_t sega315_5124_device::sprite_attributes_addr_mode4(uint16_t base) { - int max_sprites; + return base & ((BIT(m_reg[0x05], 0) << 7) | 0x3f7f); +} - /* At this point the VDP vcount still doesn't refer the new line, - because the logical start point is slightly shifted on the scanline */ - int parse_line = line - 1; - /* Check if SI is set */ - m_sprite_height = (m_reg[0x01] & 0x02) ? 16 : 8; - /* Check if MAG is set */ - m_sprite_zoom = (m_reg[0x01] & 0x01) ? 2 : 1; +uint16_t sega315_5246_device::sprite_attributes_addr_mode4(uint16_t base) +{ + return base; +} + + +uint8_t sega315_5124_device::sprite_tile_mask_mode4(uint8_t tile_number) +{ + return tile_number & (((m_reg[0x06] & 0x03) << 6) | 0x3f); +} + + +uint8_t sega315_5246_device::sprite_tile_mask_mode4(uint8_t tile_number) +{ + return tile_number; +} + + +int sega315_5124_device::select_sprite_tile_mode4(int sprite_index, int sprite_line) +{ + int sprite_tile_selected = space().read_byte( sprite_attributes_addr_mode4(m_sprite_base + 0x81) + (sprite_index << 1) ); + + sprite_tile_selected = sprite_tile_mask_mode4(sprite_tile_selected); + + if (BIT(m_reg[0x06], 2)) + { + sprite_tile_selected += 256; /* pattern table select */ + } + + if (m_sprite_height == 16) + { + sprite_tile_selected &= 0x01fe; /* force even index */ + } - if (m_sprite_zoom > 1) + if (sprite_line > 0x07) { - /* Divide before use the value for comparison, same later with sprite_y, or - else an off-by-one bug could occur, as seen with Tarzan, for Game Gear */ - parse_line >>= 1; + sprite_tile_selected += 1; } + return sprite_tile_selected; +} + + +void sega315_5124_device::sprite_count_overflow(int line, int sprite_index) +{ + /* Overflow is flagged only on active display and when VINT isn't active */ + if (!(m_status & STATUS_VINT) && line >= 0 && line < m_frame_timing[ACTIVE_DISPLAY_V]) + { + uint8_t sprite_number_bits; + m_pending_status |= STATUS_SPROVR; + if (sprite_index < 14) { + sprite_number_bits = (sprite_index + 1) / 2; + } else { + sprite_number_bits = sprite_index / 2; + } + m_pending_status &= sprite_number_bits | (STATUS_VINT | STATUS_SPROVR | STATUS_SPRCOL); + } +} + + +void sega315_5313_mode4_device::sprite_count_overflow(int line, int sprite_index) +{ + if (!m_display_disabled) + { + sega315_5124_device::sprite_count_overflow(line, sprite_index); + } +} + + +void sega315_5124_device::select_sprites( int line ) +{ + /* Check if SI is set */ + m_sprite_height = BIT(m_reg[0x01], 1) ? 16 : 8; + /* Check if MAG is set */ + m_sprite_zoom_scale = BIT(m_reg[0x01], 0) ? 2 : 1; + m_sprite_count = 0; if ( m_vdp_mode == 0 || m_vdp_mode == 2 ) { /* TMS9918 compatibility sprites */ - max_sprites = 4; + const int max_sprites = 4; m_sprite_base = ((m_reg[0x05] & 0x7f) << 7); - for (int sprite_index = 0; (sprite_index < 32 * 4) && (m_sprite_count <= max_sprites); sprite_index += 4) + for (int sprite_index = 0; (sprite_index < 32 * 4); sprite_index += 4) { + /* At this point the VDP vcount still doesn't refer the new line, + because the logical start point is slightly shifted on the scanline */ + int parse_line = line - 1; + int sprite_y = space().read_byte(m_sprite_base + sprite_index); if (sprite_y == 0xd0) break; @@ -941,8 +1154,11 @@ void sega315_5124_device::select_sprites( int line ) sprite_y -= 256; /* wrap from top if y position is >= 240 */ } - if (m_sprite_zoom > 1) + if (m_sprite_zoom_scale > 1 && m_sprite_count <= m_max_sprite_zoom_vcount) { + /* Divide before use the value for comparison, or else an + off-by-one bug could occur, as seen with Tarzan, for Game Gear */ + parse_line >>= 1; sprite_y >>= 1; } @@ -950,9 +1166,9 @@ void sega315_5124_device::select_sprites( int line ) { if (m_sprite_count < max_sprites) { - int sprite_x = space().read_byte( m_sprite_base + sprite_index + 1 ); + const int sprite_x = space().read_byte( m_sprite_base + sprite_index + 1 ); int sprite_tile_selected = space().read_byte( m_sprite_base + sprite_index + 2 ); - uint8_t flags = space().read_byte( m_sprite_base + sprite_index + 3 ); + const uint8_t flags = space().read_byte( m_sprite_base + sprite_index + 3 ); int sprite_line = parse_line - sprite_y; @@ -971,8 +1187,13 @@ void sega315_5124_device::select_sprites( int line ) m_sprite_tile_selected[m_sprite_count] = sprite_tile_selected; m_sprite_flags[m_sprite_count] = flags; m_sprite_pattern_line[m_sprite_count] = ((m_reg[0x06] & 0x07) << 11) + sprite_line; + + m_sprite_count++; + } + else + { + sprite_count_overflow(line, sprite_index); } - m_sprite_count++; } } } @@ -980,12 +1201,16 @@ void sega315_5124_device::select_sprites( int line ) { /* Regular sprites */ - max_sprites = 8; + const int max_sprites = 8; - m_sprite_base = ((m_reg[0x05] << 7) & 0x3f00); + m_sprite_base = (m_reg[0x05] << 7) & 0x3f00; - for (int sprite_index = 0; (sprite_index < 64) && (m_sprite_count <= max_sprites); sprite_index++) + for (int sprite_index = 0; (sprite_index < 64); sprite_index++) { + /* At this point the VDP vcount still doesn't refer the new line, + because the logical start point is slightly shifted on the scanline */ + int parse_line = line - 1; + int sprite_y = space().read_byte(m_sprite_base + sprite_index); if (m_y_pixels == 192 && sprite_y == 0xd0) break; @@ -995,8 +1220,11 @@ void sega315_5124_device::select_sprites( int line ) sprite_y -= 256; /* wrap from top if y position is >= 240 */ } - if (m_sprite_zoom > 1) + if (m_sprite_zoom_scale > 1 && m_sprite_count <= m_max_sprite_zoom_vcount) { + /* Divide before use the value for comparison, or else an + off-by-one bug could occur, as seen with Tarzan, for Game Gear */ + parse_line >>= 1; sprite_y >>= 1; } @@ -1004,51 +1232,85 @@ void sega315_5124_device::select_sprites( int line ) { if (m_sprite_count < max_sprites) { - int sprite_x = space().read_byte( m_sprite_base + 0x80 + (sprite_index << 1) ); - int sprite_tile_selected = space().read_byte( m_sprite_base + 0x81 + (sprite_index << 1) ); + const int sprite_line = parse_line - sprite_y; + const int sprite_tile_selected = select_sprite_tile_mode4(sprite_index, sprite_line); + int sprite_x = space().read_byte( sprite_attributes_addr_mode4(m_sprite_base + 0x80) + (sprite_index << 1) ); - if (m_reg[0x00] & 0x08) + if (BIT(m_reg[0x00], 3)) { sprite_x -= 0x08; /* sprite shift */ } - if (m_reg[0x06] & 0x04) - { - sprite_tile_selected += 256; /* pattern table select */ - } - - if (m_sprite_height == 16) - { - sprite_tile_selected &= 0x01fe; /* force even index */ - } - - int sprite_line = parse_line - sprite_y; - - if (sprite_line > 0x07) - { - sprite_tile_selected += 1; - } - m_sprite_x[m_sprite_count] = sprite_x; m_sprite_tile_selected[m_sprite_count] = sprite_tile_selected; m_sprite_pattern_line[m_sprite_count] = ((sprite_line & 0x07) << 2); + + m_sprite_count++; + } + else + { + sprite_count_overflow(line, sprite_index); } - m_sprite_count++; } } } +} - if ( m_sprite_count > max_sprites ) + +void sega315_5313_mode4_device::select_sprites( int line ) +{ + sega315_5124_device::select_sprites(line); + +/* + Info from Charles MacDonald regarding real hardware behavior: + (http://www.smspower.org/forums/15772-Sprites8x16Question) + + "As I recall the SMS will parses the sprite table on line N to find the + sprite numbers to display on line N+1, and on the next line it displays + those sprites. The data for the sprites is read in real-time and is not + buffered from any earlier time. The only thing that's buffered are the + eight sprite numbers. + + On the MD it has two line buffers (RAM) that it renders sprite data into + on one line, and displays that buffer on the next line while preparing + the next one. This only reveals itself if you play with the screen + blanking or left-column blanking bits, as the display process erases the + other buffer and on a blanked line nothing gets erased. + + So if you turn on either of those bits later they show the old sprite + data that hadn't been erased yet. This is how you can show a line of + sprite data further down on the screen than where the sprites were." +*/ + + // Runs the function that draws sprites, but only to check for + // sprite collision, due to it be flagged on the 315-5313 VDP in + // a point where the active screen was not drawn yet. + if (m_sprite_count > 0 && !m_display_disabled) { - /* Too many sprites per line */ + int blitline_buffer[256]; + int priority_selected[256]; + draw_sprites_mode4(blitline_buffer, priority_selected, line); + } +} - m_sprite_count = max_sprites; - /* Overflow is flagged only on active display and when VINT isn't active */ - if (!(m_status & STATUS_VINT) && line >= 0 && line < m_frame_timing[ACTIVE_DISPLAY_V]) - { - m_pending_status |= STATUS_SPROVR; - } +void sega315_5124_device::sprite_collision(int line, int sprite_col_x) +{ + /* SMS/GG: collisions don't occur on column 0 if it is disabled. */ + if (BIT(m_reg[0x00], 5) && sprite_col_x < 8) + return; + + m_pending_status |= STATUS_SPRCOL; + m_pending_sprcol_x = m_line_timing[SPRCOL_BASEHPOS] + sprite_col_x; +} + + +void sega315_5313_mode4_device::sprite_collision(int line, int sprite_col_x) +{ + if (line >= 0 && line < m_frame_timing[ACTIVE_DISPLAY_V]) + { + m_pending_status |= STATUS_SPRCOL; + m_pending_sprcol_x = m_line_timing[SPRCOL_BASEHPOS]; } } @@ -1058,38 +1320,33 @@ void sega315_5124_device::draw_sprites_mode4( int *line_buffer, int *priority_se bool sprite_col_occurred = false; int sprite_col_x = 255; uint8_t collision_buffer[256]; - int plot_min_x = 0; if (m_display_disabled || m_sprite_count == 0) return; - /* Sprites aren't drawn and collisions don't occur on column 0 if it is disabled. - Note: On Megadrive/Genesis VDP, collisions occur on the disabled column 0. */ - if (m_reg[0x00] & 0x20) - plot_min_x = 8; - memset(collision_buffer, 0, sizeof(collision_buffer)); /* Draw sprite layer */ for (int sprite_buffer_index = m_sprite_count - 1; sprite_buffer_index >= 0; sprite_buffer_index--) { - int sprite_x = m_sprite_x[sprite_buffer_index]; - int sprite_tile_selected = m_sprite_tile_selected[sprite_buffer_index]; - uint16_t sprite_pattern_line = m_sprite_pattern_line[sprite_buffer_index]; + const int sprite_x = m_sprite_x[sprite_buffer_index]; + const int sprite_tile_selected = m_sprite_tile_selected[sprite_buffer_index]; + const uint16_t sprite_pattern_line = m_sprite_pattern_line[sprite_buffer_index]; + const int zoom_scale = sprite_buffer_index <= m_max_sprite_zoom_hcount ? m_sprite_zoom_scale : 1; - uint8_t bit_plane_0 = space().read_byte((sprite_tile_selected << 5) + sprite_pattern_line + 0x00); - uint8_t bit_plane_1 = space().read_byte((sprite_tile_selected << 5) + sprite_pattern_line + 0x01); - uint8_t bit_plane_2 = space().read_byte((sprite_tile_selected << 5) + sprite_pattern_line + 0x02); - uint8_t bit_plane_3 = space().read_byte((sprite_tile_selected << 5) + sprite_pattern_line + 0x03); + const uint8_t bit_plane_0 = space().read_byte((sprite_tile_selected << 5) + sprite_pattern_line + 0x00); + const uint8_t bit_plane_1 = space().read_byte((sprite_tile_selected << 5) + sprite_pattern_line + 0x01); + const uint8_t bit_plane_2 = space().read_byte((sprite_tile_selected << 5) + sprite_pattern_line + 0x02); + const uint8_t bit_plane_3 = space().read_byte((sprite_tile_selected << 5) + sprite_pattern_line + 0x03); for (int pixel_x = 0; pixel_x < 8; pixel_x++) { int pixel_plot_x; - uint8_t pen_bit_0 = (bit_plane_0 >> (7 - pixel_x)) & 0x01; - uint8_t pen_bit_1 = (bit_plane_1 >> (7 - pixel_x)) & 0x01; - uint8_t pen_bit_2 = (bit_plane_2 >> (7 - pixel_x)) & 0x01; - uint8_t pen_bit_3 = (bit_plane_3 >> (7 - pixel_x)) & 0x01; - uint8_t pen_selected = (pen_bit_3 << 3 | pen_bit_2 << 2 | pen_bit_1 << 1 | pen_bit_0) | 0x10; + const uint8_t pen_bit_0 = BIT(bit_plane_0, 7 - pixel_x); + const uint8_t pen_bit_1 = BIT(bit_plane_1, 7 - pixel_x); + const uint8_t pen_bit_2 = BIT(bit_plane_2, 7 - pixel_x); + const uint8_t pen_bit_3 = BIT(bit_plane_3, 7 - pixel_x); + const uint8_t pen_selected = (pen_bit_3 << 3 | pen_bit_2 << 2 | pen_bit_1 << 1 | pen_bit_0) | 0x10; if (pen_selected == 0x10) { @@ -1097,7 +1354,7 @@ void sega315_5124_device::draw_sprites_mode4( int *line_buffer, int *priority_se continue; } - if (m_sprite_zoom > 1) + if (zoom_scale > 1) { /* sprite doubling is enabled */ pixel_plot_x = sprite_x + (pixel_x << 1); @@ -1108,12 +1365,12 @@ void sega315_5124_device::draw_sprites_mode4( int *line_buffer, int *priority_se } /* Draw at pixel position and, if zoomed, at pixel+1 */ - for (int zoom = 0; zoom < m_sprite_zoom; zoom++) + for (int zoom = 0; zoom < zoom_scale; zoom++) { pixel_plot_x += zoom; /* check to prevent going outside of active display area */ - if (pixel_plot_x < plot_min_x || pixel_plot_x > 255) + if (pixel_plot_x < 0 || pixel_plot_x > 255) { continue; } @@ -1147,8 +1404,7 @@ void sega315_5124_device::draw_sprites_mode4( int *line_buffer, int *priority_se } if (sprite_col_occurred) { - m_pending_status |= STATUS_SPRCOL; - m_pending_sprcol_x = SPRCOL_BASEHPOS + sprite_col_x; + sprite_collision(line, sprite_col_x); } } } @@ -1170,11 +1426,12 @@ void sega315_5124_device::draw_sprites_tms9918_mode( int *line_buffer, int line { int sprite_x = m_sprite_x[sprite_buffer_index]; int sprite_tile_selected = m_sprite_tile_selected[sprite_buffer_index]; - uint16_t sprite_pattern_line = m_sprite_pattern_line[sprite_buffer_index]; - uint8_t flags = m_sprite_flags[sprite_buffer_index]; - int pen_selected = m_palette_offset + ( flags & 0x0f ); + const uint16_t sprite_pattern_line = m_sprite_pattern_line[sprite_buffer_index]; + const uint8_t flags = m_sprite_flags[sprite_buffer_index]; + const int pen_selected = m_palette_offset + ( flags & 0x0f ); + const int zoom_scale = sprite_buffer_index <= m_max_sprite_zoom_hcount ? m_sprite_zoom_scale : 1; - if (flags & 0x80) + if (BIT(flags, 7)) sprite_x -= 32; for (int height = 8; height <= m_sprite_height; height += 8) @@ -1182,17 +1439,17 @@ void sega315_5124_device::draw_sprites_tms9918_mode( int *line_buffer, int line if (height == 16) { sprite_tile_selected += 2; - sprite_x += (m_sprite_zoom > 1 ? 16 : 8); + sprite_x += (zoom_scale > 1 ? 16 : 8); } uint8_t pattern = space().read_byte( sprite_pattern_line + sprite_tile_selected * 8 ); for (int pixel_x = 0; pixel_x < 8; pixel_x++) { - if (pen_selected && (pattern & (1 << (7 - pixel_x)))) + if (pen_selected && BIT(pattern, 7 - pixel_x)) { int pixel_plot_x; - if (m_sprite_zoom > 1) + if (zoom_scale > 1) { pixel_plot_x = sprite_x + (pixel_x << 1); } @@ -1202,7 +1459,7 @@ void sega315_5124_device::draw_sprites_tms9918_mode( int *line_buffer, int line } /* Draw at pixel position and, if zoomed, at pixel+1 */ - for (int zoom = 0; zoom < m_sprite_zoom; zoom++) + for (int zoom = 0; zoom < zoom_scale; zoom++) { pixel_plot_x += zoom; @@ -1229,42 +1486,35 @@ void sega315_5124_device::draw_sprites_tms9918_mode( int *line_buffer, int line } if (sprite_col_occurred) { - m_pending_status |= STATUS_SPRCOL; - m_pending_sprcol_x = SPRCOL_BASEHPOS + sprite_col_x; + sprite_collision(line, sprite_col_x); } } } +// Display mode 2 (Graphics II Mode) void sega315_5124_device::draw_scanline_mode2( int *line_buffer, int line ) { - int tile_column; - int pixel_x, pixel_plot_x; - uint16_t name_table_base, color_base, pattern_base; - int pattern_mask, color_mask, pattern_offset; - - name_table_base = ((m_reg[0x02] & 0x0f) << 10) + ((line >> 3) * 32); - color_base = ((m_reg[0x03] & 0x80) << 6); - color_mask = ((m_reg[0x03] & 0x7f) << 3) | 0x07; - pattern_base = ((m_reg[0x04] & 0x04) << 11); - pattern_mask = ((m_reg[0x04] & 0x03) << 8) | 0xff; - pattern_offset = (line & 0xc0) << 2; + const uint16_t name_table_base = ((m_reg[0x02] & 0x0f) << 10) + ((line >> 3) * 32); + const uint16_t color_base = ((m_reg[0x03] & 0x80) << 6); + const int color_mask = ((m_reg[0x03] & 0x7f) << 3) | 0x07; + const uint16_t pattern_base = ((m_reg[0x04] & 0x04) << 11); + const int pattern_mask = ((m_reg[0x04] & 0x03) << 8) | 0xff; + const int pattern_offset = (line & 0xc0) << 2; /* Draw background layer */ - for (tile_column = 0; tile_column < 32; tile_column++) + for (int tile_column = 0; tile_column < 32; tile_column++) { - uint8_t name = space().read_byte( name_table_base + tile_column ); - uint8_t pattern; - uint8_t colors; + const uint8_t name = space().read_byte( name_table_base + tile_column ); + const uint8_t pattern = space().read_byte(pattern_base + (((pattern_offset + name) & pattern_mask) * 8) + (line & 0x07) ); + const uint8_t colors = space().read_byte(color_base + (((pattern_offset + name) & color_mask) * 8) + (line & 0x07) ); - pattern = space().read_byte(pattern_base + (((pattern_offset + name) & pattern_mask) * 8) + (line & 0x07) ); - colors = space().read_byte(color_base + (((pattern_offset + name) & color_mask) * 8) + (line & 0x07) ); - - for (pixel_x = 0; pixel_x < 8; pixel_x++) + for (int pixel_x = 0; pixel_x < 8; pixel_x++) { uint8_t pen_selected; + const int pixel_plot_x = (tile_column << 3) + pixel_x; - if (pattern & (1 << (7 - pixel_x))) + if (BIT(pattern, 7 - pixel_x)) { pen_selected = colors >> 4; } @@ -1276,48 +1526,110 @@ void sega315_5124_device::draw_scanline_mode2( int *line_buffer, int line ) if (!pen_selected) pen_selected = BACKDROP_COLOR; - pixel_plot_x = (tile_column << 3) + pixel_x; - pen_selected += m_palette_offset; - line_buffer[pixel_plot_x] = m_current_palette[pen_selected]; } } } +// Display mode 0 (Graphics I Mode) void sega315_5124_device::draw_scanline_mode0( int *line_buffer, int line ) { - int tile_column; - int pixel_x, pixel_plot_x; - uint16_t name_base, color_base, pattern_base; - - name_base = ((m_reg[0x02] & 0x0f) << 10) + ((line >> 3) * 32); - color_base = ((m_reg[0x03] << 6) & (VRAM_SIZE - 1)); - pattern_base = ((m_reg[0x04] << 11) & (VRAM_SIZE - 1)); + const uint16_t name_base = ((m_reg[0x02] & 0x0f) << 10) + ((line >> 3) * 32); + const uint16_t color_base = ((m_reg[0x03] << 6) & (VRAM_SIZE - 1)); + const uint16_t pattern_base = ((m_reg[0x04] << 11) & (VRAM_SIZE - 1)); /* Draw background layer */ - for (tile_column = 0; tile_column < 32; tile_column++) + for (int tile_column = 0; tile_column < 32; tile_column++) { - uint8_t name = space().read_byte( name_base + tile_column ); - uint8_t pattern; - uint8_t colors; + const uint8_t name = space().read_byte( name_base + tile_column ); + const uint8_t pattern = space().read_byte( pattern_base + (name << 3) + (line & 0x07) ); + const uint8_t colors = space().read_byte( color_base + ( name >> 3 ) ); - pattern = space().read_byte( pattern_base + (name * 8) + (line & 0x07) ); - colors = space().read_byte( color_base + ( name >> 3 ) ); - - for (pixel_x = 0; pixel_x < 8; pixel_x++) + for (int pixel_x = 0; pixel_x < 8; pixel_x++) { int pen_selected; + const int pixel_plot_x = (tile_column << 3) + pixel_x; - if (pattern & (1 << (7 - pixel_x))) + if (BIT(pattern, 7 - pixel_x)) pen_selected = colors >> 4; else pen_selected = colors & 0x0f; + if (!pen_selected) + pen_selected = BACKDROP_COLOR; + pen_selected += m_palette_offset; + line_buffer[pixel_plot_x] = m_current_palette[pen_selected]; + } + } +} + + +// Display mode 1 (Text Mode) +void sega315_5124_device::draw_scanline_mode1( int *line_buffer, int line ) +{ + const uint16_t name_base = ((m_reg[0x02] & 0x0f) << 10) + ((line >> 3) * 32); + const uint16_t pattern_base = ((m_reg[0x04] << 11) & (VRAM_SIZE - 1)); - pixel_plot_x = (tile_column << 3) + pixel_x; + for (int pixel_plot_x = 0; pixel_plot_x < 8; pixel_plot_x++) + { + line_buffer[pixel_plot_x] = m_current_palette[BACKDROP_COLOR]; + } + + /* Draw background layer */ + for (int tile_column = 0; tile_column < 40; tile_column++) + { + const uint8_t name = space().read_byte( name_base + tile_column ); + const uint8_t pattern = space().read_byte( pattern_base + (name << 3) + (line & 0x07) ); + + for (int pixel_x = 0; pixel_x < 6; pixel_x++) + { + int pen_selected; + const int pixel_plot_x = (tile_column * 6) + pixel_x + 8; + + if (BIT(pattern, 7 - pixel_x)) + pen_selected = m_reg[0x07] >> 4; + else + pen_selected = m_reg[0x07] & 0x0f; + + if (!pen_selected) + pen_selected = BACKDROP_COLOR; + + pen_selected += m_palette_offset; + line_buffer[pixel_plot_x] = m_current_palette[pen_selected]; + } + } + + for (int pixel_plot_x = 248; pixel_plot_x < 256; pixel_plot_x++) + { + line_buffer[pixel_plot_x] = m_current_palette[BACKDROP_COLOR]; + } +} + + +// Display mode 3 (Multicolor Mode) +void sega315_5124_device::draw_scanline_mode3( int *line_buffer, int line ) +{ + const uint16_t name_base = ((m_reg[0x02] & 0x0f) << 10) + ((line >> 3) * 32); + const uint16_t pattern_base = ((m_reg[0x04] << 11) & (VRAM_SIZE - 1)); + + /* Draw background layer */ + for (int tile_column = 0; tile_column < 32; tile_column++) + { + const uint8_t name = space().read_byte( name_base + tile_column ); + const uint8_t pattern = space().read_byte( pattern_base + (name << 3) + (((line >> 3) & 3) << 1) + ((line & 4) >> 2)); + + for (int pixel_x = 0; pixel_x < 8; pixel_x++) + { + const int pixel_plot_x = (tile_column << 3) + pixel_x; + int pen_selected = (pattern >> (~pixel_x & 4)) & 0x0f; + + if (!pen_selected) + pen_selected = BACKDROP_COLOR; + + pen_selected += m_palette_offset; line_buffer[pixel_plot_x] = m_current_palette[pen_selected]; } } @@ -1349,6 +1661,14 @@ void sega315_5124_device::draw_scanline( int pixel_offset_x, int pixel_plot_y, i } break; + case 1: + if ( line >= 0 ) + { + draw_scanline_mode1( blitline_buffer, line ); + } + // Text Mode, no sprite drawing. + break; + case 2: if ( line >= 0 ) { @@ -1360,6 +1680,17 @@ void sega315_5124_device::draw_scanline( int pixel_offset_x, int pixel_plot_y, i } break; + case 3: + if ( line >= 0 ) + { + draw_scanline_mode3( blitline_buffer, line ); + } + if ( line >= 0 || ( line >= -13 && m_y_pixels == 192 ) ) + { + draw_sprites_tms9918_mode( blitline_buffer, line ); + } + break; + case 4: default: if ( line >= 0 ) @@ -1371,6 +1702,11 @@ void sega315_5124_device::draw_scanline( int pixel_offset_x, int pixel_plot_y, i draw_sprites_mode4( blitline_buffer, priority_selected, line ); } break; + + case 5: + // Mode 5 (Mega Drive / Genesis) not implemented. + break; + } } @@ -1398,7 +1734,7 @@ void sega315_5124_device::blit_scanline( int *line_buffer, int *priority_selecte uint8_t *p_y1 = &m_y1_bitmap.pix8(pixel_plot_y + line, pixel_offset_x); int x = 0; - if (m_vdp_mode == 4 && (m_reg[0x00] & 0x20)) + if (m_vdp_mode == 4 && BIT(m_reg[0x00], 5)) { /* Fill column 0 with overscan color from m_reg[0x07] */ do @@ -1418,7 +1754,7 @@ void sega315_5124_device::blit_scanline( int *line_buffer, int *priority_selecte } -void sega315_5378_device::blit_scanline( int *line_buffer, int *priority_selected, int pixel_offset_x, int pixel_plot_y, int line ) +void sega315_5377_device::blit_scanline( int *line_buffer, int *priority_selected, int pixel_offset_x, int pixel_plot_y, int line ) { if (m_sega315_5124_compatibility_mode) { @@ -1471,52 +1807,48 @@ void sega315_5378_device::blit_scanline( int *line_buffer, int *priority_selecte void sega315_5124_device::update_palette() { - int i; - /* Exit if palette has no changes */ - if (m_cram_dirty == 0) + if (!m_cram_dirty) { return; } - m_cram_dirty = 0; + m_cram_dirty = false; if (m_vdp_mode != 4) { - for(i = 0; i < 16; i++) + for(int i = 0; i < 16; i++) { m_current_palette[i] = 64 + i; } return; } - for (i = 0; i < 32; i++) + for (int i = 0; i < 32; i++) { m_current_palette[i] = m_CRAM[i] & 0x3f; } } -void sega315_5378_device::update_palette() +void sega315_5377_device::update_palette() { - int i; - /* Exit if palette has no changes */ - if (m_cram_dirty == 0) + if (!m_cram_dirty) { return; } - m_cram_dirty = 0; + m_cram_dirty = false; if (m_sega315_5124_compatibility_mode) { - for (i = 0; i < 32; i++) + for (int i = 0; i < 32; i++) { m_current_palette[i] = ((m_CRAM[i] & 0x30) << 6) | ((m_CRAM[i] & 0x0c ) << 4) | ((m_CRAM[i] & 0x03) << 2); } } else { - for (i = 0; i < 32; i++) + for (int i = 0; i < 32; i++) { m_current_palette[i] = (m_CRAM[2*i] | (m_CRAM[2*i+1] << 8)) & 0x0fff; } @@ -1530,12 +1862,12 @@ void sega315_5124_device::cram_write(uint8_t data) if (data != m_CRAM[address]) { m_CRAM[address] = data; - m_cram_dirty = 1; + m_cram_dirty = true; } } -void sega315_5378_device::cram_write(uint8_t data) +void sega315_5377_device::cram_write(uint8_t data) { if (m_sega315_5124_compatibility_mode) { @@ -1550,7 +1882,7 @@ void sega315_5378_device::cram_write(uint8_t data) { m_CRAM[address] = m_buffer; m_CRAM[address + 1] = data; - m_cram_dirty = 1; + m_cram_dirty = true; } } } @@ -1570,7 +1902,7 @@ uint32_t sega315_5124_device::screen_update( screen_device &screen, bitmap_rgb32 // of the emulator hence we stop it in systems that don't need it // Proper way to handle this would be implement the 315-5124 modes in the 315-5313 // device instead of running the two chips separately... -void sega315_5124_device::stop_timers() +void sega315_5313_mode4_device::stop_timers() { m_display_timer->adjust(attotime::never); m_pending_flags_timer->adjust(attotime::never); @@ -1599,9 +1931,6 @@ void sega315_5124_device::device_start() m_csync_cb.resolve(); m_pause_cb.resolve(); - /* Allocate video RAM */ - m_frame_timing = (m_is_pal) ? pal_192 : ntsc_192; - /* Make temp bitmap for rendering */ screen().register_screen_bitmap(m_tmpbitmap); screen().register_screen_bitmap(m_y1_bitmap); @@ -1626,8 +1955,11 @@ void sega315_5124_device::device_start() save_item(NAME(m_addr)); save_item(NAME(m_cram_mask)); save_item(NAME(m_cram_dirty)); - save_item(NAME(m_pending_reg_write)); + save_item(NAME(m_hint_occurred)); + save_item(NAME(m_pending_hint)); + save_item(NAME(m_pending_control_write)); save_item(NAME(m_buffer)); + save_item(NAME(m_control_write_data_latch)); save_item(NAME(m_sega315_5124_compatibility_mode)); save_item(NAME(m_display_disabled)); save_item(NAME(m_irq_state)); @@ -1639,7 +1971,7 @@ void sega315_5124_device::device_start() save_item(NAME(m_current_palette)); // these were created with register_screen_bitmap which is dynamic, and will reallocate if the screen size changes, saving them is NOT safe with the current core. - // The Genesis VDP (315_5313.c) which uses this as a base in order to support the legacy SMS operaiton mode can change resolutions for example. + // The Genesis VDP (315_5313.cpp) which uses this as a base in order to support the legacy SMS operation mode can change resolutions for example. //save_item(NAME(m_tmpbitmap)); //save_item(NAME(m_y1_bitmap)); save_item(NAME(m_draw_time)); @@ -1650,7 +1982,9 @@ void sega315_5124_device::device_start() save_item(NAME(m_sprite_flags)); save_item(NAME(m_sprite_count)); save_item(NAME(m_sprite_height)); - save_item(NAME(m_sprite_zoom)); + save_item(NAME(m_sprite_zoom_scale)); + save_item(NAME(m_max_sprite_zoom_hcount)); + save_item(NAME(m_max_sprite_zoom_vcount)); save_item(NAME(m_CRAM)); machine().save().register_postload(save_prepost_delegate(FUNC(sega315_5124_device::vdp_postload), this)); @@ -1659,19 +1993,18 @@ void sega315_5124_device::device_start() void sega315_5124_device::device_reset() { - int i; - /* Most register are 0x00 at power-up */ - for (i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) m_reg[i] = 0x00; m_reg[0x02] = 0x0e; m_reg[0x0a] = 0xff; - m_status = 0; - m_pending_status = 0; + m_status = m_pending_status = (uint8_t) ~(STATUS_VINT | STATUS_SPROVR | STATUS_SPRCOL); m_pending_sprcol_x = 0; - m_pending_reg_write = 0; + m_pending_control_write = false; + m_pending_hint = false; + m_hint_occurred = false; m_reg8copy = 0; m_reg9copy = 0; m_addrmode = 0; @@ -1679,14 +2012,15 @@ void sega315_5124_device::device_reset() m_sega315_5124_compatibility_mode = false; m_display_disabled = false; m_cram_mask = m_cram_size - 1; - m_cram_dirty = 1; + m_cram_dirty = true; m_buffer = 0; + m_control_write_data_latch = 0; m_irq_state = 0; m_line_counter = 0; m_hcounter = 0; m_draw_time = DRAW_TIME_SMS; - for (i = 0; i < 0x20; i++) + for (int i = 0; i < 0x20; i++) m_current_palette[i] = 0; set_display_settings(); @@ -1704,7 +2038,7 @@ MACHINE_CONFIG_START(sega315_5124_device::device_add_mconfig) MCFG_PALETTE_INIT_OWNER(sega315_5124_device, sega315_5124) MACHINE_CONFIG_END -void sega315_5378_device::device_reset() +void sega315_5377_device::device_reset() { sega315_5124_device::device_reset(); m_draw_time = DRAW_TIME_GG; @@ -1714,7 +2048,7 @@ void sega315_5378_device::device_reset() // device_add_mconfig - add machine configuration //------------------------------------------------- -MACHINE_CONFIG_START(sega315_5378_device::device_add_mconfig) - MCFG_PALETTE_ADD("palette", SEGA315_5378_PALETTE_SIZE) - MCFG_PALETTE_INIT_OWNER(sega315_5378_device, sega315_5378) +MACHINE_CONFIG_START(sega315_5377_device::device_add_mconfig) + MCFG_PALETTE_ADD("palette", SEGA315_5377_PALETTE_SIZE) + MCFG_PALETTE_INIT_OWNER(sega315_5377_device, sega315_5377) MACHINE_CONFIG_END |