summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/video/i8244.cpp
blob: 1734d83677dbfe10275e38360eaec61a2fae4d5e (plain) (tree)
1
2
3
4
5
6
7
8
                       
                                                    
                         

                                                                            
                                                    
                                     
 




                                                            
                                          

                                             





                                                                            


                   
                         

                                                              

 
                                                   
                              

                                                   
                                                                                                      
                                                         
   
 
                                                                                                                        
                                                    
                                                
                                                
                           
                                   
   
 
                                                                                                      
                                                         







                                                   
 




                                                                                                                         

 












                                       
                                                                                                                        




















                                                                                        

                                                   







                                                                                                                     






                                                             
                                   

                                                   
                                                               
 



















                                                     
 
                                                



                                                   




                                                   
                          
                                                                             
                                                                                                                        
 
                                                                             
                                                                                                      

                            
                                               
 
                   
                                    
                                                            
                                                          













                                             
                                         
                                        
 



                                            


                                        
                                    

                                     


 
                                                   
                
                                                   
 
                                                 
 








                                                       


 



                                                                            
                                                        
 
                                                        
                                    
         
                                


                                                    
                                    



                                
                             

 
 
                                    
 
                
 

                                              



                                                                


                          


                                                



                                                                                                          
                                                 
                                                       
 




                                                               
 
                              
                 


                                                  

                                                               


                              
                                                                                      

                              
                          
                                                                                      

                              





                                                           

                                                 
 
                                                                                               



                                                                    
                                                                                 


                                                                          
                              
         
 
                    


 
                                                
 
                                              
 


                                                                               




                                                                         

                       
                                                         
                                                                                              
                                      
 
                                                  


                                    
                                                   
                                                      

                              
                                                         


                                    
                       
         
                          
                                                                       



                                                                        

                              
 









                                                











                                                                                            

                              





                                 
                              
 











                                                       


 
                              
 

                                    
 
 





                                   
 
                                                                                                 
                         
 



                                                      


 












                                                       



                                           
                                                    
 
                                                                          
                         
                 








                                                           
                 
 
                                                                         
                                             
                 
                                                   




                 































































































                                                                                                                
                                                                                                                            












                                                                                                    

                                                 



                                                           




                                                                                          
 


                                                                                         
 
                                                            
                                                          
                         




                 
                                                                                            
 
                       
                                                              
         


                                                    
 




                                                                                           
                 
                                                                      
 
                                                                                            
                         
                                                                                                                                                                  
 

                                                                                                
                                                                                                                                                         
                         
                 
         
 
                                     
                                                                    



                                                
 

                                                                              
 


                                                                                                                                                  
 


                                                                                    
                                                                                                                                        


                 
 
 


                                                                                            
                                                                 




                                                                      
 





                                                                                 
 












                                                                                             

                         
                                                     
 


                                                                       
                                 
                                                                                
                                         
                                                                                    
                                                 
                                                                         
 


                                                                                                                  
 


                                                                                                                 
 

                                                                                         





                                                 
























                                                                                                       
 
                 


 



                                                                            
                                                                                                                                                  
 
                                        
                                                                                     
 
                                                      
         

                                                  
                                                                          

         
 
 









                                                             
         




                                                                                             
                                           
                             
 
                                                                      
                                         
                 

                                                                       
                 
 
                             
                                         
 


                                                       















                                                         
         
 
// license:BSD-3-Clause
// copyright-holders:Peter Trauner, Wilbert Pol, hap
// thanks-to:Kevin Horton
/***************************************************************************

Intel 8244 (NTSC)/8245 (PAL) Graphics and sound chip
Exclusively used in Odyssey 2 series.

Features summary:
- 9*8 grid
- major system (predefined 8*7 objects, 12 single + 4 quads)
- minor system (4 user-defined 8*8 sprites)
- collision detection between all layers
- 1-bit sound from rotating shift register

See Odyssey 2 driver file for known problems.

***************************************************************************/

#include "emu.h"
#include "i8244.h"

#include "screen.h"


// device type definition
DEFINE_DEVICE_TYPE(I8244, i8244_device, "i8244", "Intel 8244")
DEFINE_DEVICE_TYPE(I8245, i8245_device, "i8245", "Intel 8245")


//-------------------------------------------------
//  i8244_device - constructor
//-------------------------------------------------

i8244_device::i8244_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: i8244_device(mconfig, I8244, tag, owner, clock)
{ }

