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