1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles, Nathan Woods
3 
4 /***************************************************************************
5 
6 v9938 / v9958 emulation
7 
8 Vertical display parameters from Yamaha V9938 Technical Data Book.
9 NTSC: page 146, Table 7-2
10 PAL: page 147, Table 7-3
11 
12 Vertical timing:
13                                        PAL                 NTSC
14                                        192(LN=0) 212(LN=1) 192(LN=0) 212(LN=1)
15                                        ------------------- --------------------
16 1. Top erase (top blanking)                13        13        13        13
17 2. Top border                              53        43        26        16
18 3. Active display                         192       212       192       212
19 4. Bottom border                           49        39        25        15
20 5. Bottom erase (bottom blanking)           3         3         3         3
21 6. Vertical sync (bottom blanking)          3         3         3         3
22 7. Total                                  313       313       262       262
23 
24    Refresh rate                           50.158974           59.922743
25 
26 
27 ***************************************************************************/
28 
29 /*
30 todo:
31 
32 - sprite collision
33 - vdp engine -- make run at correct speed
34 - vr/hr/fh flags: double-check all of that
35 - make vdp engine work in exp. ram
36 */
37 
38 #include "emu.h"
39 #include "v9938.h"
40 
41 #define LOG_WARN     (1U<<1)
42 #define LOG_INT      (1U<<2)
43 #define LOG_STATUS   (1U<<3)
44 #define LOG_REGWRITE (1U<<4)
45 #define LOG_COMMAND  (1U<<5)
46 #define LOG_MODE     (1U<<6)
47 #define LOG_NOTIMP   (1U<<7)
48 #define LOG_DETAIL   (1U<<8)
49 
50 // Minimum log should be warnings
51 #define VERBOSE (LOG_GENERAL | LOG_WARN )
52 
53 #include "logmacro.h"
54 
55 enum
56 {
57 	V9938_MODE_TEXT1 = 0,
58 	V9938_MODE_MULTI,
59 	V9938_MODE_GRAPHIC1,
60 	V9938_MODE_GRAPHIC2,
61 	V9938_MODE_GRAPHIC3,
62 	V9938_MODE_GRAPHIC4,
63 	V9938_MODE_GRAPHIC5,
64 	V9938_MODE_GRAPHIC6,
65 	V9938_MODE_GRAPHIC7,
66 	V9938_MODE_TEXT2,
67 	V9938_MODE_UNKNOWN
68 };
69 
70 #define MODEL_V9938 (0)
71 #define MODEL_V9958 (1)
72 
73 #define EXPMEM_OFFSET 0x20000
74 
75 #define V9938_LONG_WIDTH (512 + 32)
76 
77 static const char *const v9938_modes[] = {
78 	"TEXT 1", "MULTICOLOR", "GRAPHIC 1", "GRAPHIC 2", "GRAPHIC 3",
79 	"GRAPHIC 4", "GRAPHIC 5", "GRAPHIC 6", "GRAPHIC 7", "TEXT 2",
80 	"UNKNOWN"
81 };
82 
83 //**************************************************************************
84 //  GLOBAL VARIABLES
85 //**************************************************************************
86 
87 /*
88 Similar to the TMS9928, the V9938 has an own address space. It can handle
89 at most 192 KiB RAM (128 KiB base, 64 KiB expansion).
90 */
memmap(address_map & map)91 void v99x8_device::memmap(address_map &map)
92 {
93 	map.global_mask(0x3ffff);
94 	map(0x00000, 0x2ffff).ram();
95 }
96 
97 
98 // devices
99 DEFINE_DEVICE_TYPE(V9938, v9938_device, "v9938", "Yamaha V9938 VDP")
100 DEFINE_DEVICE_TYPE(V9958, v9958_device, "v9958", "Yamaha V9958 VDP")
101 
102 
v99x8_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock,int model)103 v99x8_device::v99x8_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, int model)
104 :   device_t(mconfig, type, tag, owner, clock),
105 	device_memory_interface(mconfig, *this),
106 	device_palette_interface(mconfig, *this),
107 	device_video_interface(mconfig, *this),
108 	m_space_config("vram", ENDIANNESS_BIG, 8, 18),
109 	m_model(model),
110 	m_pal_config(false),
111 	m_offset_x(0),
112 	m_offset_y(0),
113 	m_visible_y(0),
114 	m_mode(0),
115 	m_pal_write_first(0),
116 	m_cmd_write_first(0),
117 	m_pal_write(0),
118 	m_cmd_write(0),
119 	m_read_ahead(0),
120 	m_v9958_sp_mode(0),
121 	m_address_latch(0),
122 	m_vram_size(0),
123 	m_int_state(0),
124 	m_int_callback(*this),
125 	m_scanline(0),
126 	m_blink(0),
127 	m_blink_count(0),
128 	m_mx_delta(0),
129 	m_my_delta(0),
130 	m_button_state(0),
131 	m_vdp_ops_count(0),
132 	m_vdp_engine(nullptr),
133 	m_pal_ntsc(0)
134 {
135 	set_addrmap(AS_DATA, address_map_constructor(FUNC(v99x8_device::memmap), this));
136 }
137 
v9938_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)138 v9938_device::v9938_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
139 : v99x8_device(mconfig, V9938, tag, owner, clock, MODEL_V9938)
140 {
141 }
142 
v9958_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)143 v9958_device::v9958_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
144 : v99x8_device(mconfig, V9958, tag, owner, clock, MODEL_V9958)
145 {
146 }
147 
memory_space_config() const148 device_memory_interface::space_config_vector v99x8_device::memory_space_config() const
149 {
150 	return space_config_vector {
151 		std::make_pair(AS_DATA, &m_space_config)
152 	};
153 }
154 
155 
device_config_complete()156 void v99x8_device::device_config_complete()
157 {
158 	if (!has_screen())
159 		return;
160 
161 	if (!screen().refresh_attoseconds())
162 		screen().set_raw(clock(),
163 			HTOTAL,
164 			0,
165 			HVISIBLE - 1,
166 			(m_pal_config ? VTOTAL_PAL : VTOTAL_NTSC) * 2,
167 			VERTICAL_ADJUST * 2,
168 			(m_pal_config ? VVISIBLE_PAL : VVISIBLE_NTSC) * 2 - 1 - VERTICAL_ADJUST * 2);
169 
170 	if (!screen().has_screen_update())
171 		screen().set_screen_update(*this, FUNC(v99x8_device::screen_update));
172 }
173 
174 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)175 void v99x8_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
176 {
177 	int scanline = (m_scanline - (m_scanline_start + m_offset_y));
178 
179 	update_command ();
180 
181 	// set flags
182 	if (m_scanline == (m_scanline_start + m_offset_y))
183 	{
184 		m_stat_reg[2] &= ~0x40;
185 	}
186 	else if (m_scanline == (m_scanline_start + m_offset_y + m_visible_y))
187 	{
188 		m_stat_reg[2] |= 0x40;
189 		m_stat_reg[0] |= 0x80;
190 	}
191 
192 	if ( (scanline >= 0) && (scanline <= m_scanline_max) &&
193 		(((scanline + m_cont_reg[23]) & 255) == m_cont_reg[19]) )
194 	{
195 		m_stat_reg[1] |= 1;
196 		LOGMASKED(LOG_INT, "Scanline interrupt (%d)\n", scanline);
197 	}
198 	else if (!(m_cont_reg[0] & 0x10))
199 	{
200 		m_stat_reg[1] &= 0xfe;
201 	}
202 
203 	check_int();
204 
205 	// check for start of vblank
206 	if (m_scanline == m_vblank_start)
207 	{
208 		interrupt_start_vblank();
209 	}
210 
211 	// render the current line
212 	if (m_scanline < m_vblank_start)
213 	{
214 		refresh_line(scanline);
215 	}
216 
217 	if (++m_scanline >= m_height)
218 	{
219 		m_scanline = 0;
220 		// PAL/NTSC changed?
221 		int pal = m_cont_reg[9] & 2;
222 		if (m_pal_ntsc != pal)
223 		{
224 			m_pal_ntsc = pal;
225 			configure_pal_ntsc();
226 		}
227 		//screen().reset_origin();
228 		m_offset_y = position_offset(m_cont_reg[18] >> 4);
229 		set_screen_parameters();
230 	}
231 }
232 
233 
set_screen_parameters()234 void v99x8_device::set_screen_parameters()
235 {
236 	if (m_pal_ntsc)
237 	{
238 		// PAL
239 		m_scanline_start = (m_cont_reg[9] & 0x80) ? 43 : 53;
240 		m_scanline_max = 255;
241 	}
242 	else
243 	{
244 		// NYSC
245 		m_scanline_start = (m_cont_reg[9] & 0x80) ? 16 : 26;
246 		m_scanline_max = (m_cont_reg[9] & 0x80) ? 234 : 244;
247 	}
248 	m_visible_y = (m_cont_reg[9] & 0x80) ? 212 : 192;
249 }
250 
251 
configure_pal_ntsc()252 void v99x8_device::configure_pal_ntsc()
253 {
254 	if (m_pal_ntsc)
255 	{
256 		// PAL
257 		m_height = VTOTAL_PAL;
258 		rectangle visible;
259 		visible.set(0, HVISIBLE - 1, VERTICAL_ADJUST * 2, VVISIBLE_PAL * 2 - 1 - VERTICAL_ADJUST * 2);
260 		screen().configure(HTOTAL, VTOTAL_PAL * 2, visible, HZ_TO_ATTOSECONDS(50.158974));
261 	}
262 	else
263 	{
264 		// NTSC
265 		m_height = VTOTAL_NTSC;
266 		rectangle visible;
267 		visible.set(0, HVISIBLE - 1, VERTICAL_ADJUST * 2, VVISIBLE_NTSC * 2 - 1 - VERTICAL_ADJUST * 2);
268 		screen().configure(HTOTAL, VTOTAL_NTSC * 2, visible, HZ_TO_ATTOSECONDS(59.922743));
269 	}
270 	m_vblank_start = m_height - VERTICAL_SYNC - TOP_ERASE; /* Sync + top erase */
271 }
272 
273 
274 /*
275     Colorbus inputs
276     vdp will process mouse deltas only if it is in mouse mode
277     Reg 8: MS LP x x x x x x
278 */
colorbus_x_input(int mx_delta)279 void v99x8_device::colorbus_x_input(int mx_delta)
280 {
281 	if ((m_cont_reg[8] & 0xc0) == 0x80)
282 	{
283 		m_mx_delta += mx_delta;
284 		if (m_mx_delta < -127) m_mx_delta = -127;
285 		if (m_mx_delta > 127) m_mx_delta = 127;
286 	}
287 }
288 
colorbus_y_input(int my_delta)289 void v99x8_device::colorbus_y_input(int my_delta)
290 {
291 	if ((m_cont_reg[8] & 0xc0) == 0x80)
292 	{
293 		m_my_delta += my_delta;
294 		if (m_my_delta < -127) m_my_delta = -127;
295 		if (m_my_delta > 127) m_my_delta = 127;
296 	}
297 }
298 
colorbus_button_input(bool switch1_pressed,bool switch2_pressed)299 void v99x8_device::colorbus_button_input(bool switch1_pressed, bool switch2_pressed)
300 {
301 	// save button state
302 	m_button_state = (switch2_pressed? 0x80 : 0x00) | (switch1_pressed? 0x40 : 0x00);
303 }
304 
305 
306 /***************************************************************************
307 
308 Palette functions
309 
310 ***************************************************************************/
311 
312 /*
313 About the colour burst registers:
314 
315 The color burst registers will only have effect on the composite video output from
316 the V9938. but the output is only NTSC (Never The Same Color ,so the
317 effects are already present) . this system is not used in europe
318 the european machines use a separate PAL  (Phase Alternating Line) encoder
319 or no encoder at all , only RGB output.
320 
321 Erik de Boer.
322 
323 --
324 Right now they're not emulated. For completeness sake they should -- with
325 a dip-switch to turn them off. I really don't know how they work though. :(
326 */
327 
328 /*
329 In screen 8, the colors are encoded as:
330 
331 7  6  5  4  3  2  1  0
332 +--+--+--+--+--+--+--+--+
333 |g2|g1|g0|r2|r1|r0|b2|b1|
334 +--+--+--+--+--+--+--+--+
335 
336 b0 is set if b2 and b1 are set (remember, color bus is 3 bits)
337 
338 */
339 
palette_init()340 void v9938_device::palette_init()
341 {
342 }
343 
344 /*
345 
346 The v9958 can display up to 19286 colours. For this we need a larger palette.
347 
348 The colours are encoded in 17 bits; however there are just 19268 different colours.
349 Here we calculate the palette and a 2^17 reference table to the palette,
350 which is: s_pal_indYJK. It's 256K in size, but I can't think of a faster way
351 to emulate this. Also it keeps the palette a reasonable size. :)
352 
353 */
354 
355 uint32_t v99x8_device::s_pal_indYJK[0x20000];
356 
palette_init()357 void v9958_device::palette_init()
358 {
359 	int r,g,b,y,j,k,k0,j0;
360 
361 	// set up YJK table
362 	LOGMASKED(LOG_DETAIL, "Building YJK table for V9958 screens, may take a while ... \n");
363 
364 	for (y=0;y<32;y++) for (k=0;k<64;k++) for (j=0;j<64;j++)
365 	{
366 		// calculate the color
367 		if (k >= 32) k0 = (k - 64); else k0 = k;
368 		if (j >= 32) j0 = (j - 64); else j0 = j;
369 		r = y + j0;
370 		b = (y * 5 - 2 * j0 - k0) / 4;
371 		g = y + k0;
372 		if (r < 0) r = 0; else if (r > 31) r = 31;
373 		if (g < 0) g = 0; else if (g > 31) g = 31;
374 		if (b < 0) b = 0; else if (b > 31) b = 31;
375 
376 		v99x8_device::s_pal_indYJK[y | j << 5 | k << (5 + 6)] = uint32_t(rgb_t(pal5bit(r), pal5bit(g), pal5bit(b)));
377 	}
378 }
379 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)380 uint32_t v99x8_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
381 {
382 	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
383 	return 0;
384 }
385 
read(offs_t offset)386 uint8_t v99x8_device::read(offs_t offset)
387 {
388 	switch (offset & 3)
389 	{
390 	case 0: return vram_r();
391 	case 1: return status_r();
392 	}
393 	return 0xff;
394 }
395 
write(offs_t offset,uint8_t data)396 void v99x8_device::write(offs_t offset, uint8_t data)
397 {
398 	switch (offset & 3)
399 	{
400 	case 0: vram_w(data);       break;
401 	case 1: command_w(data);    break;
402 	case 2: palette_w(data);    break;
403 	case 3: register_w(data);   break;
404 	}
405 }
406 
vram_r()407 uint8_t v99x8_device::vram_r()
408 {
409 	uint8_t ret;
410 	int address;
411 
412 	address = ((int)m_cont_reg[14] << 14) | m_address_latch;
413 
414 	m_cmd_write_first = 0;
415 
416 	ret = m_read_ahead;
417 
418 	if (m_cont_reg[45] & 0x40)  // Expansion memory
419 	{
420 		if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
421 			address >>= 1;  // correct?
422 		// Expansion memory only offers 64 K
423 		if (m_vram_size > 0x20000 && ((address & 0x10000)==0))
424 			m_read_ahead = m_vram_space->read_byte(address + EXPMEM_OFFSET);
425 		else
426 			m_read_ahead = 0xff;
427 	}
428 	else
429 	{
430 		m_read_ahead = vram_read(address);
431 	}
432 
433 	m_address_latch = (m_address_latch + 1) & 0x3fff;
434 	if ((!m_address_latch) && (m_cont_reg[0] & 0x0c) ) // correct ???
435 	{
436 		m_cont_reg[14] = (m_cont_reg[14] + 1) & 7;
437 	}
438 
439 	return ret;
440 }
441 
status_r()442 uint8_t v99x8_device::status_r()
443 {
444 	int reg;
445 	uint8_t ret;
446 
447 	m_cmd_write_first = 0;
448 
449 	reg = m_cont_reg[15] & 0x0f;
450 	if (reg > 9)
451 		return 0xff;
452 
453 	switch (reg)
454 	{
455 	case 0:
456 		ret = m_stat_reg[0];
457 		m_stat_reg[0] &= 0x1f;
458 		break;
459 	case 1:
460 		ret = m_stat_reg[1];
461 		m_stat_reg[1] &= 0xfe;
462 		// mouse mode: add button state
463 		if ((m_cont_reg[8] & 0xc0) == 0x80)
464 			ret |= m_button_state & 0xc0;
465 		break;
466 	case 2:
467 		/*update_command ();*/
468 		/*
469 		WTF is this? Whatever this was intended to do, it is nonsensical.
470 		Might as well pick a random number....
471 		This was an attempt to emulate H-Blank flag ;)
472 		n = cycles_currently_ran ();
473 		if ( (n < 28) || (n > 199) ) vdp.statReg[2] |= 0x20;
474 		else vdp.statReg[2] &= ~0x20;
475 		*/
476 		if (machine().rand() & 1) m_stat_reg[2] |= 0x20;
477 		else m_stat_reg[2] &= ~0x20;
478 		ret = m_stat_reg[2];
479 		break;
480 	case 3:
481 		if ((m_cont_reg[8] & 0xc0) == 0x80)
482 		{   // mouse mode: return x mouse delta
483 			ret = m_mx_delta;
484 			m_mx_delta = 0;
485 		}
486 		else
487 			ret = m_stat_reg[3];
488 		break;
489 	case 5:
490 		if ((m_cont_reg[8] & 0xc0) == 0x80)
491 		{   // mouse mode: return y mouse delta
492 			ret = m_my_delta;
493 			m_my_delta = 0;
494 		}
495 		else
496 			ret = m_stat_reg[5];
497 		break;
498 	case 7:
499 		ret = m_stat_reg[7];
500 		m_stat_reg[7] = m_cont_reg[44] = vdp_to_cpu () ;
501 		break;
502 	default:
503 		ret = m_stat_reg[reg];
504 		break;
505 	}
506 
507 	LOGMASKED(LOG_STATUS, "Read %02x from S#%d\n", ret, reg);
508 	check_int ();
509 
510 	return ret;
511 }
512 
palette_w(uint8_t data)513 void v99x8_device::palette_w(uint8_t data)
514 {
515 	int indexp;
516 
517 	if (m_pal_write_first)
518 	{
519 		// store in register
520 		indexp = m_cont_reg[0x10] & 15;
521 		m_pal_reg[indexp*2] = m_pal_write & 0x77;
522 		m_pal_reg[indexp*2+1] = data & 0x07;
523 
524 		// update palette
525 		set_pen16(indexp, uint32_t(rgb_t(pal3bit((m_pal_write & 0x70) >> 4), pal3bit(data & 0x07), pal3bit(m_pal_write & 0x07))));
526 
527 		m_cont_reg[0x10] = (m_cont_reg[0x10] + 1) & 15;
528 		m_pal_write_first = 0;
529 	}
530 	else
531 	{
532 		m_pal_write = data;
533 		m_pal_write_first = 1;
534 	}
535 }
536 
vram_w(uint8_t data)537 void v99x8_device::vram_w(uint8_t data)
538 {
539 	int address;
540 
541 	/*update_command ();*/
542 
543 	m_cmd_write_first = 0;
544 
545 	address = ((int)m_cont_reg[14] << 14) | m_address_latch;
546 
547 	if (m_cont_reg[45] & 0x40)
548 	{
549 		if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
550 			address >>= 1;  // correct?
551 		if (m_vram_size > 0x20000 && ((address & 0x10000)==0))
552 			m_vram_space->write_byte(EXPMEM_OFFSET + address, data);
553 	}
554 	else
555 	{
556 		vram_write(address, data);
557 	}
558 
559 	m_address_latch = (m_address_latch + 1) & 0x3fff;
560 	if ((!m_address_latch) && (m_cont_reg[0] & 0x0c) ) // correct ???
561 	{
562 		m_cont_reg[14] = (m_cont_reg[14] + 1) & 7;
563 	}
564 }
565 
command_w(uint8_t data)566 void v99x8_device::command_w(uint8_t data)
567 {
568 	if (m_cmd_write_first)
569 	{
570 		if (data & 0x80)
571 		{
572 			if (!(data & 0x40))
573 				register_write (data & 0x3f, m_cmd_write);
574 		}
575 		else
576 		{
577 			m_address_latch =
578 			(((uint16_t)data << 8) | m_cmd_write) & 0x3fff;
579 			if ( !(data & 0x40) ) vram_r (); // read ahead!
580 		}
581 
582 		m_cmd_write_first = 0;
583 	}
584 	else
585 	{
586 		m_cmd_write = data;
587 		m_cmd_write_first = 1;
588 	}
589 }
590 
register_w(uint8_t data)591 void v99x8_device::register_w(uint8_t data)
592 {
593 	int reg;
594 
595 	reg = m_cont_reg[17] & 0x3f;
596 	if (reg != 17)
597 		register_write(reg, data); // true ?
598 
599 	if (!(m_cont_reg[17] & 0x80))
600 		m_cont_reg[17] = (m_cont_reg[17] + 1) & 0x3f;
601 }
602 
603 /***************************************************************************
604 
605     Init/stop/reset/Interrupt functions
606 
607 ***************************************************************************/
608 
device_start()609 void v99x8_device::device_start()
610 {
611 	m_int_callback.resolve_safe();
612 	m_vdp_ops_count = 1;
613 	m_vdp_engine = nullptr;
614 
615 	screen().register_screen_bitmap(m_bitmap);
616 
617 	// Video RAM is allocated as an own address space
618 	m_vram_space = &space(AS_DATA);
619 
620 	// allocate VRAM
621 	assert(m_vram_size > 0);
622 
623 	if (m_vram_size < 0x20000)
624 	{
625 		// set unavailable RAM to 0xff
626 		for (int addr = m_vram_size; addr < 0x30000; addr++) m_vram_space->write_byte(addr, 0xff);
627 	}
628 
629 	m_line_timer = timer_alloc(TIMER_LINE);
630 
631 	palette_init();
632 
633 	save_item(NAME(m_offset_x));
634 	save_item(NAME(m_offset_y));
635 	save_item(NAME(m_visible_y));
636 	save_item(NAME(m_mode));
637 	save_item(NAME(m_pal_write_first));
638 	save_item(NAME(m_cmd_write_first));
639 	save_item(NAME(m_pal_write));
640 	save_item(NAME(m_cmd_write));
641 	save_item(NAME(m_pal_reg));
642 	save_item(NAME(m_stat_reg));
643 	save_item(NAME(m_cont_reg));
644 	save_item(NAME(m_read_ahead));
645 	save_item(NAME(m_v9958_sp_mode));
646 	save_item(NAME(m_address_latch));
647 	save_item(NAME(m_int_state));
648 	save_item(NAME(m_scanline));
649 	save_item(NAME(m_blink));
650 	save_item(NAME(m_blink_count));
651 	save_item(NAME(m_mx_delta));
652 	save_item(NAME(m_my_delta));
653 	save_item(NAME(m_button_state));
654 	save_item(NAME(m_mmc.SX));
655 	save_item(NAME(m_mmc.SY));
656 	save_item(NAME(m_mmc.DX));
657 	save_item(NAME(m_mmc.DY));
658 	save_item(NAME(m_mmc.TX));
659 	save_item(NAME(m_mmc.TY));
660 	save_item(NAME(m_mmc.NX));
661 	save_item(NAME(m_mmc.NY));
662 	save_item(NAME(m_mmc.MX));
663 	save_item(NAME(m_mmc.ASX));
664 	save_item(NAME(m_mmc.ADX));
665 	save_item(NAME(m_mmc.ANX));
666 	save_item(NAME(m_mmc.CL));
667 	save_item(NAME(m_mmc.LO));
668 	save_item(NAME(m_mmc.CM));
669 	save_item(NAME(m_mmc.MXS));
670 	save_item(NAME(m_mmc.MXD));
671 	save_item(NAME(m_vdp_ops_count));
672 	save_item(NAME(m_pal_ntsc));
673 	save_item(NAME(m_scanline_start));
674 	save_item(NAME(m_vblank_start));
675 	save_item(NAME(m_scanline_max));
676 	save_item(NAME(m_height));
677 }
678 
device_reset()679 void v99x8_device::device_reset()
680 {
681 	int i;
682 
683 	// offset reset
684 	m_offset_x = 8;
685 	m_offset_y = 0;
686 	m_visible_y = 192;
687 	// register reset
688 	reset_palette (); // palette registers
689 	for (i=0;i<10;i++) m_stat_reg[i] = 0;
690 	m_stat_reg[2] = 0x0c;
691 	if (m_model == MODEL_V9958) m_stat_reg[1] |= 4;
692 	for (i=0;i<48;i++) m_cont_reg[i] = 0;
693 	m_cmd_write_first = m_pal_write_first = 0;
694 	m_int_state = 0;
695 	m_read_ahead = 0; m_address_latch = 0; // ???
696 	m_scanline = 0;
697 	// MZ: The status registers 4 and 6 hold the high bits of the sprite
698 	// collision location. The unused bits are set to 1.
699 	// SR3: x x x x x x x x
700 	// SR4: 1 1 1 1 1 1 1 x
701 	// SR5: y y y y y y y y
702 	// SR6: 1 1 1 1 1 1 y y
703 	// Note that status register 4 is used in detection algorithms to tell
704 	// apart the tms9929 from the v99x8.
705 
706 	// TODO: SR3-S6 do not yet store the information about the sprite collision
707 	m_stat_reg[4] = 0xfe;
708 	m_stat_reg[6] = 0xfc;
709 
710 	// Start the timer
711 	m_line_timer->adjust(attotime::from_ticks(HTOTAL*2, m_clock), 0, attotime::from_ticks(HTOTAL*2, m_clock));
712 
713 	configure_pal_ntsc();
714 	set_screen_parameters();
715 }
716 
reset_palette()717 void v99x8_device::reset_palette()
718 {
719 	// taken from V9938 Technical Data book, page 148. it's in G-R-B format
720 	static const uint8_t pal16[16*3] = {
721 		0, 0, 0, // 0: black/transparent
722 		0, 0, 0, // 1: black
723 		6, 1, 1, // 2: medium green
724 		7, 3, 3, // 3: light green
725 		1, 1, 7, // 4: dark blue
726 		3, 2, 7, // 5: light blue
727 		1, 5, 1, // 6: dark red
728 		6, 2, 7, // 7: cyan
729 		1, 7, 1, // 8: medium red
730 		3, 7, 3, // 9: light red
731 		6, 6, 1, // 10: dark yellow
732 		6, 6, 4, // 11: light yellow
733 		4, 1, 1, // 12: dark green
734 		2, 6, 5, // 13: magenta
735 		5, 5, 5, // 14: gray
736 		7, 7, 7  // 15: white
737 	};
738 	int i, red;
739 
740 	for (i=0;i<16;i++)
741 	{
742 		// set the palette registers
743 		m_pal_reg[i*2+0] = pal16[i*3+1] << 4 | pal16[i*3+2];
744 		m_pal_reg[i*2+1] = pal16[i*3];
745 		// set the reference table
746 		set_pen16(i, uint32_t(rgb_t(pal3bit(pal16[i*3+1]), pal3bit(pal16[i*3]), pal3bit(pal16[i*3+2]))));
747 	}
748 
749 	// set internal palette GRAPHIC 7
750 	for (i=0;i<256;i++)
751 	{
752 		red = (i << 1) & 6; if (red == 6) red++;
753 
754 		set_pen256(i, uint32_t(rgb_t(pal3bit((i & 0x1c) >> 2), pal3bit((i & 0xe0) >> 5), pal3bit(red))));
755 	}
756 }
757 
758 /***************************************************************************
759 
760 Memory functions
761 
762 ***************************************************************************/
763 
vram_write(int offset,int data)764 void v99x8_device::vram_write(int offset, int data)
765 {
766 	int newoffset;
767 
768 	if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
769 	{
770 		newoffset = ((offset & 1) << 16) | (offset >> 1);
771 		if (newoffset < m_vram_size)
772 			m_vram_space->write_byte(newoffset, data);
773 	}
774 	else
775 	{
776 		if (offset < m_vram_size)
777 			m_vram_space->write_byte(offset, data);
778 	}
779 }
780 
vram_read(int offset)781 int v99x8_device::vram_read(int offset)
782 {
783 	if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
784 		return m_vram_space->read_byte(((offset & 1) << 16) | (offset >> 1));
785 	else
786 		return m_vram_space->read_byte(offset);
787 }
788 
check_int()789 void v99x8_device::check_int()
790 {
791 	uint8_t n;
792 
793 	n = ( (m_cont_reg[1] & 0x20) && (m_stat_reg[0] & 0x80) /*&& m_vblank_int*/) ||
794 	( (m_stat_reg[1] & 0x01) && (m_cont_reg[0] & 0x10) );
795 
796 	#if 0
797 	if(n && m_vblank_int)
798 	{
799 		m_vblank_int = 0;
800 	}
801 	#endif
802 
803 	if (n != m_int_state)
804 	{
805 		m_int_state = n;
806 		LOGMASKED(LOG_INT, "IRQ line %s\n", n ? "up" : "down");
807 	}
808 
809 	/*
810 	** Somehow the IRQ request is going down without cpu_irq_line () being
811 	** called; because of this Mr. Ghost, Xevious and SD Snatcher don't
812 	** run. As a patch it's called every scanline
813 	*/
814 	m_int_callback(n);
815 }
816 
817 /***************************************************************************
818 
819     Register functions
820 
821 ***************************************************************************/
822 
register_write(int reg,int data)823 void v99x8_device::register_write (int reg, int data)
824 {
825 	static uint8_t const reg_mask[] =
826 	{
827 		0x7e, 0x7b, 0x7f, 0xff, 0x3f, 0xff, 0x3f, 0xff,
828 		0xfb, 0xbf, 0x07, 0x03, 0xff, 0xff, 0x07, 0x0f,
829 		0x0f, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
830 		0x00, 0x7f, 0x3f, 0x07
831 	};
832 
833 	if (reg <= 27)
834 	{
835 		data &= reg_mask[reg];
836 		if (m_cont_reg[reg] == data)
837 			return;
838 	}
839 
840 	if (reg > 46)
841 	{
842 		LOGMASKED(LOG_WARN, "Attempted to write to non-existent R#%d\n", reg);
843 		return;
844 	}
845 
846 	/*update_command ();*/
847 
848 	switch (reg) {
849 		// registers that affect interrupt and display mode
850 	case 0:
851 	case 1:
852 		m_cont_reg[reg] = data;
853 		set_mode();
854 		check_int();
855 		LOGMASKED(LOG_MODE, "Mode = %s\n", v9938_modes[m_mode]);
856 		break;
857 
858 	case 18:
859 	case 9:
860 		m_cont_reg[reg] = data;
861 		// recalc offset
862 		m_offset_x = 8 + position_offset(m_cont_reg[18] & 0x0f);
863 		// Y offset is only applied once per frame?
864 		break;
865 
866 	case 15:
867 		m_pal_write_first = 0;
868 		break;
869 
870 		// color burst registers aren't emulated
871 	case 20:
872 	case 21:
873 	case 22:
874 		LOGMASKED(LOG_NOTIMP, "Write %02xh to R#%d; color burst not emulated\n", data, reg);
875 		break;
876 	case 25:
877 	case 26:
878 	case 27:
879 		if (m_model != MODEL_V9958)
880 		{
881 			LOGMASKED(LOG_WARN, "Attempting to write %02xh to R#%d (invalid on v9938)\n", data, reg);
882 			data = 0;
883 		}
884 		else
885 		{
886 			if(reg == 25)
887 				m_v9958_sp_mode = data & 0x18;
888 		}
889 		break;
890 
891 	case 44:
892 		cpu_to_vdp (data);
893 		break;
894 
895 	case 46:
896 		command_unit_w (data);
897 		break;
898 	}
899 
900 	if (reg != 15)
901 		LOGMASKED(LOG_REGWRITE, "Write %02x to R#%d\n", data, reg);
902 
903 	m_cont_reg[reg] = data;
904 }
905 
906 /***************************************************************************
907 
908 Refresh / render function
909 
910 ***************************************************************************/
911 
v9938_second_field()912 inline bool v99x8_device::v9938_second_field()
913 {
914 	return !(((m_cont_reg[9] & 0x04) && !(m_stat_reg[2] & 2)) || m_blink);
915 }
916 
917 
default_border(uint32_t * ln)918 void v99x8_device::default_border(uint32_t *ln)
919 {
920 	pen_t pen;
921 	int i;
922 
923 	pen = pen16(m_cont_reg[7] & 0x0f);
924 	i = V9938_LONG_WIDTH;
925 	while (i--) *ln++ = pen;
926 }
927 
graphic7_border(uint32_t * ln)928 void v99x8_device::graphic7_border(uint32_t *ln)
929 {
930 	pen_t pen;
931 	int i;
932 
933 	pen = pen256(m_cont_reg[7]);
934 	i = V9938_LONG_WIDTH;
935 	while (i--) *ln++ = pen;
936 }
937 
graphic5_border(uint32_t * ln)938 void v99x8_device::graphic5_border(uint32_t *ln)
939 {
940 	int i;
941 	pen_t pen0;
942 	pen_t pen1;
943 
944 	pen1 = pen16(m_cont_reg[7] & 0x03);
945 	pen0 = pen16((m_cont_reg[7] >> 2) & 0x03);
946 	i = V9938_LONG_WIDTH / 2;
947 	while (i--) { *ln++ = pen0; *ln++ = pen1; }
948 }
949 
mode_text1(uint32_t * ln,int line)950 void v99x8_device::mode_text1(uint32_t *ln, int line)
951 {
952 	int pattern, x, xx, name, xxx;
953 	pen_t fg, bg, pen;
954 	int nametbl_addr, patterntbl_addr;
955 
956 	patterntbl_addr = m_cont_reg[4] << 11;
957 	nametbl_addr = m_cont_reg[2] << 10;
958 
959 	fg = pen16(m_cont_reg[7] >> 4);
960 	bg = pen16(m_cont_reg[7] & 15);
961 
962 	name = (line/8)*40;
963 
964 	pen = pen16(m_cont_reg[7] & 0x0f);
965 
966 	xxx = (m_offset_x + 8) * 2;
967 	while (xxx--) *ln++ = pen;
968 
969 	for (x=0;x<40;x++)
970 	{
971 		pattern = m_vram_space->read_byte(patterntbl_addr + (m_vram_space->read_byte(nametbl_addr + name) * 8) +
972 			((line + m_cont_reg[23]) & 7));
973 		for (xx=0;xx<6;xx++)
974 		{
975 			*ln++ = (pattern & 0x80) ? fg : bg;
976 			*ln++ = (pattern & 0x80) ? fg : bg;
977 			pattern <<= 1;
978 		}
979 		/* width height 212, characters start repeating at the bottom */
980 		name = (name + 1) & 0x3ff;
981 	}
982 
983 	xxx = ((16 - m_offset_x) + 8) * 2;
984 	while (xxx--) *ln++ = pen;
985 }
986 
mode_text2(uint32_t * ln,int line)987 void v99x8_device::mode_text2(uint32_t *ln, int line)
988 {
989 	int pattern, x, charcode, name, xxx, patternmask, colourmask;
990 	pen_t fg, bg, fg0, bg0, pen;
991 	int nametbl_addr, patterntbl_addr, colourtbl_addr;
992 
993 	patterntbl_addr = m_cont_reg[4] << 11;
994 	colourtbl_addr =  ((m_cont_reg[3] & 0xf8) << 6) + (m_cont_reg[10] << 14);
995 	#if 0
996 	colourmask = ((m_cont_reg[3] & 7) << 5) | 0x1f; /* cause a bug in Forth+ v1.0 on Geneve */
997 	#else
998 	colourmask = ((m_cont_reg[3] & 7) << 6) | 0x3f; /* verify! */
999 	#endif
1000 	nametbl_addr = ((m_cont_reg[2] & 0xfc) << 10);
1001 	patternmask = ((m_cont_reg[2] & 3) << 10) | 0x3ff; /* seems correct */
1002 
1003 	fg = pen16(m_cont_reg[7] >> 4);
1004 	bg = pen16(m_cont_reg[7] & 15);
1005 	fg0 = pen16(m_cont_reg[12] >> 4);
1006 	bg0 = pen16(m_cont_reg[12] & 15);
1007 
1008 	name = (line/8)*80;
1009 
1010 	xxx = (m_offset_x + 8) * 2;
1011 	pen = pen16(m_cont_reg[7] & 0x0f);
1012 	while (xxx--) *ln++ = pen;
1013 
1014 	for (x=0;x<80;x++)
1015 	{
1016 		charcode = m_vram_space->read_byte(nametbl_addr + (name&patternmask));
1017 		if (m_blink)
1018 		{
1019 			pattern = m_vram_space->read_byte(colourtbl_addr + ((name/8)&colourmask));
1020 			if (pattern & (0x80 >> (name & 7) ) )
1021 			{
1022 				pattern = m_vram_space->read_byte(patterntbl_addr + ((charcode * 8) +
1023 					((line + m_cont_reg[23]) & 7)));
1024 
1025 				*ln++ = (pattern & 0x80) ? fg0 : bg0;
1026 				*ln++ = (pattern & 0x40) ? fg0 : bg0;
1027 				*ln++ = (pattern & 0x20) ? fg0 : bg0;
1028 				*ln++ = (pattern & 0x10) ? fg0 : bg0;
1029 				*ln++ = (pattern & 0x08) ? fg0 : bg0;
1030 				*ln++ = (pattern & 0x04) ? fg0 : bg0;
1031 
1032 				name++;
1033 				continue;
1034 			}
1035 		}
1036 
1037 		pattern = m_vram_space->read_byte(patterntbl_addr + ((charcode * 8) +
1038 			((line + m_cont_reg[23]) & 7)));
1039 
1040 		*ln++ = (pattern & 0x80) ? fg : bg;
1041 		*ln++ = (pattern & 0x40) ? fg : bg;
1042 		*ln++ = (pattern & 0x20) ? fg : bg;
1043 		*ln++ = (pattern & 0x10) ? fg : bg;
1044 		*ln++ = (pattern & 0x08) ? fg : bg;
1045 		*ln++ = (pattern & 0x04) ? fg : bg;
1046 
1047 		name++;
1048 	}
1049 
1050 	xxx = (16 - m_offset_x + 8) * 2;
1051 	while (xxx--) *ln++ = pen;
1052 }
1053 
mode_multi(uint32_t * ln,int line)1054 void v99x8_device::mode_multi(uint32_t *ln, int line)
1055 {
1056 	int nametbl_addr, patterntbl_addr, colour;
1057 	int name, line2, x, xx;
1058 	pen_t pen, pen_bg;
1059 
1060 	nametbl_addr = (m_cont_reg[2] << 10);
1061 	patterntbl_addr = (m_cont_reg[4] << 11);
1062 
1063 	line2 = (line - m_cont_reg[23]) & 255;
1064 	name = (line2/8)*32;
1065 
1066 	pen_bg = pen16(m_cont_reg[7] & 0x0f);
1067 	xx = m_offset_x * 2;
1068 	while (xx--) *ln++ = pen_bg;
1069 
1070 	for (x=0;x<32;x++)
1071 	{
1072 		colour = m_vram_space->read_byte(patterntbl_addr + (m_vram_space->read_byte(nametbl_addr + name) * 8) + ((line2/4)&7));
1073 		pen = pen16(colour >> 4);
1074 		/* eight pixels */
1075 		*ln++ = pen;
1076 		*ln++ = pen;
1077 		*ln++ = pen;
1078 		*ln++ = pen;
1079 		*ln++ = pen;
1080 		*ln++ = pen;
1081 		*ln++ = pen;
1082 		*ln++ = pen;
1083 		pen = pen16(colour & 15);
1084 		/* eight pixels */
1085 		*ln++ = pen;
1086 		*ln++ = pen;
1087 		*ln++ = pen;
1088 		*ln++ = pen;
1089 		*ln++ = pen;
1090 		*ln++ = pen;
1091 		*ln++ = pen;
1092 		*ln++ = pen;
1093 		name++;
1094 	}
1095 
1096 	xx = (16 - m_offset_x) * 2;
1097 	while (xx--) *ln++ = pen_bg;
1098 }
1099 
mode_graphic1(uint32_t * ln,int line)1100 void v99x8_device::mode_graphic1(uint32_t *ln, int line)
1101 {
1102 	pen_t fg, bg, pen;
1103 	int nametbl_addr, patterntbl_addr, colourtbl_addr;
1104 	int pattern, x, xx, line2, name, charcode, colour, xxx;
1105 
1106 	nametbl_addr = (m_cont_reg[2] << 10);
1107 	colourtbl_addr = (m_cont_reg[3] << 6) + (m_cont_reg[10] << 14);
1108 	patterntbl_addr = (m_cont_reg[4] << 11);
1109 
1110 	line2 = (line - m_cont_reg[23]) & 255;
1111 
1112 	name = (line2/8)*32;
1113 
1114 	pen = pen16(m_cont_reg[7] & 0x0f);
1115 	xxx = m_offset_x * 2;
1116 	while (xxx--) *ln++ = pen;
1117 
1118 	for (x=0;x<32;x++)
1119 	{
1120 		charcode = m_vram_space->read_byte(nametbl_addr + name);
1121 		colour = m_vram_space->read_byte(colourtbl_addr + charcode/8);
1122 		fg = pen16(colour >> 4);
1123 		bg = pen16(colour & 15);
1124 		pattern = m_vram_space->read_byte(patterntbl_addr + (charcode * 8 + (line2 & 7)));
1125 
1126 		for (xx=0;xx<8;xx++)
1127 		{
1128 			*ln++ = (pattern & 0x80) ? fg : bg;
1129 			*ln++ = (pattern & 0x80) ? fg : bg;
1130 			pattern <<= 1;
1131 		}
1132 		name++;
1133 	}
1134 
1135 	xx = (16 - m_offset_x) * 2;
1136 	while (xx--) *ln++ = pen;
1137 }
1138 
mode_graphic23(uint32_t * ln,int line)1139 void v99x8_device::mode_graphic23(uint32_t *ln, int line)
1140 {
1141 	pen_t fg, bg, pen;
1142 	int nametbl_addr, patterntbl_addr, colourtbl_addr;
1143 	int pattern, x, xx, line2, name, charcode,
1144 	colour, colourmask, patternmask, xxx;
1145 
1146 	colourmask = ((m_cont_reg[3] & 0x7f) * 8) | 7;
1147 	patternmask = ((m_cont_reg[4] & 0x03) * 256) | 0xff;
1148 
1149 	nametbl_addr =  (m_cont_reg[2] << 10);
1150 	colourtbl_addr =  ((m_cont_reg[3] & 0x80) << 6) + (m_cont_reg[10] << 14);
1151 	patterntbl_addr = ((m_cont_reg[4] & 0x3c) << 11);
1152 
1153 	line2 = (line + m_cont_reg[23]) & 255;
1154 	name = (line2/8)*32;
1155 
1156 	pen = pen16(m_cont_reg[7] & 0x0f);
1157 	xxx = m_offset_x * 2;
1158 	while (xxx--) *ln++ = pen;
1159 
1160 	for (x=0;x<32;x++)
1161 	{
1162 		charcode = m_vram_space->read_byte(nametbl_addr + name) + (line2&0xc0)*4;
1163 		colour = m_vram_space->read_byte(colourtbl_addr + ((charcode&colourmask)*8+(line2&7)));
1164 		pattern = m_vram_space->read_byte(patterntbl_addr + ((charcode&patternmask)*8+(line2&7)));
1165 		fg = pen16(colour >> 4);
1166 		bg = pen16(colour & 15);
1167 		for (xx=0;xx<8;xx++)
1168 		{
1169 			*ln++ = (pattern & 0x80) ? fg : bg;
1170 			*ln++ = (pattern & 0x80) ? fg : bg;
1171 			pattern <<= 1;
1172 		}
1173 		name++;
1174 	}
1175 
1176 	xx = (16 - m_offset_x) * 2;
1177 	while (xx--) *ln++ = pen;
1178 }
1179 
mode_graphic4(uint32_t * ln,int line)1180 void v99x8_device::mode_graphic4(uint32_t *ln, int line)
1181 {
1182 	int nametbl_addr, colour;
1183 	int line2, linemask, x, xx;
1184 	pen_t pen, pen_bg;
1185 
1186 	linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;
1187 
1188 	line2 = ((line + m_cont_reg[23]) & linemask) & 255;
1189 
1190 	nametbl_addr = ((m_cont_reg[2] & 0x40) << 10) + line2 * 128;
1191 	if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
1192 		nametbl_addr += 0x8000;
1193 
1194 	pen_bg = pen16(m_cont_reg[7] & 0x0f);
1195 	xx = m_offset_x * 2;
1196 	while (xx--) *ln++ = pen_bg;
1197 
1198 	for (x=0;x<128;x++)
1199 	{
1200 		colour = m_vram_space->read_byte(nametbl_addr++);
1201 		pen = pen16(colour >> 4);
1202 		*ln++ = pen;
1203 		*ln++ = pen;
1204 		pen = pen16(colour & 15);
1205 		*ln++ = pen;
1206 		*ln++ = pen;
1207 	}
1208 
1209 	xx = (16 - m_offset_x) * 2;
1210 	while (xx--) *ln++ = pen_bg;
1211 }
1212 
mode_graphic5(uint32_t * ln,int line)1213 void v99x8_device::mode_graphic5(uint32_t *ln, int line)
1214 {
1215 	int nametbl_addr, colour;
1216 	int line2, linemask, x, xx;
1217 	pen_t pen_bg0[4];
1218 	pen_t pen_bg1[4];
1219 
1220 	linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;
1221 
1222 	line2 = ((line + m_cont_reg[23]) & linemask) & 255;
1223 
1224 	nametbl_addr = ((m_cont_reg[2] & 0x40) << 10) + line2 * 128;
1225 	if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
1226 		nametbl_addr += 0x8000;
1227 
1228 	pen_bg1[0] = pen16(m_cont_reg[7] & 0x03);
1229 	pen_bg0[0] = pen16((m_cont_reg[7] >> 2) & 0x03);
1230 
1231 	xx = m_offset_x;
1232 	while (xx--) { *ln++ = pen_bg0[0]; *ln++ = pen_bg1[0]; }
1233 
1234 	x = (m_cont_reg[8] & 0x20) ? 0 : 1;
1235 
1236 	for (;x<4;x++)
1237 	{
1238 		pen_bg0[x] = pen16(x);
1239 		pen_bg1[x] = pen16(x);
1240 	}
1241 
1242 	for (x=0;x<128;x++)
1243 	{
1244 		colour = m_vram_space->read_byte(nametbl_addr++);
1245 
1246 		*ln++ = pen_bg0[colour>>6];
1247 		*ln++ = pen_bg1[(colour>>4)&3];
1248 		*ln++ = pen_bg0[(colour>>2)&3];
1249 		*ln++ = pen_bg1[(colour&3)];
1250 	}
1251 
1252 	pen_bg1[0] = pen16(m_cont_reg[7] & 0x03);
1253 	pen_bg0[0] = pen16((m_cont_reg[7] >> 2) & 0x03);
1254 	xx = 16 - m_offset_x;
1255 	while (xx--) { *ln++ = pen_bg0[0]; *ln++ = pen_bg1[0]; }
1256 }
1257 
mode_graphic6(uint32_t * ln,int line)1258 void v99x8_device::mode_graphic6(uint32_t *ln, int line)
1259 {
1260 	uint8_t colour;
1261 	int line2, linemask, x, xx, nametbl_addr;
1262 	pen_t pen_bg, fg0;
1263 	pen_t fg1;
1264 
1265 	linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;
1266 
1267 	line2 = ((line + m_cont_reg[23]) & linemask) & 255;
1268 
1269 	nametbl_addr = line2 << 8 ;
1270 	if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
1271 		nametbl_addr += 0x10000;
1272 
1273 	pen_bg = pen16(m_cont_reg[7] & 0x0f);
1274 	xx = m_offset_x * 2;
1275 	while (xx--) *ln++ = pen_bg;
1276 
1277 	if (m_cont_reg[2] & 0x40)
1278 	{
1279 		for (x=0;x<32;x++)
1280 		{
1281 			nametbl_addr++;
1282 			colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1283 			fg0 = pen16(colour >> 4);
1284 			fg1 = pen16(colour & 15);
1285 			*ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
1286 			*ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
1287 			*ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
1288 			*ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
1289 			nametbl_addr += 7;
1290 		}
1291 	}
1292 	else
1293 	{
1294 		for (x=0;x<256;x++)
1295 		{
1296 			colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1297 			*ln++ = pen16(colour >> 4);
1298 			*ln++ = pen16(colour & 15);
1299 			nametbl_addr++;
1300 		}
1301 	}
1302 
1303 	xx = (16 - m_offset_x) * 2;
1304 	while (xx--) *ln++ = pen_bg;
1305 }
1306 
mode_graphic7(uint32_t * ln,int line)1307 void v99x8_device::mode_graphic7(uint32_t *ln, int line)
1308 {
1309 	uint8_t colour;
1310 	int line2, linemask, x, xx, nametbl_addr;
1311 	pen_t pen, pen_bg;
1312 
1313 	linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;
1314 
1315 	line2 = ((line + m_cont_reg[23]) & linemask) & 255;
1316 
1317 	nametbl_addr = line2 << 8;
1318 	if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
1319 		nametbl_addr += 0x10000;
1320 
1321 	pen_bg = pen256(m_cont_reg[7]);
1322 	xx = m_offset_x * 2;
1323 	while (xx--) *ln++ = pen_bg;
1324 
1325 	if ((m_v9958_sp_mode & 0x18) == 0x08) // v9958 screen 12, puzzle star title screen
1326 	{
1327 		for (x=0;x<64;x++)
1328 		{
1329 			int colour[4];
1330 			int ind;
1331 
1332 			colour[0] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1333 			nametbl_addr++;
1334 			colour[1] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1335 			nametbl_addr++;
1336 			colour[2] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1337 			nametbl_addr++;
1338 			colour[3] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1339 
1340 			ind = (colour[0] & 7) << 11 | (colour[1] & 7) << 14 |
1341 			(colour[2] & 7) << 5 | (colour[3] & 7) << 8;
1342 
1343 			*ln++ = s_pal_indYJK[ind | ((colour[0] >> 3) & 31)];
1344 			*ln++ = s_pal_indYJK[ind | ((colour[0] >> 3) & 31)];
1345 
1346 			*ln++ = s_pal_indYJK[ind | ((colour[1] >> 3) & 31)];
1347 			*ln++ = s_pal_indYJK[ind | ((colour[1] >> 3) & 31)];
1348 
1349 			*ln++ = s_pal_indYJK[ind | ((colour[2] >> 3) & 31)];
1350 			*ln++ = s_pal_indYJK[ind | ((colour[2] >> 3) & 31)];
1351 
1352 			*ln++ = s_pal_indYJK[ind | ((colour[3] >> 3) & 31)];
1353 			*ln++ = s_pal_indYJK[ind | ((colour[3] >> 3) & 31)];
1354 
1355 			nametbl_addr++;
1356 		}
1357 	}
1358 	else if ((m_v9958_sp_mode & 0x18) == 0x18) // v9958 screen 10/11, puzzle star & sexy boom gameplay
1359 	{
1360 		for (x=0;x<64;x++)
1361 		{
1362 			int colour[4];
1363 			int ind;
1364 
1365 			colour[0] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1366 			nametbl_addr++;
1367 			colour[1] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1368 			nametbl_addr++;
1369 			colour[2] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1370 			nametbl_addr++;
1371 			colour[3] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1372 
1373 			ind = (colour[0] & 7) << 11 | (colour[1] & 7) << 14 |
1374 			(colour[2] & 7) << 5 | (colour[3] & 7) << 8;
1375 
1376 			*ln++ = colour[0] & 8 ? pen16(colour[0] >> 4) : s_pal_indYJK[ind | ((colour[0] >> 3) & 30)];
1377 			*ln++ = colour[0] & 8 ? pen16(colour[0] >> 4) : s_pal_indYJK[ind | ((colour[0] >> 3) & 30)];
1378 
1379 			*ln++ = colour[1] & 8 ? pen16(colour[1] >> 4) : s_pal_indYJK[ind | ((colour[1] >> 3) & 30)];
1380 			*ln++ = colour[1] & 8 ? pen16(colour[1] >> 4) : s_pal_indYJK[ind | ((colour[1] >> 3) & 30)];
1381 
1382 			*ln++ = colour[2] & 8 ? pen16(colour[2] >> 4) : s_pal_indYJK[ind | ((colour[2] >> 3) & 30)];
1383 			*ln++ = colour[2] & 8 ? pen16(colour[2] >> 4) : s_pal_indYJK[ind | ((colour[2] >> 3) & 30)];
1384 
1385 			*ln++ = colour[3] & 8 ? pen16(colour[3] >> 4) : s_pal_indYJK[ind | ((colour[3] >> 3) & 30)];
1386 			*ln++ = colour[3] & 8 ? pen16(colour[3] >> 4) : s_pal_indYJK[ind | ((colour[3] >> 3) & 30)];
1387 
1388 			nametbl_addr++;
1389 		}
1390 	}
1391 	else if (m_cont_reg[2] & 0x40)
1392 	{
1393 		for (x=0;x<32;x++)
1394 		{
1395 			nametbl_addr++;
1396 			colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1397 			pen = pen256(colour);
1398 			*ln++ = pen; *ln++ = pen;
1399 			*ln++ = pen; *ln++ = pen;
1400 			*ln++ = pen; *ln++ = pen;
1401 			*ln++ = pen; *ln++ = pen;
1402 			*ln++ = pen; *ln++ = pen;
1403 			*ln++ = pen; *ln++ = pen;
1404 			*ln++ = pen; *ln++ = pen;
1405 			*ln++ = pen; *ln++ = pen;
1406 			nametbl_addr++;
1407 		}
1408 	}
1409 	else
1410 	{
1411 		for (x=0;x<256;x++)
1412 		{
1413 			colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1414 			pen = pen256(colour);
1415 			*ln++ = pen;
1416 			*ln++ = pen;
1417 			nametbl_addr++;
1418 		}
1419 	}
1420 
1421 	xx = (16 - m_offset_x) * 2;
1422 	while (xx--) *ln++ = pen_bg;
1423 }
1424 
mode_unknown(uint32_t * ln,int line)1425 void v99x8_device::mode_unknown(uint32_t *ln, int line)
1426 {
1427 	pen_t fg, bg;
1428 	int x;
1429 
1430 	fg = pen16(m_cont_reg[7] >> 4);
1431 	bg = pen16(m_cont_reg[7] & 15);
1432 
1433 	x = m_offset_x * 2;
1434 	while (x--) *ln++ = bg;
1435 
1436 	x = 512;
1437 	while (x--) *ln++ = fg;
1438 
1439 	x = (16 - m_offset_x) * 2;
1440 	while (x--) *ln++ = bg;
1441 }
1442 
default_draw_sprite(uint32_t * ln,uint8_t * col)1443 void v99x8_device::default_draw_sprite(uint32_t *ln, uint8_t *col)
1444 {
1445 	int i;
1446 	ln += m_offset_x * 2;
1447 
1448 	for (i=0;i<256;i++)
1449 	{
1450 		if (col[i] & 0x80)
1451 		{
1452 			*ln++ = pen16(col[i] & 0x0f);
1453 			*ln++ = pen16(col[i] & 0x0f);
1454 		}
1455 		else
1456 		{
1457 			ln += 2;
1458 		}
1459 	}
1460 }
1461 
graphic5_draw_sprite(uint32_t * ln,uint8_t * col)1462 void v99x8_device::graphic5_draw_sprite(uint32_t *ln, uint8_t *col)
1463 {
1464 	int i;
1465 	ln += m_offset_x * 2;
1466 
1467 	for (i=0;i<256;i++)
1468 	{
1469 		if (col[i] & 0x80)
1470 		{
1471 			*ln++ = pen16((col[i] >> 2) & 0x03);
1472 			*ln++ = pen16(col[i] & 0x03);
1473 		}
1474 		else
1475 		{
1476 			ln += 2;
1477 		}
1478 	}
1479 }
1480 
1481 
graphic7_draw_sprite(uint32_t * ln,uint8_t * col)1482 void v99x8_device::graphic7_draw_sprite(uint32_t *ln, uint8_t *col)
1483 {
1484 	static const uint16_t g7_ind16[16] = {
1485 		0, 2, 192, 194, 48, 50, 240, 242,
1486 	482, 7, 448, 455, 56, 63, 504, 511  };
1487 	int i;
1488 
1489 	ln += m_offset_x * 2;
1490 
1491 	for (i=0;i<256;i++)
1492 	{
1493 		if (col[i] & 0x80)
1494 		{
1495 			rgb_t color = rgb_t(pal3bit(g7_ind16[col[i] & 0x0f] >> 6), pal3bit(g7_ind16[col[i] & 0x0f] >> 3), pal3bit(g7_ind16[col[i] & 0x0f]));
1496 			*ln++ = uint32_t(color);
1497 			*ln++ = uint32_t(color);
1498 		}
1499 		else
1500 		{
1501 			ln += 2;
1502 		}
1503 	}
1504 }
1505 
1506 
sprite_mode1(int line,uint8_t * col)1507 void v99x8_device::sprite_mode1 (int line, uint8_t *col)
1508 {
1509 	int attrtbl_addr, patterntbl_addr, pattern_addr;
1510 	int x, y, p, height, c, p2, i, n, pattern;
1511 
1512 	memset(col, 0, 256);
1513 
1514 	// are sprites disabled?
1515 	if (m_cont_reg[8] & 0x02) return;
1516 
1517 	attrtbl_addr = (m_cont_reg[5] << 7) + (m_cont_reg[11] << 15);
1518 	patterntbl_addr = (m_cont_reg[6] << 11);
1519 
1520 	// 16x16 or 8x8 sprites
1521 	height = (m_cont_reg[1] & 2) ? 16 : 8;
1522 	// magnified sprites (zoomed)
1523 	if (m_cont_reg[1] & 1) height *= 2;
1524 
1525 	p2 = p = 0;
1526 	while (1)
1527 	{
1528 		y = m_vram_space->read_byte(attrtbl_addr);
1529 		if (y == 208) break;
1530 		y = (y - m_cont_reg[23]) & 255;
1531 		if (y > 208)
1532 			y = -(~y&255);
1533 		else
1534 			y++;
1535 
1536 		// if sprite in range, has to be drawn
1537 		if ( (line >= y) && (line  < (y + height) ) )
1538 		{
1539 			if (p2 == 4)
1540 			{
1541 				// max maximum sprites per line!
1542 				if ( !(m_stat_reg[0] & 0x40) )
1543 					m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | 0x40 | p;
1544 
1545 				break;
1546 			}
1547 			// get x
1548 			x = m_vram_space->read_byte(attrtbl_addr + 1);
1549 			if (m_vram_space->read_byte(attrtbl_addr + 3) & 0x80) x -= 32;
1550 
1551 			// get pattern
1552 			pattern = m_vram_space->read_byte(attrtbl_addr + 2);
1553 			if (m_cont_reg[1] & 2)
1554 				pattern &= 0xfc;
1555 			n = line - y;
1556 			pattern_addr = patterntbl_addr + pattern * 8 + ((m_cont_reg[1] & 1) ? n/2  : n);
1557 			pattern = (m_vram_space->read_byte(pattern_addr) << 8) | m_vram_space->read_byte(pattern_addr+16);
1558 
1559 			// get colour
1560 			c = m_vram_space->read_byte(attrtbl_addr + 3) & 0x0f;
1561 
1562 			// draw left part
1563 			n = 0;
1564 			while (1)
1565 			{
1566 				if (n == 0) pattern = m_vram_space->read_byte(pattern_addr);
1567 				else if ( (n == 1) && (m_cont_reg[1] & 2) ) pattern = m_vram_space->read_byte(pattern_addr + 16);
1568 				else break;
1569 
1570 				n++;
1571 
1572 				for (i=0;i<8;i++)
1573 				{
1574 					if (pattern & 0x80)
1575 					{
1576 						if ( (x >= 0) && (x < 256) )
1577 						{
1578 							if (col[x] & 0x40)
1579 							{
1580 								// we have a collision!
1581 								if (p2 < 4)
1582 									m_stat_reg[0] |= 0x20;
1583 							}
1584 							if ( !(col[x] & 0x80) )
1585 							{
1586 								if (c || (m_cont_reg[8] & 0x20) )
1587 									col[x] |= 0xc0 | c;
1588 								else
1589 									col[x] |= 0x40;
1590 							}
1591 
1592 							// if zoomed, draw another pixel
1593 							if (m_cont_reg[1] & 1)
1594 							{
1595 								if (col[x+1] & 0x40)
1596 								{
1597 									// we have a collision!
1598 									if (p2 < 4)
1599 										m_stat_reg[0] |= 0x20;
1600 								}
1601 								if ( !(col[x+1] & 0x80) )
1602 								{
1603 									if (c || (m_cont_reg[8] & 0x20) )
1604 										col[x+1] |= 0xc0 | c;
1605 									else
1606 										col[x+1] |= 0x80;
1607 								}
1608 							}
1609 						}
1610 					}
1611 					if (m_cont_reg[1] & 1) x += 2; else x++;
1612 					pattern <<= 1;
1613 				}
1614 			}
1615 
1616 			p2++;
1617 		}
1618 
1619 		if (p >= 31) break;
1620 		p++;
1621 		attrtbl_addr += 4;
1622 	}
1623 
1624 	if ( !(m_stat_reg[0] & 0x40) )
1625 		m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | p;
1626 }
1627 
sprite_mode2(int line,uint8_t * col)1628 void v99x8_device::sprite_mode2 (int line, uint8_t *col)
1629 {
1630 	int attrtbl_addr, patterntbl_addr, pattern_addr, colourtbl_addr;
1631 	int x, i, y, p, height, c, p2, n, pattern, colourmask, first_cc_seen;
1632 
1633 	memset(col, 0, 256);
1634 
1635 	// are sprites disabled?
1636 	if (m_cont_reg[8] & 0x02) return;
1637 
1638 	attrtbl_addr = ( (m_cont_reg[5] & 0xfc) << 7) + (m_cont_reg[11] << 15);
1639 	colourtbl_addr =  ( (m_cont_reg[5] & 0xf8) << 7) + (m_cont_reg[11] << 15);
1640 	patterntbl_addr = (m_cont_reg[6] << 11);
1641 	colourmask = ( (m_cont_reg[5] & 3) << 3) | 0x7; // check this!
1642 
1643 	// 16x16 or 8x8 sprites
1644 	height = (m_cont_reg[1] & 2) ? 16 : 8;
1645 	// magnified sprites (zoomed)
1646 	if (m_cont_reg[1] & 1) height *= 2;
1647 
1648 	p2 = p = first_cc_seen = 0;
1649 	while (1)
1650 	{
1651 		y = vram_read(attrtbl_addr);
1652 		if (y == 216) break;
1653 		y = (y - m_cont_reg[23]) & 255;
1654 		if (y > 216)
1655 			y = -(~y&255);
1656 		else
1657 			y++;
1658 
1659 		// if sprite in range, has to be drawn
1660 		if ( (line >= y) && (line  < (y + height) ) )
1661 		{
1662 			if (p2 == 8)
1663 			{
1664 				// max maximum sprites per line!
1665 				if ( !(m_stat_reg[0] & 0x40) )
1666 					m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | 0x40 | p;
1667 
1668 				break;
1669 			}
1670 
1671 			n = line - y; if (m_cont_reg[1] & 1) n /= 2;
1672 			// get colour
1673 			c = vram_read(colourtbl_addr + (((p&colourmask)*16) + n));
1674 
1675 			// don't draw all sprite with CC set before any sprites
1676 			// with CC = 0 are seen on this line
1677 			if (c & 0x40)
1678 			{
1679 				if (!first_cc_seen)
1680 					goto skip_first_cc_set;
1681 			}
1682 			else
1683 				first_cc_seen = 1;
1684 
1685 			// get pattern
1686 			pattern = vram_read(attrtbl_addr + 2);
1687 			if (m_cont_reg[1] & 2)
1688 				pattern &= 0xfc;
1689 			pattern_addr = patterntbl_addr + pattern * 8 + n;
1690 			pattern = (vram_read(pattern_addr) << 8) | vram_read(pattern_addr + 16);
1691 
1692 			// get x
1693 			x = vram_read(attrtbl_addr + 1);
1694 			if (c & 0x80) x -= 32;
1695 
1696 			n = (m_cont_reg[1] & 2) ? 16 : 8;
1697 			while (n--)
1698 			{
1699 				for (i=0;i<=(m_cont_reg[1] & 1);i++)
1700 				{
1701 					if ( (x >= 0) && (x < 256) )
1702 					{
1703 						if ( (pattern & 0x8000) && !(col[x] & 0x10) )
1704 						{
1705 							if ( (c & 15) || (m_cont_reg[8] & 0x20) )
1706 							{
1707 								if ( !(c & 0x40) )
1708 								{
1709 									if (col[x] & 0x20) col[x] |= 0x10;
1710 									else
1711 										col[x] |= 0x20 | (c & 15);
1712 								}
1713 								else
1714 									col[x] |= c & 15;
1715 
1716 								col[x] |= 0x80;
1717 							}
1718 						}
1719 						else
1720 						{
1721 							if ( !(c & 0x40) && (col[x] & 0x20) )
1722 								col[x] |= 0x10;
1723 						}
1724 
1725 						if ( !(c & 0x60) && (pattern & 0x8000) )
1726 						{
1727 							if (col[x] & 0x40)
1728 							{
1729 								// sprite collision!
1730 								if (p2 < 8)
1731 									m_stat_reg[0] |= 0x20;
1732 							}
1733 							else
1734 								col[x] |= 0x40;
1735 						}
1736 
1737 						x++;
1738 					}
1739 				}
1740 
1741 				pattern <<= 1;
1742 			}
1743 
1744 		skip_first_cc_set:
1745 			p2++;
1746 		}
1747 
1748 		if (p >= 31) break;
1749 		p++;
1750 		attrtbl_addr += 4;
1751 	}
1752 
1753 	if ( !(m_stat_reg[0] & 0x40) )
1754 		m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | p;
1755 }
1756 
1757 
1758 const v99x8_device::v99x8_mode v99x8_device::s_modes[] = {
1759 	{ 0x02,
1760 		&v99x8_device::mode_text1,
1761 		&v99x8_device::default_border,
1762 		nullptr,
1763 		nullptr
1764 	},
1765 	{ 0x01,
1766 		&v99x8_device::mode_multi,
1767 		&v99x8_device::default_border,
1768 		&v99x8_device::sprite_mode1,
1769 		&v99x8_device::default_draw_sprite
1770 	},
1771 	{ 0x00,
1772 		&v99x8_device::mode_graphic1,
1773 		&v99x8_device::default_border,
1774 		&v99x8_device::sprite_mode1,
1775 		&v99x8_device::default_draw_sprite
1776 	},
1777 	{ 0x04,
1778 		&v99x8_device::mode_graphic23,
1779 		&v99x8_device::default_border,
1780 		&v99x8_device::sprite_mode1,
1781 		&v99x8_device::default_draw_sprite
1782 	},
1783 	{ 0x08,
1784 		&v99x8_device::mode_graphic23,
1785 		&v99x8_device::default_border,
1786 		&v99x8_device::sprite_mode2,
1787 		&v99x8_device::default_draw_sprite
1788 	},
1789 	{ 0x0c,
1790 		&v99x8_device::mode_graphic4,
1791 		&v99x8_device::default_border,
1792 		&v99x8_device::sprite_mode2,
1793 		&v99x8_device::default_draw_sprite
1794 	},
1795 	{ 0x10,
1796 		&v99x8_device::mode_graphic5,
1797 		&v99x8_device::graphic5_border,
1798 		&v99x8_device::sprite_mode2,
1799 		&v99x8_device::graphic5_draw_sprite
1800 	},
1801 	{ 0x14,
1802 		&v99x8_device::mode_graphic6,
1803 		&v99x8_device::default_border,
1804 		&v99x8_device::sprite_mode2,
1805 		&v99x8_device::default_draw_sprite
1806 	},
1807 	{ 0x1c,
1808 		&v99x8_device::mode_graphic7,
1809 		&v99x8_device::graphic7_border,
1810 		&v99x8_device::sprite_mode2,
1811 		&v99x8_device::graphic7_draw_sprite
1812 	},
1813 	{ 0x0a,
1814 		&v99x8_device::mode_text2,
1815 		&v99x8_device::default_border,
1816 		nullptr,
1817 		nullptr
1818 	},
1819 	{ 0xff,
1820 		&v99x8_device::mode_unknown,
1821 		&v99x8_device::default_border,
1822 		nullptr,
1823 		nullptr
1824 	}
1825 };
1826 
set_mode()1827 void v99x8_device::set_mode()
1828 {
1829 	int n,i;
1830 
1831 	n = (((m_cont_reg[0] & 0x0e) << 1) | ((m_cont_reg[1] & 0x18) >> 3));
1832 	for (i=0;;i++)
1833 	{
1834 		if ( (s_modes[i].m == n) || (s_modes[i].m == 0xff) ) break;
1835 	}
1836 
1837 	// MZ: What happens when the mode is changed during command execution?
1838 	// This is left unspecified in the docs. On a Geneve, experiments showed
1839 	// that the command is not aborted (the CE flag is still 1) and runs for
1840 	// about 90% of the nominal execution time, but VRAM is only correctly
1841 	// filled up to the time of switching, and after that, isolated locations
1842 	// within the normally affected area are changed, but inconsistently.
1843 	// Obviously, it depends on the time when the switch happened.
1844 	// This behavior occurs on every switch from a mode Graphics4 and higher
1845 	// to another mode, e.g. also from Graphics7 to Graphics6.
1846 	// Due to the lack of more information, we simply abort the command.
1847 
1848 	if (m_vdp_engine && m_mode != i)
1849 	{
1850 		LOGMASKED(LOG_WARN, "Command aborted due to mode change\n");
1851 		m_vdp_engine = nullptr;
1852 		m_stat_reg[2] &= 0xFE;
1853 	}
1854 
1855 	m_mode = i;
1856 }
1857 
refresh_32(int line)1858 void v99x8_device::refresh_32(int line)
1859 {
1860 	bool double_lines = false;
1861 	uint8_t col[256];
1862 	uint32_t *ln, *ln2 = nullptr;
1863 
1864 	if (m_cont_reg[9] & 0x08)
1865 	{
1866 		ln = &m_bitmap.pix(m_scanline*2+((m_stat_reg[2]>>1)&1));
1867 	}
1868 	else
1869 	{
1870 		ln = &m_bitmap.pix(m_scanline*2);
1871 		ln2 = &m_bitmap.pix(m_scanline*2+1);
1872 		double_lines = true;
1873 	}
1874 
1875 	if ( !(m_cont_reg[1] & 0x40) || (m_stat_reg[2] & 0x40) )
1876 	{
1877 		(this->*s_modes[m_mode].border_32)(ln);
1878 	}
1879 	else
1880 	{
1881 		(this->*s_modes[m_mode].visible_32)(ln, line);
1882 		if (s_modes[m_mode].sprites)
1883 		{
1884 			(this->*s_modes[m_mode].sprites)(line, col);
1885 			(this->*s_modes[m_mode].draw_sprite_32)(ln, col);
1886 		}
1887 	}
1888 
1889 	if (double_lines)
1890 		memcpy(ln2, ln, (512 + 32) * sizeof(*ln));
1891 }
1892 
refresh_line(int line)1893 void v99x8_device::refresh_line(int line)
1894 {
1895 	pen_t ind16, ind256;
1896 
1897 	ind16 = pen16(0);
1898 	ind256 = pen256(0);
1899 
1900 	if ( !(m_cont_reg[8] & 0x20) && (m_mode != V9938_MODE_GRAPHIC5) )
1901 	{
1902 		set_pen16(0, pen16(m_cont_reg[7] & 0x0f));
1903 		set_pen256(0, pen256(m_cont_reg[7]));
1904 	}
1905 
1906 	refresh_32(line);
1907 
1908 	if ( !(m_cont_reg[8] & 0x20) && (m_mode != V9938_MODE_GRAPHIC5) )
1909 	{
1910 		set_pen16(0, ind16);
1911 		set_pen256(0, ind256);
1912 	}
1913 }
1914 
1915 /*
1916 
1917 From: awulms@inter.nl.net (Alex Wulms)
1918 *** About the HR/VR topic: this is how it works according to me:
1919 
1920 *** HR:
1921 HR is very straightforward:
1922 -HR=1 during 'display time'
1923 -HR=0 during 'horizontal border, horizontal retrace'
1924 I have put 'display time' and 'horizontal border, horizontal retrace' between
1925 quotes because HR does not only flip between 0 and 1 during the display of
1926 the 192/212 display lines, but also during the vertical border and during the
1927 vertical retrace.
1928 
1929 *** VR:
1930 VR is a little bit tricky
1931 -VR always gets set to 0 when the VDP starts with display line 0
1932 -VR gets set to 1 when the VDP reaches display line (192 if LN=0) or (212 if
1933 LN=1)
1934 -The VDP displays contents of VRAM as long as VR=0
1935 
1936 As a consequence of this behaviour, it is possible to program the famous
1937 overscan trick, where VRAM contents is shown in the borders:
1938 Generate an interrupt at line 230 (or so) and on this interrupt: set LN=1
1939 Generate an interrupt at line 200 (or so) and on this interrupt: set LN=0
1940 Repeat the above two steps
1941 
1942 *** The top/bottom border contents during overscan:
1943 On screen 0:
1944 1) The VDP keeps increasing the name table address pointer during bottom
1945 border, vertical retrace and top border
1946 2) The VDP resets the name table address pointer when the first display line
1947 is reached
1948 
1949 On the other screens:
1950 1) The VDP keeps increasing the name table address pointer during the bottom
1951 border
1952 2) The VDP resets the name table address pointer such that the top border
1953 contents connects up with the first display line. E.g., when the top border
1954 is 26 lines high, the VDP will take:
1955 'logical'      vram line
1956 TOPB000  256-26
1957 ...
1958 TOPB025  256-01
1959 DISPL000 000
1960 ...
1961 DISPL211 211
1962 BOTB000  212
1963 ...
1964 BOTB024  236
1965 
1966 
1967 
1968 *** About the horizontal interrupt
1969 
1970 All relevant definitions on a row:
1971 -FH: Bit 0 of status register 1
1972 -IE1: Bit 4 of mode register 0
1973 -IL: Line number in mode register 19
1974 -DL: The line that the VDP is going to display (corrected for vertical scroll)
1975 -IRQ: Interrupt request line of VDP to Z80
1976 
1977 At the *start* of every new line (display, bottom border, part of vertical
1978 display), the VDP does:
1979 -FH = (FH && IE1) || (IL==DL)
1980 
1981 After reading of status register 1 by the CPU, the VDP does:
1982 -FH = 0
1983 
1984 Furthermore, the following is true all the time:
1985 -IRQ = FH && IE1
1986 
1987 The resulting behaviour:
1988 When IE1=0:
1989 -FH will be set as soon as display of line IL starts
1990 -FH will be reset as soon as status register 1 is read
1991 -FH will be reset as soon as the next display line is reached
1992 
1993 When IE=1:
1994 -FH and IRQ will be set as soon as display line IL is reached
1995 -FH and IRQ will be reset as soon as status register 1 is read
1996 
1997 Another subtile result:
1998 If, while FH and IRQ are set, IE1 gets reset, the next happens:
1999 -IRQ is reset immediately (since IRQ is always FH && IE1)
2000 -FH will be reset as soon as display of the next line starts (unless the next
2001 line is line IL)
2002 
2003 
2004 *** About the vertical interrupt:
2005 Another relevant definition:
2006 -FV: Bit 7 of status register 0
2007 -IE0: Bit 5 of mode register 1
2008 
2009 I only know for sure the behaviour when IE0=1:
2010 -FV and IRQ will be set as soon as VR changes from 0 to 1
2011 -FV and IRQ will be reset as soon as status register 0 is read
2012 
2013 A consequence is that NO vertical interrupts will be generated during the
2014 overscan trick, described in the VR section above.
2015 
2016 I do not know the behaviour of FV when IE0=0. That is the part that I still
2017 have to test.
2018 */
2019 
interrupt_start_vblank()2020 void v99x8_device::interrupt_start_vblank()
2021 {
2022 	#if 0
2023 	if (machine.input().code_pressed (KEYCODE_D) )
2024 	{
2025 		for (i=0;i<24;i++) osd_printf_debug ("R#%d = %02x\n", i, m_cont_reg[i]);
2026 	}
2027 	#endif
2028 
2029 	// at every frame, vdp switches fields
2030 	m_stat_reg[2] = (m_stat_reg[2] & 0xfd) | (~m_stat_reg[2] & 2);
2031 
2032 	// color blinking
2033 	if (!(m_cont_reg[13] & 0xf0))
2034 		m_blink = 0;
2035 	else if (!(m_cont_reg[13] & 0x0f))
2036 		m_blink = 1;
2037 	else
2038 	{
2039 		// both on and off counter are non-zero: timed blinking
2040 		if (m_blink_count)
2041 			m_blink_count--;
2042 		if (!m_blink_count)
2043 		{
2044 			m_blink = !m_blink;
2045 			if (m_blink)
2046 				m_blink_count = (m_cont_reg[13] >> 4) * 10;
2047 			else
2048 				m_blink_count = (m_cont_reg[13] & 0x0f) * 10;
2049 		}
2050 	}
2051 }
2052 
2053 /***************************************************************************
2054 
2055 Command unit
2056 
2057 ***************************************************************************/
2058 
2059 /*************************************************************/
2060 /** Completely rewritten by Alex Wulms:                     **/
2061 /**  - VDP Command execution 'in parallel' with CPU         **/
2062 /**  - Corrected behaviour of VDP commands                  **/
2063 /**  - Made it easier to implement correct S7/8 mapping     **/
2064 /**    by concentrating VRAM access in one single place     **/
2065 /**  - Made use of the 'in parallel' VDP command exec       **/
2066 /**    and correct timing. You must call the function       **/
2067 /**    LoopVDP() from LoopZ80 in MSX.c. You must call it    **/
2068 /**    exactly 256 times per screen refresh.                **/
2069 /** Started on       : 11-11-1999                           **/
2070 /** Beta release 1 on:  9-12-1999                           **/
2071 /** Beta release 2 on: 20-01-2000                           **/
2072 /**  - Corrected behaviour of VRM <-> Z80 transfer          **/
2073 /**  - Improved performance of the code                     **/
2074 /** Public release 1.0: 20-04-2000                          **/
2075 /*************************************************************/
2076 
2077 #define VDP_VRMP5(MX, X, Y) ((!MX) ? (((Y&1023)<<7) + ((X&255)>>1)) : (EXPMEM_OFFSET + ((Y&511)<<7) + ((X&255)>>1)))
2078 #define VDP_VRMP6(MX, X, Y) ((!MX) ? (((Y&1023)<<7) + ((X&511)>>2)) : (EXPMEM_OFFSET + ((Y&511)<<7) + ((X&511)>>2)))
2079 //#define VDP_VRMP7(MX, X, Y) ((!MX) ? (((Y&511)<<8) + ((X&511)>>1)) : (EXPMEM_OFFSET + ((Y&255)<<8) + ((X&511)>>1)))
2080 #define VDP_VRMP7(MX, X, Y) ((!MX) ? (((X&2)<<15) + ((Y&511)<<7) + ((X&511)>>2)) : (EXPMEM_OFFSET + ((Y&511)<<7) + ((X&511)>>2))/*(EXPMEM_OFFSET + ((Y&255)<<8) + ((X&511)>>1))*/)
2081 //#define VDP_VRMP8(MX, X, Y) ((!MX) ? (((Y&511)<<8) + (X&255)) : (EXPMEM_OFFSET + ((Y&255)<<8) + (X&255)))
2082 #define VDP_VRMP8(MX, X, Y) ((!MX) ? (((X&1)<<16) + ((Y&511)<<7) + ((X>>1)&127)) : (EXPMEM_OFFSET + ((Y&511)<<7) + ((X>>1)&127))/*(EXPMEM_OFFSET + ((Y&255)<<8) + (X&255))*/)
2083 
2084 #define VDP_VRMP(M, MX, X, Y) VDPVRMP(M, MX, X, Y)
2085 #define VDP_POINT(M, MX, X, Y) VDPpoint(M, MX, X, Y)
2086 #define VDP_PSET(M, MX, X, Y, C, O) VDPpset(M, MX, X, Y, C, O)
2087 
2088 #define CM_ABRT  0x0
2089 #define CM_POINT 0x4
2090 #define CM_PSET  0x5
2091 #define CM_SRCH  0x6
2092 #define CM_LINE  0x7
2093 #define CM_LMMV  0x8
2094 #define CM_LMMM  0x9
2095 #define CM_LMCM  0xA
2096 #define CM_LMMC  0xB
2097 #define CM_HMMV  0xC
2098 #define CM_HMMM  0xD
2099 #define CM_YMMM  0xE
2100 #define CM_HMMC  0xF
2101 
2102 /*************************************************************
2103 Many VDP commands are executed in some kind of loop but
2104 essentially, there are only a few basic loop structures
2105 that are re-used. We define the loop structures that are
2106 re-used here so that they have to be entered only once
2107 *************************************************************/
2108 #define pre_loop \
2109 while ((cnt-=delta) > 0) {
2110 	#define post_loop \
2111 }
2112 
2113 // Loop over DX, DY
2114 #define post__x_y(MX) \
2115 if (!--ANX || ((ADX+=TX)&MX)) { \
2116 	if (!(--NY&1023) || (DY+=TY)==-1) \
2117 		break; \
2118 	else { \
2119 		ADX=DX; \
2120 		ANX=NX; \
2121 	} \
2122 } \
2123 post_loop
2124 
2125 // Loop over DX, SY, DY
2126 #define post__xyy(MX) \
2127 if ((ADX+=TX)&MX) { \
2128 	if (!(--NY&1023) || (SY+=TY)==-1 || (DY+=TY)==-1) \
2129 		break; \
2130 	else \
2131 		ADX=DX; \
2132 } \
2133 post_loop
2134 
2135 // Loop over SX, DX, SY, DY
2136 #define post_xxyy(MX) \
2137 if (!--ANX || ((ASX+=TX)&MX) || ((ADX+=TX)&MX)) { \
2138 	if (!(--NY&1023) || (SY+=TY)==-1 || (DY+=TY)==-1) \
2139 		break; \
2140 	else { \
2141 		ASX=SX; \
2142 		ADX=DX; \
2143 		ANX=NX; \
2144 	} \
2145 } \
2146 post_loop
2147 
2148 /*************************************************************/
2149 /** Variables visible only in this module                   **/
2150 /*************************************************************/
2151 static const uint8_t Mask[4] = { 0x0F,0x03,0x0F,0xFF };
2152 static const int  PPB[4]  = { 2,4,2,1 };
2153 static const int  PPL[4]  = { 256,512,512,256 };
2154 
2155 //  SprOn SprOn SprOf SprOf
2156 //  ScrOf ScrOn ScrOf ScrOn
2157 static const int srch_timing[8]={
2158 	818, 1025,  818,  830, // ntsc
2159 	696,  854,  696,  684  // pal
2160 };
2161 static const int line_timing[8]={
2162 	1063, 1259, 1063, 1161,
2163 	904,  1026, 904,  953
2164 };
2165 static const int hmmv_timing[8]={
2166 	439,  549,  439,  531,
2167 	366,  439,  366,  427
2168 };
2169 static const int lmmv_timing[8]={
2170 	873,  1135, 873, 1056,
2171 	732,  909,  732,  854
2172 };
2173 static const int ymmm_timing[8]={
2174 	586,  952,  586,  610,
2175 	488,  720,  488,  500
2176 };
2177 static const int hmmm_timing[8]={
2178 	818,  1111, 818,  854,
2179 	684,  879,  684,  708
2180 };
2181 static const int lmmm_timing[8]={
2182 	1160, 1599, 1160, 1172,
2183 	964,  1257, 964,  977
2184 };
2185 
2186 /** VDPVRMP() **********************************************/
2187 /** Calculate addr of a pixel in vram                       **/
2188 /*************************************************************/
VDPVRMP(uint8_t M,int MX,int X,int Y)2189 inline int v99x8_device::VDPVRMP(uint8_t M,int MX,int X,int Y)
2190 {
2191 	switch(M)
2192 	{
2193 	case 0: return VDP_VRMP5(MX,X,Y);
2194 	case 1: return VDP_VRMP6(MX,X,Y);
2195 	case 2: return VDP_VRMP7(MX,X,Y);
2196 	case 3: return VDP_VRMP8(MX,X,Y);
2197 	}
2198 
2199 	return 0;
2200 }
2201 
2202 /** VDPpoint5() ***********************************************/
2203 /** Get a pixel on screen 5                                 **/
2204 /*************************************************************/
VDPpoint5(int MXS,int SX,int SY)2205 inline uint8_t v99x8_device::VDPpoint5(int MXS, int SX, int SY)
2206 {
2207 	return (m_vram_space->read_byte(VDP_VRMP5(MXS, SX, SY)) >>
2208 		(((~SX)&1)<<2)
2209 		)&15;
2210 }
2211 
2212 /** VDPpoint6() ***********************************************/
2213 /** Get a pixel on screen 6                                 **/
2214 /*************************************************************/
VDPpoint6(int MXS,int SX,int SY)2215 inline uint8_t v99x8_device::VDPpoint6(int MXS, int SX, int SY)
2216 {
2217 	return (m_vram_space->read_byte(VDP_VRMP6(MXS, SX, SY)) >>
2218 		(((~SX)&3)<<1)
2219 		)&3;
2220 }
2221 
2222 /** VDPpoint7() ***********************************************/
2223 /** Get a pixel on screen 7                                 **/
2224 /*************************************************************/
VDPpoint7(int MXS,int SX,int SY)2225 inline uint8_t v99x8_device::VDPpoint7(int MXS, int SX, int SY)
2226 {
2227 	return (m_vram_space->read_byte(VDP_VRMP7(MXS, SX, SY)) >>
2228 		(((~SX)&1)<<2)
2229 		)&15;
2230 }
2231 
2232 /** VDPpoint8() ***********************************************/
2233 /** Get a pixel on screen 8                                 **/
2234 /*************************************************************/
VDPpoint8(int MXS,int SX,int SY)2235 inline uint8_t v99x8_device::VDPpoint8(int MXS, int SX, int SY)
2236 {
2237 	return m_vram_space->read_byte(VDP_VRMP8(MXS, SX, SY));
2238 }
2239 
2240 /** VDPpoint() ************************************************/
2241 /** Get a pixel on a screen                                 **/
2242 /*************************************************************/
VDPpoint(uint8_t SM,int MXS,int SX,int SY)2243 inline uint8_t v99x8_device::VDPpoint(uint8_t SM, int MXS, int SX, int SY)
2244 {
2245 	switch(SM)
2246 	{
2247 	case 0: return VDPpoint5(MXS,SX,SY);
2248 	case 1: return VDPpoint6(MXS,SX,SY);
2249 	case 2: return VDPpoint7(MXS,SX,SY);
2250 	case 3: return VDPpoint8(MXS,SX,SY);
2251 	}
2252 
2253 	return(0);
2254 }
2255 
2256 /** VDPpsetlowlevel() ****************************************/
2257 /** Low level function to set a pixel on a screen           **/
2258 /** Make it inline to make it fast                          **/
2259 /*************************************************************/
VDPpsetlowlevel(int addr,uint8_t CL,uint8_t M,uint8_t OP)2260 inline void v99x8_device::VDPpsetlowlevel(int addr, uint8_t CL, uint8_t M, uint8_t OP)
2261 {
2262 	// If this turns out to be too slow, get a pointer to the address space
2263 	// and work directly on it.
2264 	uint8_t val = m_vram_space->read_byte(addr);
2265 	switch (OP)
2266 	{
2267 	case 0: val = (val & M) | CL; break;
2268 	case 1: val = val & (CL | M); break;
2269 	case 2: val |= CL; break;
2270 	case 3: val ^= CL; break;
2271 	case 4: val = (val & M) | ~(CL | M); break;
2272 	case 8: if (CL) val = (val & M) | CL; break;
2273 	case 9: if (CL) val = val & (CL | M); break;
2274 	case 10: if (CL) val |= CL; break;
2275 	case 11:  if (CL) val ^= CL; break;
2276 	case 12:  if (CL) val = (val & M) | ~(CL|M); break;
2277 	default:
2278 		LOGMASKED(LOG_WARN, "Invalid operation %d in pset\n", OP);
2279 	}
2280 
2281 	m_vram_space->write_byte(addr, val);
2282 }
2283 
2284 /** VDPpset5() ***********************************************/
2285 /** Set a pixel on screen 5                                 **/
2286 /*************************************************************/
VDPpset5(int MXD,int DX,int DY,uint8_t CL,uint8_t OP)2287 inline void v99x8_device::VDPpset5(int MXD, int DX, int DY, uint8_t CL, uint8_t OP)
2288 {
2289 	uint8_t SH = ((~DX)&1)<<2;
2290 	VDPpsetlowlevel(VDP_VRMP5(MXD, DX, DY), CL << SH, ~(15<<SH), OP);
2291 }
2292 
2293 /** VDPpset6() ***********************************************/
2294 /** Set a pixel on screen 6                                 **/
2295 /*************************************************************/
VDPpset6(int MXD,int DX,int DY,uint8_t CL,uint8_t OP)2296 inline void v99x8_device::VDPpset6(int MXD, int DX, int DY, uint8_t CL, uint8_t OP)
2297 {
2298 	uint8_t SH = ((~DX)&3)<<1;
2299 
2300 	VDPpsetlowlevel(VDP_VRMP6(MXD, DX, DY), CL << SH, ~(3<<SH), OP);
2301 }
2302 
2303 /** VDPpset7() ***********************************************/
2304 /** Set a pixel on screen 7                                 **/
2305 /*************************************************************/
VDPpset7(int MXD,int DX,int DY,uint8_t CL,uint8_t OP)2306 inline void v99x8_device::VDPpset7(int MXD, int DX, int DY, uint8_t CL, uint8_t OP)
2307 {
2308 	uint8_t SH = ((~DX)&1)<<2;
2309 
2310 	VDPpsetlowlevel(VDP_VRMP7(MXD, DX, DY), CL << SH, ~(15<<SH), OP);
2311 }
2312 
2313 /** VDPpset8() ***********************************************/
2314 /** Set a pixel on screen 8                                 **/
2315 /*************************************************************/
VDPpset8(int MXD,int DX,int DY,uint8_t CL,uint8_t OP)2316 inline void v99x8_device::VDPpset8(int MXD, int DX, int DY, uint8_t CL, uint8_t OP)
2317 {
2318 	VDPpsetlowlevel(VDP_VRMP8(MXD, DX, DY), CL, 0, OP);
2319 }
2320 
2321 /** VDPpset() ************************************************/
2322 /** Set a pixel on a screen                                 **/
2323 /*************************************************************/
VDPpset(uint8_t SM,int MXD,int DX,int DY,uint8_t CL,uint8_t OP)2324 inline void v99x8_device::VDPpset(uint8_t SM, int MXD, int DX, int DY, uint8_t CL, uint8_t OP)
2325 {
2326 	switch (SM) {
2327 	case 0: VDPpset5(MXD, DX, DY, CL, OP); break;
2328 	case 1: VDPpset6(MXD, DX, DY, CL, OP); break;
2329 	case 2: VDPpset7(MXD, DX, DY, CL, OP); break;
2330 	case 3: VDPpset8(MXD, DX, DY, CL, OP); break;
2331 	}
2332 }
2333 
2334 /** get_vdp_timing_value() **************************************/
2335 /** Get timing value for a certain VDP command              **/
2336 /*************************************************************/
get_vdp_timing_value(const int * timing_values)2337 int v99x8_device::get_vdp_timing_value(const int *timing_values)
2338 {
2339 	return(timing_values[((m_cont_reg[1]>>6)&1)|(m_cont_reg[8]&2)|((m_cont_reg[9]<<1)&4)]);
2340 }
2341 
2342 /** SrchEgine()** ********************************************/
2343 /** Search a dot                                            **/
2344 /*************************************************************/
srch_engine()2345 void v99x8_device::srch_engine()
2346 {
2347 	int SX=m_mmc.SX;
2348 	int SY=m_mmc.SY;
2349 	int TX=m_mmc.TX;
2350 	int ANX=m_mmc.ANX;
2351 	uint8_t CL=m_mmc.CL;
2352 	int MXD = m_mmc.MXD;
2353 	int cnt;
2354 	int delta;
2355 
2356 	delta = get_vdp_timing_value(srch_timing);
2357 	cnt = m_vdp_ops_count;
2358 
2359 	#define post_srch(MX) \
2360 	{ m_stat_reg[2]|=0x10; /* Border detected */ break; } \
2361 	if ((SX+=TX) & MX) { m_stat_reg[2] &= 0xEF; /* Border not detected */ break; }
2362 	switch (m_mode) {
2363 	default:
2364 	case V9938_MODE_GRAPHIC4: pre_loop if ((VDPpoint5(MXD, SX, SY)==CL) ^ANX)  post_srch(256) post_loop
2365 			break;
2366 	case V9938_MODE_GRAPHIC5: pre_loop if ((VDPpoint6(MXD, SX, SY)==CL) ^ANX)  post_srch(512) post_loop
2367 			break;
2368 	case V9938_MODE_GRAPHIC6: pre_loop if ((VDPpoint7(MXD, SX, SY)==CL) ^ANX)  post_srch(512) post_loop
2369 			break;
2370 	case V9938_MODE_GRAPHIC7: pre_loop if ((VDPpoint8(MXD, SX, SY)==CL) ^ANX)  post_srch(256) post_loop
2371 			break;
2372 	}
2373 
2374 	if ((m_vdp_ops_count=cnt)>0) {
2375 		// Command execution done
2376 		m_stat_reg[2] &= 0xFE;
2377 		m_vdp_engine = nullptr;
2378 		// Update SX in VDP registers
2379 		m_stat_reg[8] = SX & 0xFF;
2380 		m_stat_reg[9] = (SX>>8) | 0xFE;
2381 	}
2382 	else {
2383 		m_mmc.SX=SX;
2384 	}
2385 }
2386 
2387 /** LineEgine()** ********************************************/
2388 /** Draw a line                                             **/
2389 /*************************************************************/
line_engine()2390 void v99x8_device::line_engine()
2391 {
2392 	int DX=m_mmc.DX;
2393 	int DY=m_mmc.DY;
2394 	int TX=m_mmc.TX;
2395 	int TY=m_mmc.TY;
2396 	int NX=m_mmc.NX;
2397 	int NY=m_mmc.NY;
2398 	int ASX=m_mmc.ASX;
2399 	int ADX=m_mmc.ADX;
2400 	uint8_t CL=m_mmc.CL;
2401 	uint8_t LO=m_mmc.LO;
2402 	int MXD = m_mmc.MXD;
2403 	int cnt;
2404 	int delta;
2405 
2406 	delta = get_vdp_timing_value(line_timing);
2407 	cnt = m_vdp_ops_count;
2408 
2409 	#define post_linexmaj(MX) \
2410 	DX+=TX; \
2411 	if ((ASX-=NY)<0) { \
2412 		ASX+=NX; \
2413 		DY+=TY; \
2414 	} \
2415 	ASX&=1023; /* Mask to 10 bits range */\
2416 	if (ADX++==NX || (DX&MX)) \
2417 		break; \
2418 	post_loop
2419 
2420 	#define post_lineymaj(MX) \
2421 	DY+=TY; \
2422 	if ((ASX-=NY)<0) { \
2423 		ASX+=NX; \
2424 		DX+=TX; \
2425 	} \
2426 	ASX&=1023; /* Mask to 10 bits range */\
2427 	if (ADX++==NX || (DX&MX)) \
2428 		break; \
2429 	post_loop
2430 
2431 	if ((m_cont_reg[45]&0x01)==0)
2432 		// X-Axis is major direction
2433 	switch (m_mode) {
2434 	default:
2435 	case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, DX, DY, CL, LO); post_linexmaj(256)
2436 		break;
2437 	case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, DX, DY, CL, LO); post_linexmaj(512)
2438 		break;
2439 	case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, DX, DY, CL, LO); post_linexmaj(512)
2440 		break;
2441 	case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, DX, DY, CL, LO); post_linexmaj(256)
2442 		break;
2443 	}
2444 	else
2445 		// Y-Axis is major direction
2446 	switch (m_mode) {
2447 	default:
2448 	case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, DX, DY, CL, LO); post_lineymaj(256)
2449 		break;
2450 	case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, DX, DY, CL, LO); post_lineymaj(512)
2451 		break;
2452 	case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, DX, DY, CL, LO); post_lineymaj(512)
2453 		break;
2454 	case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, DX, DY, CL, LO); post_lineymaj(256)
2455 		break;
2456 	}
2457 
2458 	if ((m_vdp_ops_count=cnt)>0) {
2459 		// Command execution done
2460 		m_stat_reg[2]&=0xFE;
2461 		m_vdp_engine=nullptr;
2462 		m_cont_reg[38]=DY & 0xFF;
2463 		m_cont_reg[39]=(DY>>8) & 0x03;
2464 	}
2465 	else {
2466 		m_mmc.DX=DX;
2467 		m_mmc.DY=DY;
2468 		m_mmc.ASX=ASX;
2469 		m_mmc.ADX=ADX;
2470 	}
2471 }
2472 
2473 /** lmmv_engine() *********************************************/
2474 /** VDP -> Vram                                             **/
2475 /*************************************************************/
lmmv_engine()2476 void v99x8_device::lmmv_engine()
2477 {
2478 	int DX=m_mmc.DX;
2479 	int DY=m_mmc.DY;
2480 	int TX=m_mmc.TX;
2481 	int TY=m_mmc.TY;
2482 	int NX=m_mmc.NX;
2483 	int NY=m_mmc.NY;
2484 	int ADX=m_mmc.ADX;
2485 	int ANX=m_mmc.ANX;
2486 	uint8_t CL=m_mmc.CL;
2487 	uint8_t LO=m_mmc.LO;
2488 	int MXD = m_mmc.MXD;
2489 	int cnt;
2490 	int delta;
2491 
2492 	delta = get_vdp_timing_value(lmmv_timing);
2493 	cnt = m_vdp_ops_count;
2494 
2495 	switch (m_mode) {
2496 	default:
2497 	case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, ADX, DY, CL, LO); post__x_y(256)
2498 		break;
2499 	case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, ADX, DY, CL, LO); post__x_y(512)
2500 		break;
2501 	case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, ADX, DY, CL, LO); post__x_y(512)
2502 		break;
2503 	case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, ADX, DY, CL, LO); post__x_y(256)
2504 		break;
2505 	}
2506 
2507 	if ((m_vdp_ops_count=cnt)>0) {
2508 		// Command execution done
2509 		m_stat_reg[2]&=0xFE;
2510 		m_vdp_engine=nullptr;
2511 		if (!NY)
2512 			DY+=TY;
2513 		m_cont_reg[38]=DY & 0xFF;
2514 		m_cont_reg[39]=(DY>>8) & 0x03;
2515 		m_cont_reg[42]=NY & 0xFF;
2516 		m_cont_reg[43]=(NY>>8) & 0x03;
2517 	}
2518 	else {
2519 		m_mmc.DY=DY;
2520 		m_mmc.NY=NY;
2521 		m_mmc.ANX=ANX;
2522 		m_mmc.ADX=ADX;
2523 	}
2524 }
2525 
2526 /** lmmm_engine() *********************************************/
2527 /** Vram -> Vram                                            **/
2528 /*************************************************************/
lmmm_engine()2529 void v99x8_device::lmmm_engine()
2530 {
2531 	int SX=m_mmc.SX;
2532 	int SY=m_mmc.SY;
2533 	int DX=m_mmc.DX;
2534 	int DY=m_mmc.DY;
2535 	int TX=m_mmc.TX;
2536 	int TY=m_mmc.TY;
2537 	int NX=m_mmc.NX;
2538 	int NY=m_mmc.NY;
2539 	int ASX=m_mmc.ASX;
2540 	int ADX=m_mmc.ADX;
2541 	int ANX=m_mmc.ANX;
2542 	uint8_t LO=m_mmc.LO;
2543 	int MXS = m_mmc.MXS;
2544 	int MXD = m_mmc.MXD;
2545 	int cnt;
2546 	int delta;
2547 
2548 	delta = get_vdp_timing_value(lmmm_timing);
2549 	cnt = m_vdp_ops_count;
2550 
2551 	switch (m_mode) {
2552 	default:
2553 	case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, ADX, DY, VDPpoint5(MXS, ASX, SY), LO); post_xxyy(256)
2554 		break;
2555 	case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, ADX, DY, VDPpoint6(MXS, ASX, SY), LO); post_xxyy(512)
2556 		break;
2557 	case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, ADX, DY, VDPpoint7(MXS, ASX, SY), LO); post_xxyy(512)
2558 		break;
2559 	case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, ADX, DY, VDPpoint8(MXS, ASX, SY), LO); post_xxyy(256)
2560 		break;
2561 	}
2562 
2563 	if ((m_vdp_ops_count=cnt)>0) {
2564 		// Command execution done
2565 		m_stat_reg[2]&=0xFE;
2566 		m_vdp_engine=nullptr;
2567 		if (!NY) {
2568 			SY+=TY;
2569 			DY+=TY;
2570 		}
2571 		else
2572 			if (SY==-1)
2573 			DY+=TY;
2574 		m_cont_reg[42]=NY & 0xFF;
2575 		m_cont_reg[43]=(NY>>8) & 0x03;
2576 		m_cont_reg[34]=SY & 0xFF;
2577 		m_cont_reg[35]=(SY>>8) & 0x03;
2578 		m_cont_reg[38]=DY & 0xFF;
2579 		m_cont_reg[39]=(DY>>8) & 0x03;
2580 	}
2581 	else {
2582 		m_mmc.SY=SY;
2583 		m_mmc.DY=DY;
2584 		m_mmc.NY=NY;
2585 		m_mmc.ANX=ANX;
2586 		m_mmc.ASX=ASX;
2587 		m_mmc.ADX=ADX;
2588 	}
2589 }
2590 
2591 /** lmcm_engine() *********************************************/
2592 /** Vram -> CPU                                             **/
2593 /*************************************************************/
lmcm_engine()2594 void v99x8_device::lmcm_engine()
2595 {
2596 	if ((m_stat_reg[2]&0x80)!=0x80) {
2597 		m_stat_reg[7]=m_cont_reg[44]=VDP_POINT(((m_mode >= 5) && (m_mode <= 8)) ? (m_mode-5) : 0, m_mmc.MXS, m_mmc.ASX, m_mmc.SY);
2598 		m_vdp_ops_count-=get_vdp_timing_value(lmmv_timing);
2599 		m_stat_reg[2]|=0x80;
2600 
2601 		if (!--m_mmc.ANX || ((m_mmc.ASX+=m_mmc.TX)&m_mmc.MX)) {
2602 			if (!(--m_mmc.NY & 1023) || (m_mmc.SY+=m_mmc.TY)==-1) {
2603 				m_stat_reg[2]&=0xFE;
2604 				m_vdp_engine=nullptr;
2605 				if (!m_mmc.NY)
2606 					m_mmc.DY+=m_mmc.TY;
2607 				m_cont_reg[42]=m_mmc.NY & 0xFF;
2608 				m_cont_reg[43]=(m_mmc.NY>>8) & 0x03;
2609 				m_cont_reg[34]=m_mmc.SY & 0xFF;
2610 				m_cont_reg[35]=(m_mmc.SY>>8) & 0x03;
2611 			}
2612 			else {
2613 				m_mmc.ASX=m_mmc.SX;
2614 				m_mmc.ANX=m_mmc.NX;
2615 			}
2616 		}
2617 	}
2618 }
2619 
2620 /** lmmc_engine() *********************************************/
2621 /** CPU -> Vram                                             **/
2622 /*************************************************************/
lmmc_engine()2623 void v99x8_device::lmmc_engine()
2624 {
2625 	if ((m_stat_reg[2]&0x80)!=0x80) {
2626 		uint8_t SM=((m_mode >= 5) && (m_mode <= 8)) ? (m_mode-5) : 0;
2627 
2628 		m_stat_reg[7]=m_cont_reg[44]&=Mask[SM];
2629 		VDP_PSET(SM, m_mmc.MXD, m_mmc.ADX, m_mmc.DY, m_cont_reg[44], m_mmc.LO);
2630 		m_vdp_ops_count-=get_vdp_timing_value(lmmv_timing);
2631 		m_stat_reg[2]|=0x80;
2632 
2633 		if (!--m_mmc.ANX || ((m_mmc.ADX+=m_mmc.TX)&m_mmc.MX)) {
2634 			if (!(--m_mmc.NY&1023) || (m_mmc.DY+=m_mmc.TY)==-1) {
2635 				m_stat_reg[2]&=0xFE;
2636 				m_vdp_engine=nullptr;
2637 				if (!m_mmc.NY)
2638 					m_mmc.DY+=m_mmc.TY;
2639 				m_cont_reg[42]=m_mmc.NY & 0xFF;
2640 				m_cont_reg[43]=(m_mmc.NY>>8) & 0x03;
2641 				m_cont_reg[38]=m_mmc.DY & 0xFF;
2642 				m_cont_reg[39]=(m_mmc.DY>>8) & 0x03;
2643 			}
2644 			else {
2645 				m_mmc.ADX=m_mmc.DX;
2646 				m_mmc.ANX=m_mmc.NX;
2647 			}
2648 		}
2649 	}
2650 }
2651 
2652 /** hmmv_engine() *********************************************/
2653 /** VDP --> Vram                                            **/
2654 /*************************************************************/
hmmv_engine()2655 void v99x8_device::hmmv_engine()
2656 {
2657 	int DX=m_mmc.DX;
2658 	int DY=m_mmc.DY;
2659 	int TX=m_mmc.TX;
2660 	int TY=m_mmc.TY;
2661 	int NX=m_mmc.NX;
2662 	int NY=m_mmc.NY;
2663 	int ADX=m_mmc.ADX;
2664 	int ANX=m_mmc.ANX;
2665 	uint8_t CL=m_mmc.CL;
2666 	int MXD = m_mmc.MXD;
2667 	int cnt;
2668 	int delta;
2669 
2670 	delta = get_vdp_timing_value(hmmv_timing);
2671 	cnt = m_vdp_ops_count;
2672 
2673 	switch (m_mode) {
2674 	default:
2675 	case V9938_MODE_GRAPHIC4: pre_loop m_vram_space->write_byte(VDP_VRMP5(MXD, ADX, DY), CL); post__x_y(256)
2676 		break;
2677 	case V9938_MODE_GRAPHIC5: pre_loop m_vram_space->write_byte(VDP_VRMP6(MXD, ADX, DY), CL); post__x_y(512)
2678 		break;
2679 	case V9938_MODE_GRAPHIC6: pre_loop m_vram_space->write_byte(VDP_VRMP7(MXD, ADX, DY), CL); post__x_y(512)
2680 		break;
2681 	case V9938_MODE_GRAPHIC7: pre_loop m_vram_space->write_byte(VDP_VRMP8(MXD, ADX, DY), CL); post__x_y(256)
2682 		break;
2683 	}
2684 
2685 	if ((m_vdp_ops_count=cnt)>0) {
2686 		// Command execution done
2687 		m_stat_reg[2]&=0xFE;
2688 		m_vdp_engine=nullptr;
2689 		if (!NY)
2690 			DY+=TY;
2691 		m_cont_reg[42]=NY & 0xFF;
2692 		m_cont_reg[43]=(NY>>8) & 0x03;
2693 		m_cont_reg[38]=DY & 0xFF;
2694 		m_cont_reg[39]=(DY>>8) & 0x03;
2695 	}
2696 	else {
2697 		m_mmc.DY=DY;
2698 		m_mmc.NY=NY;
2699 		m_mmc.ANX=ANX;
2700 		m_mmc.ADX=ADX;
2701 	}
2702 }
2703 
2704 /** hmmm_engine() *********************************************/
2705 /** Vram -> Vram                                            **/
2706 /*************************************************************/
hmmm_engine()2707 void v99x8_device::hmmm_engine()
2708 {
2709 	int SX=m_mmc.SX;
2710 	int SY=m_mmc.SY;
2711 	int DX=m_mmc.DX;
2712 	int DY=m_mmc.DY;
2713 	int TX=m_mmc.TX;
2714 	int TY=m_mmc.TY;
2715 	int NX=m_mmc.NX;
2716 	int NY=m_mmc.NY;
2717 	int ASX=m_mmc.ASX;
2718 	int ADX=m_mmc.ADX;
2719 	int ANX=m_mmc.ANX;
2720 	int MXS = m_mmc.MXS;
2721 	int MXD = m_mmc.MXD;
2722 	int cnt;
2723 	int delta;
2724 
2725 	delta = get_vdp_timing_value(hmmm_timing);
2726 	cnt = m_vdp_ops_count;
2727 
2728 	switch (m_mode) {
2729 	default:
2730 	case V9938_MODE_GRAPHIC4: pre_loop m_vram_space->write_byte(VDP_VRMP5(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP5(MXS, ASX, SY))); post_xxyy(256)
2731 		break;
2732 	case V9938_MODE_GRAPHIC5: pre_loop m_vram_space->write_byte(VDP_VRMP6(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP6(MXS, ASX, SY))); post_xxyy(512)
2733 		break;
2734 	case V9938_MODE_GRAPHIC6: pre_loop m_vram_space->write_byte(VDP_VRMP7(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP7(MXS, ASX, SY))); post_xxyy(512)
2735 		break;
2736 	case V9938_MODE_GRAPHIC7: pre_loop m_vram_space->write_byte(VDP_VRMP8(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP8(MXS, ASX, SY))); post_xxyy(256)
2737 		break;
2738 	}
2739 
2740 	if ((m_vdp_ops_count=cnt)>0) {
2741 		// Command execution done
2742 		m_stat_reg[2]&=0xFE;
2743 		m_vdp_engine=nullptr;
2744 		if (!NY) {
2745 			SY+=TY;
2746 			DY+=TY;
2747 		}
2748 		else
2749 			if (SY==-1)
2750 			DY+=TY;
2751 		m_cont_reg[42]=NY & 0xFF;
2752 		m_cont_reg[43]=(NY>>8) & 0x03;
2753 		m_cont_reg[34]=SY & 0xFF;
2754 		m_cont_reg[35]=(SY>>8) & 0x03;
2755 		m_cont_reg[38]=DY & 0xFF;
2756 		m_cont_reg[39]=(DY>>8) & 0x03;
2757 	}
2758 	else {
2759 		m_mmc.SY=SY;
2760 		m_mmc.DY=DY;
2761 		m_mmc.NY=NY;
2762 		m_mmc.ANX=ANX;
2763 		m_mmc.ASX=ASX;
2764 		m_mmc.ADX=ADX;
2765 	}
2766 }
2767 
2768 /** ymmm_engine() *********************************************/
2769 /** Vram -> Vram                                            **/
2770 /*************************************************************/
2771 
ymmm_engine()2772 void v99x8_device::ymmm_engine()
2773 {
2774 	int SY=m_mmc.SY;
2775 	int DX=m_mmc.DX;
2776 	int DY=m_mmc.DY;
2777 	int TX=m_mmc.TX;
2778 	int TY=m_mmc.TY;
2779 	int NY=m_mmc.NY;
2780 	int ADX=m_mmc.ADX;
2781 	int MXD = m_mmc.MXD;
2782 	int cnt;
2783 	int delta;
2784 
2785 	delta = get_vdp_timing_value(ymmm_timing);
2786 	cnt = m_vdp_ops_count;
2787 
2788 	switch (m_mode) {
2789 	default:
2790 	case V9938_MODE_GRAPHIC4: pre_loop m_vram_space->write_byte(VDP_VRMP5(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP5(MXD, ADX, SY))); post__xyy(256)
2791 		break;
2792 	case V9938_MODE_GRAPHIC5: pre_loop m_vram_space->write_byte(VDP_VRMP6(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP6(MXD, ADX, SY))); post__xyy(512)
2793 		break;
2794 	case V9938_MODE_GRAPHIC6: pre_loop m_vram_space->write_byte(VDP_VRMP7(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP7(MXD, ADX, SY))); post__xyy(512)
2795 		break;
2796 	case V9938_MODE_GRAPHIC7: pre_loop m_vram_space->write_byte(VDP_VRMP8(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP8(MXD, ADX, SY))); post__xyy(256)
2797 		break;
2798 	}
2799 
2800 	if ((m_vdp_ops_count=cnt)>0) {
2801 		// Command execution done
2802 		m_stat_reg[2]&=0xFE;
2803 		m_vdp_engine=nullptr;
2804 		if (!NY) {
2805 			SY+=TY;
2806 			DY+=TY;
2807 		}
2808 		else
2809 			if (SY==-1)
2810 			DY+=TY;
2811 		m_cont_reg[42]=NY & 0xFF;
2812 		m_cont_reg[43]=(NY>>8) & 0x03;
2813 		m_cont_reg[34]=SY & 0xFF;
2814 		m_cont_reg[35]=(SY>>8) & 0x03;
2815 		m_cont_reg[38]=DY & 0xFF;
2816 		m_cont_reg[39]=(DY>>8) & 0x03;
2817 	}
2818 	else {
2819 		m_mmc.SY=SY;
2820 		m_mmc.DY=DY;
2821 		m_mmc.NY=NY;
2822 		m_mmc.ADX=ADX;
2823 	}
2824 }
2825 
2826 /** hmmc_engine() *********************************************/
2827 /** CPU -> Vram                                             **/
2828 /*************************************************************/
hmmc_engine()2829 void v99x8_device::hmmc_engine()
2830 {
2831 	if ((m_stat_reg[2]&0x80)!=0x80) {
2832 		m_vram_space->write_byte(VDP_VRMP(((m_mode >= 5) && (m_mode <= 8)) ? (m_mode-5) : 0, m_mmc.MXD, m_mmc.ADX, m_mmc.DY), m_cont_reg[44]);
2833 		m_vdp_ops_count -= get_vdp_timing_value(hmmv_timing);
2834 		m_stat_reg[2]|=0x80;
2835 
2836 		if (!--m_mmc.ANX || ((m_mmc.ADX+=m_mmc.TX)&m_mmc.MX)) {
2837 			if (!(--m_mmc.NY&1023) || (m_mmc.DY+=m_mmc.TY)==-1) {
2838 				m_stat_reg[2]&=0xFE;
2839 				m_vdp_engine=nullptr;
2840 				if (!m_mmc.NY)
2841 					m_mmc.DY+=m_mmc.TY;
2842 				m_cont_reg[42]=m_mmc.NY & 0xFF;
2843 				m_cont_reg[43]=(m_mmc.NY>>8) & 0x03;
2844 				m_cont_reg[38]=m_mmc.DY & 0xFF;
2845 				m_cont_reg[39]=(m_mmc.DY>>8) & 0x03;
2846 			}
2847 			else {
2848 				m_mmc.ADX=m_mmc.DX;
2849 				m_mmc.ANX=m_mmc.NX;
2850 			}
2851 		}
2852 	}
2853 }
2854 
2855 /** VDPWrite() ***********************************************/
2856 /** Use this function to transfer pixel(s) from CPU to m_ **/
2857 /*************************************************************/
cpu_to_vdp(uint8_t V)2858 void v99x8_device::cpu_to_vdp(uint8_t V)
2859 {
2860 	m_stat_reg[2]&=0x7F;
2861 	m_stat_reg[7]=m_cont_reg[44]=V;
2862 	if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
2863 }
2864 
2865 /** VDPRead() ************************************************/
2866 /** Use this function to transfer pixel(s) from VDP to CPU. **/
2867 /*************************************************************/
vdp_to_cpu()2868 uint8_t v99x8_device::vdp_to_cpu()
2869 {
2870 	m_stat_reg[2]&=0x7F;
2871 	if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
2872 	return(m_cont_reg[44]);
2873 }
2874 
2875 /** report_vdp_command() ***************************************/
2876 /** Report VDP Command to be executed                       **/
2877 /*************************************************************/
report_vdp_command(uint8_t Op)2878 void v99x8_device::report_vdp_command(uint8_t Op)
2879 {
2880 	static const char *const Ops[16] =
2881 	{
2882 		"SET ","AND ","OR  ","XOR ","NOT ","NOP ","NOP ","NOP ",
2883 		"TSET","TAND","TOR ","TXOR","TNOT","NOP ","NOP ","NOP "
2884 	};
2885 	static const char *const Commands[16] =
2886 	{
2887 		" ABRT"," ????"," ????"," ????","POINT"," PSET"," SRCH"," LINE",
2888 		" LMMV"," LMMM"," LMCM"," LMMC"," HMMV"," HMMM"," YMMM"," HMMC"
2889 	};
2890 
2891 	uint8_t CL, CM, LO;
2892 	int SX,SY, DX,DY, NX,NY;
2893 
2894 	// Fetch arguments
2895 	CL = m_cont_reg[44];
2896 	SX = (m_cont_reg[32]+((int)m_cont_reg[33]<<8)) & 511;
2897 	SY = (m_cont_reg[34]+((int)m_cont_reg[35]<<8)) & 1023;
2898 	DX = (m_cont_reg[36]+((int)m_cont_reg[37]<<8)) & 511;
2899 	DY = (m_cont_reg[38]+((int)m_cont_reg[39]<<8)) & 1023;
2900 	NX = (m_cont_reg[40]+((int)m_cont_reg[41]<<8)) & 1023;
2901 	NY = (m_cont_reg[42]+((int)m_cont_reg[43]<<8)) & 1023;
2902 	CM = Op>>4;
2903 	LO = Op&0x0F;
2904 
2905 	LOGMASKED(LOG_COMMAND, "Opcode %02x %s-%s s=(%d,%d), d=(%d,%d), c=%02x, wh=[%d,%d]%s\n",
2906 		Op, Commands[CM], Ops[LO],
2907 		SX,SY, DX,DY, CL&0xff, m_cont_reg[45]&0x04? -NX:NX,
2908 		m_cont_reg[45]&0x08? -NY:NY,
2909 		m_cont_reg[45]&0x70? " on ExtVRAM":""
2910 		);
2911 }
2912 
2913 /** VDPDraw() ************************************************/
2914 /** Perform a given V9938 operation Op.                     **/
2915 /*************************************************************/
command_unit_w(uint8_t Op)2916 uint8_t v99x8_device::command_unit_w(uint8_t Op)
2917 {
2918 	int SM;
2919 
2920 	// V9938 ops only work in SCREENs 5-8
2921 	if (m_mode<5)
2922 		return(0);
2923 
2924 	SM = m_mode-5;         // Screen mode index 0..3
2925 
2926 	m_mmc.CM = Op>>4;
2927 	if ((m_mmc.CM & 0x0C) != 0x0C && m_mmc.CM != 0)
2928 		// Dot operation: use only relevant bits of color
2929 	m_stat_reg[7]=(m_cont_reg[44]&=Mask[SM]);
2930 
2931 	//  if(Verbose&0x02)
2932 	report_vdp_command(Op);
2933 
2934 	if ((m_vdp_engine != nullptr) && (m_mmc.CM != CM_ABRT))
2935 		LOGMASKED(LOG_WARN, "Command overrun; previous command not completed\n");
2936 
2937 	switch(Op>>4) {
2938 	case CM_ABRT:
2939 		m_stat_reg[2]&=0xFE;
2940 		m_vdp_engine=nullptr;
2941 		return 1;
2942 	case CM_POINT:
2943 		m_stat_reg[2]&=0xFE;
2944 		m_vdp_engine=nullptr;
2945 		m_stat_reg[7]=m_cont_reg[44]=
2946 		VDP_POINT(SM, (m_cont_reg[45] & 0x10) != 0,
2947 			m_cont_reg[32]+((int)m_cont_reg[33]<<8),
2948 			m_cont_reg[34]+((int)m_cont_reg[35]<<8));
2949 		return 1;
2950 	case CM_PSET:
2951 		m_stat_reg[2]&=0xFE;
2952 		m_vdp_engine=nullptr;
2953 		VDP_PSET(SM, (m_cont_reg[45] & 0x20) != 0,
2954 			m_cont_reg[36]+((int)m_cont_reg[37]<<8),
2955 			m_cont_reg[38]+((int)m_cont_reg[39]<<8),
2956 			m_cont_reg[44],
2957 			Op&0x0F);
2958 		return 1;
2959 	case CM_SRCH:
2960 		m_vdp_engine=&v99x8_device::srch_engine;
2961 		break;
2962 	case CM_LINE:
2963 		m_vdp_engine=&v99x8_device::line_engine;
2964 		break;
2965 	case CM_LMMV:
2966 		m_vdp_engine=&v99x8_device::lmmv_engine;
2967 		break;
2968 	case CM_LMMM:
2969 		m_vdp_engine=&v99x8_device::lmmm_engine;
2970 		break;
2971 	case CM_LMCM:
2972 		m_vdp_engine=&v99x8_device::lmcm_engine;
2973 		break;
2974 	case CM_LMMC:
2975 		m_vdp_engine=&v99x8_device::lmmc_engine;
2976 		break;
2977 	case CM_HMMV:
2978 		m_vdp_engine=&v99x8_device::hmmv_engine;
2979 		break;
2980 	case CM_HMMM:
2981 		m_vdp_engine=&v99x8_device::hmmm_engine;
2982 		break;
2983 	case CM_YMMM:
2984 		m_vdp_engine=&v99x8_device::ymmm_engine;
2985 		break;
2986 	case CM_HMMC:
2987 		m_vdp_engine=&v99x8_device::hmmc_engine;
2988 		break;
2989 	default:
2990 		LOGMASKED(LOG_WARN, "Unrecognized opcode %02Xh\n",Op);
2991 		return(0);
2992 	}
2993 
2994 	// Fetch unconditional arguments
2995 	m_mmc.SX = (m_cont_reg[32]+((int)m_cont_reg[33]<<8)) & 511;
2996 	m_mmc.SY = (m_cont_reg[34]+((int)m_cont_reg[35]<<8)) & 1023;
2997 	m_mmc.DX = (m_cont_reg[36]+((int)m_cont_reg[37]<<8)) & 511;
2998 	m_mmc.DY = (m_cont_reg[38]+((int)m_cont_reg[39]<<8)) & 1023;
2999 	m_mmc.NY = (m_cont_reg[42]+((int)m_cont_reg[43]<<8)) & 1023;
3000 	m_mmc.TY = m_cont_reg[45]&0x08? -1:1;
3001 	m_mmc.MX = PPL[SM];
3002 	m_mmc.CL = m_cont_reg[44];
3003 	m_mmc.LO = Op&0x0F;
3004 	m_mmc.MXS = (m_cont_reg[45] & 0x10) != 0;
3005 	m_mmc.MXD = (m_cont_reg[45] & 0x20) != 0;
3006 
3007 	// Argument depends on uint8_t or dot operation
3008 	if ((m_mmc.CM & 0x0C) == 0x0C) {
3009 		m_mmc.TX = m_cont_reg[45]&0x04? -PPB[SM]:PPB[SM];
3010 		m_mmc.NX = ((m_cont_reg[40]+((int)m_cont_reg[41]<<8)) & 1023)/PPB[SM];
3011 	}
3012 	else {
3013 		m_mmc.TX = m_cont_reg[45]&0x04? -1:1;
3014 		m_mmc.NX = (m_cont_reg[40]+((int)m_cont_reg[41]<<8)) & 1023;
3015 	}
3016 
3017 	// X loop variables are treated specially for LINE command
3018 	if (m_mmc.CM == CM_LINE) {
3019 		m_mmc.ASX=((m_mmc.NX-1)>>1);
3020 		m_mmc.ADX=0;
3021 	}
3022 	else {
3023 		m_mmc.ASX = m_mmc.SX;
3024 		m_mmc.ADX = m_mmc.DX;
3025 	}
3026 
3027 	// NX loop variable is treated specially for SRCH command
3028 	if (m_mmc.CM == CM_SRCH)
3029 		m_mmc.ANX=(m_cont_reg[45]&0x02)!=0; // Do we look for "==" or "!="?
3030 	else
3031 		m_mmc.ANX = m_mmc.NX;
3032 
3033 	// Command execution started
3034 	m_stat_reg[2]|=0x01;
3035 
3036 	// Start execution if we still have time slices
3037 	if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
3038 
3039 	// Operation successfully initiated
3040 	return(1);
3041 }
3042 
3043 /** LoopVDP() ************************************************
3044 Run X steps of active VDP command
3045 *************************************************************/
update_command()3046 void v99x8_device::update_command()
3047 {
3048 	if(m_vdp_ops_count<=0)
3049 	{
3050 		m_vdp_ops_count+=13662;
3051 		if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
3052 	}
3053 	else
3054 	{
3055 		m_vdp_ops_count=13662;
3056 		if(m_vdp_engine) (this->*m_vdp_engine)();
3057 	}
3058 }
3059 
device_post_load()3060 void v99x8_device::device_post_load() // TODO: is there a better way to restore this?
3061 {
3062 	switch(m_mmc.CM)
3063 	{
3064 	case CM_ABRT:
3065 	case CM_POINT:
3066 	case CM_PSET:
3067 		m_vdp_engine=nullptr;
3068 		break;
3069 	case CM_SRCH:
3070 		m_vdp_engine=&v99x8_device::srch_engine;
3071 		break;
3072 	case CM_LINE:
3073 		m_vdp_engine=&v99x8_device::line_engine;
3074 		break;
3075 	case CM_LMMV:
3076 		m_vdp_engine=&v99x8_device::lmmv_engine;
3077 		break;
3078 	case CM_LMMM:
3079 		m_vdp_engine=&v99x8_device::lmmm_engine;
3080 		break;
3081 	case CM_LMCM:
3082 		m_vdp_engine=&v99x8_device::lmcm_engine;
3083 		break;
3084 	case CM_LMMC:
3085 		m_vdp_engine=&v99x8_device::lmmc_engine;
3086 		break;
3087 	case CM_HMMV:
3088 		m_vdp_engine=&v99x8_device::hmmv_engine;
3089 		break;
3090 	case CM_HMMM:
3091 		m_vdp_engine=&v99x8_device::hmmm_engine;
3092 		break;
3093 	case CM_YMMM:
3094 		m_vdp_engine=&v99x8_device::ymmm_engine;
3095 		break;
3096 	case CM_HMMC:
3097 		m_vdp_engine=&v99x8_device::hmmc_engine;
3098 		break;
3099 	}
3100 }
3101