i8244_device::i8244_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
	: device_t(mconfig, type, tag, owner, clock)
	, device_sound_interface(mconfig, *this)
	, device_video_interface(mconfig, *this)
	, m_irq_func(*this)
	, m_charset(*this, "cgrom")
{ }

i8245_device::i8245_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: i8244_device(mconfig, I8245, tag, owner, clock)
{ }


//-------------------------------------------------
//  device configuration
//-------------------------------------------------

void i8244_device::device_config_complete()
{
	if (!has_screen())
		return;

	if (!screen().refresh_attoseconds())
		screen().set_raw(clock()*2, m_htotal, m_cropx, m_cropx + m_width, m_vtotal, m_cropy, m_cropy + m_height);
}

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()
{
	// this timing is partially derived externally, 8245 on the PAL console is set to slave mode in vblank (M/S pin)
	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
//-------------------------------------------------

ROM_START( i8244 )
	ROM_REGION( 0x200, "cgrom", 0 )
	ROM_LOAD( "charset_i8244.bin", 0x0000, 0x0200, CRC(b46a3f31) SHA1(415382715455b47b69401b3d60bd8f0036dd7fef) )
ROM_END

const tiny_rom_entry *i8244_device::device_rom_region() const
{
	return ROM_NAME( i8244 );
}


//-------------------------------------------------
//  i8244_palette - default palette
//-------------------------------------------------

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
		{ 0xb6, 0x00, 0x00 }, // i R g b
		{ 0x00, 0xb6, 0x00 }, // i r G b
		{ 0xb6, 0xb6, 0x00 }, // i R G b
		{ 0x00, 0x00, 0xb6 }, // i r g B
		{ 0xb6, 0x00, 0xb6 }, // i R g B
		{ 0x00, 0xb6, 0xb6 }, // i r G B
		{ 0xb6, 0xb6, 0xb6 }, // i R G B
		{ 0x49, 0x49, 0x49 }, // I r g b
		{ 0xff, 0x49, 0x49 }, // I R g b
		{ 0x49, 0xff, 0x49 }, // I r G b
		{ 0xff, 0xff, 0x49 }, // I R G b
		{ 0x49, 0x49, 0xff }, // I r g B
		{ 0xff, 0x49, 0xff }, // I R g B
		{ 0x49, 0xff, 0xff }, // I r G B
		{ 0xff, 0xff, 0xff }  // I R G B
	};

	palette.set_pen_colors(0, i8244_colors);
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void i8244_device::device_start()
{
	// allocate timers
	m_vblank_timer = timer_alloc(FUNC(i8244_device::vblank_start), this);
	m_vblank_timer->adjust(screen().time_until_pos(m_vblank_start, m_hblank_start - 1), 0, screen().frame_period());

	m_hblank_timer = timer_alloc(FUNC(i8244_device::hblank_start), this);
	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());

	// zerofill
	memset(m_vdc.reg, 0, 0x100);
	memset(m_collision_map, 0, sizeof(m_collision_map));
	memset(m_priority_map, 0, sizeof(m_priority_map));

	m_x_beam_pos = 0;
	m_y_beam_pos = 0;
	m_control_status = 0;
	m_collision_status = 0;
	m_sh_written = false;
	m_sh_pending = false;
	m_sh_prescaler = 0;
	m_sh_count = 0;
	m_sh_output = 0;
	m_sh_duty = 0;

	// register our state
	save_pointer(NAME(m_vdc.reg), 0x100);
	save_item(NAME(m_collision_map));
	save_item(NAME(m_priority_map));

	save_item(NAME(m_x_beam_pos));
	save_item(NAME(m_y_beam_pos));
	save_item(NAME(m_control_status));
	save_item(NAME(m_collision_status));
	save_item(NAME(m_sh_written));
	save_item(NAME(m_sh_pending));
	save_item(NAME(m_sh_prescaler));
	save_item(NAME(m_sh_count));
	save_item(NAME(m_sh_output));
	save_item(NAME(m_sh_duty));
}


//-------------------------------------------------
//  timer events
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(i8244_device::hblank_start)
{
	// hblank starts (updates sound shift register)
	sound_update();
}

TIMER_CALLBACK_MEMBER(i8244_device::vblank_start)
{
	// vblank starts
	m_control_status |= 0x08;
	m_irq_func(ASSERT_LINE);
}


/***************************************************************************
    I/O
***************************************************************************/

offs_t i8244_device::fix_register_mirrors(offs_t offset)
{
	// quad x/y registers are mirrored for each quad
	if ((offset & 0xc2) == 0x40)
	{
		offset &= ~0x0c;
	}

	// registers $A0-$AF are mirrored at $B0-$BF
	if ((offset & 0xe0) == 0xa0)
	{
		offset &= ~0x10;
	}

	return offset & 0xff;
}


