1 // license:BSD-3-Clause
2 // copyright-holders:Wilbert Pol
3 /**********************************************************************
4 
5     NEC HuC6270 Video Display Controller
6 
7     The HuC6270 basically outputs a 9-bit stream of pixel data which
8     holds a color index, a palette index, and an indication whether
9     the pixel contains background data or from sprite data.
10 
11     This data can be used by a colour encoder to output graphics.
12 
13     A regular screen is displayed as follows:
14 
15         |<- HDS ->|<--       HDW       -->|<- HDE ->|<- HSW ->|
16         |---------|-----------------------|---------|---------|
17     VSW |                                                     |
18         |---------|-----------------------|---------|---------|
19     VDS |                                                     |
20         |                  overscan                           |
21         |---------|-----------------------|---------|---------|
22         |         |                       |                   |
23         |         |                       |                   |
24         |         |                       |                   |
25         |         |                       |                   |
26     VDW | overscan|    active display     |      overscan     |
27         |         |                       |                   |
28         |         |                       |                   |
29         |         |                       |                   |
30         |         |                       |                   |
31         |---------|-----------------------|---------|---------|
32     VCR |                  overscan                           |
33         |                                                     |
34         |---------|-----------------------|---------|---------|
35         ^end hsync
36          ^start vsync (30 cycles after hsync)
37 
38 
39 KNOWN ISSUES
40   - Violent Soldier (probably connected):
41     - In the intro some artefacts appear at the top of the
42       screen every now and then.
43   - In ccovell's splitres test not all sections seem to be aligned properly.
44   - Side Arms: Seems to be totally broken.
45 
46 
47 TODO
48   - Fix timing of VRAM-SATB DMA
49   - Implement VRAM-VRAM DMA
50   - DMA speeds differ depending on the dot clock selected in the huc6270
51   - Convert VRAM bus to actual space address (optimization)
52 
53 **********************************************************************/
54 
55 #include "emu.h"
56 #include "huc6270.h"
57 
58 //#define VERBOSE 1
59 #include "logmacro.h"
60 
61 
62 enum {
63 	MAWR = 0x00,
64 	MARR = 0x01,
65 	VxR = 0x02,
66 	CR = 0x05,
67 	RCR = 0x06,
68 	BXR = 0x07,
69 	BYR = 0x08,
70 	MWR = 0x09,
71 	HSR = 0x0A,
72 	HDR = 0x0B,
73 	VPR = 0x0C,
74 	VDW = 0x0D,
75 	VCR = 0x0E,
76 	DCR = 0x0F,
77 	SOUR = 0x10,
78 	DESR = 0x11,
79 	LENR = 0x12,
80 	DVSSR = 0x13
81 };
82 
83 ALLOW_SAVE_TYPE(huc6270_device::v_state);
84 ALLOW_SAVE_TYPE(huc6270_device::h_state);
85 
86 
87 /* Bits in the VDC status register */
88 #define HUC6270_BSY         0x40    /* Set when the VDC accesses VRAM */
89 #define HUC6270_VD          0x20    /* Set when in the vertical blanking period */
90 #define HUC6270_DV          0x10    /* Set when a VRAM > VRAM DMA transfer is done */
91 #define HUC6270_DS          0x08    /* Set when a VRAM > SATB DMA transfer is done */
92 #define HUC6270_RR          0x04    /* Set when the current scanline equals the RCR register */
93 #define HUC6270_OR          0x02    /* Set when there are more than 16 sprites on a line */
94 #define HUC6270_CR          0x01    /* Set when sprite #0 overlaps with another sprite */
95 
96 
97 DEFINE_DEVICE_TYPE(HUC6270, huc6270_device, "huc6270", "Hudson HuC6270 VDC")
98 
99 
100 constexpr uint8_t huc6270_device::vram_increments[4];
101 
huc6270_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)102 huc6270_device::huc6270_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
103 	: device_t(mconfig, HUC6270, tag, owner, clock)
104 	, m_vram_size(0)
105 	, m_irq_changed_cb(*this)
106 {
107 }
108 
109 
110 /*
111   Read one row of tile data from video ram
112 */
fetch_bat_tile_row()113 inline void huc6270_device::fetch_bat_tile_row()
114 {
115 	const uint16_t bat_data = m_vram[ m_bat_address & m_vram_mask ];
116 	const uint16_t tile_palette = ( bat_data >> 8 ) & 0xF0;
117 	uint16_t data1 = m_vram[ ( ( ( bat_data & 0x0FFF ) << 4 ) + m_bat_row + 0 ) & m_vram_mask ];
118 	uint16_t data2 = ( data1 >> 7 ) & 0x1FE;
119 	uint16_t data3 = m_vram[ ( ( ( bat_data & 0x0FFF ) << 4 ) + m_bat_row + 8 ) & m_vram_mask ];
120 	uint16_t data4 = ( data3 >> 5 ) & 0x7F8;
121 	data3 <<= 2;
122 
123 	for ( int i = 7; i >= 0; i-- )
124 	{
125 		uint16_t c = ( data1 & 0x01 ) | ( data2 & 0x02 ) | ( data3 & 0x04 ) | ( data4 & 0x08 );
126 
127 		/* Colour 0 for background tiles is always taken from palette 0 */
128 		if ( c )
129 			c |= tile_palette;
130 
131 		m_bat_tile_row[i] = c;
132 
133 		data1 >>= 1;
134 		data2 >>= 1;
135 		data3 >>= 1;
136 		data4 >>= 1;
137 	}
138 }
139 
140 
add_sprite(int index,int x,int pattern,int line,int flip_x,int palette,int priority,int sat_lsb)141 void huc6270_device::add_sprite( int index, int x, int pattern, int line, int flip_x, int palette, int priority, int sat_lsb )
142 {
143 	int i = m_sprites_this_line;
144 
145 	if ( i < 16 )
146 	{
147 		uint32_t b0, b1, b2, b3;
148 		int j;
149 
150 		if ( flip_x )
151 			flip_x = 0x0F;
152 
153 		pattern += ( ( line >> 4 ) << 1 );
154 
155 		if ( ( m_mwr & 0x0c ) == 0x04 )
156 		{
157 			if ( ! sat_lsb )
158 			{
159 				b0 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x00 ) & m_vram_mask ];
160 				b1 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x10 ) & m_vram_mask ] << 1;
161 			}
162 			else
163 			{
164 				b0 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x20 ) & m_vram_mask ];
165 				b1 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x30 ) & m_vram_mask ] << 1;
166 			}
167 			b2 = 0;
168 			b3 = 0;
169 		}
170 		else
171 		{
172 			b0 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x00 ) & m_vram_mask ];
173 			b1 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x10 ) & m_vram_mask ] << 1;
174 			b2 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x20 ) & m_vram_mask ] << 2;
175 			b3 = m_vram[ ( ( pattern * 0x40 ) + ( line & 0x0F ) + 0x30 ) & m_vram_mask ] << 3;
176 		}
177 
178 		for ( j = 15; j >= 0; j-- )
179 		{
180 			uint8_t data = ( b3 & 0x08 ) | ( b2 & 0x04 ) | ( b1 & 0x02 ) | ( b0 & 0x01 );
181 
182 			if ( data )
183 			{
184 				data |= palette << 4;
185 
186 				if ( x + ( j ^ flip_x ) < 1024 )
187 				{
188 					if (! m_sprite_row[ x + ( j ^ flip_x ) ] )
189 					{
190 						m_sprite_row[ x + ( j ^ flip_x ) ] = ( priority ? 0x4000 : 0x0000 ) | ( index << 8 ) | data;
191 					}
192 					else
193 					{
194 						if ( ! ( m_sprite_row[ x + ( j ^ flip_x ) ] & 0xFF00 ) )
195 						{
196 							/* Sprite 0 collission */
197 							m_sprite_row[ x + ( j ^ flip_x ) ] |= 0x8000;
198 						}
199 					}
200 				}
201 			}
202 
203 			b0 >>= 1;
204 			b1 >>= 1;
205 			b2 >>= 1;
206 			b3 >>= 1;
207 		}
208 
209 		m_sprites_this_line += 1;
210 	}
211 }
212 
213 
select_sprites()214 void huc6270_device::select_sprites()
215 {
216 	int i;
217 
218 	m_sprites_this_line = 0;
219 	memset( m_sprite_row, 0, sizeof( m_sprite_row ) );
220 	m_sprite_row_index = 0x20;
221 
222 	for ( i = 0; i < 4 * 64; i += 4 )
223 	{
224 		static const int cgy_table[4] = { 16, 32, 64, 64 };
225 		int cgy = ( m_sat[i+3] >> 12 ) & 0x03;
226 		int height = cgy_table[ cgy ];
227 		int sprite_line = m_raster_count - m_sat[i];
228 
229 		if ( sprite_line >= 0 && sprite_line < height )
230 		{
231 			int pattern = m_sat[i+2] >> 1;
232 			int sat_lsb = m_sat[i+2] & 0x01;
233 			int palette = m_sat[i+3] & 0x0F;
234 			int priority = m_sat[i+3] & 0x80;
235 			int cgx = m_sat[i+3] & 0x0100;
236 
237 			/* If CGY is set to 1, bit 1 of the sprite pattern index is forced to 0 */
238 			if ( cgy & 1 )
239 				pattern &= ~0x0002;
240 
241 			/* If CGY is set to 2 or 3, bits 1 and 2 of the sprite pattern index are forced to 0 */
242 			if ( cgy & 2 )
243 				pattern &= ~0x0006;
244 
245 			/* Recalculate line index when sprite is flipped vertically */
246 			if ( m_sat[i+3] & 0x8000 )
247 				sprite_line = ( height - 1 ) - sprite_line;
248 
249 			/* Is the sprite 32 pixels wide */
250 			if ( cgx )
251 			{
252 				/* If CGX is set, bit 0 of the sprite pattern index is forced to 0 */
253 				pattern &= ~0x0001;
254 
255 				/* Check for horizontal flip */
256 				if ( m_sat[i+3] & 0x0800 )
257 				{
258 					/* Add to our list of sprites for this line */
259 					add_sprite( i/4, m_sat[i+1], pattern + 1, sprite_line, 1, palette, priority, sat_lsb );
260 					add_sprite( i/4, m_sat[i+1] + 16, pattern, sprite_line, 1, palette, priority, sat_lsb );
261 				}
262 				else
263 				{
264 					/* Add to our list of sprites for this line */
265 					add_sprite( i/4, m_sat[i+1], pattern, sprite_line, 0, palette, priority, sat_lsb );
266 					add_sprite( i/4, m_sat[i+1] + 16, pattern + 1, sprite_line, 0, palette, priority, sat_lsb );
267 				}
268 			}
269 			else
270 			{
271 				/* Add to our list of sprites for this line */
272 				add_sprite( i/4, m_sat[i+1], pattern, sprite_line, m_sat[i+3] & 0x0800, palette, priority, sat_lsb );
273 			}
274 		}
275 	}
276 
277 	/* Check for sprite overflow */
278 	if ( m_sprites_this_line >= 16 )
279 	{
280 		/* note: flag is set only if irq is taken, Mizubaku Daibouken relies on this behaviour */
281 		if ( m_cr & 0x02 )
282 		{
283 			m_status |= HUC6270_OR;
284 			m_irq_changed_cb( ASSERT_LINE );
285 		}
286 	}
287 }
288 
289 
handle_vblank()290 inline void huc6270_device::handle_vblank()
291 {
292 	if ( ! m_vd_triggered )
293 	{
294 		if ( m_cr & 0x08 )
295 		{
296 			m_status |= HUC6270_VD;
297 			m_irq_changed_cb( ASSERT_LINE );
298 		}
299 
300 		/* Should we initiate a VRAM->SATB DMA transfer.
301 		   The timing for this is incorrect.
302 		 */
303 		if ( m_dvssr_written || ( m_dcr & 0x10 ) )
304 		{
305 			int i;
306 
307 			LOG("SATB transfer from %05x\n", m_dvssr << 1 );
308 			for ( i = 0; i < 4 * 64; i += 4 )
309 			{
310 				m_sat[i + 0] = m_vram[ ( m_dvssr + i + 0 ) & m_vram_mask ] & 0x03FF;
311 				m_sat[i + 1] = m_vram[ ( m_dvssr + i + 1 ) & m_vram_mask ] & 0x03FF;
312 				m_sat[i + 2] = m_vram[ ( m_dvssr + i + 2 ) & m_vram_mask ] & 0x07FF;
313 				m_sat[i + 3] = m_vram[ ( m_dvssr + i + 3 ) & m_vram_mask ];
314 			}
315 			m_dvssr_written = 0;
316 
317 			/* Generate SATB interrupt if requested */
318 			if ( m_dcr & 0x01 )
319 			{
320 				m_satb_countdown = 4;
321 //                  m_status |= HUC6270_DS;
322 //                  m_irq_changed_cb( ASSERT_LINE );
323 			}
324 		}
325 
326 		m_vd_triggered = 1;
327 	}
328 }
329 
330 
next_vert_state()331 inline void huc6270_device::next_vert_state()
332 {
333 	switch ( m_vert_state )
334 	{
335 	case v_state::VSW:
336 		m_vert_state = v_state::VDS;
337 		m_vert_to_go = ( ( m_vpr >> 8 ) & 0xFF ) + 2;
338 		break;
339 
340 	case v_state::VDS:
341 		m_vert_state = v_state::VDW;
342 		m_vert_to_go = ( m_vdw & 0x1FF ) + 1;
343 		m_byr_latched = m_byr;
344 		m_vd_triggered = 0;
345 		break;
346 
347 	case v_state::VDW:
348 		m_vert_state = v_state::VCR;
349 		m_vert_to_go = ( m_vcr & 0xFF );
350 		handle_vblank();
351 		break;
352 
353 	case v_state::VCR:
354 		m_vert_state = v_state::VSW;
355 		m_vert_to_go = ( m_vpr & 0x1F ) + 1;
356 		break;
357 	}
358 }
359 
360 
next_horz_state()361 inline void huc6270_device::next_horz_state()
362 {
363 	switch ( m_horz_state )
364 	{
365 	case h_state::HDS:
366 		m_bxr_latched = m_bxr;
367 		m_horz_state = h_state::HDW;
368 		m_horz_to_go = ( m_hdr & 0x7F ) + 1;
369 		{
370 			static const int width_shift[4] = { 5, 6, 7, 7 };
371 			uint16_t v;
372 
373 			v = ( m_byr_latched ) & ( ( m_mwr & 0x40 ) ? 0x1FF : 0xFF );
374 			m_bat_row = v & 7;
375 			m_bat_address_mask = ( 1 << width_shift[ ( m_mwr >> 4 ) & 0x03 ] ) - 1;
376 			m_bat_address = ( ( v >> 3 ) << ( width_shift[ ( m_mwr >> 4 ) & 0x03 ] ) )
377 				| ( ( m_bxr_latched >> 3 ) & m_bat_address_mask );
378 			m_bat_column = m_bxr & 7;
379 			fetch_bat_tile_row();
380 		}
381 		break;
382 
383 	case h_state::HDW:
384 		m_horz_state = h_state::HDE;
385 		m_horz_to_go = ( ( m_hdr >> 8 ) & 0x7F ) + 1;
386 		break;
387 
388 	case h_state::HDE:
389 		m_horz_state = h_state::HSW;
390 		m_horz_to_go = ( m_hsr & 0x1F ) + 1;
391 		break;
392 
393 	case h_state::HSW:
394 		m_horz_state = h_state::HDS;
395 		m_horz_to_go = std::max( ( ( m_hsr >> 8 ) & 0x7F ), 2 ) + 1;
396 
397 		/* If section has ended, advance to next vertical state */
398 		while ( m_vert_to_go == 0 )
399 			next_vert_state();
400 
401 		/* Select sprites for the coming line */
402 		select_sprites();
403 		break;
404 	}
405 	m_horz_steps = 0;
406 }
407 
408 
next_pixel()409 u16 huc6270_device::next_pixel()
410 {
411 	uint16_t data = HUC6270_SPRITE;
412 
413 	/* Check if we're on an active display line */
414 	if ( m_vert_state == v_state::VDW )
415 	{
416 		/* Check if we're in active display area */
417 		if ( m_horz_state == h_state::HDW )
418 		{
419 			uint8_t sprite_data = m_sprite_row[ m_sprite_row_index ] & 0x00FF;
420 			int collission = ( m_sprite_row[ m_sprite_row_index ] & 0x8000 ) ? 1 : 0;
421 
422 			if ( m_cr & 0x80 )
423 			{
424 				data = HUC6270_BACKGROUND | m_bat_tile_row[ m_bat_column ];
425 				if ( sprite_data && ( m_cr & 0x40 ) )
426 				{
427 					if ( m_sprite_row[ m_sprite_row_index ] & 0x4000 )
428 					{
429 						data = HUC6270_SPRITE | sprite_data;
430 					}
431 					else
432 					{
433 						if ( data == HUC6270_BACKGROUND )
434 						{
435 							data = HUC6270_SPRITE | sprite_data;
436 						}
437 					}
438 				}
439 			}
440 			else
441 			{
442 				if ( m_cr & 0x40 )
443 				{
444 					data = HUC6270_SPRITE | sprite_data;
445 				}
446 			}
447 
448 			m_sprite_row_index = m_sprite_row_index + 1;
449 			m_bat_column += 1;
450 			if ( m_bat_column >= 8 )
451 			{
452 				m_bat_address = ( m_bat_address & ~m_bat_address_mask )
453 					| ( ( m_bat_address + 1 ) & m_bat_address_mask );
454 				m_bat_column = 0;
455 				fetch_bat_tile_row();
456 			}
457 
458 			if ( collission && ( m_cr & 0x01 ) )
459 			{
460 				m_status |= HUC6270_CR;
461 				m_irq_changed_cb( ASSERT_LINE );
462 			}
463 		}
464 	}
465 
466 	m_horz_steps++;
467 	if ( m_horz_steps == 8 )
468 	{
469 		m_horz_to_go -= 1;
470 		m_horz_steps = 0;
471 		while ( m_horz_to_go == 0 )
472 			next_horz_state();
473 	}
474 	return data;
475 }
476 
477 
478 //inline u16 huc6270_device::time_until_next_event()
479 //{
480 //  return m_horz_to_go * 8 + m_horz_steps;
481 //}
482 
483 
WRITE_LINE_MEMBER(huc6270_device::vsync_changed)484 WRITE_LINE_MEMBER( huc6270_device::vsync_changed )
485 {
486 	state &= 0x01;
487 	if ( m_vsync != state )
488 	{
489 		/* Check for high->low VSYNC transition */
490 		if ( !state )
491 		{
492 			m_vert_state = v_state::VCR;
493 			m_vert_to_go = 0;
494 
495 			while ( m_vert_to_go == 0 )
496 				next_vert_state();
497 		}
498 		else
499 		{
500 			/* Check for low->high VSYNC transition */
501 			// VBlank IRQ happens at the beginning of HDW period after VDW ends
502 			handle_vblank();
503 		}
504 	}
505 
506 	m_vsync = state;
507 }
508 
509 
WRITE_LINE_MEMBER(huc6270_device::hsync_changed)510 WRITE_LINE_MEMBER( huc6270_device::hsync_changed )
511 {
512 	state &= 0x01;
513 
514 	if(m_hsync != state)
515 	{
516 		/* Check for low->high HSYNC transition */
517 		if(state)
518 		{
519 			if ( m_satb_countdown )
520 			{
521 				m_satb_countdown--;
522 
523 				if ( m_satb_countdown == 0 )
524 				{
525 					m_status |= HUC6270_DS;
526 					m_irq_changed_cb( ASSERT_LINE );
527 				}
528 			}
529 
530 			m_horz_state = h_state::HSW;
531 			m_horz_to_go = 0;
532 			m_horz_steps = 0;
533 			m_byr_latched += 1;
534 			m_raster_count += 1;
535 			if ( m_vert_to_go == 1 && m_vert_state == v_state::VDS )
536 			{
537 				m_raster_count = 0x40;
538 			}
539 
540 			m_vert_to_go -= 1;
541 
542 			while ( m_horz_to_go == 0 )
543 				next_horz_state();
544 
545 			handle_dma();
546 		}
547 		else
548 		{
549 			/* Check for high->low HSYNC transition */
550 			// RCR IRQ happens near the end of the HDW period
551 			if ( m_raster_count == m_rcr && ( m_cr & 0x04 ) )
552 			{
553 				m_status |= HUC6270_RR;
554 				m_irq_changed_cb( ASSERT_LINE );
555 			}
556 		}
557 	}
558 
559 	m_hsync = state;
560 }
561 
handle_dma()562 inline void huc6270_device::handle_dma()
563 {
564 	/* Should we perform VRAM-VRAM dma.
565 	   The timing for this is incorrect.
566 	 */
567 	if ( m_dma_enabled )
568 	{
569 		int desr_inc = ( m_dcr & 0x0008 ) ? -1 : +1;
570 		int sour_inc = ( m_dcr & 0x0004 ) ? -1 : +1;
571 
572 		LOG("doing dma sour = %04x, desr = %04x, lenr = %04x\n", m_sour, m_desr, m_lenr );
573 
574 		do {
575 			uint16_t data;
576 
577 			// area 0x8000-0xffff cannot be r/w (open bus)
578 			if(m_sour <= m_vram_mask)
579 				data = m_vram[ m_sour ];
580 			else
581 				data = 0;
582 
583 			if(m_desr <= m_vram_mask)
584 				m_vram[ m_desr ] = data;
585 			m_sour += sour_inc;
586 			m_desr += desr_inc;
587 			m_lenr -= 1;
588 		} while ( m_lenr != 0xFFFF );
589 
590 		if ( m_dcr & 0x0002 )
591 		{
592 			m_status |= HUC6270_DV;
593 			m_irq_changed_cb( ASSERT_LINE );
594 		}
595 		m_dma_enabled = 0;
596 	}
597 }
598 
read(offs_t offset)599 u8 huc6270_device::read(offs_t offset)
600 {
601 	uint8_t data = 0x00;
602 
603 	switch ( offset & 3 )
604 	{
605 		case 0x00:  /* status */
606 			data = m_status;
607 			m_status &= ~( HUC6270_VD | HUC6270_DV | HUC6270_RR | HUC6270_CR | HUC6270_OR | HUC6270_DS );
608 			m_irq_changed_cb( CLEAR_LINE );
609 			break;
610 
611 		case 0x02:
612 			data = m_vrr & 0xFF;
613 			break;
614 
615 		case 0x03:
616 			data = m_vrr >> 8;
617 			if ( m_register_index == VxR )
618 			{
619 				m_marr += vram_increments[ ( m_cr >> 11 ) & 3 ];
620 
621 				if(m_marr <= m_vram_mask)
622 					m_vrr = m_vram[ m_marr ];
623 				else
624 				{
625 					// TODO: test with real HW
626 					m_vrr = 0;
627 					logerror("%s Open Bus VRAM read (register read) %04x\n",this->tag(),m_marr);
628 				}
629 			}
630 			break;
631 	}
632 	return data;
633 }
634 
635 
write(offs_t offset,u8 data)636 void huc6270_device::write(offs_t offset, u8 data)
637 {
638 	LOG("%s: huc6270 write %02x <- %02x ", machine().describe_context(), offset, data);
639 
640 	switch ( offset & 3 )
641 	{
642 		case 0x00:  /* VDC register select */
643 			m_register_index = data & 0x1F;
644 			break;
645 
646 		case 0x02:  /* VDC data LSB */
647 			switch ( m_register_index )
648 			{
649 				case MAWR:      /* memory address write register LSB */
650 					m_mawr = ( m_mawr & 0xFF00 ) | data;
651 					break;
652 
653 				case MARR:      /* memory address read register LSB */
654 					m_marr = ( m_marr & 0xFF00 ) | data;
655 					if(m_marr <= m_vram_mask)
656 						m_vrr = m_vram[ m_marr ];
657 					else
658 					{
659 						// TODO: test with real HW
660 						m_vrr = 0;
661 						logerror("%s Open Bus VRAM read (memory address) %04x\n",this->tag(),m_marr);
662 					}
663 					break;
664 
665 				case VxR:       /* vram write data LSB */
666 					m_vwr = ( m_vwr & 0xFF00 ) | data;
667 					break;
668 
669 				case CR:        /* control register LSB */
670 					m_cr = ( m_cr & 0xFF00 ) | data;
671 					break;
672 
673 				case RCR:       /* raster compare register LSB */
674 					m_rcr = ( m_rcr & 0x0300 ) | data;
675 //                  if ( m_raster_count == m_rcr && m_cr & 0x04 )
676 //                  {
677 //                      m_status |= HUC6270_RR;
678 //                      m_irq_changed_cb( ASSERT_LINE );
679 //                  }
680 					break;
681 
682 				case BXR:       /* background x-scroll register LSB */
683 					m_bxr = ( m_bxr & 0x0300 ) | data;
684 					break;
685 
686 				case BYR:       /* background y-scroll register LSB */
687 					m_byr = ( m_byr & 0x0100 ) | data;
688 					m_byr_latched = m_byr;
689 					break;
690 
691 				case MWR:       /* memory width register LSB */
692 					m_mwr = ( m_mwr & 0xFF00 ) | data;
693 					break;
694 
695 				case HSR:       /* horizontal sync register LSB */
696 					m_hsr = ( m_hsr & 0xFF00 ) | data;
697 					break;
698 
699 				case HDR:       /* horizontal display register LSB */
700 					m_hdr = ( m_hdr & 0xFF00 ) | data;
701 					break;
702 
703 				case VPR:       /* vertical sync register LSB */
704 					m_vpr = ( m_vpr & 0xFF00 ) | data;
705 					break;
706 
707 				case VDW:       /* vertical display register LSB */
708 					m_vdw = ( m_vdw & 0xFF00 ) | data;
709 					break;
710 
711 				case VCR:       /* vertical display end position register LSB */
712 					m_vcr = ( m_vcr & 0xFF00 ) | data;
713 					break;
714 
715 				case DCR:       /* DMA control register LSB */
716 					m_dcr = ( m_dcr & 0xFF00 ) | data;
717 					break;
718 
719 				case SOUR:      /* DMA source address register LSB */
720 					m_sour = ( m_sour & 0xFF00 ) | data;
721 					break;
722 
723 				case DESR:      /* DMA destination address register LSB */
724 					m_desr = ( m_desr & 0xFF00 ) | data;
725 					break;
726 
727 				case LENR:      /* DMA length register LSB */
728 					m_lenr = ( m_lenr & 0xFF00 ) | data;
729 					break;
730 
731 				case DVSSR:     /* Sprite attribute table LSB */
732 					m_dvssr = ( m_dvssr & 0xFF00 ) | data;
733 					m_dvssr_written = 1;
734 					break;
735 			}
736 			break;
737 
738 		case 0x03:  /* VDC data MSB */
739 			switch ( m_register_index )
740 			{
741 				case MAWR:      /* memory address write register MSB */
742 					m_mawr = ( m_mawr & 0x00FF ) | ( data << 8 );
743 					break;
744 
745 				case MARR:      /* memory address read register MSB */
746 					m_marr = ( m_marr & 0x00FF ) | ( data << 8 );
747 					if(m_marr <= m_vram_mask)
748 						m_vrr = m_vram[ m_marr ];
749 					else
750 						m_vrr = 0;
751 					break;
752 
753 				case VxR:       /* vram write data MSB */
754 					m_vwr = ( m_vwr & 0x00FF ) | ( data << 8 );
755 					// area 0x8000-0xffff is NOP and cannot be written to.
756 					if(m_mawr <= m_vram_mask)
757 						m_vram[ m_mawr ] = m_vwr;
758 					m_mawr += vram_increments[ ( m_cr >> 11 ) & 3 ];
759 					break;
760 
761 				case CR:        /* control register MSB */
762 					m_cr = ( m_cr & 0x00FF ) | ( data << 8 );
763 					break;
764 
765 				case RCR:       /* raster compare register MSB */
766 					m_rcr = ( m_rcr & 0x00FF ) | ( ( data & 0x03 ) << 8 );
767 //printf("%s: RCR set to %03x\n", machine().describe_context().c_str(), m_rcr);
768 //                  if ( m_raster_count == m_rcr && m_cr & 0x04 )
769 //                  {
770 //                      m_status |= HUC6270_RR;
771 //                      m_irq_changed_cb( ASSERT_LINE );
772 //                  }
773 					break;
774 
775 				case BXR:       /* background x-scroll register MSB */
776 					m_bxr = ( m_bxr & 0x00FF ) | ( ( data & 0x03 ) << 8 );
777 					break;
778 
779 				case BYR:       /* background y-scroll register MSB */
780 					m_byr = ( m_byr & 0x00FF ) | ( ( data & 0x01 ) << 8 );
781 					m_byr_latched = m_byr;
782 					break;
783 
784 				case MWR:       /* memory width register MSB */
785 					m_mwr = ( m_mwr & 0x00FF ) | ( data << 8 );
786 					break;
787 
788 				case HSR:       /* horizontal sync register MSB */
789 					m_hsr = ( m_hsr & 0x00FF ) | ( data << 8 );
790 					break;
791 
792 				case HDR:       /* horizontal display register MSB */
793 					m_hdr = ( m_hdr & 0x00FF ) | ( data << 8 );
794 					break;
795 
796 				case VPR:       /* vertical sync register MSB */
797 					m_vpr = ( m_vpr & 0x00FF ) | ( data << 8 );
798 					break;
799 
800 				case VDW:       /* vertical display register MSB */
801 					m_vdw = ( m_vdw & 0x00FF ) | ( data << 8 );
802 					break;
803 
804 				case VCR:       /* vertical display end position register MSB */
805 					m_vcr = ( m_vcr & 0x00FF ) | ( data << 8 );
806 					break;
807 
808 				case DCR:       /* DMA control register MSB */
809 					m_dcr = ( m_dcr & 0x00FF ) | ( data << 8 );
810 					break;
811 
812 				case SOUR:      /* DMA source address register MSB */
813 					m_sour = ( m_sour & 0x00FF ) | ( data << 8 );
814 					break;
815 
816 				case DESR:      /* DMA destination address register MSB */
817 					m_desr = ( m_desr & 0x00FF ) | ( data << 8 );
818 					break;
819 
820 				case LENR:      /* DMA length register MSB */
821 					m_lenr = ( m_lenr & 0x00FF ) | ( data << 8 );
822 					m_dma_enabled = 1;
823 //logerror("DMA is not supported yet.\n");
824 					break;
825 
826 				case DVSSR:     /* Sprite attribute table MSB */
827 					m_dvssr = ( m_dvssr & 0x00FF ) | ( data << 8 );
828 					m_dvssr_written = 1;
829 					break;
830 			}
831 			break;
832 	}
833 	LOG("\n");
834 }
835 
836 
device_start()837 void huc6270_device::device_start()
838 {
839 	/* Resolve callbacks */
840 	m_irq_changed_cb.resolve_safe();
841 
842 	m_vram = make_unique_clear<uint16_t[]>(m_vram_size/sizeof(uint16_t));
843 	m_vram_mask = (m_vram_size >> 1) - 1;
844 
845 	save_pointer(NAME(m_vram), m_vram_size/sizeof(uint16_t));
846 
847 	save_item(NAME(m_register_index));
848 	save_item(NAME(m_mawr));
849 	save_item(NAME(m_marr));
850 	save_item(NAME(m_vrr));
851 	save_item(NAME(m_vwr));
852 	save_item(NAME(m_cr));
853 	save_item(NAME(m_rcr));
854 	save_item(NAME(m_bxr));
855 	save_item(NAME(m_byr));
856 	save_item(NAME(m_mwr));
857 	save_item(NAME(m_hsr));
858 	save_item(NAME(m_hdr));
859 	save_item(NAME(m_vpr));
860 	save_item(NAME(m_vdw));
861 	save_item(NAME(m_vcr));
862 	save_item(NAME(m_dcr));
863 	save_item(NAME(m_sour));
864 	save_item(NAME(m_desr));
865 	save_item(NAME(m_lenr));
866 	save_item(NAME(m_dvssr));
867 	save_item(NAME(m_status));
868 	save_item(NAME(m_hsync));
869 	save_item(NAME(m_vsync));
870 	save_item(NAME(m_vert_state));
871 	save_item(NAME(m_horz_state));
872 	save_item(NAME(m_vd_triggered));
873 	save_item(NAME(m_vert_to_go));
874 	save_item(NAME(m_horz_to_go));
875 	save_item(NAME(m_horz_steps));
876 	save_item(NAME(m_raster_count));
877 	save_item(NAME(m_dvssr_written));
878 	save_item(NAME(m_satb_countdown));
879 	save_item(NAME(m_dma_enabled));
880 	save_item(NAME(m_byr_latched));
881 	save_item(NAME(m_bxr_latched));
882 	save_item(NAME(m_bat_address));
883 	save_item(NAME(m_bat_address_mask));
884 	save_item(NAME(m_bat_row));
885 	save_item(NAME(m_bat_column));
886 	save_item(NAME(m_bat_tile_row));
887 	save_item(NAME(m_sat));
888 	save_item(NAME(m_sprites_this_line));
889 	save_item(NAME(m_sprite_row_index));
890 	save_item(NAME(m_sprite_row));
891 }
892 
893 
device_reset()894 void huc6270_device::device_reset()
895 {
896 	m_mawr = 0;
897 	m_marr = 0;
898 	m_vrr = 0;
899 	m_vwr = 0;
900 	m_cr = 0;
901 	m_rcr = 0;
902 	m_bxr = 0;
903 	m_byr = 0;
904 	m_mwr = 0;
905 	m_hsr = 0x0202;     /* Take some defaults for horizontal timing */
906 	m_hdr = 0x041f;
907 	m_vpr = 0x0f02;     /* Take some defaults for vertical timing */
908 	m_vdw = 0x00ef;
909 	m_vcr = 0x0004;
910 	m_dcr = 0;
911 	m_sour = 0;
912 	m_lenr = 0;
913 	m_dvssr = 0;
914 	m_status = 0;
915 	m_vd_triggered = 0;
916 	m_dvssr_written = 0;
917 	m_satb_countdown = 0;
918 	m_raster_count = 0x4000;
919 	m_vert_to_go = 0;
920 	m_vert_state = v_state::VSW;
921 	m_horz_steps = 0;
922 	m_horz_to_go = 0;
923 	m_horz_state = h_state::HDS;
924 	m_hsync = 0;
925 	m_vsync = 0;
926 	m_dma_enabled = 0;
927 	m_byr_latched = 0;
928 
929 	memset(m_sat, 0, sizeof(m_sat));
930 }
931