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