1 // license:BSD-3-Clause
2 // copyright-holders:Wilbert Pol
3 /**********************************************************************
4 
5     Hudson/NEC HuC6202 Video Priority Controller
6 
7 **********************************************************************/
8 
9 #include "emu.h"
10 #include "huc6202.h"
11 
12 #include "huc6270.h"
13 
14 
15 DEFINE_DEVICE_TYPE(HUC6202, huc6202_device, "huc6202", "Hudson HuC6202 VPC")
16 
17 
huc6202_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)18 huc6202_device::huc6202_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
19 	: device_t(mconfig, HUC6202, tag, owner, clock)
20 	, m_next_pixel_0_cb(*this)
21 	, m_time_til_next_event_0_cb(*this)
22 	, m_vsync_changed_0_cb(*this)
23 	, m_hsync_changed_0_cb(*this)
24 	, m_read_0_cb(*this)
25 	, m_write_0_cb(*this)
26 	, m_next_pixel_1_cb(*this)
27 	, m_time_til_next_event_1_cb(*this)
28 	, m_vsync_changed_1_cb(*this)
29 	, m_hsync_changed_1_cb(*this)
30 	, m_read_1_cb(*this)
31 	, m_write_1_cb(*this)
32 	, m_window1(0), m_window2(0), m_io_device(0), m_map_index(0), m_map_dirty(0)
33 {
34 }
35 
36 
next_pixel()37 u16 huc6202_device::next_pixel()
38 {
39 	uint16_t data = huc6270_device::HUC6270_BACKGROUND;
40 
41 	uint16_t data_0 = m_next_pixel_0_cb();
42 	uint16_t data_1 = m_next_pixel_1_cb();
43 
44 	if ( data_0 == huc6270_device::HUC6270_SPRITE && data_1 == huc6270_device::HUC6270_SPRITE )
45 	{
46 		m_map_index = 0;
47 		if ( m_map_dirty )
48 		{
49 			int i;
50 
51 			for ( i = 0; i < 512; i++ )
52 			{
53 				m_prio_map[ i ] = 0;
54 				if ( m_window1 < 0x40 || i > m_window1 )
55 				{
56 					m_prio_map [ i ] |= 1;
57 				}
58 				if ( m_window2 < 0x40 || i > m_window2 )
59 				{
60 					m_prio_map[ i ] |= 2;
61 				}
62 			}
63 			m_map_dirty = 0;
64 		}
65 	}
66 	else
67 	{
68 		uint8_t   prio_index = m_prio_map[ m_map_index ];
69 
70 		if ( m_prio[ prio_index ].dev0_enabled && data_0 != huc6270_device::HUC6270_SPRITE )
71 		{
72 			if ( m_prio[ prio_index ].dev1_enabled && data_1 != huc6270_device::HUC6270_SPRITE )
73 			{
74 				switch ( m_prio[ prio_index ].prio_type )
75 				{
76 				case 0:     /* Back - BG1 SP1 BG0 SP0 - Front */
77 					data = ( data_0 & 0x0F ) ? data_0 : data_1;
78 					break;
79 
80 				case 1:     /* Back - BG1 BG0 SP1 SP0 - Front */
81 					if ( data_0 > huc6270_device::HUC6270_SPRITE )
82 					{
83 						/* Device 0 sprite */
84 						data = data_0;
85 					}
86 					else if ( data_1 > huc6270_device::HUC6270_SPRITE )
87 					{
88 						/* Device 1 sprite */
89 						data = data_1;
90 					}
91 					else
92 					{
93 						/* Device 0 and 1 backgrounds */
94 						data = ( data_0 & 0x0F ) ? data_0 : data_1;
95 					}
96 					break;
97 
98 				case 2:     /* Back - BG0 + SP1 => BG0 - Front
99 				                      BG0 + BG1 => BG0
100 				                      BG1 + SP0 => BG1
101 				                      SP0 + SP1 => SP0
102 				            */
103 					if ( data_1 > huc6270_device::HUC6270_SPRITE )
104 					{
105 						if ( data_0 > huc6270_device::HUC6270_SPRITE )
106 						{
107 							/* Device 1 sprite, device 0 sprite */
108 							data = data_0;
109 						}
110 						else
111 						{
112 							/* Device 1 sprite, device 0 background */
113 							data = ( data_0 & 0x0F ) ? data_0 : data_1;
114 						}
115 					}
116 					else
117 					{
118 						if ( data_0 > huc6270_device::HUC6270_SPRITE )
119 						{
120 							/* Device 1 background, device 0 sprite */
121 							data = data_1;
122 						}
123 						else
124 						{
125 							/* Device 1 background, device 0 background */
126 							data = ( data_0 & 0x0F ) ? data_0 : data_1;
127 						}
128 					}
129 					break;
130 
131 				case 3:     /* ?? */
132 					break;
133 				}
134 			}
135 			else
136 			{
137 				/* Only device 0 is enabled */
138 				data = data_0;
139 			}
140 		}
141 		else
142 		{
143 			/* Only device 1 is enabled */
144 			if ( m_prio[ prio_index ].dev1_enabled && data_1 != huc6270_device::HUC6270_SPRITE )
145 			{
146 				data = data_1;
147 			}
148 		}
149 		m_map_index += 1;
150 	}
151 	return data;
152 }
153 
154 
time_until_next_event()155 u16 huc6202_device::time_until_next_event()
156 {
157 	uint16_t next_event_clocks_0 = m_time_til_next_event_0_cb();
158 	uint16_t next_event_clocks_1 = m_time_til_next_event_1_cb();
159 
160 	return std::min( next_event_clocks_0, next_event_clocks_1 );
161 }
162 
163 
WRITE_LINE_MEMBER(huc6202_device::vsync_changed)164 WRITE_LINE_MEMBER( huc6202_device::vsync_changed )
165 {
166 	m_vsync_changed_0_cb( state );
167 	m_vsync_changed_1_cb( state );
168 }
169 
170 
WRITE_LINE_MEMBER(huc6202_device::hsync_changed)171 WRITE_LINE_MEMBER( huc6202_device::hsync_changed )
172 {
173 	m_hsync_changed_0_cb( state );
174 	m_hsync_changed_1_cb( state );
175 }
176 
177 
read(offs_t offset)178 u8 huc6202_device::read(offs_t offset)
179 {
180 	uint8_t data = 0xFF;
181 
182 	switch ( offset & 7 )
183 	{
184 		case 0x00:  /* Priority register #0 */
185 			data = ( m_prio[0].prio_type << 2 ) |
186 				( m_prio[0].dev0_enabled ? 0x01 : 0 ) |
187 				( m_prio[0].dev1_enabled ? 0x02 : 0 ) |
188 				( m_prio[1].prio_type << 6 ) |
189 				( m_prio[1].dev0_enabled ? 0x10 : 0 ) |
190 				( m_prio[1].dev1_enabled ? 0x20 : 0 );
191 			break;
192 
193 		case 0x01:  /* Priority register #1 */
194 			data = ( m_prio[2].prio_type << 2 ) |
195 				( m_prio[2].dev0_enabled ? 0x01 : 0 ) |
196 				( m_prio[2].dev1_enabled ? 0x02 : 0 ) |
197 				( m_prio[3].prio_type << 6 ) |
198 				( m_prio[3].dev0_enabled ? 0x10 : 0 ) |
199 				( m_prio[3].dev1_enabled ? 0x20 : 0 );
200 			break;
201 
202 		case 0x02:  /* Window 1 LSB */
203 			data = m_window1 & 0xFF;
204 			break;
205 
206 		case 0x03:  /* Window 1 MSB */
207 			data = ( m_window1 >> 8 ) & 0xFF;
208 			break;
209 
210 		case 0x04:  /* Window 2 LSB */
211 			data = m_window2 & 0xFF;
212 			break;
213 
214 		case 0x05:  /* Window 2 MSB */
215 			data = ( m_window2 >> 8 ) & 0xFF;
216 			break;
217 	}
218 
219 	return data;
220 }
221 
222 
write(offs_t offset,u8 data)223 void huc6202_device::write(offs_t offset, u8 data)
224 {
225 	switch ( offset & 7 )
226 	{
227 		case 0x00:  /* Priority register #0 */
228 			m_prio[0].dev0_enabled = data & 0x01;
229 			m_prio[0].dev1_enabled = data & 0x02;
230 			m_prio[0].prio_type = ( data >> 2 ) & 0x03;
231 			m_prio[1].dev0_enabled = data & 0x10;
232 			m_prio[1].dev1_enabled = data & 0x20;
233 			m_prio[1].prio_type = ( data >> 6 ) & 0x03;
234 			break;
235 
236 		case 0x01:  /* Priority register #1 */
237 			m_prio[2].dev0_enabled = data & 0x01;
238 			m_prio[2].dev1_enabled = data & 0x02;
239 			m_prio[2].prio_type = ( data >> 2 ) & 0x03;
240 			m_prio[3].dev0_enabled = data & 0x10;
241 			m_prio[3].dev1_enabled = data & 0x20;
242 			m_prio[3].prio_type = ( data >> 6 ) & 0x03;
243 			break;
244 
245 		case 0x02:  /* Window 1 LSB */
246 			m_window1 = ( m_window1 & 0xFF00 ) | data;
247 			m_map_dirty = 1;
248 			break;
249 
250 		case 0x03:  /* Window 1 MSB */
251 			m_window1 = ( ( m_window1 & 0x00FF ) | ( data << 8 ) ) & 0x3FF;
252 			m_map_dirty = 1;
253 			break;
254 
255 		case 0x04:  /* Window 2 LSB */
256 			m_window2 = ( m_window2 & 0xFF00 ) | data;
257 			m_map_dirty = 1;
258 			break;
259 
260 		case 0x05:  /* Window 2 MSB */
261 			m_window2 = ( ( m_window2 & 0x00FF ) | ( data << 8 ) ) & 0x3FF;
262 			m_map_dirty = 1;
263 			break;
264 
265 		case 0x06:  /* I/O select */
266 			m_io_device = data & 0x01;
267 			break;
268 	}
269 }
270 
271 
io_read(offs_t offset)272 u8 huc6202_device::io_read(offs_t offset)
273 {
274 	if ( m_io_device )
275 	{
276 		return m_read_1_cb( offset );
277 	}
278 	else
279 	{
280 		return m_read_0_cb( offset );
281 	}
282 }
283 
284 
io_write(offs_t offset,u8 data)285 void huc6202_device::io_write(offs_t offset, u8 data)
286 {
287 	if ( m_io_device )
288 	{
289 		m_write_1_cb( offset, data );
290 	}
291 	else
292 	{
293 		m_write_0_cb( offset, data );
294 	}
295 }
296 
297 
device_start()298 void huc6202_device::device_start()
299 {
300 	/* Resolve callbacks */
301 	m_next_pixel_0_cb.resolve();
302 	m_time_til_next_event_0_cb.resolve();
303 	m_hsync_changed_0_cb.resolve();
304 	m_vsync_changed_0_cb.resolve();
305 	m_read_0_cb.resolve();
306 	m_write_0_cb.resolve();
307 
308 	m_next_pixel_1_cb.resolve();
309 	m_time_til_next_event_1_cb.resolve();
310 	m_hsync_changed_1_cb.resolve();
311 	m_vsync_changed_1_cb.resolve();
312 	m_read_1_cb.resolve();
313 	m_write_1_cb.resolve();
314 
315 	/* We want all our callbacks to be resolved */
316 	assert( ! m_next_pixel_0_cb.isnull() );
317 	assert( ! m_time_til_next_event_0_cb.isnull() );
318 	assert( ! m_hsync_changed_0_cb.isnull() );
319 	assert( ! m_vsync_changed_0_cb.isnull() );
320 	assert( ! m_read_0_cb.isnull() );
321 	assert( ! m_write_0_cb.isnull() );
322 	assert( ! m_next_pixel_1_cb.isnull() );
323 	assert( ! m_time_til_next_event_1_cb.isnull() );
324 	assert( ! m_hsync_changed_1_cb.isnull() );
325 	assert( ! m_vsync_changed_1_cb.isnull() );
326 	assert( ! m_read_1_cb.isnull() );
327 	assert( ! m_write_1_cb.isnull() );
328 
329 	/* Register save items */
330 	save_item(NAME(m_prio[0].prio_type));
331 	save_item(NAME(m_prio[0].dev0_enabled));
332 	save_item(NAME(m_prio[0].dev1_enabled));
333 	save_item(NAME(m_prio[1].prio_type));
334 	save_item(NAME(m_prio[1].dev0_enabled));
335 	save_item(NAME(m_prio[1].dev1_enabled));
336 	save_item(NAME(m_prio[2].prio_type));
337 	save_item(NAME(m_prio[2].dev0_enabled));
338 	save_item(NAME(m_prio[2].dev1_enabled));
339 	save_item(NAME(m_prio[3].prio_type));
340 	save_item(NAME(m_prio[3].dev0_enabled));
341 	save_item(NAME(m_prio[3].dev1_enabled));
342 	save_item(NAME(m_window1));
343 	save_item(NAME(m_window2));
344 	save_item(NAME(m_io_device));
345 	save_item(NAME(m_map_index));
346 	save_item(NAME(m_map_dirty));
347 	save_item(NAME(m_prio_map));
348 }
349 
350 
device_reset()351 void huc6202_device::device_reset()
352 {
353 	m_prio[0].prio_type = 0;
354 	m_prio[0].dev0_enabled = 1;
355 	m_prio[0].dev1_enabled = 0;
356 	m_prio[1].prio_type = 0;
357 	m_prio[1].dev0_enabled = 1;
358 	m_prio[1].dev1_enabled = 0;
359 	m_prio[2].prio_type = 0;
360 	m_prio[2].dev0_enabled = 1;
361 	m_prio[2].dev1_enabled = 0;
362 	m_prio[3].prio_type = 0;
363 	m_prio[3].dev0_enabled = 1;
364 	m_prio[3].dev1_enabled = 0;
365 	m_map_dirty = 1;
366 	m_window1 = 0;
367 	m_window2 = 0;
368 	m_io_device = 0;
369 }
370