diff options
author | 2020-08-12 16:00:39 +0200 | |
---|---|---|
committer | 2020-08-12 16:00:56 +0200 | |
commit | 34559bb4cbf6da08ce440f39755b9b1e1b51c7c7 (patch) | |
tree | 280dca56acf1cb8c3d2f4f809e0ee1da46e0733f | |
parent | 30d0813c4603da03c3e702da9ba4cf85291a825d (diff) |
i8244: fix problem with zoomed sprites, update sound/video at tighter intervals
-rw-r--r-- | src/devices/video/i8244.cpp | 826 | ||||
-rw-r--r-- | src/devices/video/i8244.h | 75 | ||||
-rw-r--r-- | src/emu/xtal.cpp | 4 | ||||
-rw-r--r-- | src/mame/drivers/odyssey2.cpp | 269 |
4 files changed, 596 insertions, 578 deletions
diff --git a/src/devices/video/i8244.cpp b/src/devices/video/i8244.cpp index 141b2e85198..66fcbd96ca6 100644 --- a/src/devices/video/i8244.cpp +++ b/src/devices/video/i8244.cpp @@ -1,13 +1,11 @@ // license:BSD-3-Clause -// copyright-holders:Wilbert Pol +// copyright-holders:Wilbert Pol, hap +// thanks-to:Kevin Horton /*************************************************************************** Intel 8244 (NTSC)/8245 (PAL) Graphics and sound chip -TODO: -- NTSC has 263 scanlines, PAL has 313 scanlines, a quick fix will probably - cause small regressions here and there -- PAL has 228 clocks per line (so, 456 half clocks) +Exclusively used in Odyssey 2 series. See driver file for known problems. ***************************************************************************/ @@ -17,50 +15,70 @@ TODO: #include "screen.h" - // device type definition DEFINE_DEVICE_TYPE(I8244, i8244_device, "i8244", "Intel 8244") DEFINE_DEVICE_TYPE(I8245, i8245_device, "i8245", "Intel 8245") -// Background and grid information is stored in RGB format -// while the character and sprite colors are stored in BGR -// format. -static const uint8_t bgr2rgb[8] = -{ - 0x00, 0x04, 0x02, 0x06, 0x01, 0x05, 0x03, 0x07 -}; - - //------------------------------------------------- // i8244_device - constructor //------------------------------------------------- i8244_device::i8244_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : i8244_device(mconfig, I8244, tag, owner, clock, i8244_device::LINES) + : i8244_device(mconfig, I8244, tag, owner, clock) { } -i8244_device::i8244_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, int lines) +i8244_device::i8244_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_sound_interface(mconfig, *this) , device_video_interface(mconfig, *this) , m_irq_func(*this) - , m_postprocess_func(*this) , m_charset(*this, "cgrom") - , m_start_vpos(START_Y) - , m_start_vblank(START_Y + SCREEN_HEIGHT) - , m_screen_lines(lines) { } i8245_device::i8245_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : i8244_device(mconfig, I8245, tag, owner, clock, i8245_device::LINES) + : i8244_device(mconfig, I8245, tag, owner, clock) { } +void i8244_device::set_default_params() +{ + m_htotal = 455; + m_vtotal = 263; + m_vblank_start = 242; + m_vblank_end = 0; + m_hblank_start = 366; + m_hblank_end = 453; + m_bgate_start = 413; +} + +void i8245_device::set_default_params() +{ + m_htotal = 456; + m_vtotal = 313; + m_vblank_start = 242; + m_vblank_end = 312; + m_hblank_start = 366; + m_hblank_end = 454; + m_bgate_start = 414; +} + +i8244_device &i8244_device::set_screen_size(int width, int height, int cropx, int cropy) +{ + m_width = width; + m_height = height; + m_cropx = cropx; + m_cropy = cropy; + + set_default_params(); + + return *this; +} + //------------------------------------------------- // internal character set rom @@ -90,7 +108,7 @@ void i8244_device::device_config_complete() return; if (!screen().refresh_attoseconds()) - screen().set_raw(clock()*2, LINE_CLOCKS, START_ACTIVE_SCAN, END_ACTIVE_SCAN, m_screen_lines, START_Y, START_Y + SCREEN_HEIGHT); + screen().set_raw(clock()*2, m_htotal, m_cropx, m_cropx + m_width, m_vtotal, m_cropy, m_cropy + m_height); } @@ -100,68 +118,56 @@ void i8244_device::device_config_complete() void i8244_device::device_start() { - // Let the screen create our temporary bitmap with the screen's dimensions - screen().register_screen_bitmap(m_tmp_bitmap); - - m_line_timer = timer_alloc(TIMER_LINE); - m_line_timer->adjust( screen().time_until_pos(1, START_ACTIVE_SCAN ), 0, screen().scan_period() ); + memset(m_vdc.reg, 0, 0x100); + m_irq_func.resolve_safe(); - m_hblank_timer = timer_alloc(TIMER_HBLANK); - m_hblank_timer->adjust( screen().time_until_pos(1, END_ACTIVE_SCAN + 18 ), 0, screen().scan_period() ); + // allocate timers + m_vblank_timer = timer_alloc(TIMER_VBLANK_START); + m_vblank_timer->adjust(screen().time_until_pos(m_vblank_start, m_hblank_start - 1), 0, screen().frame_period()); - m_irq_func.resolve_safe(); - m_postprocess_func.resolve_safe(); + m_hblank_timer = timer_alloc(TIMER_HBLANK_START); + m_hblank_timer->adjust(screen().time_until_pos(0, m_hblank_start), 0, screen().scan_period()); // allocate a stream - m_stream = stream_alloc( 0, 1, (clock()*2)/(LINE_CLOCKS*4) ); + m_stream = stream_alloc(0, 1, clock()); // register our state save_pointer(NAME(m_vdc.reg), 0x100); - save_item(NAME(m_sh_count)); save_item(NAME(m_x_beam_pos)); save_item(NAME(m_y_beam_pos)); + save_item(NAME(m_y_latch)); save_item(NAME(m_control_status)); save_item(NAME(m_collision_status)); - save_item(NAME(m_iff)); -} - - -//------------------------------------------------- -// device_reset - device-specific reset -//------------------------------------------------- - -void i8244_device::device_reset() -{ - memset(m_vdc.reg, 0, 0x100); - - m_sh_count = 0; - m_x_beam_pos = 0; - m_y_beam_pos = 0; - m_control_status = 0; - m_collision_status = 0; - m_iff = 0; + save_item(NAME(m_pos_hold)); + save_item(NAME(m_y_hold)); + save_item(NAME(m_sh_written)); + save_item(NAME(m_sh_pending)); + save_item(NAME(m_sh_prescaler)); + save_item(NAME(m_sh_output)); + save_item(NAME(m_sh_duty)); } void i8244_device::i8244_palette(palette_device &palette) const { + // RGB output, before any NTSC/PAL RF encoder static constexpr rgb_t i8244_colors[16] = { { 0x00, 0x00, 0x00 }, // i r g b - { 0x00, 0x00, 0xaa }, // i r g B - { 0x00, 0xaa, 0x00 }, // i r G b - { 0x00, 0xaa, 0xaa }, // i r G B - { 0xaa, 0x00, 0x00 }, // i R g b - { 0xaa, 0x00, 0xaa }, // i R g B - { 0xaa, 0xaa, 0x00 }, // i R G b - { 0xaa, 0xaa, 0xaa }, // i R G B - { 0x55, 0x55, 0x55 }, // I r g b - { 0x55, 0x55, 0xff }, // I r g B - { 0x55, 0xff, 0x55 }, // I r G b - { 0x55, 0xff, 0xff }, // I r G B - { 0xff, 0x55, 0x55 }, // I R g b - { 0xff, 0x55, 0xff }, // I R g B - { 0xff, 0xff, 0x55 }, // I R G b + { 0x00, 0x00, 0xb6 }, // i r g B + { 0x00, 0xb6, 0x00 }, // i r G b + { 0x00, 0xb6, 0xb6 }, // i r G B + { 0xb6, 0x00, 0x00 }, // i R g b + { 0xb6, 0x00, 0xb6 }, // i R g B + { 0xb6, 0xb6, 0x00 }, // i R G b + { 0xb6, 0xb6, 0xb6 }, // i R G B + { 0x49, 0x49, 0x49 }, // I r g b + { 0x49, 0x49, 0xff }, // I r g B + { 0x49, 0xff, 0x49 }, // I r G b + { 0x49, 0xff, 0xff }, // I r G B + { 0xff, 0x49, 0x49 }, // I R g b + { 0xff, 0x49, 0xff }, // I R g B + { 0xff, 0xff, 0x49 }, // I R G b { 0xff, 0xff, 0xff } // I R G B }; @@ -171,55 +177,22 @@ void i8244_device::i8244_palette(palette_device &palette) const void i8244_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { - int vpos = screen().vpos(); - switch ( id ) { - case TIMER_LINE: - // handle i824x line timer - render_scanline(vpos); + case TIMER_HBLANK_START: + // hblank starts (updates sound shift register) + sound_update(); break; - case TIMER_HBLANK: - // handle i824x HBlank timer - if ( vpos < m_start_vpos - 1 ) - { - return; - } - - if ( vpos < m_start_vblank - 1 ) - { - m_control_status |= 0x01; - } + case TIMER_VBLANK_START: + // vblank starts + m_control_status |= 0x08; + m_irq_func(ASSERT_LINE); break; - } -} - -int i8244_device::get_y_beam() -{ - int y = screen().vpos() - m_start_vpos; - - // The Y register becomes 0 only when the VBlank signal is turned off! - if ( y < 0 || ( y == 0 && screen().hpos() < 366+42 ) ) - { - y += m_screen_lines; - } - - return y; -} - - -int i8244_device::get_x_beam() -{ - int x = screen().hpos() - START_ACTIVE_SCAN; - - if ( x < 0 ) - { - x += LINE_CLOCKS; + default: + break; } - - return x >> 1; } @@ -240,36 +213,67 @@ offs_t i8244_device::fix_register_mirrors( offs_t offset ) return offset & 0xFF; } -bool i8244_device::unused_register( offs_t offset ) + +bool i8244_device::invalid_register( offs_t offset, bool rw ) { - bool unused = false; + // object x/y/attr registers are not accessible when display is enabled + // (sprite shape registers still are) + if (offset < 0x80 && m_vdc.s.control & 0x20) + return true; - u8 low = offset & 0xf; + // grid registers are not accessible when grid is enabled + if (offset >= 0xc0 && m_vdc.s.control & 0x08) + return true; - // these registers are inaccessible + bool invalid = false; + + u8 n = offset & 0xf; + + // these registers are always inaccessible switch (offset & 0xf0) { case 0x00: - unused = ((low & 0x3) == 0x3); + invalid = ((n & 0x3) == 0x3); + + // write-only + if (!invalid && rw) + invalid = ((n & 0x3) == 0x2); + break; + case 0xa0: - unused = (low == 0x6 || low >= 0xb); + invalid = (n == 0x6 || n >= 0xb); + + if (!invalid) + { + // write-only + if (rw) + invalid = (n == 0x3 || n == 0x7 || n == 0x8 || n == 0x9); + + // read-only + else + invalid = (n == 0x1 || n == 0x4 || n == 0x5); + } + break; + case 0xc0: case 0xd0: - unused = (low >= 0x9); + invalid = (n >= 0x9); break; + case 0xe0: - unused = (low >= 0xa); + invalid = (n >= 0xa); break; + case 0xf0: - unused = true; + invalid = true; break; default: break; } - return unused; + return invalid; } @@ -279,21 +283,34 @@ uint8_t i8244_device::read(offs_t offset) offset = fix_register_mirrors(offset); - if (unused_register(offset)) + if (machine().side_effects_disabled()) + return m_vdc.reg[offset]; + + if (invalid_register(offset, true)) return 0; + // update screen before accessing video status registers + if (offset == 0xa1 || offset == 0xa2) + screen().update_now(); + switch (offset) { case 0xa1: - data = m_control_status & 0xFE; - m_iff = 0; + { + data = m_control_status; + + // position strobe status + data |= m_vdc.s.control & 0x02; + + // hstatus (not same as hblank), goes high at falling edge of X=0x70 + int h = screen().hpos(); + data |= (h >= 225 && h < m_bgate_start && get_y_beam() <= m_vblank_start) ? 1 : 0; + m_irq_func(CLEAR_LINE); - m_control_status &= ~0x08; - if ( hblank() ) - { - data |= 1; - } + m_control_status &= ~0x88; + break; + } case 0xa2: data = m_collision_status; @@ -301,26 +318,34 @@ uint8_t i8244_device::read(offs_t offset) break; case 0xa4: - if (m_vdc.s.control & VDC_CONTROL_REG_STROBE_XY) + if (m_y_hold) { - m_y_beam_pos = get_y_beam(); + data = m_y_latch; + m_y_hold = false; } - data = m_y_beam_pos; + else if (m_pos_hold || !(m_vdc.s.control & 0x02)) + data = m_y_beam_pos; + else + data = get_y_beam(); + break; case 0xa5: - if ((m_vdc.s.control & VDC_CONTROL_REG_STROBE_XY)) + { + if (m_pos_hold || !(m_vdc.s.control & 0x02)) { - m_x_beam_pos = get_x_beam(); + data = m_x_beam_pos; + m_pos_hold = false; } - data = m_x_beam_pos; - break; + else + data = get_x_beam(); + + // Y is latched when reading X + m_y_hold = true; + m_y_latch = (m_vdc.s.control & 0x02) ? get_y_beam() : m_y_beam_pos; - case 0x02: case 0x06: case 0x0a: case 0x0e: - case 0xa3: case 0xa7: case 0xa8: case 0xa9: - // write-only registers - data = 0; break; + } default: data = m_vdc.reg[offset]; @@ -335,86 +360,109 @@ void i8244_device::write(offs_t offset, uint8_t data) { offset = fix_register_mirrors(offset); - if (unused_register(offset)) + if (invalid_register(offset, false)) return; - // read-only registers - if (offset == 0xa1 || offset == 0xa4 || offset == 0xa5) - return; + // update screen before accessing video registers + if (offset < 0xa4 || offset >= 0xc0) + screen().update_now(); - // Color registers d4-d7 are not connected + // color registers d4-d7 are not connected if ((offset & 0x83) == 0x03) data &= 0x0f; - // Major systems Y CAM d0 is not connected! + // major systems Y CAM d0 is not connected! if (offset >= 0x10 && (offset & 0x83) == 0x00) data &= ~0x01; - // Horizontal grid high byte only d0 is connected + // horizontal grid high byte only d0 is connected if ((offset & 0xf0) == 0xd0) data &= 0x01; - // Update the sound - if (offset >= 0xa7 && offset <= 0xaa) + switch (offset) { - if (offset == 0xaa) - data &= 0xbf; - m_stream->update(); - } + case 0xa0: + if ((m_vdc.s.control & 0x02) && !(data & 0x02) && !m_pos_hold) + { + // toggling strobe bit, tuck away values + m_x_beam_pos = get_x_beam(); + m_y_beam_pos = get_y_beam(); + m_pos_hold = true; + } + break; - if (offset == 0xa0) - { - if ( ( m_vdc.s.control & VDC_CONTROL_REG_STROBE_XY ) - && !(data & VDC_CONTROL_REG_STROBE_XY)) - { - // Toggling strobe bit, tuck away values - m_x_beam_pos = get_x_beam(); - m_y_beam_pos = get_y_beam(); - } + case 0xa7: case 0xa8: case 0xa9: + m_sh_written = true; + break; + + case 0xaa: + // update the sound + m_stream->update(); + data &= ~0x40; + break; + + default: + break; } m_vdc.reg[offset] = data; } -int i8244_device::vblank() +int i8244_device::get_y_beam() { - if ( screen().vpos() > m_start_vpos && screen().vpos() < m_start_vblank ) - { - return 0; - } - return 1; + int h = screen().hpos(); + int v = screen().vpos(); + + // Y resets before hblank on the first scanline + if (v == 0 && h < m_hblank_start) + v = m_vtotal; + + // Y increments on BG sync + if (h >= m_bgate_start) + v++; + + return (v > 263) ? 263 : v; } -int i8244_device::hblank() +int i8244_device::get_x_beam() { - int hpos = screen().hpos(); - int vpos = screen().vpos(); + return screen().hpos() >> 1; +} - if ( hpos >= START_ACTIVE_SCAN && hpos < END_ACTIVE_SCAN ) - { - return 0; - } - // Before active area? - if ( vpos < m_start_vpos - 1 ) - { - return 0; - } +int i8244_device::vblank() +{ + int h = screen().hpos(); + int v = screen().vpos(); + int start = m_vblank_start; + int end = m_vblank_end; - // During active area? - if ( vpos < m_start_vblank - 1 ) - { + if ((v == start && h >= (m_hblank_start - 1)) || (v == end && h <= (m_hblank_start - 1))) return 1; - } - // After active area - return 0; + if (end < start) + return (v > start || v < end) ? 1 : 0; + else + return (v > start && v < end) ? 1 : 0; } -void i8244_device::render_scanline(int vpos) +int i8244_device::hblank() +{ + int h = screen().hpos(); + int start = m_hblank_start; + int end = m_hblank_end; + + if (end < start) + return (h >= start || h < end) ? 1 : 0; + else + return (h >= start && h < end) ? 1 : 0; +} + + +uint32_t i8244_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { // Some local constants for this method //static const uint8_t COLLISION_SPRITE_0 = 0x01; @@ -423,38 +471,36 @@ void i8244_device::render_scanline(int vpos) //static const uint8_t COLLISION_SPRITE_3 = 0x08; static const uint8_t COLLISION_VERTICAL_GRID = 0x10; static const uint8_t COLLISION_HORIZ_GRID_DOTS = 0x20; - //static const uint8_t COLLISION_EXTERNAL_UNUSED = 0x40; + static const uint8_t COLLISION_EXTERNAL = 0x40; static const uint8_t COLLISION_CHARACTERS = 0x80; - if ( vpos == m_start_vpos ) + // Background and grid information is stored in RGB format + // while the character and sprite colors are stored in BGR + // format. + static const uint8_t bgr2rgb[8] = { - m_control_status &= ~0x08; - } + 0x00, 0x04, 0x02, 0x06, 0x01, 0x05, 0x03, 0x07 + }; - /* Clear collision map */ - uint8_t collision_map[160]; - memset( collision_map, 0, sizeof( collision_map ) ); + uint8_t collision_map[0x200]; /* Draw background color */ - rectangle rect(START_ACTIVE_SCAN, END_ACTIVE_SCAN - 1, vpos, vpos); - rect &= screen().visible_area(); - m_tmp_bitmap.fill( (m_vdc.s.color >> 3) & 0x7, rect ); + bitmap.fill( (m_vdc.s.color >> 3) & 0x7, cliprect ); - if ( m_start_vpos < vpos && vpos < m_start_vblank ) + for (int scanline = cliprect.min_y; scanline <= cliprect.max_y; scanline++) { - int scanline = vpos - m_start_vpos; - - m_control_status &= ~ 0x01; + /* Clear collision map */ + memset( collision_map, 0, sizeof( collision_map ) ); /* Display grid if enabled */ if ( m_vdc.s.control & 0x08 ) { uint16_t color = ( m_vdc.s.color & 7 ) | ( ( m_vdc.s.color >> 3 ) & 0x08 ); - int x_grid_offset = 8; - int y_grid_offset = 24; - int width = 16; - int height = 24; - int w = ( m_vdc.s.control & 0x80 ) ? width : 2; + int x_grid_offset = 13; + int y_grid_offset = 24; + int width = 16; + int height = 24; + int w = ( m_vdc.s.control & 0x80 ) ? width : 2; /* Draw horizontal part of the grid */ for ( int j = 1, y = 0; y < 9; y++, j <<= 1 ) @@ -467,13 +513,15 @@ void i8244_device::render_scanline(int vpos) { for ( int k = 0; k < width + 2; k++ ) { - int px = x_grid_offset + i * width + k; + int x = (x_grid_offset + i * width + k) * 2; - if ( px < 160 ) + for (int px = x; px < x + 2; px++) { - collision_map[ px ] |= COLLISION_HORIZ_GRID_DOTS; - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * px ) = color; - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * px + 1 ) = color; + if (cliprect.contains(px, scanline)) + { + collision_map[ px ] |= COLLISION_HORIZ_GRID_DOTS; + bitmap.pix16( scanline, px ) = color; + } } } } @@ -481,24 +529,26 @@ void i8244_device::render_scanline(int vpos) } } - /* Draw vertical part of the grid */ - for( int j = 1, y = 0; y < 8; y++, j <<= 1 ) + /* Draw dots part of the grid */ + if ( m_vdc.s.control & 0x40 ) { - if ( y_grid_offset + y * height <= scanline && scanline < y_grid_offset + ( y + 1 ) * height ) + for ( int y = 0; y < 9; y++ ) { - for ( int i = 0; i < 10; i++ ) + if ( y_grid_offset + y * height <= scanline && scanline < y_grid_offset + y * height + 3 ) { - if ( m_vdc.s.vgrid[i] & j ) + for ( int i = 0; i < 10; i++ ) { - for ( int k = 0; k < w; k++ ) + for ( int k = 0; k < 2; k++ ) { - int px = x_grid_offset + i * width + k; + int x = (x_grid_offset + i * width + k) * 2; - if ( px < 160 ) + for (int px = x; px < x + 2; px++) { - collision_map[ px ] |= COLLISION_VERTICAL_GRID; - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * px ) = color; - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * px + 1 ) = color; + if (cliprect.contains(px, scanline)) + { + collision_map[ px ] |= COLLISION_HORIZ_GRID_DOTS; + bitmap.pix16( scanline, px ) = color; + } } } } @@ -506,24 +556,26 @@ void i8244_device::render_scanline(int vpos) } } - /* Draw dots part of the grid */ - if ( m_vdc.s.control & 0x40 ) + /* Draw vertical part of the grid */ + for( int j = 1, y = 0; y < 8; y++, j <<= 1 ) { - for ( int y = 0; y < 9; y++ ) + if ( y_grid_offset + y * height <= scanline && scanline < y_grid_offset + ( y + 1 ) * height ) { - if ( y_grid_offset + y * height <= scanline && scanline < y_grid_offset + y * height + 3 ) + for ( int i = 0; i < 10; i++ ) { - for ( int i = 0; i < 10; i++ ) + if ( m_vdc.s.vgrid[i] & j ) { - for ( int k = 0; k < 2; k++ ) + for ( int k = 0; k < w; k++ ) { - int px = x_grid_offset + i * width + k; + int x = (x_grid_offset + i * width + k) * 2; - if ( px < 160 ) + for (int px = x; px < x + 2; px++) { - collision_map[ px ] |= COLLISION_HORIZ_GRID_DOTS; - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * px ) = color; - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * px + 1 ) = color; + if (cliprect.contains(px, scanline)) + { + collision_map[ px ] |= COLLISION_VERTICAL_GRID; + bitmap.pix16( scanline, px ) = color; + } } } } @@ -533,7 +585,7 @@ void i8244_device::render_scanline(int vpos) } /* Display objects if enabled */ - if ( m_vdc.s.control & 0x20 ) + if ( m_vdc.s.control & 0x20 && scanline <= 242 ) { /* Regular foreground objects */ for ( int i = ARRAY_LENGTH( m_vdc.s.foreground ) - 1; i >= 0; i-- ) @@ -545,29 +597,39 @@ void i8244_device::render_scanline(int vpos) if ( y <= scanline && scanline < y + height * 2 ) { uint16_t color = 8 + bgr2rgb[ ( ( m_vdc.s.foreground[i].color >> 1 ) & 0x07 ) ]; - int offset = ( m_vdc.s.foreground[i].ptr | ( ( m_vdc.s.foreground[i].color & 0x01 ) << 8 ) ) + ( y >> 1 ) + ( ( scanline - y ) >> 1 ); - uint8_t chr = m_charset[ offset & 0x1FF ]; - int x = m_vdc.s.foreground[i].x; + int offset = ( m_vdc.s.foreground[i].ptr | ( ( m_vdc.s.foreground[i].color & 0x01 ) << 8 ) ) + ( y >> 1 ) + ( ( scanline - y ) >> 1 ); + uint8_t chr = m_charset[ offset & 0x1FF ]; + int x = (m_vdc.s.foreground[i].x + 5) * 2; - for ( uint8_t m = 0x80; m > 0; m >>= 1, x++ ) + for ( uint8_t m = 0x80; m > 0; m >>= 1, x += 2 ) { if ( chr & m ) { - if ( x >= 0 && x < 160 ) + for (int px = x; px < x + 2; px++) { - /* Check if we collide with an already drawn source object */ - if ( collision_map[ x ] & m_vdc.s.collision ) - { - m_collision_status |= COLLISION_CHARACTERS; - } - /* Check if an already drawn object would collide with us */ - if ( COLLISION_CHARACTERS & m_vdc.s.collision && collision_map[ x ] ) + if (cliprect.contains(px, scanline)) { - m_collision_status |= collision_map[ x ]; + /* Check collision with self */ + u8 colx = collision_map[ px ] & ~COLLISION_EXTERNAL; + if (colx & COLLISION_CHARACTERS) + { + colx &= ~COLLISION_CHARACTERS; + m_control_status |= COLLISION_CHARACTERS; + } + + /* Check if we collide with an already drawn source object */ + if ( colx & m_vdc.s.collision ) + { + m_collision_status |= COLLISION_CHARACTERS; + } + /* Check if an already drawn object would collide with us */ + if ( COLLISION_CHARACTERS & m_vdc.s.collision && colx ) + { + m_collision_status |= colx; + } + collision_map[ px ] |= COLLISION_CHARACTERS; + bitmap.pix16( scanline, px ) = color; } - collision_map[ x ] |= COLLISION_CHARACTERS; - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * x ) = color; - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * x + 1 ) = color; } } } @@ -585,33 +647,43 @@ void i8244_device::render_scanline(int vpos) if ( y <= scanline && scanline < y + height * 2 ) { - int x = m_vdc.s.quad[i].single[0].x; + int x = (m_vdc.s.quad[i].single[0].x + 5) * 2; - for ( int j = 0; j < ARRAY_LENGTH( m_vdc.s.quad[0].single ); j++, x += 8 ) + for ( int j = 0; j < ARRAY_LENGTH( m_vdc.s.quad[0].single ); j++, x += 16 ) { uint16_t color = 8 + bgr2rgb[ ( ( m_vdc.s.quad[i].single[j].color >> 1 ) & 0x07 ) ]; int offset = ( m_vdc.s.quad[i].single[j].ptr | ( ( m_vdc.s.quad[i].single[j].color & 0x01 ) << 8 ) ) + ( y >> 1 ) + ( ( scanline - y ) >> 1 ); uint8_t chr = m_charset[ offset & 0x1FF ]; - for ( uint8_t m = 0x80; m > 0; m >>= 1, x++ ) + for ( uint8_t m = 0x80; m > 0; m >>= 1, x += 2 ) { if ( chr & m ) { - if ( x >= 0 && x < 160 ) + for (int px = x; px < x + 2; px++) { - /* Check if we collide with an already drawn source object */ - if ( collision_map[ x ] & m_vdc.s.collision ) + if (cliprect.contains(px, scanline)) { - m_collision_status |= COLLISION_CHARACTERS; - } - /* Check if an already drawn object would collide with us */ - if ( COLLISION_CHARACTERS & m_vdc.s.collision && collision_map[ x ] ) - { - m_collision_status |= collision_map[ x ]; + /* Check collision with self */ + u8 colx = collision_map[ px ] & ~COLLISION_EXTERNAL; + if (colx & COLLISION_CHARACTERS) + { + colx &= ~COLLISION_CHARACTERS; + m_control_status |= COLLISION_CHARACTERS; + } + + /* Check if we collide with an already drawn source object */ + if ( colx & m_vdc.s.collision ) + { + m_collision_status |= COLLISION_CHARACTERS; + } + /* Check if an already drawn object would collide with us */ + if ( COLLISION_CHARACTERS & m_vdc.s.collision && colx ) + { + m_collision_status |= colx; + } + collision_map[ px ] |= COLLISION_CHARACTERS; + bitmap.pix16( scanline, px ) = color; } - collision_map[ x ] |= COLLISION_CHARACTERS; - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * x ) = color; - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + 2 * x + 1 ) = color; } } } @@ -624,116 +696,53 @@ void i8244_device::render_scanline(int vpos) { int y = m_vdc.s.sprites[i].y; int height = 8; + bool zoom_enable = bool(m_vdc.s.sprites[i].color & 4); + int zoom_px = zoom_enable ? 4 : 2; - if ( m_vdc.s.sprites[i].color & 4 ) + if ( y <= scanline && scanline < y + height * zoom_px ) { - /* Zoomed sprite */ - if ( y <= scanline && scanline < y + height * 4 ) - { - uint16_t color = 8 + bgr2rgb[ ( ( m_vdc.s.sprites[i].color >> 3 ) & 0x07 ) ]; - uint8_t chr = m_vdc.s.shape[i][ ( ( scanline - y ) >> 2 ) ]; - int x = m_vdc.s.sprites[i].x; - int x_shift = 0; - - switch ( m_vdc.s.sprites[i].color & 0x03 ) - { - case 1: // Xg attribute set - x_shift = 2; - break; - case 2: // S attribute set - x_shift = ( ( ( scanline - y ) >> 1 ) & 0x01 ) ^ 0x01; - break; - case 3: // Xg and S attributes set - x_shift = ( ( scanline - y ) >> 1 ) & 0x01; - break; - } - x_shift <<= 1; + uint16_t color = 8 + bgr2rgb[ ( ( m_vdc.s.sprites[i].color >> 3 ) & 0x07 ) ]; + uint8_t chr = m_vdc.s.shape[i][ ( ( scanline - y ) / zoom_px ) ]; + int x = (m_vdc.s.sprites[i].x + 5) * 2; + int x_shift = 0; - for ( uint8_t m = 0x01; m > 0; m <<= 1, x += 2 ) - { - if ( chr & m ) - { - if ( x >= 0 && x < 160 ) - { - /* Check if we collide with an already drawn source object */ - if ( collision_map[ x ] & m_vdc.s.collision ) - { - m_collision_status |= ( 1 << i ); - } - /* Check if an already drawn object would collide with us */ - if ( ( 1 << i ) & m_vdc.s.collision && collision_map[ x ] ) - { - m_collision_status |= collision_map[ x ]; - } - collision_map[ x ] |= ( 1 << i ); - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + x_shift + 2 * x ) = color; - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + x_shift + 2 * x + 1 ) = color; - } - if ( x >= -1 && x < 159 ) - { - if ( x >= 0 ) - { - /* Check if we collide with an already drawn source object */ - if ( collision_map[ x ] & m_vdc.s.collision ) - { - m_collision_status |= ( 1 << i ); - } - /* Check if an already drawn object would collide with us */ - if ( ( 1 << i ) & m_vdc.s.collision && collision_map[ x ] ) - { - m_collision_status |= collision_map[ x ]; - } - collision_map[ x ] |= ( 1 << i ); - } - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + x_shift + 2 * x + 2 ) = color; - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + x_shift + 2 * x + 3 ) = color; - } - } - } - } - } - else - { - /* Regular sprite */ - if ( y <= scanline && scanline < y + height * 2 ) + switch ( m_vdc.s.sprites[i].color & 0x03 ) { - uint16_t color = 8 + bgr2rgb[ ( ( m_vdc.s.sprites[i].color >> 3 ) & 0x07 ) ]; - uint8_t chr = m_vdc.s.shape[i][ ( ( scanline - y ) >> 1 ) ]; - int x = m_vdc.s.sprites[i].x; - int x_shift = 0; + case 1: // Xg attribute set + x_shift = 1; + break; + case 2: // S attribute set + x_shift = ( ( ( scanline - y ) / zoom_px ) & 0x01 ) ^ 0x01; + break; + case 3: // Xg and S attributes set + x_shift = ( ( scanline - y ) / zoom_px ) & 0x01; + break; + default: + break; + } - switch ( m_vdc.s.sprites[i].color & 0x03 ) - { - case 1: // Xg attribute set - x_shift = 1; - break; - case 2: // S attribute set - x_shift = ( ( ( scanline - y ) >> 1 ) & 0x01 ) ^ 0x01; - break; - case 3: // Xg and S attributes set - x_shift = ( ( scanline - y ) >> 1 ) & 0x01; - break; - } + x += x_shift * (zoom_px / 2); - for ( uint8_t m = 0x01; m > 0; m <<= 1, x++ ) + for ( uint8_t m = 0x01; m > 0; m <<= 1, x += zoom_px ) + { + if ( chr & m ) { - if ( chr & m ) + for (int px = x; px < x + zoom_px; px++) { - if ( x >= 0 && x < 160 ) + if (cliprect.contains(px, scanline)) { /* Check if we collide with an already drawn source object */ - if ( collision_map[ x ] & m_vdc.s.collision ) + if ( collision_map[ px ] & m_vdc.s.collision ) { m_collision_status |= ( 1 << i ); } /* Check if an already drawn object would collide with us */ - if ( ( 1 << i ) & m_vdc.s.collision && collision_map[ x ] ) + if ( ( 1 << i ) & m_vdc.s.collision && collision_map[ px ] ) { - m_collision_status |= collision_map[ x ]; + m_collision_status |= collision_map[ px ]; } - collision_map[ x ] |= ( 1 << i ); - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + x_shift + 2 * x ) = color; - m_tmp_bitmap.pix16( vpos, START_ACTIVE_SCAN + 10 + x_shift + 2 * x + 1 ) = color; + collision_map[ px ] |= ( 1 << i ); + bitmap.pix16( scanline, px ) = color; } } } @@ -743,83 +752,58 @@ void i8244_device::render_scanline(int vpos) } } - // Allow driver to do additional processing - m_postprocess_func( vpos ); - - /* Check for start of VBlank */ - if ( vpos == m_start_vblank ) - { - m_control_status |= 0x08; - if ( ! m_iff ) - { - m_iff = 1; - m_irq_func(ASSERT_LINE); - } - } + return 0; } void i8244_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { - uint32_t old_signal, signal; - int ii; - int period; - stream_sample_t *buffer = outputs[0]; - - /* Generate the signal */ - old_signal = signal = m_vdc.s.shift3 | (m_vdc.s.shift2 << 8) | (m_vdc.s.shift1 << 16); + u8 volume = m_vdc.s.sound & 0xf; + int sample_on = (m_sh_output & m_vdc.s.sound >> 7) * 0x4000; - if( m_vdc.s.sound & 0x80 ) /* Sound is enabled */ + for (int i = 0; i < samples; i++) { - for( ii = 0; ii < samples; ii++, buffer++ ) - { - *buffer = signal & 0x1; - period = (m_vdc.s.sound & 0x20) ? 1 : 4; - if( ++m_sh_count >= period ) - { - m_sh_count = 0; - signal >>= 1; - /* Loop sound */ - signal |= *buffer << 23; - /* Check if noise should be applied */ - if ( m_vdc.s.sound & 0x10 ) - { - /* Noise tap is on bits 0 and 5 and fed back to bits 15 (and 23!) */ - uint32_t new_bit = ( ( old_signal ) ^ ( old_signal >> 5 ) ) & 0x01; - signal = ( old_signal & 0xFF0000 ) | ( ( old_signal & 0xFFFF ) >> 1 ) | ( new_bit << 15 ) | ( new_bit << 23 ); - } - m_vdc.s.shift3 = signal & 0xFF; - m_vdc.s.shift2 = ( signal >> 8 ) & 0xFF; - m_vdc.s.shift1 = ( signal >> 16 ) & 0xFF; - old_signal = signal; - } + // clock duty cycle + m_sh_duty = (m_sh_duty + 1) & 0xf; + outputs[0][i] = (m_sh_duty < volume) ? sample_on : 0; + } +} - /* Throw an interrupt if enabled */ - if( m_vdc.s.control & 0x4 ) - { - // This feature does not seem to be finished/enabled in hardware! - } - /* Adjust volume */ - *buffer *= m_vdc.s.sound & 0xf; - /* Pump the volume up */ - *buffer <<= 10; - } - } - else +void i8244_device::sound_update() +{ + // clock prescaler + m_sh_prescaler++; + u8 prescaler_mask = (m_vdc.s.sound & 0x20) ? 3 : 0xf; + if ((m_sh_prescaler & prescaler_mask) == 0) + m_sh_pending = true; + + // clock shift registers + if (m_sh_pending && !m_sh_written) { - /* Sound disabled, so clear the buffer */ - for( ii = 0; ii < samples; ii++, buffer++ ) + m_stream->update(); + m_sh_pending = false; + + u32 signal = m_vdc.s.shift3 | (m_vdc.s.shift2 << 8) | (m_vdc.s.shift1 << 16); + m_sh_output = signal & 1; + signal >>= 1; + + bool noise_enabled = bool(m_vdc.s.sound & 0x10); + int feedback = m_sh_output; + + /* Noise tap is on bits 0 and 5 and fed back to bit 15 */ + if (noise_enabled) { - *buffer = 0; + feedback ^= signal >> 4 & 1; // pre-shift bit 5 + signal = (signal & ~0x8000) | (feedback << 15); } - } -} - -uint32_t i8244_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) -{ - copybitmap( bitmap, m_tmp_bitmap, 0, 0, 0, 0, cliprect ); + /* Loop sound */ + signal |= feedback << 23; - return 0; + m_vdc.s.shift3 = signal & 0xFF; + m_vdc.s.shift2 = ( signal >> 8 ) & 0xFF; + m_vdc.s.shift1 = ( signal >> 16 ) & 0xFF; + } + m_sh_written = false; } diff --git a/src/devices/video/i8244.h b/src/devices/video/i8244.h index 8db0fb511d9..70f417c7c70 100644 --- a/src/devices/video/i8244.h +++ b/src/devices/video/i8244.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Wilbert Pol +// copyright-holders:Wilbert Pol, hap /*************************************************************************** Intel 8244 (NTSC)/8245 (PAL) Graphics and sound chip @@ -31,7 +31,7 @@ public: // configuration helpers auto irq_cb() { return m_irq_func.bind(); } - auto postprocess_cb() { return m_postprocess_func.bind(); } + i8244_device &set_screen_size(int width, int height, int cropx, int cropy); uint8_t read(offs_t offset); void write(offs_t offset, uint8_t data); @@ -41,17 +41,6 @@ public: uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); - inline bitmap_ind16 *get_bitmap() { return &m_tmp_bitmap; } - - // Global constants - static constexpr int START_ACTIVE_SCAN = 42; - static constexpr int BORDER_SIZE = 10; - static constexpr int END_ACTIVE_SCAN = 42 + 10 + 320 + 10; - static constexpr int START_Y = 1; - static constexpr int SCREEN_HEIGHT = 243; - static constexpr int LINE_CLOCKS = 455; - static constexpr int LINES = 262; - protected: union vdc_t { uint8_t reg[0x100]; @@ -97,53 +86,68 @@ protected: } s; }; - i8244_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, int lines); + i8244_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); // device-level overrides virtual void device_config_complete() override; virtual void device_start() override; - virtual void device_reset() override; virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; virtual const tiny_rom_entry *device_rom_region() const override; // device_sound_interface overrides virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; - void render_scanline(int vpos); + virtual void set_default_params(); + + //void render_scanline(int vpos); int get_y_beam(); int get_x_beam(); offs_t fix_register_mirrors( offs_t offset ); - bool unused_register( offs_t offset ); - - // Local constants - static constexpr uint8_t VDC_CONTROL_REG_STROBE_XY = 0x02; + bool invalid_register( offs_t offset, bool rw ); /* timers */ - static constexpr device_timer_id TIMER_LINE = 0; - static constexpr device_timer_id TIMER_HBLANK = 1; + static constexpr device_timer_id TIMER_VBLANK_START = 0; + static constexpr device_timer_id TIMER_HBLANK_START = 1; // callbacks devcb_write_line m_irq_func; - devcb_write16 m_postprocess_func; required_region_ptr<uint8_t> m_charset; - bitmap_ind16 m_tmp_bitmap; - emu_timer *m_line_timer; emu_timer *m_hblank_timer; + emu_timer *m_vblank_timer; sound_stream *m_stream; - int m_start_vpos; - int m_start_vblank; - int m_screen_lines; + void sound_update(); + + int m_htotal; + int m_vtotal; + int m_width; + int m_height; + int m_cropx; + int m_cropy; + + int m_vblank_start; + int m_vblank_end; + int m_hblank_start; + int m_hblank_end; + int m_bgate_start; vdc_t m_vdc; - uint16_t m_sh_count; - uint8_t m_x_beam_pos; - uint8_t m_y_beam_pos; - uint8_t m_control_status; - uint8_t m_collision_status; - int m_iff; + + uint8_t m_x_beam_pos = 0; + uint8_t m_y_beam_pos = 0; + uint8_t m_y_latch = 0; + uint8_t m_control_status = 0; + uint8_t m_collision_status = 0; + bool m_pos_hold = false; + bool m_y_hold = false; + + bool m_sh_written = false; + bool m_sh_pending = false; + u8 m_sh_prescaler = 0 ; + int m_sh_output = 0; + u8 m_sh_duty = 0; }; @@ -153,7 +157,8 @@ public: // construction/destruction i8245_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - static constexpr int LINES = 312; +protected: + virtual void set_default_params() override; }; diff --git a/src/emu/xtal.cpp b/src/emu/xtal.cpp index bc960f8c8ee..c1d3b9f6467 100644 --- a/src/emu/xtal.cpp +++ b/src/emu/xtal.cpp @@ -90,8 +90,8 @@ const double XTAL::known_xtals[] = { 3'120'000, /* 3.12_MHz_XTAL SP0250 clock on Gottlieb games */ 3'276'800, /* 3.2768_MHz_XTAL SP0256 clock in Speech Synthesis for Dragon 32 */ 3'521'280, /* 3.52128_MHz_XTAL RCA COSMAC VIP */ - 3'546'800, /* 3.5468_MHz_XTAL Atari 400 PAL, Philips G7200 */ - 3'547'000, /* 3.547_MHz_XTAL Philips C7240 */ + 3'546'800, /* 3.5468_MHz_XTAL Atari 400 PAL */ + 3'547'000, /* 3.547_MHz_XTAL Philips G7200, Philips C7240 */ 3'570'000, /* 3.57_MHz_XTAL Telmac TMC-600 */ 3'578'640, /* 3.57864_MHz_XTAL Atari Portfolio PCD3311T */ 3'579'000, /* 3.579_MHz_XTAL BeebOPL */ diff --git a/src/mame/drivers/odyssey2.cpp b/src/mame/drivers/odyssey2.cpp index 5226574e1cd..b83f0a9d92a 100644 --- a/src/mame/drivers/odyssey2.cpp +++ b/src/mame/drivers/odyssey2.cpp @@ -1,17 +1,52 @@ // license:BSD-3-Clause -// copyright-holders:Wilbert Pol +// copyright-holders:Wilbert Pol, hap /*************************************************************************** -Driver file to handle emulation of the Odyssey2. +Driver file to handle emulation of the Magnavox Odyssey 2/Philips Videopac +G7000 and Philips Videopac+ G7400. + +Magnavox was wholly owned by Philips at the time. Limited release as the +Philips G7000 late-1978 in Europe, but quickly halted due to a hardware defect, +wide release continued in 1979. + +Odyssey 2/Videopac hardware notes: +- Intel 8048 (1KB internal ROM, 64 bytes internal RAM) +- 128 bytes RAM(6810) +- Intel 8244 for video and sound (8245 on PAL consoles) +- 2 joysticks(either hardwired, or connectors), keyboard + +Videopac+ G7400 hardware notes: +- same base hardware +- Intel 8243 I/O expander +- EF9340 + EF9341 graphics chips +- larger keyboard + +XTAL notes (differs per model): +- Odyssey 2: 7.15909MHz +- G7000: 17.734476MHz +- C52/N60: 17.812 +- G7200: 5.911MHz + 3.547MHz +- G7400: 5.911MHz + 8.867MHz TODO: -- odyssey3 cpu/video should probably have a different XTAL -- backgamm has display timing problems (it does a lot of partial screen updates) -- homecomp does not work, needs new slot device -- g7400 keyboard has 2 more columns +- verify odyssey3 cpu/video clocks +- backgamm doesn't draw all the sprites, what causes it? It doesn't seem like + it's a 824x bug since it does properly write data in the partial screen updates +- 824x screen resolution is not strictly defined +- 824x on the real console, overlapping characters on eachother will cause glitches + (it is used to an advantage in some as-of-yet undumped homebrews) +- 8244(NTSC) is not supposed to show characters near the upper border, but + hiding them will cause bugs in some Euro games +- 8245(PAL) video timing is not 100% accurate, though vtotal and htotal should + be correct(see backgamm) +- ppp(the tetris game) does not work properly on PAL, is this homebrew NTSC-only, + or is it due to PAL video timing? The game does mid-scanline updates +- add 824x vs ef934x collision detection +- g7400 probably has different video timing too (not same as g7000) +- g7400 graphics problems, some of them severe, eg. rally (for homecomp, + enter command TX 1,0,0 to see the text) - g7400 EF9341 R/W is connected to CPU A2, what happens if it is disobeyed? -- a lot more issues, probably, this TODO list was written by someone with - not much knowledge on odyssey2 +- g7400 keyboard has 2 more columns ***************************************************************************/ @@ -37,6 +72,7 @@ public: driver_device(mconfig, type, tag), m_maincpu(*this, "maincpu"), m_i8244(*this, "i8244"), + m_screen(*this, "screen"), m_cart(*this, "cartslot"), m_keyboard(*this, "KEY.%u", 0), m_joysticks(*this, "JOY.%u", 0) @@ -49,23 +85,19 @@ public: void videopac(machine_config &config); void videopacf(machine_config &config); - uint32_t screen_update_odyssey2(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); - protected: required_device<i8048_device> m_maincpu; required_device<i8244_device> m_i8244; + required_device<screen_device> m_screen; required_device<o2_cart_slot_device> m_cart; uint8_t m_ram[0x80]; uint8_t m_p1; uint8_t m_p2; - uint8_t m_lum; DECLARE_READ_LINE_MEMBER(t1_read); void odyssey2_palette(palette_device &palette) const; - void scanline_postprocess(uint16_t data); - void odyssey2_io(address_map &map); void odyssey2_mem(address_map &map); @@ -93,6 +125,9 @@ protected: void p1_write(uint8_t data); uint8_t p2_read(); void p2_write(uint8_t data); + +private: + uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); }; class g7400_state : public odyssey2_state @@ -111,7 +146,8 @@ private: required_device<i8243_device> m_i8243; required_device<ef9340_1_device> m_ef9340_1; - void g7400_palette(palette_device &palette) const; + uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); + virtual void machine_start() override; virtual void machine_reset() override; void p2_write(uint8_t data); @@ -121,7 +157,6 @@ private: void i8243_p5_w(uint8_t data); void i8243_p6_w(uint8_t data); void i8243_p7_w(uint8_t data); - void scanline_postprocess(uint16_t data); void g7400_io(address_map &map); @@ -208,8 +243,8 @@ static INPUT_PORTS_START( odyssey2 ) PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_EQUALS) PORT_CHAR('=') PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(DEF_STR(Yes)) PORT_CODE(KEYCODE_Y) PORT_CHAR('Y') PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME(DEF_STR(No)) PORT_CODE(KEYCODE_N) PORT_CHAR('N') - PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("CLR") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) - PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("ENT") PORT_CODE(KEYCODE_ENTER) PORT_CHAR('\r') + PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Clear") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER) PORT_CHAR('\r') PORT_START("KEY.6") PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED) @@ -237,13 +272,30 @@ static INPUT_PORTS_START( odyssey2 ) PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_F1) PORT_CHANGED_MEMBER(DEVICE_SELF, odyssey2_state, reset_button, 0) PORT_NAME("Reset") INPUT_PORTS_END +static INPUT_PORTS_START( g7400 ) + PORT_INCLUDE( odyssey2 ) + + PORT_MODIFY("KEY.6") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_COMMA) PORT_CHAR(',') + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED) + + PORT_MODIFY("KEY.7") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_UNUSED) + PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED) +INPUT_PORTS_END -/* character sprite colors - dark grey, red, green, yellow, blue, violet, light grey, white - dark back / grid colors - black, dark blue, dark green, light green, red, violet, yellow, light grey - light back / grid colors - black, blue, green, light green, red, violet, yellow, light grey */ constexpr rgb_t odyssey2_colors[] = { @@ -275,40 +327,13 @@ void odyssey2_state::odyssey2_palette(palette_device &palette) const } -void g7400_state::g7400_palette(palette_device &palette) const -{ - constexpr rgb_t g7400_colors[]{ - { 0x00, 0x00, 0x00 }, // Black - { 0x1a, 0x37, 0xbe }, // Blue - { 0x00, 0x6d, 0x07 }, // Green - { 0x2a, 0xaa, 0xbe }, // Blue-Green - { 0x79, 0x00, 0x00 }, // Red - { 0x94, 0x30, 0x9f }, // Violet - { 0x77, 0x67, 0x0b }, // Khaki - { 0xce, 0xce, 0xce }, // Lt Grey - - { 0x67, 0x67, 0x67 }, // Grey - { 0x5c, 0x80, 0xf6 }, // Lt Blue - { 0x56, 0xc4, 0x69 }, // Lt Green - { 0x77, 0xe6, 0xeb }, // Lt Blue-Green - { 0xc7, 0x51, 0x51 }, // Lt Red - { 0xdc, 0x84, 0xe8 }, // Lt Violet - { 0xc6, 0xb8, 0x6a }, // Lt Yellow - { 0xff, 0xff, 0xff } // White - }; - - palette.set_pen_colors(0, g7400_colors); -} - - void odyssey2_state::machine_start() { - memset(m_ram, 0, 0x80); + memset(m_ram, 0, sizeof(m_ram)); save_item(NAME(m_ram)); save_item(NAME(m_p1)); save_item(NAME(m_p2)); - save_item(NAME(m_lum)); } @@ -321,8 +346,11 @@ void g7400_state::machine_start() { odyssey2_state::machine_start(); - save_pointer(NAME(m_ic674_decode),8); - save_pointer(NAME(m_ic678_decode),8); + memset(m_ic674_decode, 0, sizeof(m_ic674_decode)); + memset(m_ic678_decode, 0, sizeof(m_ic678_decode)); + + save_item(NAME(m_ic674_decode)); + save_item(NAME(m_ic678_decode)); } @@ -386,66 +414,58 @@ void g7400_state::io_write(offs_t offset, uint8_t data) } -void odyssey2_state::scanline_postprocess(uint16_t data) +uint32_t odyssey2_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { - int vpos = data; - bitmap_ind16 *bitmap = m_i8244->get_bitmap(); + m_i8244->screen_update(screen, bitmap, cliprect); - if ( vpos < i8244_device::START_Y || vpos >= i8244_device::START_Y + i8244_device::SCREEN_HEIGHT ) - { - return; - } + u8 lum = ~m_p1 >> 4 & 0x08; // apply external LUM setting - for ( int x = i8244_device::START_ACTIVE_SCAN; x < i8244_device::END_ACTIVE_SCAN; x++ ) - { - bitmap->pix16( vpos, x ) |= ( m_lum ^ 0x08 ); - } + for (int y = cliprect.min_y; y <= cliprect.max_y; y++) + for (int x = cliprect.min_x; x <= cliprect.max_x; x++) + bitmap.pix16(y, x) |= lum; + + return 0; } -void g7400_state::scanline_postprocess(uint16_t data) +uint32_t g7400_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { - int vpos = data; - int y = vpos - i8244_device::START_Y - 5; - bitmap_ind16 *bitmap = m_i8244->get_bitmap(); + m_i8244->screen_update(screen, bitmap, cliprect); + + u8 lum = ~m_p1 >> 4 & 0x08; bitmap_ind16 *ef934x_bitmap = m_ef9340_1->get_bitmap(); - if ( vpos < i8244_device::START_Y || vpos >= i8244_device::START_Y + i8244_device::SCREEN_HEIGHT ) - { - return; - } + const int xoffs = 15; + const int yoffs = 5; // apply external LUM setting - int x_real_start = i8244_device::START_ACTIVE_SCAN + i8244_device::BORDER_SIZE + 5; - int x_real_end = i8244_device::END_ACTIVE_SCAN - i8244_device::BORDER_SIZE + 5; - for ( int x = i8244_device::START_ACTIVE_SCAN; x < i8244_device::END_ACTIVE_SCAN; x++ ) + for (int y = cliprect.min_y; y <= cliprect.max_y; y++) { - uint16_t d = bitmap->pix16( vpos, x ); - - if ( ( ! m_ic678_decode[ d & 0x07 ] ) && x >= x_real_start && x < x_real_end && y >= 0 && y < 240 ) + for (int x = cliprect.min_x; x <= cliprect.max_x; x++) { - // Use EF934x input - d = ef934x_bitmap->pix16( y, x - x_real_start ) & 0x07; + uint16_t d = bitmap.pix16(y, x); - if ( ! m_ic674_decode[ d & 0x07 ] ) + if ( ( ! m_ic678_decode[ d & 0x07 ] ) && x >= xoffs && x < (320 + xoffs) && y >= yoffs ) { - d |= 0x08; + // Use EF934x input + d = ef934x_bitmap->pix16( y - yoffs, x - xoffs ) & 0x07; + + if ( ! m_ic674_decode[ d & 0x07 ] ) + { + d |= 0x08; + } } + else + { + // Use i8245 input + d |= lum; + } + bitmap.pix16(y, x) = d; } - else - { - // Use i8245 input - d |= ( m_lum ^ 0x08 ); - } - bitmap->pix16( vpos, x ) = d; } -} - -uint32_t odyssey2_state::screen_update_odyssey2(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) -{ - return m_i8244->screen_update(screen, bitmap, cliprect); + return 0; } @@ -457,23 +477,24 @@ READ_LINE_MEMBER(odyssey2_state::t1_read) uint8_t odyssey2_state::p1_read() { - uint8_t data = m_p1; - - return data; + return 0xff; } void odyssey2_state::p1_write(uint8_t data) { + // LUM changed + if ((m_p1 ^ data) & 0x80) + m_screen->update_now(); + m_p1 = data; - m_lum = ( data & 0x80 ) >> 4; m_cart->write_p1(m_p1 & 0x13); } uint8_t odyssey2_state::p2_read() { - u8 data = m_p2; + u8 data = 0xff; if (!(m_p1 & P1_KEYBOARD_SCAN_ENABLE)) { @@ -529,6 +550,7 @@ void odyssey2_state::bus_write(uint8_t data) void g7400_state::i8243_p4_w(uint8_t data) { // "port 4" + m_screen->update_now(); logerror("setting ef-port4 to %02x\n", data); m_ic674_decode[4] = BIT(data,0); m_ic674_decode[5] = BIT(data,1); @@ -540,6 +562,7 @@ void g7400_state::i8243_p4_w(uint8_t data) void g7400_state::i8243_p5_w(uint8_t data) { // "port 5" + m_screen->update_now(); logerror("setting ef-port5 to %02x\n", data); m_ic674_decode[0] = BIT(data,0); m_ic674_decode[1] = BIT(data,1); @@ -551,6 +574,7 @@ void g7400_state::i8243_p5_w(uint8_t data) void g7400_state::i8243_p6_w(uint8_t data) { // "port 6" + m_screen->update_now(); logerror("setting vdc-port6 to %02x\n", data); m_ic678_decode[4] = BIT(data,0); m_ic678_decode[5] = BIT(data,1); @@ -562,6 +586,7 @@ void g7400_state::i8243_p6_w(uint8_t data) void g7400_state::i8243_p7_w(uint8_t data) { // "port 7" + m_screen->update_now(); logerror("setting vdc-port7 to %02x\n", data); m_ic678_decode[0] = BIT(data,0); m_ic678_decode[1] = BIT(data,1); @@ -587,17 +612,18 @@ void odyssey2_state::odyssey2(machine_config &config) m_maincpu->t1_in_cb().set(FUNC(odyssey2_state::t1_read)); /* video hardware */ - screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); - screen.set_screen_update(FUNC(odyssey2_state::screen_update_odyssey2)); - screen.set_palette("palette"); + SCREEN(config, m_screen, SCREEN_TYPE_RASTER); + m_screen->set_screen_update(FUNC(odyssey2_state::screen_update)); + m_screen->set_video_attributes(VIDEO_ALWAYS_UPDATE); + m_screen->set_palette("palette"); PALETTE(config, "palette", FUNC(odyssey2_state::odyssey2_palette), 16); SPEAKER(config, "mono").front_center(); I8244(config, m_i8244, XTAL(7'159'090) / 2); m_i8244->set_screen("screen"); + m_i8244->set_screen_size(344, 242, 0, 0); m_i8244->irq_cb().set_inputline(m_maincpu, MCS48_INPUT_IRQ); - m_i8244->postprocess_cb().set(FUNC(odyssey2_state::scanline_postprocess)); m_i8244->add_route(ALL_OUTPUTS, "mono", 0.40); /* cartridge */ @@ -609,15 +635,14 @@ void odyssey2_state::videopac(machine_config &config) { odyssey2(config); - // different master XTAL - m_maincpu->set_clock(XTAL(17'734'470) / 3); - // PAL video chip - I8245(config.replace(), m_i8244, XTAL(17'734'470) / 5); + I8245(config.replace(), m_i8244, XTAL(17'734'476) / 5); m_i8244->set_screen("screen"); + m_i8244->set_screen_size(344, 242, 0, 0); m_i8244->irq_cb().set_inputline(m_maincpu, MCS48_INPUT_IRQ); - m_i8244->postprocess_cb().set(FUNC(odyssey2_state::scanline_postprocess)); m_i8244->add_route(ALL_OUTPUTS, "mono", 0.40); + + m_maincpu->set_clock(XTAL(17'734'476) / 3); } void odyssey2_state::videopacf(machine_config &config) @@ -647,11 +672,12 @@ void g7400_state::g7400(machine_config &config) m_maincpu->prog_out_cb().set(m_i8243, FUNC(i8243_device::prog_w)); /* video hardware */ - screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); - screen.set_screen_update(FUNC(odyssey2_state::screen_update_odyssey2)); - screen.set_palette("palette"); + SCREEN(config, m_screen, SCREEN_TYPE_RASTER); + m_screen->set_screen_update(FUNC(g7400_state::screen_update)); + m_screen->set_video_attributes(VIDEO_ALWAYS_UPDATE); + m_screen->set_palette("palette"); - PALETTE(config, "palette", FUNC(g7400_state::g7400_palette), 16); + PALETTE(config, "palette", m_i8244, FUNC(i8244_device::i8244_palette), 16); I8243(config, m_i8243); m_i8243->p4_out_cb().set(FUNC(g7400_state::i8243_p4_w)); @@ -664,8 +690,8 @@ void g7400_state::g7400(machine_config &config) SPEAKER(config, "mono").front_center(); I8245(config, m_i8244, XTAL(8'867'000)/5 * 2); m_i8244->set_screen("screen"); + m_i8244->set_screen_size(320, 240, 15, 5); m_i8244->irq_cb().set_inputline(m_maincpu, MCS48_INPUT_IRQ); - m_i8244->postprocess_cb().set(FUNC(g7400_state::scanline_postprocess)); m_i8244->add_route(ALL_OUTPUTS, "mono", 0.40); /* cartridge */ @@ -679,11 +705,14 @@ void g7400_state::odyssey3(machine_config &config) g7400(config); // NTSC video chip - I8244(config.replace(), m_i8244, XTAL(8'867'000)/5 * 2); + I8244(config.replace(), m_i8244, XTAL(7'159'090) / 2); m_i8244->set_screen("screen"); + m_i8244->set_screen_size(320, 240, 15, 5); m_i8244->irq_cb().set_inputline(m_maincpu, MCS48_INPUT_IRQ); - m_i8244->postprocess_cb().set(FUNC(g7400_state::scanline_postprocess)); m_i8244->add_route(ALL_OUTPUTS, "mono", 0.40); + + m_ef9340_1->set_clock(XTAL(7'159'090) / 2); + m_maincpu->set_clock((XTAL(7'159'090) * 3) / 4); } @@ -720,10 +749,10 @@ ROM_END /* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY, FULLNAME, FLAGS */ -COMP( 1979, odyssey2, 0, 0, odyssey2, odyssey2, odyssey2_state, empty_init, "Magnavox", "Odyssey 2 (US)", 0 ) -COMP( 1979, videopac, odyssey2, 0, videopac, odyssey2, odyssey2_state, empty_init, "Philips", "Videopac G7000 (Europe)", 0 ) -COMP( 1979, videopacf, odyssey2, 0, videopacf, odyssey2, odyssey2_state, empty_init, "Philips", "Videopac C52 (France)", 0 ) +COMP( 1979, odyssey2, 0, 0, odyssey2, odyssey2, odyssey2_state, empty_init, "Magnavox", "Odyssey 2 (US)", MACHINE_SUPPORTS_SAVE ) +COMP( 1978, videopac, odyssey2, 0, videopac, odyssey2, odyssey2_state, empty_init, "Philips", "Videopac G7000 (Europe)", MACHINE_SUPPORTS_SAVE ) +COMP( 1979, videopacf, odyssey2, 0, videopacf, odyssey2, odyssey2_state, empty_init, "Philips", "Videopac C52 (France)", MACHINE_SUPPORTS_SAVE ) -COMP( 1983, g7400, 0, 0, g7400, odyssey2, g7400_state, empty_init, "Philips", "Videopac Plus G7400 (Europe)", MACHINE_IMPERFECT_GRAPHICS ) -COMP( 1983, jopac, g7400, 0, g7400, odyssey2, g7400_state, empty_init, "Philips (Brandt license)", "Jopac JO7400 (France)", MACHINE_IMPERFECT_GRAPHICS ) -COMP( 1983, odyssey3, g7400, 0, odyssey3, odyssey2, g7400_state, empty_init, "Magnavox", "Odyssey 3 Command Center (US, prototype)", MACHINE_IMPERFECT_GRAPHICS ) +COMP( 1983, g7400, 0, 0, g7400, g7400, g7400_state, empty_init, "Philips", "Videopac+ G7400 (Europe)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS ) +COMP( 1983, jopac, g7400, 0, g7400, g7400, g7400_state, empty_init, "Philips (Brandt license)", "Jopac JO7400 (France)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS ) +COMP( 1983, odyssey3, g7400, 0, odyssey3, g7400, g7400_state, empty_init, "Magnavox", "Odyssey 3 Command Center (US, prototype)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS ) |