summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/video/huc6261.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/video/huc6261.cpp')
-rw-r--r--src/devices/video/huc6261.cpp434
1 files changed, 434 insertions, 0 deletions
diff --git a/src/devices/video/huc6261.cpp b/src/devices/video/huc6261.cpp
new file mode 100644
index 00000000000..f22bdfba582
--- /dev/null
+++ b/src/devices/video/huc6261.cpp
@@ -0,0 +1,434 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/**********************************************************************
+
+ Hudson/NEC HuC6261 Video Colour Encoder
+
+ The HuC6261 generates the tv control signals. A full line lasts
+ 1365 "master" cycles (typically at 21.47727Mhz).
+
+ HSync is low for 237 and high for 1128 master cycles.
+ VSync is low for 4095 master cycles (3 lines).
+ VSync changes 30 master cycles after HSync would go low.
+
+**********************************************************************/
+
+#include "emu.h"
+#include "huc6261.h"
+
+#define LOG 0
+
+#define HUC6261_HSYNC_LENGTH 237
+#define HUC6261_HSYNC_START ( HUC6261_WPF - HUC6261_HSYNC_LENGTH )
+
+
+const device_type HUC6261 = &device_creator<huc6261_device>;
+
+
+huc6261_device::huc6261_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, HUC6261, "HuC6261", tag, owner, clock, "huc6261", __FILE__),
+ device_video_interface(mconfig, *this)
+{
+ // Set up UV lookup table
+ for ( int ur = 0; ur < 256; ur++ )
+ {
+ for ( int vr = 0; vr < 256; vr++ )
+ {
+ INT32 r,g,b;
+ INT32 u = ur - 128;
+ INT32 v = vr - 128;
+
+ r = - 1.13983 * v;
+ g = -0.35465 * u - 0.58060 * v;
+ b = 2.03211 * u;
+
+ m_uv_lookup[ ( ur << 8 ) | vr ][0] = r;
+ m_uv_lookup[ ( ur << 8 ) | vr ][1] = g;
+ m_uv_lookup[ ( ur << 8 ) | vr ][2] = b;
+ }
+ }
+}
+
+
+inline UINT32 huc6261_device::yuv2rgb(UINT32 yuv)
+{
+ INT32 r, g, b;
+ UINT8 y = yuv >> 16;
+ UINT16 uv = yuv & 0xffff;
+
+ r = y + m_uv_lookup[uv][0];
+ g = y + m_uv_lookup[uv][1];
+ b = y + m_uv_lookup[uv][2];
+
+ if ( r < 0 ) r = 0;
+ if ( g < 0 ) g = 0;
+ if ( b < 0 ) b = 0;
+ if ( r > 255 ) r = 255;
+ if ( g > 255 ) g = 255;
+ if ( b > 255 ) b = 255;
+
+ return ( r << 16 ) | ( g << 8 ) | b;
+}
+
+void huc6261_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ int vpos = m_screen->vpos();
+ int hpos = m_screen->hpos();
+ int h = m_last_h;
+ int v = m_last_v;
+ UINT32 *bitmap_line = &m_bmp->pix32(v);
+
+ while ( h != hpos || v != vpos )
+ {
+ if ( m_pixel_clock == 0 )
+ {
+ g_profiler.start( PROFILER_VIDEO );
+ /* Get next pixel information */
+ m_pixel_data = m_huc6270_b->next_pixel( machine().driver_data()->generic_space(), 0, 0xffff );
+ g_profiler.stop();
+ }
+
+ bitmap_line[ h ] = yuv2rgb( ( ( m_palette[m_pixel_data] & 0xff00 ) << 8 ) | ( ( m_palette[m_pixel_data] & 0xf0 ) << 8 ) | ( ( m_palette[m_pixel_data] & 0x0f ) << 4 ) );
+ m_pixel_clock = ( m_pixel_clock + 1 ) % m_pixels_per_clock;
+ h = ( h + 1 ) % HUC6261_WPF;
+
+ switch( h )
+ {
+ case HUC6261_HSYNC_START: /* Start of HSync */
+ m_huc6270_a->hsync_changed( 0 );
+ m_huc6270_b->hsync_changed( 0 );
+// if ( v == 0 )
+// {
+// /* Check if the screen should be resized */
+// m_height = HUC6261_LPF - ( m_blur ? 1 : 0 );
+// if ( m_height != video_screen_get_height( m_screen ) )
+// {
+// rectangle visible_area;
+//
+// /* TODO: Set proper visible area parameters */
+// visible_area.min_x = 64;
+// visible_area.min_y = 18;
+// visible_area.max_x = 64 + 1024 + 64 - 1;
+// visible_area.max_y = 18 + 242 - 1;
+//
+// video_screen_configure( m_screen, HUC6261_WPF, m_height, &visible_area, HZ_TO_ATTOSECONDS( device->clock / ( HUC6261_WPF * m_height ) ) );
+// }
+// }
+ break;
+
+ case 0: /* End of HSync */
+ m_huc6270_a->hsync_changed( 1 );
+ m_huc6270_b->hsync_changed( 1 );
+ m_pixel_clock = 0;
+ v = ( v + 1 ) % m_height;
+ bitmap_line = &m_bmp->pix32(v);
+ break;
+
+ case HUC6261_HSYNC_START + 30: /* End/Start of VSync */
+ if ( v>= m_height - 4 )
+ {
+ int vsync = ( v >= m_height - 4 && v < m_height - 1 ) ? 0 : 1;
+
+ m_huc6270_a->vsync_changed( vsync );
+ m_huc6270_b->vsync_changed( vsync );
+ }
+ break;
+ }
+ }
+
+ m_last_h = h;
+ m_last_v = v;
+
+ /* Reschedule timer */
+ if ( m_last_h < HUC6261_HSYNC_START )
+ {
+ /* Next event is start of HSync signal */
+ v = m_last_v;
+ h = HUC6261_HSYNC_START;
+ }
+ else if ( ( m_last_v == m_height - 4 || m_last_v == m_height - 1 ) && m_last_h < HUC6261_HSYNC_START + 30 )
+ {
+ /* Next event is start/end of VSync signal */
+ v = m_last_v;
+ h = HUC6261_HSYNC_START + 30;
+ }
+ else
+ {
+ /* Next event is end of HSync signal */
+ v = ( m_last_v + 1 ) % m_height;
+ h = 0;
+ }
+
+ /* Ask our slave device for time until next possible event */
+ {
+ UINT16 next_event_clocks = HUC6261_WPF; //m_get_time_til_next_event( 0, 0xffff );
+ int event_hpos, event_vpos;
+
+ /* Adjust for pixel clocks per pixel */
+ next_event_clocks *= m_pixels_per_clock;
+
+ /* Adjust for clocks left to go for current pixel */
+ next_event_clocks += ( m_pixels_per_clock - ( m_pixel_clock + 1 ) );
+
+ event_hpos = hpos + next_event_clocks;
+ event_vpos = vpos;
+ while ( event_hpos > HUC6261_WPF )
+ {
+ event_vpos += 1;
+ event_hpos -= HUC6261_WPF;
+ }
+
+ if ( event_vpos < v || ( event_vpos == v && event_hpos <= h ) )
+ {
+ if ( event_vpos > vpos || ( event_vpos == vpos && event_hpos > hpos ) )
+ {
+ v = event_vpos;
+ h = event_hpos;
+ }
+ }
+ }
+
+ m_timer->adjust( m_screen->time_until_pos( v, h ) );
+}
+
+
+void huc6261_device::video_update( bitmap_rgb32 &bitmap, const rectangle &cliprect )
+{
+ copybitmap( bitmap, *m_bmp, 0, 0, 0, 0, cliprect );
+}
+
+
+READ16_MEMBER( huc6261_device::read )
+{
+ UINT16 data = 0xFFFF;
+
+ switch ( offset & 1 )
+ {
+ /* Status info */
+ case 0x00:
+ {
+ UINT16 vpos = m_screen->vpos();
+ UINT16 hpos = m_screen->hpos();
+
+ data = ( vpos << 5 ) | ( m_register & 0x1F);
+
+ if ( vpos >= 22 && vpos < 262 && hpos < HUC6261_HSYNC_START )
+ {
+ data |= 0x8000;
+ }
+ }
+ break;
+
+ /* Register contents(?) */
+ case 0x01:
+ switch( m_register )
+ {
+ case 0x00:
+ data = m_control;
+ break;
+
+ case 0x01:
+ data = m_address;
+ break;
+
+ case 0x02:
+ case 0x03:
+ data = m_palette_latch;
+ m_address = ( m_address + 1 ) & 0x1FF;
+ m_palette_latch = m_palette[ m_address ];
+ break;
+
+ case 0x08:
+ data = m_priority[4] | ( m_priority[5] << 4 ) | ( m_priority[6] << 8 );
+ break;
+
+ case 0x09:
+ data = m_priority[0] | ( m_priority[1] << 4 ) | ( m_priority[2] << 8 ) | ( m_priority[3] << 12 );;
+ break;
+ }
+ break;
+ }
+
+ return data;
+}
+
+
+WRITE16_MEMBER( huc6261_device::write )
+{
+ switch ( offset & 1 )
+ {
+ /* Register */
+ case 0x00:
+ m_register = data;
+ break;
+
+ case 0x01:
+ logerror("huc6261: writing 0x%04x to register 0x%02x\n", data, m_register );
+ switch( m_register )
+ {
+ /* Control register */
+ // -x-------------- Enable HuC6271: 0 - disabled, 1 - enabled
+ // --x------------- Enable HuC6272 BG3: 0 - disabled, 1 - enabled
+ // ---x------------ Enable HuC6272 BG2: 0 - disabled, 1 - enabled
+ // ----x----------- Enable Huc6272 BG1: 0 - disabled, 1 - enabled
+ // -----x---------- Enable HuC6272 BG0: 0 - disabled, 1 - enabled
+ // ------x--------- Enable HuC6270 SPR: 0 - disabled, 1 - enabled
+ // -------x-------- Enable HuC6270 BG: 0 - disabled, 1 - enabled
+ // --------x------- Number of SPR colors?: 0 - 16, 1 - 256
+ // ---------x------ Number of BG colors?: 0 - 16, 1 - 256
+ // ------------x--- Dot clock: 0 - 5MHz, 1 - 7MHz
+ // -------------x-- Synchronization: 0 - internal, 1 - external
+ // --------------xx Screen height: 00 - 262 lines, 01 - 263 lines, 10 - interlace, 11 - unknown/undefined
+ case 0x00:
+ m_control = data;
+ m_pixels_per_clock = ( data & 0x04 ) ? 3 : 4;
+ break;
+
+ // Palette address
+ case 0x01:
+ m_address = data & 0x1FF;
+ m_palette_latch = m_palette[ m_address ];
+ break;
+
+ // Palette data
+ case 0x02:
+ m_palette_latch = data;
+ m_palette[ m_address ] = m_palette_latch;
+ m_address = ( m_address + 1 ) & 0x1FF;
+ break;
+
+ // Palette offset 0
+ case 0x04:
+ break;
+
+ // Palette offset 1
+ case 0x05:
+ break;
+
+ // Palette offset 2
+ case 0x06:
+ break;
+
+ // Palette offset 3
+ case 0x07:
+ break;
+
+ // Priority 0
+ // -----xxx-------- HuC6271 Rainbow priority
+ // ---------xxx---- HuC6270 SPR priority
+ // -------------xxx HuC6270 BG priority
+ case 0x08:
+ m_priority[4] = ( data >> 0 ) & 0x07;
+ m_priority[5] = ( data >> 4 ) & 0x07;
+ m_priority[6] = ( data >> 8 ) & 0x07;
+ break;
+
+ // Priority 1
+ // -xxx------------ HuC6272 BG3 priority
+ // -----xxx-------- HuC6272 BG2 priority
+ // ---------xxx---- HuC6272 BG1 priority
+ // -------------xxx HuC6272 BG0 priority
+ case 0x09:
+ m_priority[0] = ( data >> 0 ) & 0x07;
+ m_priority[1] = ( data >> 4 ) & 0x07;
+ m_priority[2] = ( data >> 8 ) & 0x07;
+ m_priority[3] = ( data >> 12 ) & 0x07;
+ break;
+
+ // Chroma key Y
+ case 0x0A:
+ break;
+
+ // Chroma key U
+ case 0x0B:
+ break;
+
+ // Chroma key V
+ case 0x0C:
+ break;
+
+ //
+ case 0x0D:
+ break;
+
+ //
+ case 0x0E:
+ break;
+
+ //
+ case 0x0F:
+ break;
+
+ //
+ case 0x10:
+ break;
+
+ //
+ case 0x11:
+ break;
+
+ //
+ case 0x12:
+ break;
+
+ //
+ case 0x13:
+ break;
+
+ //
+ case 0x14:
+ break;
+
+ //
+ case 0x15:
+ break;
+ }
+ break;
+ }
+}
+
+
+void huc6261_device::device_start()
+{
+ /* Make sure we are supplied all our mandatory tags */
+ assert( m_huc6270_a_tag != NULL );
+ assert( m_huc6270_b_tag != NULL );
+
+ m_timer = timer_alloc();
+ m_huc6270_a = machine().device<huc6270_device>(m_huc6270_a_tag);
+ m_huc6270_b = machine().device<huc6270_device>(m_huc6270_b_tag);
+
+ m_bmp = auto_bitmap_rgb32_alloc( machine(), HUC6261_WPF, HUC6261_LPF );
+
+ /* We want to have valid devices */
+ assert( m_huc6270_a != NULL );
+ assert( m_huc6270_b != NULL );
+
+ save_item(NAME(m_last_h));
+ save_item(NAME(m_last_v));
+ save_item(NAME(m_height));
+ save_item(NAME(m_palette));
+ save_item(NAME(m_palette_latch));
+ save_item(NAME(m_address));
+ save_item(NAME(m_register));
+ save_item(NAME(m_control));
+ save_item(NAME(m_priority));
+ save_item(NAME(m_pixels_per_clock));
+ save_item(NAME(m_pixel_data));
+ save_item(NAME(m_pixel_clock));
+}
+
+
+void huc6261_device::device_reset()
+{
+ m_register = 0;
+ m_pixels_per_clock = 4;
+ m_height = 263;
+ m_pixel_clock = 0;
+
+ memset(m_palette, 0, sizeof(m_palette));
+
+ m_last_v = m_screen->vpos();
+ m_last_h = m_screen->hpos();
+ m_timer->adjust( m_screen->time_until_pos( ( m_screen->vpos() + 1 ) % 263, 0 ) );
+}