summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author hap <happppp@users.noreply.github.com>2020-08-12 16:00:39 +0200
committer hap <happppp@users.noreply.github.com>2020-08-12 16:00:56 +0200
commit34559bb4cbf6da08ce440f39755b9b1e1b51c7c7 (patch)
tree280dca56acf1cb8c3d2f4f809e0ee1da46e0733f
parent30d0813c4603da03c3e702da9ba4cf85291a825d (diff)
i8244: fix problem with zoomed sprites, update sound/video at tighter intervals
-rw-r--r--src/devices/video/i8244.cpp826
-rw-r--r--src/devices/video/i8244.h75
-rw-r--r--src/emu/xtal.cpp4
-rw-r--r--src/mame/drivers/odyssey2.cpp269
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 )