u8 i8244_device::read(offs_t offset)
{
	u8 data;

	offset = fix_register_mirrors(offset);

	// update screen before accessing video status registers
	if (offset == 0xa1 || offset == 0xa2)
		screen().update_now();

	switch (offset)
	{
		case 0xa1:
		{
			data = m_control_status;

			// 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;

			// position strobe status
			data |= m_vdc.s.control & 0x02;

			if (!machine().side_effects_disabled())
			{
				m_irq_func(CLEAR_LINE);
				m_control_status &= ~0xcc;
			}

			break;
		}

		case 0xa2:
			data = m_collision_status;
			if (!machine().side_effects_disabled())
				m_collision_status = 0;
			break;

		case 0xa4:
			data = (m_vdc.s.control & 0x02) ? get_y_beam() : m_y_beam_pos;
			break;

		case 0xa5:
			data = (m_vdc.s.control & 0x02) ? get_x_beam() : m_x_beam_pos;
			break;

		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];

			// 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)
				data = 0;

			// grid registers are not accessible when grid is enabled
			else if (offset >= 0xc0 && m_vdc.s.control & 0x08)
				data = 0;

			break;
	}

	return data;
}


void i8244_device::write(offs_t offset, u8 data)
{
	offset = fix_register_mirrors(offset);

	// 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;

	// grid registers are not accessible when grid is enabled
	// grid registers >= 0xf0 are unmapped
	if ((offset >= 0xc0 && m_vdc.s.control & 0x08) || offset >= 0xf0)
		return;

	// update screen before accessing video registers
	if ((offset <= 0xa0 || offset == 0xa2 || offset == 0xa3) && data != m_vdc.reg[offset])
		screen().update_now();

	// color registers d4-d7 are not connected
	if ((offset & 0x83) == 0x03)
		data &= 0x0f;

	// major systems Y CAM d0 is not connected!
	if (offset >= 0x10 && (offset & 0x83) == 0x00)
		data &= ~0x01;

	// horizontal grid high byte only d0 is connected
	if ((offset & 0xf0) == 0xd0)
		data &= 0x01;

	switch (offset)
	{
		case 0xa0:
			if ((m_vdc.s.control & 0x02) && !(data & 0x02))
			{
				// toggling strobe bit, tuck away values
				m_x_beam_pos = get_x_beam();
				m_y_beam_pos = get_y_beam();
			}
			break;

		case 0xa7: case 0xa8: case 0xa9:
			m_sh_written = true;
			break;

		case 0xaa:
			// update the sound
			m_stream->update();
			data &= ~0x40;
			break;

		case 0xa1: case 0xa4: case 0xa5:
			// read-only registers
			return;

		case 0x03: case 0x07: case 0x0b: case 0x0f:
		case 0xa6: case 0xab: case 0xac: case 0xad: case 0xae: case 0xaf:
		case 0xc9: case 0xca: case 0xcb: case 0xcc: case 0xcd: case 0xce: case 0xcf:
		case 0xd9: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf:
		case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef:
			// unused registers
			return;

		default:
			break;
	}

	m_vdc.reg[offset] = data;
}


int i8244_device::get_y_beam()
{
	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::get_x_beam()
{
	return screen().hpos() >> 1;
}


int i8244_device::vblank()
{
	int h = screen().hpos();
	int v = screen().vpos();
	int start = m_vblank_start;
	int end = m_vblank_end;

	if ((v == start && h >= (m_hblank_start - 1)) || (v == end && h <= (m_hblank_start - 1)))
		return 1;

	if (end < start)
		return (v > start || v < end) ? 1 : 0;
	else
		return (v > start && v < end) ? 1 : 0;
}


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;
}


void i8244_device::write_cx(int x, bool cx)
{
	if (cx)
	{
		u8 colx = m_collision_map[x] & 0x3f;

		// check if we collide with an already drawn source object
		if (colx)
		{
			// external overlap interrupt
			if (m_vdc.s.control & 0x10)
			{
				m_irq_func(ASSERT_LINE);
				m_control_status |= 0x40;
			}

			if (colx & m_vdc.s.collision)
				m_collision_status |= 0x40;
		}

		// check if an already drawn object would collide with us
		if (m_vdc.s.collision & 0x40)
		{
			m_collision_status |= colx;
		}
	}
}


/***************************************************************************
    RENDER
***************************************************************************/

