1 // license:BSD-3-Clause
2 // copyright-holders:Wilbert Pol
3 /**********************************************************************
4 
5     Hudson/NEC HuC6261 Video Colour Encoder
6 
7     The HuC6261 generates the tv control signals. A full line lasts
8     1365 "master" cycles (typically at 21.47727Mhz).
9 
10     HSync is low for 237 and high for 1128 master cycles.
11     VSync is low for 4095 master cycles (3 lines).
12     VSync changes 30 master cycles after HSync would go low.
13 
14 **********************************************************************/
15 
16 #include "emu.h"
17 #include "huc6261.h"
18 
19 #include "screen.h"
20 
21 //#define VERBOSE 1
22 #include "logmacro.h"
23 
24 
25 #define HUC6261_HSYNC_LENGTH    237
26 #define HUC6261_HSYNC_START     ( huc6261_device::WPF - HUC6261_HSYNC_LENGTH )
27 
28 constexpr unsigned huc6261_device::WPF;
29 constexpr unsigned huc6261_device::LPF;
30 
31 DEFINE_DEVICE_TYPE(HUC6261, huc6261_device, "huc6261", "Hudson HuC6261 VCE")
32 
33 
huc6261_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)34 huc6261_device::huc6261_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
35 	:   device_t(mconfig, HUC6261, tag, owner, clock),
36 		device_video_interface(mconfig, *this),
37 		m_huc6270_a(*this, finder_base::DUMMY_TAG),
38 		m_huc6270_b(*this, finder_base::DUMMY_TAG),
39 		m_huc6272(*this, finder_base::DUMMY_TAG),
40 		m_last_h(0), m_last_v(0), m_height(0), m_address(0), m_palette_latch(0), m_register(0), m_control(0), m_pixels_per_clock(0), m_pixel_data_a(0), m_pixel_data_b(0), m_pixel_clock(0), m_timer(nullptr), m_bmp(nullptr)
41 {
42 	// Set up UV lookup table
43 	for ( int ur = 0; ur < 256; ur++ )
44 	{
45 		for ( int vr = 0; vr < 256; vr++ )
46 		{
47 			int32_t r,g,b;
48 			int32_t u = ur - 128;
49 			int32_t v = vr - 128;
50 
51 			r =              + 1.13983 * v;
52 			g = -0.35465 * u - 0.58060 * v;
53 			b =  2.03211 * u;
54 
55 			m_uv_lookup[ ( ur << 8 ) | vr ][0] = r;
56 			m_uv_lookup[ ( ur << 8 ) | vr ][1] = g;
57 			m_uv_lookup[ ( ur << 8 ) | vr ][2] = b;
58 		}
59 	}
60 }
61 
62 
yuv2rgb(uint32_t yuv)63 inline uint32_t huc6261_device::yuv2rgb(uint32_t yuv)
64 {
65 	int32_t r, g, b;
66 	uint8_t y = yuv >> 8;
67 	uint16_t uv = ((yuv & 0xf0) << 8) | ((yuv & 0xf) << 4);
68 
69 	r = y + m_uv_lookup[uv][0];
70 	g = y + m_uv_lookup[uv][1];
71 	b = y + m_uv_lookup[uv][2];
72 
73 	if ( r < 0 ) r = 0;
74 	if ( g < 0 ) g = 0;
75 	if ( b < 0 ) b = 0;
76 	if ( r > 255 ) r = 255;
77 	if ( g > 255 ) g = 255;
78 	if ( b > 255 ) b = 255;
79 
80 	return ( r << 16 ) | ( g << 8 ) | b;
81 }
82 
apply_pal_offs(uint16_t * pix_data)83 void huc6261_device::apply_pal_offs(uint16_t *pix_data)
84 {
85 	// sprite
86 	if(*pix_data & 0x100)
87 	{
88 		*pix_data &= 0xff;
89 		*pix_data += ((m_palette_offset[0] & 0x7f00) >> 8) << 1;
90 	}
91 	else // background
92 		*pix_data += (m_palette_offset[0] & 0x7f) << 1;
93 
94 	*pix_data &= 0x1ff;
95 }
96 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)97 void huc6261_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
98 {
99 	int vpos = screen().vpos();
100 	int hpos = screen().hpos();
101 	int h = m_last_h;
102 	int v = m_last_v;
103 	uint32_t *bitmap_line = &m_bmp->pix(v);
104 
105 	while ( h != hpos || v != vpos )
106 	{
107 		if ( m_pixel_clock == 0 )
108 		{
109 			g_profiler.start( PROFILER_VIDEO );
110 			/* Get next pixel information */
111 			m_pixel_data_a = m_huc6270_a->next_pixel();
112 			m_pixel_data_b = m_huc6270_b->next_pixel();
113 			apply_pal_offs(&m_pixel_data_a);
114 			apply_pal_offs(&m_pixel_data_b);
115 
116 			g_profiler.stop();
117 		}
118 
119 		bitmap_line[ h ] = yuv2rgb( m_palette[ m_pixel_data_a ] );
120 		// TODO: is mixing correct?
121 		if((m_pixel_data_b & 0xff) != 0)
122 			bitmap_line[ h ] = yuv2rgb( m_palette[ m_pixel_data_b ] );
123 
124 		m_pixel_clock = ( m_pixel_clock + 1 ) % m_pixels_per_clock;
125 		h = ( h + 1 ) % WPF;
126 
127 		switch( h )
128 		{
129 		case HUC6261_HSYNC_START:       /* Start of HSync */
130 			m_huc6270_a->hsync_changed( 0 );
131 			m_huc6270_b->hsync_changed( 0 );
132 //          if ( v == 0 )
133 //          {
134 //              /* Check if the screen should be resized */
135 //              m_height = LPF - ( m_blur ? 1 : 0 );
136 //              if ( m_height != video_screen_get_height( m_screen ) )
137 //              {
138 //                  rectangle visible_area;
139 //
140 //                  /* TODO: Set proper visible area parameters */
141 //                  visible_area.min_x = 64;
142 //                  visible_area.min_y = 18;
143 //                  visible_area.max_x = 64 + 1024 + 64 - 1;
144 //                  visible_area.max_y = 18 + 242 - 1;
145 //
146 //                  video_screen_configure( m_screen, WPF, m_height, &visible_area, HZ_TO_ATTOSECONDS( device->clock / ( WPF * m_height ) ) );
147 //              }
148 //          }
149 			break;
150 
151 		case 0:     /* End of HSync */
152 			m_huc6270_a->hsync_changed( 1 );
153 			m_huc6270_b->hsync_changed( 1 );
154 			m_pixel_clock = 0;
155 			v = ( v + 1 ) % m_height;
156 			bitmap_line = &m_bmp->pix(v);
157 			break;
158 
159 		case HUC6261_HSYNC_START + 30:      /* End/Start of VSync */
160 			if ( v>= m_height - 4 )
161 			{
162 				int vsync = ( v >= m_height - 4 && v < m_height - 1 ) ? 0 : 1;
163 
164 				m_huc6270_a->vsync_changed( vsync );
165 				m_huc6270_b->vsync_changed( vsync );
166 			}
167 			break;
168 		}
169 	}
170 
171 	m_last_h = h;
172 	m_last_v = v;
173 
174 	/* Reschedule timer */
175 	if ( m_last_h < HUC6261_HSYNC_START )
176 	{
177 		/* Next event is start of HSync signal */
178 		v = m_last_v;
179 		h = HUC6261_HSYNC_START;
180 	}
181 	else if ( ( m_last_v == m_height - 4 || m_last_v == m_height - 1 ) && m_last_h < HUC6261_HSYNC_START + 30 )
182 	{
183 		/* Next event is start/end of VSync signal */
184 		v = m_last_v;
185 		h = HUC6261_HSYNC_START + 30;
186 	}
187 	else
188 	{
189 		/* Next event is end of HSync signal */
190 		v = ( m_last_v + 1 ) % m_height;
191 		h = 0;
192 	}
193 
194 	/* Ask our slave device for time until next possible event */
195 	{
196 		uint16_t next_event_clocks = WPF; //m_get_time_til_next_event( 0, 0xffff );
197 		int event_hpos, event_vpos;
198 
199 		/* Adjust for pixel clocks per pixel */
200 		next_event_clocks *= m_pixels_per_clock;
201 
202 		/* Adjust for clocks left to go for current pixel */
203 		next_event_clocks += ( m_pixels_per_clock - ( m_pixel_clock + 1 ) );
204 
205 		event_hpos = hpos + next_event_clocks;
206 		event_vpos = vpos;
207 		while ( event_hpos > WPF )
208 		{
209 			event_vpos += 1;
210 			event_hpos -= WPF;
211 		}
212 
213 		if ( event_vpos < v || ( event_vpos == v && event_hpos <= h ) )
214 		{
215 			if ( event_vpos > vpos || ( event_vpos == vpos && event_hpos > hpos ) )
216 			{
217 				v = event_vpos;
218 				h = event_hpos;
219 			}
220 		}
221 	}
222 
223 	m_timer->adjust( screen().time_until_pos( v, h ) );
224 }
225 
226 
video_update(bitmap_rgb32 & bitmap,const rectangle & cliprect)227 void huc6261_device::video_update( bitmap_rgb32 &bitmap, const rectangle &cliprect )
228 {
229 	copybitmap( bitmap, *m_bmp, 0, 0, 0, 0, cliprect );
230 }
231 
232 
read(offs_t offset)233 uint16_t huc6261_device::read(offs_t offset)
234 {
235 	uint16_t data = 0xFFFF;
236 
237 	switch ( offset & 1 )
238 	{
239 		/* Status info */
240 		case 0x00:
241 			{
242 				uint16_t vpos = screen().vpos();
243 				uint16_t hpos = screen().hpos();
244 
245 				data = ( vpos << 5 ) | ( m_register & 0x1F);
246 
247 				if ( vpos >= 22 && vpos < 262 && hpos < HUC6261_HSYNC_START )
248 				{
249 					data |= 0x8000;
250 				}
251 			}
252 			break;
253 
254 		/* Register contents(?) */
255 		case 0x01:
256 			switch( m_register )
257 			{
258 				case 0x00:
259 					data = m_control;
260 					break;
261 
262 				case 0x01:
263 					data = m_address;
264 					break;
265 
266 				case 0x02:
267 				case 0x03:
268 					data = m_palette_latch;
269 					m_address = ( m_address + 1 ) & 0x1FF;
270 					m_palette_latch = m_palette[ m_address ];
271 					break;
272 
273 				case 0x08:
274 					data = m_priority[4] | ( m_priority[5] << 4 ) | ( m_priority[6] << 8 );
275 					break;
276 
277 				case 0x09:
278 					data = m_priority[0] | ( m_priority[1] << 4 ) | ( m_priority[2] << 8 ) | ( m_priority[3] << 12 );
279 					break;
280 			}
281 			break;
282 	}
283 
284 	return data;
285 }
286 
287 
write(offs_t offset,uint16_t data)288 void huc6261_device::write(offs_t offset, uint16_t data)
289 {
290 	switch ( offset & 1 )
291 	{
292 		/* Register */
293 		case 0x00:
294 			m_register = data;
295 			break;
296 
297 		case 0x01:
298 			logerror("huc6261: writing 0x%04x to register 0x%02x\n", data, m_register );
299 			switch( m_register )
300 			{
301 				/* Control register */
302 				// -x-- ---- ---- ---- Enable HuC6271: 0 - disabled, 1 - enabled
303 				// --x- ---- ---- ---- Enable HuC6272 BG3: 0 - disabled, 1 - enabled
304 				// ---x ---- ---- ---- Enable HuC6272 BG2: 0 - disabled, 1 - enabled
305 				// ---- x--- ---- ---- Enable Huc6272 BG1: 0 - disabled, 1 - enabled
306 				// ---- -x-- ---- ---- Enable HuC6272 BG0: 0 - disabled, 1 - enabled
307 				// ---- --x- ---- ---- Enable HuC6270 SPR: 0 - disabled, 1 - enabled
308 				// ---- ---x ---- ---- Enable HuC6270 BG: 0 - disabled, 1 - enabled
309 				// ---- ---- x--- ---- Number of SPR colors?: 0 - 16, 1 - 256
310 				// ---- ---- -x-- ---- Number of BG colors?: 0 - 16, 1 - 256
311 				// ---- ---- ---- x--- Dot clock: 0 - 5MHz, 1 - 7MHz
312 				// ---- ---- ---- -x-- Synchronization: 0 - internal, 1 - external
313 				// ---- ---- ---- --xx Screen height: 00 - 262 lines, 01 - 263 lines, 10 - interlace, 11 - unknown/undefined
314 				case 0x00:
315 					m_control = data;
316 					m_pixels_per_clock = ( data & 0x08 ) ? 3 : 4;
317 					break;
318 
319 				// Palette address
320 				case 0x01:
321 					m_address = data & 0x1FF;
322 					m_palette_latch = m_palette[ m_address ];
323 					break;
324 
325 				// Palette data
326 				case 0x02:
327 					m_palette_latch = data;
328 					m_palette[ m_address ] = m_palette_latch;
329 					m_address = ( m_address + 1 ) & 0x1FF;
330 					break;
331 
332 				// Palette offset 0-3
333 				case 0x04:
334 				case 0x05:
335 				case 0x06:
336 				case 0x07:
337 					m_palette_offset[m_register & 3] = data;
338 					break;
339 
340 				// Priority 0
341 				// -----xxx-------- HuC6271 Rainbow priority
342 				// ---------xxx---- HuC6270 SPR priority
343 				// -------------xxx HuC6270 BG priority
344 				case 0x08:
345 					m_priority[4] = ( data >> 0 ) & 0x07;
346 					m_priority[5] = ( data >> 4 ) & 0x07;
347 					m_priority[6] = ( data >> 8 ) & 0x07;
348 					break;
349 
350 				// Priority 1
351 				// -xxx------------ HuC6272 BG3 priority
352 				// -----xxx-------- HuC6272 BG2 priority
353 				// ---------xxx---- HuC6272 BG1 priority
354 				// -------------xxx HuC6272 BG0 priority
355 				case 0x09:
356 					m_priority[0] = ( data >>  0 ) & 0x07;
357 					m_priority[1] = ( data >>  4 ) & 0x07;
358 					m_priority[2] = ( data >>  8 ) & 0x07;
359 					m_priority[3] = ( data >> 12 ) & 0x07;
360 					break;
361 
362 				// Chroma key Y
363 				case 0x0A:
364 					break;
365 
366 				// Chroma key U
367 				case 0x0B:
368 					break;
369 
370 				// Chroma key V
371 				case 0x0C:
372 					break;
373 
374 				//
375 				case 0x0D:
376 					break;
377 
378 				//
379 				case 0x0E:
380 					break;
381 
382 				//
383 				case 0x0F:
384 					break;
385 
386 				//
387 				case 0x10:
388 					break;
389 
390 				//
391 				case 0x11:
392 					break;
393 
394 				//
395 				case 0x12:
396 					break;
397 
398 				//
399 				case 0x13:
400 					break;
401 
402 				//
403 				case 0x14:
404 					break;
405 
406 				//
407 				case 0x15:
408 					break;
409 			}
410 			break;
411 	}
412 }
413 
414 
device_start()415 void huc6261_device::device_start()
416 {
417 	m_timer = timer_alloc();
418 
419 	m_bmp = std::make_unique<bitmap_rgb32>(WPF, LPF);
420 
421 	save_item(NAME(m_last_h));
422 	save_item(NAME(m_last_v));
423 	save_item(NAME(m_height));
424 	save_item(NAME(m_palette));
425 	save_item(NAME(m_palette_latch));
426 	save_item(NAME(m_address));
427 	save_item(NAME(m_register));
428 	save_item(NAME(m_control));
429 	save_item(NAME(m_priority));
430 	save_item(NAME(m_pixels_per_clock));
431 	save_item(NAME(m_pixel_data_a));
432 	save_item(NAME(m_pixel_data_b));
433 	save_item(NAME(m_pixel_clock));
434 }
435 
436 
device_reset()437 void huc6261_device::device_reset()
438 {
439 	m_register = 0;
440 	m_pixels_per_clock = 4;
441 	m_height = 263;
442 	m_pixel_clock = 0;
443 
444 	memset(m_palette, 0, sizeof(m_palette));
445 
446 	m_last_v = screen().vpos();
447 	m_last_h = screen().hpos();
448 	m_timer->adjust( screen().time_until_pos( ( screen().vpos() + 1 ) % 263, 0 ) );
449 }
450