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