void i8244_device::draw_grid(int scanline, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	u16 color = bitswap<4>(m_vdc.s.color,6,0,1,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 y = 0; y < 9; y++)
	{
		if (y_grid_offset + y * height <= scanline && scanline < y_grid_offset + y * height + 3)
		{
			for (int i = 0; i < 9; i++)
			{
				if (BIT(m_vdc.s.hgrid[1][i] << 8 | m_vdc.s.hgrid[0][i], y))
				{
					for (int k = 0; k < width + 2; k++)
					{
						int x = (x_grid_offset + i * width + k) * 2;

						for (int px = x; px < x + 2; px++)
						{
							if (cliprect.contains(px, scanline))
							{
								m_collision_map[px] |= 0x20;
								bitmap.pix(scanline, px) = color;
							}
						}
					}
				}
			}
		}
	}

	// draw dots part of the grid
	if (m_vdc.s.control & 0x40)
	{
		for (int y = 0; y < 9; y++)
		{
			if (y_grid_offset + y * height <= scanline && scanline < y_grid_offset + y * height + 3)
			{
				for (int i = 0; i < 10; i++)
				{
					for (int k = 0; k < 2; k++)
					{
						int x = (x_grid_offset + i * width + k) * 2;

						for (int px = x; px < x + 2; px++)
						{
							if (cliprect.contains(px, scanline))
							{
								m_collision_map[px] |= 0x20;
								bitmap.pix(scanline, px) = color;
							}
						}
					}
				}
			}
		}
	}

	// draw vertical part of the grid
	for (int j = 1, y = 0; y < 8; y++, j <<= 1)
	{
		if (y_grid_offset + y * height <= scanline && scanline < y_grid_offset + (y + 1) * height)
		{
			for (int i = 0; i < 10; i++)
			{
				if (m_vdc.s.vgrid[i] & j)
				{
					for (int k = 0; k < w; k++)
					{
						int x = (x_grid_offset + i * width + k) * 2;

						for (int px = x; px < x + 2; px++)
						{
							if (cliprect.contains(px, scanline))
							{
								m_collision_map[px] |= 0x10;
								bitmap.pix(scanline, px) = color;
							}
						}
					}
				}
			}
		}
	}
}


void i8244_device::major_pixel(u8 index, int x, int y, u8 pixel, u16 color, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	for (int px = x; px < x + 2; px++)
	{
		if (cliprect.contains(px, y))
		{
			u8 colx = m_collision_map[px];

			// check collision with self
			if (index < m_priority_map[px])
			{
				m_control_status |= 0x80;

				// TODO: much more complex on actual console (weird glitches happen)
				if (colx & 0x80)
					continue;
			}
			else
				m_priority_map[px] = index;

			if (pixel)
			{
				// check if we collide with an already drawn source object
				if (m_vdc.s.collision & colx)
					m_collision_status |= 0x80;

				// check if an already drawn object would collide with us
				if (m_vdc.s.collision & 0x80)
					m_collision_status |= colx;

				m_collision_map[px] |= 0x80;
				bitmap.pix(y, px) = color;
			}
		}
	}
}


void i8244_device::draw_major(int scanline, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// quad objects
	for (int i = std::size(m_vdc.s.quad) - 1; i >= 0; i--)
	{
		int y = m_vdc.s.quad[i].single[0].y;
		if (is_ntsc() && y < 0xe)
			continue;

		// character height is always determined by the height of the 4th character
		int height = 7 - (((y >> 1) + m_vdc.s.quad[i].single[3].ptr) & 7);
		if (height == 0) height = 8;

		if (y <= scanline && scanline < y + height * 2)
		{
			int x = (m_vdc.s.quad[i].single[0].x + 5) * 2;

			for (int j = 0; j < std::size(m_vdc.s.quad[0].single); j++, x += 16)
			{
				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);

				u16 color = 8 + ((m_vdc.s.quad[i].single[j].color >> 1) & 0x07);
				for (int cx = 0; cx < 8; cx++, x += 2)
					major_pixel(4 * j + 16 * i + 0x40, x, scanline, BIT(m_charset[offset & 0x1ff], cx ^ 7), color, bitmap, cliprect);
			}
		}
	}

	// regular foreground objects
	for (int i = std::size(m_vdc.s.foreground) - 1; i >= 0; i--)
	{
		int y = m_vdc.s.foreground[i].y;
		if (is_ntsc() && y < 0xe)
			continue;

		int height = 7 - (((y >> 1) + m_vdc.s.foreground[i].ptr) & 7);
		if (height == 0) height = 8;

		if (y <= scanline && scanline < y + height * 2)
		{
			int offset = (m_vdc.s.foreground[i].ptr | ((m_vdc.s.foreground[i].color & 0x01) << 8)) + (y >> 1) + ((scanline - y) >> 1);

			int x = (m_vdc.s.foreground[i].x + 5) * 2;
			u16 color = 8 + ((m_vdc.s.foreground[i].color >> 1) & 0x07);
			for (int cx = 0; cx < 8; cx++, x += 2)
				major_pixel(4 * i + 0x10, x, scanline, BIT(m_charset[offset & 0x1ff], cx ^ 7), color, bitmap, cliprect);
		}
	}
}


void i8244_device::draw_minor(int scanline, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// minor system (sprites)
	for (int i = std::size(m_vdc.s.sprites) - 1; i >= 0; i--)
	{
		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 (y <= scanline && scanline < y + height * zoom_px)
		{
			u16 color = 8 + ((m_vdc.s.sprites[i].color >> 3) & 0x07);
			u8 chr = m_vdc.s.shape[i][((scanline - y) / zoom_px)];
			int x = (m_vdc.s.sprites[i].x + 5) * 2;
			int x_shift = 0;

			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) / zoom_px) & 0x01) ^ 0x01;
					break;
				case 3: // Xg and S attributes set
					x_shift = ((scanline - y) / zoom_px) & 0x01;
					break;
				default:
					break;
			}

			x += x_shift * (zoom_px / 2);

			for (u8 m = 0x01; m > 0; m <<= 1, x += zoom_px)
			{
				if (chr & m)
				{
					for (int px = x; px < x + zoom_px; px++)
					{
						if (cliprect.contains(px, scanline))
						{
							u8 mask = 1 << i;

							// check if we collide with an already drawn source object
							if (m_vdc.s.collision & m_collision_map[px])
								m_collision_status |= mask;

							// check if an already drawn object would collide with us
							if (m_vdc.s.collision & mask)
								m_collision_status |= m_collision_map[px];

							m_collision_map[px] |= mask;
							bitmap.pix(scanline, px) = color;
						}
					}
				}
			}
		}
	}
}


u32 i8244_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	// draw background color
	bitmap.fill(bitswap<3>(m_vdc.s.color,3,4,5), cliprect);

	for (int scanline = cliprect.min_y; scanline <= cliprect.max_y; scanline++)
	{
		// clear collision maps
		memset(m_collision_map, 0, sizeof(m_collision_map));
		memset(m_priority_map, 0, sizeof(m_priority_map));

		// display grid if enabled
		if (m_vdc.s.control & 0x08 && scanline >= 24 && scanline <= 218)
			draw_grid(scanline, bitmap, cliprect);

		// display objects if enabled
		if (m_vdc.s.control & 0x20 && scanline <= 242)
		{
			draw_major(scanline, bitmap, cliprect);
			draw_minor(scanline, bitmap, cliprect);
		}
	}

	return 0;
}


/***************************************************************************
    SOUND
***************************************************************************/

void i8244_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
	u8 volume = m_vdc.s.sound & 0xf;
	stream_buffer::sample_t sample_on = (m_sh_output & m_vdc.s.sound >> 7) * 0.5;

	for (int i = 0; i < outputs[0].samples(); i++)
	{
		// clock duty cycle
		m_sh_duty = (m_sh_duty + 1) & 0xf;
		outputs[0].put(i, (m_sh_duty < volume) ? sample_on : 0.0);
	}
}


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)
	{
		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;
		int feedback = m_sh_output;
		signal >>= 1;

		// noise tap is on bits 0 and 5 and fed back to bit 15
		if (m_vdc.s.sound & 0x10)
		{
			feedback ^= signal >> 4 & 1; // pre-shift bit 5
			signal = (signal & ~0x8000) | (feedback << 15);
		}

		// loop sound
		signal |= feedback << 23;

		m_vdc.s.shift3 = signal & 0xff;
		m_vdc.s.shift2 = (signal >> 8) & 0xff;
		m_vdc.s.shift1 = (signal >> 16) & 0xff;

		// sound interrupt
		if (++m_sh_count == 24)
		{
			m_sh_count = 0;
			if (m_vdc.s.control & 0x04)
			{
				m_control_status |= 0x04;
				m_irq_func(ASSERT_LINE);
			}
		}
	}
	else if (m_sh_written)
	{
		m_sh_count = 0;
		m_sh_written = false;
	}
}