1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles, Nathan Woods
4 /***************************************************************************
6 v9938 / v9958 emulation
8 Vertical display parameters from Yamaha V9938 Technical Data Book.
9 NTSC: page 146, Table 7-2
10 PAL: page 147, Table 7-3
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
24 Refresh rate 50.158974 59.922743
27 ***************************************************************************/
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
41 #define m_vram_space this
44 //#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
45 #define LOG(x) void(x)
62 #define MODEL_V9938 (0)
63 #define MODEL_V9958 (1)
65 #define EXPMEM_OFFSET 0x20000
67 // LONG_WIDTH has already defined limits.h for GCC.20180614 K.O
68 #define _V9938_LONG_WIDTH (512 + 32)
70 static const char *const v9938_modes[] = {
71 "TEXT 1", "MULTICOLOR", "GRAPHIC 1", "GRAPHIC 2", "GRAPHIC 3",
72 "GRAPHIC 4", "GRAPHIC 5", "GRAPHIC 6", "GRAPHIC 7", "TEXT 2",
76 //**************************************************************************
78 //**************************************************************************
81 Similar to the TMS9928, the V9938 has an own address space. It can handle
82 at most 192 KiB RAM (128 KiB base, 64 KiB expansion).
84 //static ADDRESS_MAP_START(memmap, AS_DATA, 8, v99x8_device)
85 // ADDRESS_MAP_GLOBAL_MASK(0x3ffff)
86 // AM_RANGE(0x00000, 0x2ffff) AM_RAM
91 //const device_type V9938 = &device_creator<v9938_device>;
92 //const device_type V9958 = &device_creator<v9958_device>;
95 //v99x8_device::v99x8_device(const machine_config &mconfig, device_type type, const char *name, const char *shortname, const char *tag, device_t *owner, UINT32 clock)
96 //: device_t(mconfig, type, name, tag, owner, clock, shortname, __FILE__),
97 v99x8_device::v99x8_device(VM_TEMPLATE* parent_vm, EMU* parent_emu)
98 : DEVICE(parent_vm, parent_emu),
99 // device_memory_interface(mconfig, *this),
100 // device_video_interface(mconfig, *this),
101 // m_space_config("vram", ENDIANNESS_BIG, 8, 18),
107 m_pal_write_first(0),
108 m_cmd_write_first(0),
114 m_vram_size(/*0*/0x20000),
116 // m_int_callback(*this),
125 // m_palette(*this, "palette"),
128 // static_set_addrmap(*this, AS_DATA, ADDRESS_MAP_NAME(memmap));
129 initialize_output_signals(&outputs_irq);
130 set_device_name(_T("V99x8 VDP"));
133 //v9938_device::v9938_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
134 //: v99x8_device(mconfig, V9938, "V9938 VDP", "v9938", tag, owner, clock)
135 v9938_device::v9938_device(VM_TEMPLATE* parent_vm, EMU* parent_emu)
136 : v99x8_device(parent_vm, parent_emu)
138 m_model = MODEL_V9938;
140 set_device_name(_T("V9938 VDP"));
143 //v9958_device::v9958_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
144 //: v99x8_device(mconfig, V9938, "V9958 VDP", "v9958", tag, owner, clock)
145 v9958_device::v9958_device(VM_TEMPLATE* parent_vm, EMU* parent_emu)
146 : v99x8_device(parent_vm, parent_emu)
148 m_model = MODEL_V9958;
150 set_device_name(_T("V9958 VDP"));
154 //void v99x8_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
155 void v99x8_device::device_timer(int v)
157 int scanline = (m_scanline - (m_scanline_start + m_offset_y));
162 if (m_scanline == (m_scanline_start + m_offset_y))
164 m_stat_reg[2] &= ~0x40;
166 else if (m_scanline == (m_scanline_start + m_offset_y + m_visible_y))
168 m_stat_reg[2] |= 0x40;
169 m_stat_reg[0] |= 0x80;
172 if ( (scanline >= 0) && (scanline <= m_scanline_max) &&
173 (((scanline + m_cont_reg[23]) & 255) == m_cont_reg[19]) )
176 LOG(("V9938: scanline interrupt (%d)\n", scanline));
178 else if (!(m_cont_reg[0] & 0x10))
180 m_stat_reg[1] &= 0xfe;
185 // check for start of vblank
186 if (m_scanline == m_vblank_start)
188 interrupt_start_vblank();
191 // render the current line
192 if (m_scanline < m_vblank_start)
194 refresh_line(scanline);
197 if (++m_scanline >= m_height)
201 /*int pal = m_cont_reg[9] & 2;
202 if (m_pal_ntsc != pal)
205 configure_pal_ntsc();
206 }*/ /* umaiboux: NTSC only! */
207 //m_screen->reset_origin();
208 m_offset_y = position_offset(m_cont_reg[18] >> 4);
209 set_screen_parameters();
214 void v99x8_device::set_screen_parameters()
219 m_scanline_start = (m_cont_reg[9] & 0x80) ? 43 : 53;
220 m_scanline_max = 255;
225 m_scanline_start = (m_cont_reg[9] & 0x80) ? 16 : 26;
226 m_scanline_max = (m_cont_reg[9] & 0x80) ? 234 : 244;
228 m_visible_y = (m_cont_reg[9] & 0x80) ? 212 : 192;
232 void v99x8_device::configure_pal_ntsc()
237 m_height = VTOTAL_PAL;
238 // rectangle visible;
239 // visible.set(0, HVISIBLE - 1, VERTICAL_ADJUST * 2, VVISIBLE_PAL * 2 - 1 - VERTICAL_ADJUST * 2);
240 // m_screen->configure(HTOTAL, VTOTAL_PAL * 2, visible, HZ_TO_ATTOSECONDS(50.158974));
245 m_height = VTOTAL_NTSC;
246 // rectangle visible;
247 // visible.set(0, HVISIBLE - 1, VERTICAL_ADJUST * 2, VVISIBLE_NTSC * 2 - 1 - VERTICAL_ADJUST * 2);
248 // m_screen->configure(HTOTAL, VTOTAL_NTSC * 2, visible, HZ_TO_ATTOSECONDS(59.922743));
250 m_vblank_start = m_height - VERTICAL_SYNC - TOP_ERASE; /* Sync + top erase */
255 Not really right... won't work with sprites in graphics 7
256 and with palette updated mid-screen
258 int v99x8_device::get_transpen()
260 if (m_mode == V9938_MODE_GRAPHIC7)
262 return m_pal_ind256[0];
266 return m_pal_ind16[0];
271 Driver-specific function: update the vdp mouse state
273 /*void v99x8_device::update_mouse_state(int mx_delta, int my_delta, int button_state)
276 m_button_state = (button_state << 6) & 0xc0;
278 if ((m_cont_reg[8] & 0xc0) == 0x80)
279 { // vdp will process mouse deltas only if it is in mouse mode
280 m_mx_delta += mx_delta;
281 m_my_delta += my_delta;
287 /***************************************************************************
291 ***************************************************************************/
294 About the colour burst registers:
296 The color burst registers will only have effect on the composite video output from
297 the V9938. but the output is only NTSC (Never The Same Color ,so the
298 effects are already present) . this system is not used in europe
299 the european machines use a separate PAL (Phase Alternating Line) encoder
300 or no encoder at all , only RGB output.
305 Right now they're not emulated. For completeness sake they should -- with
306 a dip-switch to turn them off. I really don't know how they work though. :(
310 In screen 8, the colors are encoded as:
313 +--+--+--+--+--+--+--+--+
314 |g2|g1|g0|r2|r1|r0|b2|b1|
315 +--+--+--+--+--+--+--+--+
317 b0 is set if b2 and b1 are set (remember, color bus is 3 bits)
321 /*PALETTE_INIT_MEMBER(v9938_device, v9938)*/
322 void v9938_device::init_palette()
326 // create the full 512 colour palette
328 // palette.set_pen_color(i, pal3bit(i >> 6), pal3bit(i >> 3), pal3bit(i >> 0));
329 this->set_pen_color(i, pal3bit(i >> 6), pal3bit(i >> 3), pal3bit(i >> 0));
334 The v9958 can display up to 19286 colours. For this we need a larger palette.
336 The colours are encoded in 17 bits; however there are just 19268 different colours.
337 Here we calculate the palette and a 2^17 reference table to the palette,
338 which is: s_pal_indYJK. It's 256K in size, but I can't think of a faster way
339 to emulate this. Also it keeps the palette a reasonable size. :)
343 UINT16 v99x8_device::s_pal_indYJK[0x20000];
345 /*PALETTE_INIT_MEMBER(v9958_device, v9958)*/
346 void v9958_device::init_palette()
348 int r,g,b,y,j,k,i,k0,j0,n;
351 // init v9938 512-color palette
353 // palette.set_pen_color(i, pal3bit(i >> 6), pal3bit(i >> 3), pal3bit(i >> 0));
354 this->set_pen_color(i, pal3bit(i >> 6), pal3bit(i >> 3), pal3bit(i >> 0));
357 // if(palette.entries() != 19780)
358 // fatalerror("V9958: not enough palette, must be 19780");
361 LOG(("Building YJK table for V9958 screens, may take a while ... \n"));
363 for (y=0;y<32;y++) for (k=0;k<64;k++) for (j=0;j<64;j++)
365 // calculate the color
366 if (k >= 32) k0 = (k - 64); else k0 = k;
367 if (j >= 32) j0 = (j - 64); else j0 = j;
369 b = (y * 5 - 2 * j0 - k0) / 4;
371 if (r < 0) r = 0; else if (r > 31) r = 31;
372 if (g < 0) g = 0; else if (g > 31) g = 31;
373 if (b < 0) b = 0; else if (b > 31) b = 31;
375 //r = (r << 3) | (r >> 2);
376 //b = (b << 3) | (b >> 2);
377 //g = (g << 3) | (g >> 2);
378 // have we seen this one before?
382 if (pal[n*3+0] == r && pal[n*3+1] == g && pal[n*3+2] == b)
384 v99x8_device::s_pal_indYJK[y | j << 5 | k << (5 + 6)] = n + 512;
392 // so we haven't; add it
396 // palette.set_pen_color(i+512, rgb_t(pal5bit(r), pal5bit(g), pal5bit(b)));
397 this->set_pen_color(i+512, pal5bit(r), pal5bit(g), pal5bit(b));
398 v99x8_device::s_pal_indYJK[y | j << 5 | k << (5 + 6)] = i + 512;
404 LOG( ("Table creation failed - %d colours out of 19286 created\n", i));
407 /*UINT32 v99x8_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
409 copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
413 /*READ8_MEMBER( v99x8_device::read )*/
414 uint32_t v99x8_device::read_io8(uint32_t offset)
418 case 0: return vram_r();
419 case 1: return status_r();
424 /*WRITE8_MEMBER( v99x8_device::write )*/
425 void v99x8_device::write_io8(uint32_t offset, uint32_t data)
429 case 0: vram_w(data); break;
430 case 1: command_w(data); break;
431 case 2: palette_w(data); break;
432 case 3: register_w(data); break;
436 UINT8 v99x8_device::vram_r()
441 address = ((int)m_cont_reg[14] << 14) | m_address_latch;
443 m_cmd_write_first = 0;
447 if (m_cont_reg[45] & 0x40) // Expansion memory
449 if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
450 address >>= 1; // correct?
451 // Expansion memory only offers 64 K
452 if (m_vram_size > 0x20000 && ((address & 0x10000)==0))
453 m_read_ahead = m_vram_space->read_byte(address + EXPMEM_OFFSET);
459 m_read_ahead = vram_read(address);
462 m_address_latch = (m_address_latch + 1) & 0x3fff;
463 if ((!m_address_latch) && (m_cont_reg[0] & 0x0c) ) // correct ???
465 m_cont_reg[14] = (m_cont_reg[14] + 1) & 7;
471 UINT8 v99x8_device::status_r()
476 m_cmd_write_first = 0;
478 reg = m_cont_reg[15] & 0x0f;
486 m_stat_reg[0] &= 0x1f;
490 m_stat_reg[1] &= 0xfe;
491 if ((m_cont_reg[8] & 0xc0) == 0x80)
492 // mouse mode: add button state
493 ret |= m_button_state & 0xc0;
496 /*update_command ();*/
498 WTF is this? Whatever this was intended to do, it is nonsensical.
499 Might as well pick a random number....
500 This was an attempt to emulate H-Blank flag ;)
501 n = cycles_currently_ran ();
502 if ( (n < 28) || (n > 199) ) vdp.statReg[2] |= 0x20;
503 else vdp.statReg[2] &= ~0x20;
505 // if (machine().rand() & 1) m_stat_reg[2] |= 0x20;
506 // else m_stat_reg[2] &= ~0x20;
510 if ((m_cont_reg[8] & 0xc0) == 0x80)
511 { // mouse mode: return x mouse delta
519 if ((m_cont_reg[8] & 0xc0) == 0x80)
520 { // mouse mode: return y mouse delta
529 m_stat_reg[7] = m_cont_reg[44] = vdp_to_cpu () ;
532 ret = m_stat_reg[reg];
536 LOG(("V9938: Read %02x from S#%d\n", ret, reg));
542 void v99x8_device::palette_w(UINT8 data)
546 if (m_pal_write_first)
549 indexp = m_cont_reg[0x10] & 15;
550 m_pal_reg[indexp*2] = m_pal_write & 0x77;
551 m_pal_reg[indexp*2+1] = data & 0x07;
553 m_pal_ind16[indexp] = (((int)m_pal_write << 2) & 0x01c0) |
554 (((int)data << 3) & 0x0038) |
555 ((int)m_pal_write & 0x0007);
557 m_cont_reg[0x10] = (m_cont_reg[0x10] + 1) & 15;
558 m_pal_write_first = 0;
563 m_pal_write_first = 1;
567 void v99x8_device::vram_w(UINT8 data)
571 /*update_command ();*/
573 m_cmd_write_first = 0;
575 address = ((int)m_cont_reg[14] << 14) | m_address_latch;
577 if (m_cont_reg[45] & 0x40)
579 if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
580 address >>= 1; // correct?
581 if (m_vram_size > 0x20000 && ((address & 0x10000)==0))
582 m_vram_space->write_byte(EXPMEM_OFFSET + address, data);
586 vram_write(address, data);
589 m_address_latch = (m_address_latch + 1) & 0x3fff;
590 if ((!m_address_latch) && (m_cont_reg[0] & 0x0c) ) // correct ???
592 m_cont_reg[14] = (m_cont_reg[14] + 1) & 7;
596 void v99x8_device::command_w(UINT8 data)
598 if (m_cmd_write_first)
603 register_write (data & 0x3f, m_cmd_write);
608 (((UINT16)data << 8) | m_cmd_write) & 0x3fff;
609 if ( !(data & 0x40) ) vram_r (); // read ahead!
612 m_cmd_write_first = 0;
617 m_cmd_write_first = 1;
621 void v99x8_device::register_w(UINT8 data)
625 reg = m_cont_reg[17] & 0x3f;
627 register_write(reg, data); // true ?
629 if (!(m_cont_reg[17] & 0x80))
630 m_cont_reg[17] = (m_cont_reg[17] + 1) & 0x3f;
633 /*void v99x8_device::static_set_vram_size(device_t &device, UINT32 vram_size)
635 downcast<v99x8_device &>(device).m_vram_size = vram_size;
638 /***************************************************************************
640 Init/stop/reset/Interrupt functions
642 ***************************************************************************/
644 void v99x8_device::device_start()
646 // m_int_callback.resolve_safe();
650 // m_screen->register_screen_bitmap(m_bitmap);
652 // Video RAM is allocated as an own address space
653 // m_vram_space = &space(AS_DATA);
656 assert(m_vram_size > 0);
658 if (m_vram_size < 0x20000)
660 // set unavailable RAM to 0xff
661 for (int addr = m_vram_size; addr < 0x30000; addr++) m_vram_space->write_byte(addr, 0xff);
664 // m_line_timer = timer_alloc(TIMER_LINE);
666 /* save_item(NAME(m_offset_x));
667 save_item(NAME(m_offset_y));
668 save_item(NAME(m_visible_y));
669 save_item(NAME(m_mode));
670 save_item(NAME(m_pal_write_first));
671 save_item(NAME(m_cmd_write_first));
672 save_item(NAME(m_pal_write));
673 save_item(NAME(m_cmd_write));
674 save_item(NAME(m_pal_reg));
675 save_item(NAME(m_stat_reg));
676 save_item(NAME(m_cont_reg));
677 save_item(NAME(m_read_ahead));
678 // save_item(NAME(m_vram));
679 // if ( m_vram_exp != NULL )
680 // save_pointer(NAME(m_vram_exp), 0x10000);
681 save_item(NAME(m_int_state));
682 save_item(NAME(m_scanline));
683 save_item(NAME(m_blink));
684 save_item(NAME(m_blink_count));
685 save_item(NAME(m_mx_delta));
686 save_item(NAME(m_my_delta));
687 save_item(NAME(m_button_state));
688 save_item(NAME(m_pal_ind16));
689 save_item(NAME(m_pal_ind256));
690 save_item(NAME(m_mmc.SX));
691 save_item(NAME(m_mmc.SY));
692 save_item(NAME(m_mmc.DX));
693 save_item(NAME(m_mmc.DY));
694 save_item(NAME(m_mmc.TX));
695 save_item(NAME(m_mmc.TY));
696 save_item(NAME(m_mmc.NX));
697 save_item(NAME(m_mmc.NY));
698 save_item(NAME(m_mmc.MX));
699 save_item(NAME(m_mmc.ASX));
700 save_item(NAME(m_mmc.ADX));
701 save_item(NAME(m_mmc.ANX));
702 save_item(NAME(m_mmc.CL));
703 save_item(NAME(m_mmc.LO));
704 save_item(NAME(m_mmc.CM));
705 save_item(NAME(m_mmc.MXS));
706 save_item(NAME(m_mmc.MXD));
707 save_item(NAME(m_vdp_ops_count));
708 save_item(NAME(m_pal_ntsc));
709 save_item(NAME(m_scanline_start));
710 save_item(NAME(m_vblank_start));
711 save_item(NAME(m_scanline_max));
712 save_item(NAME(m_height));*/
715 void v99x8_device::device_reset()
724 reset_palette (); // palette registers
725 for (i=0;i<10;i++) m_stat_reg[i] = 0;
726 m_stat_reg[2] = 0x0c;
727 if (m_model == MODEL_V9958) m_stat_reg[1] |= 4;
728 for (i=0;i<48;i++) m_cont_reg[i] = 0;
729 m_cmd_write_first = m_pal_write_first = 0;
731 m_read_ahead = 0; m_address_latch = 0; // ???
733 // MZ: The status registers 4 and 6 hold the high bits of the sprite
734 // collision location. The unused bits are set to 1.
735 // SR3: x x x x x x x x
736 // SR4: 1 1 1 1 1 1 1 x
737 // SR5: y y y y y y y y
738 // SR6: 1 1 1 1 1 1 y y
739 // Note that status register 4 is used in detection algorithms to tell
740 // apart the tms9929 from the v99x8.
742 // TODO: SR3-S6 do not yet store the information about the sprite collision
743 m_stat_reg[4] = 0xfe;
744 m_stat_reg[6] = 0xfc;
747 // m_line_timer->adjust(attotime::from_ticks(HTOTAL*2, m_clock), 0, attotime::from_ticks(HTOTAL*2, m_clock));
749 configure_pal_ntsc();
750 set_screen_parameters();
754 void v99x8_device::reset_palette()
756 // taken from V9938 Technical Data book, page 148. it's in G-R-B format
757 static const UINT8 pal16[16*3] = {
758 0, 0, 0, // 0: black/transparent
760 6, 1, 1, // 2: medium green
761 7, 3, 3, // 3: light green
762 1, 1, 7, // 4: dark blue
763 3, 2, 7, // 5: light blue
764 1, 5, 1, // 6: dark red
766 1, 7, 1, // 8: medium red
767 3, 7, 3, // 9: light red
768 6, 6, 1, // 10: dark yellow
769 6, 6, 4, // 11: light yellow
770 4, 1, 1, // 12: dark green
771 2, 6, 5, // 13: magenta
779 // set the palette registers
780 m_pal_reg[i*2+0] = pal16[i*3+1] << 4 | pal16[i*3+2];
781 m_pal_reg[i*2+1] = pal16[i*3];
782 // set the reference table
783 m_pal_ind16[i] = pal16[i*3+1] << 6 | pal16[i*3] << 3 | pal16[i*3+2];
786 // set internal palette GRAPHIC 7
789 ind = (i << 4) & 0x01c0;
790 ind |= (i >> 2) & 0x0038;
791 red = (i << 1) & 6; if (red == 6) red++;
794 m_pal_ind256[i] = ind;
798 /***************************************************************************
802 ***************************************************************************/
804 void v99x8_device::vram_write(int offset, int data)
808 if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
810 newoffset = ((offset & 1) << 16) | (offset >> 1);
811 if (newoffset < m_vram_size)
812 m_vram_space->write_byte(newoffset, data);
816 if (offset < m_vram_size)
817 m_vram_space->write_byte(offset, data);
821 int v99x8_device::vram_read(int offset)
823 if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
824 return m_vram_space->read_byte(((offset & 1) << 16) | (offset >> 1));
826 return m_vram_space->read_byte(offset);
829 void v99x8_device::check_int()
833 n = ( (m_cont_reg[1] & 0x20) && (m_stat_reg[0] & 0x80) /*&& m_vblank_int*/) ||
834 ( (m_stat_reg[1] & 0x01) && (m_cont_reg[0] & 0x10) );
837 if(n && m_vblank_int)
843 if (n != m_int_state)
846 LOG(("V9938: IRQ line %s\n", n ? "up" : "down"));
850 ** Somehow the IRQ request is going down without cpu_irq_line () being
851 ** called; because of this Mr. Ghost, Xevious and SD Snatcher don't
852 ** run. As a patch it's called every scanline
854 // m_int_callback(n);
855 write_signals(&outputs_irq, n ? 0xffffffff : 0);
858 /***************************************************************************
862 ***************************************************************************/
864 void v99x8_device::register_write (int reg, int data)
866 static UINT8 const reg_mask[] =
868 0x7e, 0x7b, 0x7f, 0xff, 0x3f, 0xff, 0x3f, 0xff,
869 0xfb, 0xbf, 0x07, 0x03, 0xff, 0xff, 0x07, 0x0f,
870 0x0f, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
871 0x00, 0x7f, 0x3f, 0x07
876 data &= reg_mask[reg];
877 if (m_cont_reg[reg] == data)
883 LOG(("V9938: Attempted to write to non-existant R#%d\n", reg));
887 /*update_command ();*/
890 // registers that affect interrupt and display mode
893 m_cont_reg[reg] = data;
896 LOG(("v9938: mode = %s\n", v9938_modes[m_mode]));
901 m_cont_reg[reg] = data;
903 m_offset_x = 8 + position_offset(m_cont_reg[18] & 0x0f);
904 // Y offset is only applied once per frame?
908 m_pal_write_first = 0;
911 // color burst registers aren't emulated
915 LOG(("v9938: Write %02xh to R#%d; color burst not emulated\n", data, reg));
920 if (m_model != MODEL_V9958)
922 LOG(("v9938: Attempting to write %02xh to V9958 R#%d\n", data, reg));
928 m_v9958_sp_mode = data & 0x18;
937 command_unit_w (data);
942 LOG(("v9938: Write %02x to R#%d\n", data, reg));
944 m_cont_reg[reg] = data;
947 /***************************************************************************
949 Refresh / render function
951 ***************************************************************************/
953 inline bool v99x8_device::v9938_second_field()
955 return !(((m_cont_reg[9] & 0x04) && !(m_stat_reg[2] & 2)) || m_blink);
959 void v99x8_device::default_border(const scrntype_t *pens, scrntype_t *ln)
964 pen = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
965 i = _V9938_LONG_WIDTH;
966 while (i--) *ln++ = pen;
969 void v99x8_device::graphic7_border(const scrntype_t *pens, scrntype_t *ln)
974 pen = pens[m_pal_ind256[m_cont_reg[7]]];
975 i = _V9938_LONG_WIDTH;
976 while (i--) *ln++ = pen;
979 void v99x8_device::graphic5_border(const scrntype_t *pens, scrntype_t *ln)
985 pen1 = pens[m_pal_ind16[(m_cont_reg[7]&0x03)]];
986 pen0 = pens[m_pal_ind16[((m_cont_reg[7]>>2)&0x03)]];
987 i = _V9938_LONG_WIDTH / 2;
988 while (i--) { *ln++ = pen0; *ln++ = pen1; }
991 void v99x8_device::mode_text1(const scrntype_t *pens, scrntype_t *ln, int line)
993 int pattern, x, xx, name, xxx;
994 scrntype_t fg, bg, pen;
995 int nametbl_addr, patterntbl_addr;
997 patterntbl_addr = m_cont_reg[4] << 11;
998 nametbl_addr = m_cont_reg[2] << 10;
1000 fg = pens[m_pal_ind16[m_cont_reg[7] >> 4]];
1001 bg = pens[m_pal_ind16[m_cont_reg[7] & 15]];
1005 pen = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
1007 xxx = (m_offset_x + 8) * 2;
1008 while (xxx--) *ln++ = pen;
1012 pattern = m_vram_space->read_byte(patterntbl_addr + (m_vram_space->read_byte(nametbl_addr + name) * 8) +
1013 ((line + m_cont_reg[23]) & 7));
1014 for (xx=0;xx<6;xx++)
1016 *ln++ = (pattern & 0x80) ? fg : bg;
1017 *ln++ = (pattern & 0x80) ? fg : bg;
1020 /* width height 212, characters start repeating at the bottom */
1021 name = (name + 1) & 0x3ff;
1024 xxx = ((16 - m_offset_x) + 8) * 2;
1025 while (xxx--) *ln++ = pen;
1028 void v99x8_device::mode_text2(const scrntype_t *pens, scrntype_t *ln, int line)
1030 int pattern, x, charcode, name, xxx, patternmask, colourmask;
1031 scrntype_t fg, bg, fg0, bg0, pen;
1032 int nametbl_addr, patterntbl_addr, colourtbl_addr;
1034 patterntbl_addr = m_cont_reg[4] << 11;
1035 colourtbl_addr = ((m_cont_reg[3] & 0xf8) << 6) + (m_cont_reg[10] << 14);
1037 colourmask = ((m_cont_reg[3] & 7) << 5) | 0x1f; /* cause a bug in Forth+ v1.0 on Geneve */
1039 colourmask = ((m_cont_reg[3] & 7) << 6) | 0x3f; /* verify! */
1041 nametbl_addr = ((m_cont_reg[2] & 0xfc) << 10);
1042 patternmask = ((m_cont_reg[2] & 3) << 10) | 0x3ff; /* seems correct */
1044 fg = pens[m_pal_ind16[m_cont_reg[7] >> 4]];
1045 bg = pens[m_pal_ind16[m_cont_reg[7] & 15]];
1046 fg0 = pens[m_pal_ind16[m_cont_reg[12] >> 4]];
1047 bg0 = pens[m_pal_ind16[m_cont_reg[12] & 15]];
1051 xxx = (m_offset_x + 8) * 2;
1052 pen = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
1053 while (xxx--) *ln++ = pen;
1057 charcode = m_vram_space->read_byte(nametbl_addr + (name&patternmask));
1060 pattern = m_vram_space->read_byte(colourtbl_addr + ((name/8)&colourmask));
1061 if (pattern & (0x80 >> (name & 7) ) )
1063 pattern = m_vram_space->read_byte(patterntbl_addr + ((charcode * 8) +
1064 ((line + m_cont_reg[23]) & 7)));
1066 *ln++ = (pattern & 0x80) ? fg0 : bg0;
1067 *ln++ = (pattern & 0x40) ? fg0 : bg0;
1068 *ln++ = (pattern & 0x20) ? fg0 : bg0;
1069 *ln++ = (pattern & 0x10) ? fg0 : bg0;
1070 *ln++ = (pattern & 0x08) ? fg0 : bg0;
1071 *ln++ = (pattern & 0x04) ? fg0 : bg0;
1078 pattern = m_vram_space->read_byte(patterntbl_addr + ((charcode * 8) +
1079 ((line + m_cont_reg[23]) & 7)));
1081 *ln++ = (pattern & 0x80) ? fg : bg;
1082 *ln++ = (pattern & 0x40) ? fg : bg;
1083 *ln++ = (pattern & 0x20) ? fg : bg;
1084 *ln++ = (pattern & 0x10) ? fg : bg;
1085 *ln++ = (pattern & 0x08) ? fg : bg;
1086 *ln++ = (pattern & 0x04) ? fg : bg;
1091 xxx = (16 - m_offset_x + 8) * 2;
1092 while (xxx--) *ln++ = pen;
1095 void v99x8_device::mode_multi(const scrntype_t *pens, scrntype_t *ln, int line)
1097 int nametbl_addr, patterntbl_addr, colour;
1098 int name, line2, x, xx;
1099 scrntype_t pen, pen_bg;
1101 nametbl_addr = (m_cont_reg[2] << 10);
1102 patterntbl_addr = (m_cont_reg[4] << 11);
1104 line2 = (line - m_cont_reg[23]) & 255;
1105 name = (line2/8)*32;
1107 pen_bg = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
1108 xx = m_offset_x * 2;
1109 while (xx--) *ln++ = pen_bg;
1113 colour = m_vram_space->read_byte(patterntbl_addr + (m_vram_space->read_byte(nametbl_addr + name) * 8) + ((line2/4)&7));
1114 pen = pens[m_pal_ind16[colour>>4]];
1124 pen = pens[m_pal_ind16[colour&15]];
1137 xx = (16 - m_offset_x) * 2;
1138 while (xx--) *ln++ = pen_bg;
1141 void v99x8_device::mode_graphic1(const scrntype_t *pens, scrntype_t *ln, int line)
1143 scrntype_t fg, bg, pen;
1144 int nametbl_addr, patterntbl_addr, colourtbl_addr;
1145 int pattern, x, xx, line2, name, charcode, colour, xxx;
1147 nametbl_addr = (m_cont_reg[2] << 10);
1148 colourtbl_addr = (m_cont_reg[3] << 6) + (m_cont_reg[10] << 14);
1149 patterntbl_addr = (m_cont_reg[4] << 11);
1151 line2 = (line - m_cont_reg[23]) & 255;
1153 name = (line2/8)*32;
1155 pen = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
1156 xxx = m_offset_x * 2;
1157 while (xxx--) *ln++ = pen;
1161 charcode = m_vram_space->read_byte(nametbl_addr + name);
1162 colour = m_vram_space->read_byte(colourtbl_addr + charcode/8);
1163 fg = pens[m_pal_ind16[colour>>4]];
1164 bg = pens[m_pal_ind16[colour&15]];
1165 pattern = m_vram_space->read_byte(patterntbl_addr + (charcode * 8 + (line2 & 7)));
1167 for (xx=0;xx<8;xx++)
1169 *ln++ = (pattern & 0x80) ? fg : bg;
1170 *ln++ = (pattern & 0x80) ? fg : bg;
1176 xx = (16 - m_offset_x) * 2;
1177 while (xx--) *ln++ = pen;
1180 void v99x8_device::mode_graphic23(const scrntype_t *pens, scrntype_t *ln, int line)
1182 scrntype_t fg, bg, pen;
1183 int nametbl_addr, patterntbl_addr, colourtbl_addr;
1184 int pattern, x, xx, line2, name, charcode,
1185 colour, colourmask, patternmask, xxx;
1187 colourmask = ((m_cont_reg[3] & 0x7f) * 8) | 7;
1188 patternmask = ((m_cont_reg[4] & 0x03) * 256) | 0xff;
1190 nametbl_addr = (m_cont_reg[2] << 10);
1191 colourtbl_addr = ((m_cont_reg[3] & 0x80) << 6) + (m_cont_reg[10] << 14);
1192 patterntbl_addr = ((m_cont_reg[4] & 0x3c) << 11);
1194 line2 = (line + m_cont_reg[23]) & 255;
1195 name = (line2/8)*32;
1197 pen = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
1198 xxx = m_offset_x * 2;
1199 while (xxx--) *ln++ = pen;
1203 charcode = m_vram_space->read_byte(nametbl_addr + name) + (line2&0xc0)*4;
1204 colour = m_vram_space->read_byte(colourtbl_addr + ((charcode&colourmask)*8+(line2&7)));
1205 pattern = m_vram_space->read_byte(patterntbl_addr + ((charcode&patternmask)*8+(line2&7)));
1206 fg = pens[m_pal_ind16[colour>>4]];
1207 bg = pens[m_pal_ind16[colour&15]];
1208 for (xx=0;xx<8;xx++)
1210 *ln++ = (pattern & 0x80) ? fg : bg;
1211 *ln++ = (pattern & 0x80) ? fg : bg;
1217 xx = (16 - m_offset_x) * 2;
1218 while (xx--) *ln++ = pen;
1221 void v99x8_device::mode_graphic4(const scrntype_t *pens, scrntype_t *ln, int line)
1223 int nametbl_addr, colour;
1224 int line2, linemask, x, xx;
1225 scrntype_t pen, pen_bg;
1227 linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;
1229 line2 = ((line + m_cont_reg[23]) & linemask) & 255;
1231 nametbl_addr = ((m_cont_reg[2] & 0x40) << 10) + line2 * 128;
1232 if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
1233 nametbl_addr += 0x8000;
1235 pen_bg = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
1236 xx = m_offset_x * 2;
1237 while (xx--) *ln++ = pen_bg;
1241 colour = m_vram_space->read_byte(nametbl_addr++);
1242 pen = pens[m_pal_ind16[colour>>4]];
1245 pen = pens[m_pal_ind16[colour&15]];
1250 xx = (16 - m_offset_x) * 2;
1251 while (xx--) *ln++ = pen_bg;
1254 void v99x8_device::mode_graphic5(const scrntype_t *pens, scrntype_t *ln, int line)
1256 int nametbl_addr, colour;
1257 int line2, linemask, x, xx;
1258 scrntype_t pen_bg0[4];
1259 scrntype_t pen_bg1[4];
1261 linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;
1263 line2 = ((line + m_cont_reg[23]) & linemask) & 255;
1265 nametbl_addr = ((m_cont_reg[2] & 0x40) << 10) + line2 * 128;
1266 if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
1267 nametbl_addr += 0x8000;
1269 pen_bg1[0] = pens[m_pal_ind16[(m_cont_reg[7]&0x03)]];
1270 pen_bg0[0] = pens[m_pal_ind16[((m_cont_reg[7]>>2)&0x03)]];
1273 while (xx--) { *ln++ = pen_bg0[0]; *ln++ = pen_bg1[0]; }
1275 x = (m_cont_reg[8] & 0x20) ? 0 : 1;
1279 pen_bg0[x] = pens[m_pal_ind16[x]];
1280 pen_bg1[x] = pens[m_pal_ind16[x]];
1285 colour = m_vram_space->read_byte(nametbl_addr++);
1287 *ln++ = pen_bg0[colour>>6];
1288 *ln++ = pen_bg1[(colour>>4)&3];
1289 *ln++ = pen_bg0[(colour>>2)&3];
1290 *ln++ = pen_bg1[(colour&3)];
1293 pen_bg1[0] = pens[m_pal_ind16[(m_cont_reg[7]&0x03)]];
1294 pen_bg0[0] = pens[m_pal_ind16[((m_cont_reg[7]>>2)&0x03)]];
1295 xx = 16 - m_offset_x;
1296 while (xx--) { *ln++ = pen_bg0[0]; *ln++ = pen_bg1[0]; }
1299 void v99x8_device::mode_graphic6(const scrntype_t *pens, scrntype_t *ln, int line)
1302 int line2, linemask, x, xx, nametbl_addr;
1303 scrntype_t pen_bg, fg0;
1306 linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;
1308 line2 = ((line + m_cont_reg[23]) & linemask) & 255;
1310 nametbl_addr = line2 << 8 ;
1311 if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
1312 nametbl_addr += 0x10000;
1314 pen_bg = pens[m_pal_ind16[(m_cont_reg[7]&0x0f)]];
1315 xx = m_offset_x * 2;
1316 while (xx--) *ln++ = pen_bg;
1318 if (m_cont_reg[2] & 0x40)
1323 colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1324 fg0 = pens[m_pal_ind16[colour>>4]];
1325 fg1 = pens[m_pal_ind16[colour&15]];
1326 *ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
1327 *ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
1328 *ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
1329 *ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
1337 colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1338 *ln++ = pens[m_pal_ind16[colour>>4]];
1339 *ln++ = pens[m_pal_ind16[colour&15]];
1344 xx = (16 - m_offset_x) * 2;
1345 while (xx--) *ln++ = pen_bg;
1348 void v99x8_device::mode_graphic7(const scrntype_t *pens, scrntype_t *ln, int line)
1351 int line2, linemask, x, xx, nametbl_addr;
1352 scrntype_t pen, pen_bg;
1354 linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;
1356 line2 = ((line + m_cont_reg[23]) & linemask) & 255;
1358 nametbl_addr = line2 << 8;
1359 if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
1360 nametbl_addr += 0x10000;
1362 pen_bg = pens[m_pal_ind256[m_cont_reg[7]]];
1363 xx = m_offset_x * 2;
1364 while (xx--) *ln++ = pen_bg;
1366 if ((m_v9958_sp_mode & 0x18) == 0x08) // v9958 screen 12, puzzle star title screen
1373 colour[0] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1375 colour[1] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1377 colour[2] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1379 colour[3] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1381 ind = (colour[0] & 7) << 11 | (colour[1] & 7) << 14 |
1382 (colour[2] & 7) << 5 | (colour[3] & 7) << 8;
1384 *ln++ = pens[s_pal_indYJK[ind | ((colour[0] >> 3) & 31)]];
1385 *ln++ = pens[s_pal_indYJK[ind | ((colour[0] >> 3) & 31)]];
1387 *ln++ = pens[s_pal_indYJK[ind | ((colour[1] >> 3) & 31)]];
1388 *ln++ = pens[s_pal_indYJK[ind | ((colour[1] >> 3) & 31)]];
1390 *ln++ = pens[s_pal_indYJK[ind | ((colour[2] >> 3) & 31)]];
1391 *ln++ = pens[s_pal_indYJK[ind | ((colour[2] >> 3) & 31)]];
1393 *ln++ = pens[s_pal_indYJK[ind | ((colour[3] >> 3) & 31)]];
1394 *ln++ = pens[s_pal_indYJK[ind | ((colour[3] >> 3) & 31)]];
1399 else if ((m_v9958_sp_mode & 0x18) == 0x18) // v9958 screen 10/11, puzzle star & sexy boom gameplay
1406 colour[0] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1408 colour[1] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1410 colour[2] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1412 colour[3] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1414 ind = (colour[0] & 7) << 11 | (colour[1] & 7) << 14 |
1415 (colour[2] & 7) << 5 | (colour[3] & 7) << 8;
1417 *ln++ = pens[colour[0] & 8 ? m_pal_ind16[colour[0] >> 4] : s_pal_indYJK[ind | ((colour[0] >> 3) & 30)]];
1418 *ln++ = pens[colour[0] & 8 ? m_pal_ind16[colour[0] >> 4] : s_pal_indYJK[ind | ((colour[0] >> 3) & 30)]];
1420 *ln++ = pens[colour[1] & 8 ? m_pal_ind16[colour[1] >> 4] : s_pal_indYJK[ind | ((colour[1] >> 3) & 30)]];
1421 *ln++ = pens[colour[1] & 8 ? m_pal_ind16[colour[1] >> 4] : s_pal_indYJK[ind | ((colour[1] >> 3) & 30)]];
1423 *ln++ = pens[colour[2] & 8 ? m_pal_ind16[colour[2] >> 4] : s_pal_indYJK[ind | ((colour[2] >> 3) & 30)]];
1424 *ln++ = pens[colour[2] & 8 ? m_pal_ind16[colour[2] >> 4] : s_pal_indYJK[ind | ((colour[2] >> 3) & 30)]];
1426 *ln++ = pens[colour[3] & 8 ? m_pal_ind16[colour[3] >> 4] : s_pal_indYJK[ind | ((colour[3] >> 3) & 30)]];
1427 *ln++ = pens[colour[3] & 8 ? m_pal_ind16[colour[3] >> 4] : s_pal_indYJK[ind | ((colour[3] >> 3) & 30)]];
1432 else if (m_cont_reg[2] & 0x40)
1437 colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1438 pen = pens[m_pal_ind256[colour]];
1439 *ln++ = pen; *ln++ = pen;
1440 *ln++ = pen; *ln++ = pen;
1441 *ln++ = pen; *ln++ = pen;
1442 *ln++ = pen; *ln++ = pen;
1443 *ln++ = pen; *ln++ = pen;
1444 *ln++ = pen; *ln++ = pen;
1445 *ln++ = pen; *ln++ = pen;
1446 *ln++ = pen; *ln++ = pen;
1454 colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
1455 pen = pens[m_pal_ind256[colour]];
1462 xx = (16 - m_offset_x) * 2;
1463 while (xx--) *ln++ = pen_bg;
1466 void v99x8_device::mode_unknown(const scrntype_t *pens, scrntype_t *ln, int line)
1471 fg = pens[m_pal_ind16[m_cont_reg[7] >> 4]];
1472 bg = pens[m_pal_ind16[m_cont_reg[7] & 15]];
1475 while (x--) *ln++ = bg;
1478 while (x--) *ln++ = fg;
1480 x = (16 - m_offset_x) * 2;
1481 while (x--) *ln++ = bg;
1484 void v99x8_device::default_draw_sprite(const scrntype_t *pens, scrntype_t *ln, UINT8 *col)
1487 ln += m_offset_x * 2;
1493 *ln++ = pens[m_pal_ind16[col[i]&0x0f]];
1494 *ln++ = pens[m_pal_ind16[col[i]&0x0f]];
1503 void v99x8_device::graphic5_draw_sprite(const scrntype_t *pens, scrntype_t *ln, UINT8 *col)
1506 ln += m_offset_x * 2;
1512 *ln++ = pens[m_pal_ind16[(col[i]>>2)&0x03]];
1513 *ln++ = pens[m_pal_ind16[col[i]&0x03]];
1523 void v99x8_device::graphic7_draw_sprite(const scrntype_t *pens, scrntype_t *ln, UINT8 *col)
1525 static const UINT16 g7_ind16[16] = {
1526 0, 2, 192, 194, 48, 50, 240, 242,
1527 482, 7, 448, 455, 56, 63, 504, 511 };
1530 ln += m_offset_x * 2;
1536 *ln++ = pens[g7_ind16[col[i]&0x0f]];
1537 *ln++ = pens[g7_ind16[col[i]&0x0f]];
1547 void v99x8_device::sprite_mode1 (int line, UINT8 *col)
1549 int attrtbl_addr, patterntbl_addr, pattern_addr;
1550 int x, y, p, height, c, p2, i, n, pattern;
1552 memset(col, 0, 256);
1554 // are sprites disabled?
1555 if (m_cont_reg[8] & 0x02) return;
1557 attrtbl_addr = (m_cont_reg[5] << 7) + (m_cont_reg[11] << 15);
1558 patterntbl_addr = (m_cont_reg[6] << 11);
1560 // 16x16 or 8x8 sprites
1561 height = (m_cont_reg[1] & 2) ? 16 : 8;
1562 // magnified sprites (zoomed)
1563 if (m_cont_reg[1] & 1) height *= 2;
1568 y = m_vram_space->read_byte(attrtbl_addr);
1569 if (y == 208) break;
1570 y = (y - m_cont_reg[23]) & 255;
1576 // if sprite in range, has to be drawn
1577 if ( (line >= y) && (line < (y + height) ) )
1581 // max maximum sprites per line!
1582 if ( !(m_stat_reg[0] & 0x40) )
1583 m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | 0x40 | p;
1588 x = m_vram_space->read_byte(attrtbl_addr + 1);
1589 if (m_vram_space->read_byte(attrtbl_addr + 3) & 0x80) x -= 32;
1592 pattern = m_vram_space->read_byte(attrtbl_addr + 2);
1593 if (m_cont_reg[1] & 2)
1596 pattern_addr = patterntbl_addr + pattern * 8 + ((m_cont_reg[1] & 1) ? n/2 : n);
1597 pattern = (m_vram_space->read_byte(pattern_addr) << 8) | m_vram_space->read_byte(pattern_addr+16);
1600 c = m_vram_space->read_byte(attrtbl_addr + 3) & 0x0f;
1606 if (n == 0) pattern = m_vram_space->read_byte(pattern_addr);
1607 else if ( (n == 1) && (m_cont_reg[1] & 2) ) pattern = m_vram_space->read_byte(pattern_addr + 16);
1616 if ( (x >= 0) && (x < 256) )
1620 // we have a collision!
1622 m_stat_reg[0] |= 0x20;
1624 if ( !(col[x] & 0x80) )
1626 if (c || (m_cont_reg[8] & 0x20) )
1632 // if zoomed, draw another pixel
1633 if (m_cont_reg[1] & 1)
1635 if (col[x+1] & 0x40)
1637 // we have a collision!
1639 m_stat_reg[0] |= 0x20;
1641 if ( !(col[x+1] & 0x80) )
1643 if (c || (m_cont_reg[8] & 0x20) )
1644 col[x+1] |= 0xc0 | c;
1651 if (m_cont_reg[1] & 1) x += 2; else x++;
1664 if ( !(m_stat_reg[0] & 0x40) )
1665 m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | p;
1668 void v99x8_device::sprite_mode2 (int line, UINT8 *col)
1670 int attrtbl_addr, patterntbl_addr, pattern_addr, colourtbl_addr;
1671 int x, i, y, p, height, c, p2, n, pattern, colourmask, first_cc_seen;
1673 memset(col, 0, 256);
1675 // are sprites disabled?
1676 if (m_cont_reg[8] & 0x02) return;
1678 attrtbl_addr = ( (m_cont_reg[5] & 0xfc) << 7) + (m_cont_reg[11] << 15);
1679 colourtbl_addr = ( (m_cont_reg[5] & 0xf8) << 7) + (m_cont_reg[11] << 15);
1680 patterntbl_addr = (m_cont_reg[6] << 11);
1681 colourmask = ( (m_cont_reg[5] & 3) << 3) | 0x7; // check this!
1683 // 16x16 or 8x8 sprites
1684 height = (m_cont_reg[1] & 2) ? 16 : 8;
1685 // magnified sprites (zoomed)
1686 if (m_cont_reg[1] & 1) height *= 2;
1688 p2 = p = first_cc_seen = 0;
1691 y = vram_read(attrtbl_addr);
1692 if (y == 216) break;
1693 y = (y - m_cont_reg[23]) & 255;
1699 // if sprite in range, has to be drawn
1700 if ( (line >= y) && (line < (y + height) ) )
1704 // max maximum sprites per line!
1705 if ( !(m_stat_reg[0] & 0x40) )
1706 m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | 0x40 | p;
1711 n = line - y; if (m_cont_reg[1] & 1) n /= 2;
1713 c = vram_read(colourtbl_addr + (((p&colourmask)*16) + n));
1715 // don't draw all sprite with CC set before any sprites
1716 // with CC = 0 are seen on this line
1720 goto skip_first_cc_set;
1726 pattern = vram_read(attrtbl_addr + 2);
1727 if (m_cont_reg[1] & 2)
1729 pattern_addr = patterntbl_addr + pattern * 8 + n;
1730 pattern = (vram_read(pattern_addr) << 8) | vram_read(pattern_addr + 16);
1733 x = vram_read(attrtbl_addr + 1);
1734 if (c & 0x80) x -= 32;
1736 n = (m_cont_reg[1] & 2) ? 16 : 8;
1739 for (i=0;i<=(m_cont_reg[1] & 1);i++)
1741 if ( (x >= 0) && (x < 256) )
1743 if ( (pattern & 0x8000) && !(col[x] & 0x10) )
1745 if ( (c & 15) || (m_cont_reg[8] & 0x20) )
1749 if (col[x] & 0x20) col[x] |= 0x10;
1751 col[x] |= 0x20 | (c & 15);
1761 if ( !(c & 0x40) && (col[x] & 0x20) )
1765 if ( !(c & 0x60) && (pattern & 0x8000) )
1769 // sprite collision!
1771 m_stat_reg[0] |= 0x20;
1793 if ( !(m_stat_reg[0] & 0x40) )
1794 m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | p;
1798 const v99x8_device::v99x8_mode v99x8_device::s_modes[] = {
1800 &v99x8_device::mode_text1,
1801 &v99x8_device::default_border,
1806 &v99x8_device::mode_multi,
1807 &v99x8_device::default_border,
1808 &v99x8_device::sprite_mode1,
1809 &v99x8_device::default_draw_sprite
1812 &v99x8_device::mode_graphic1,
1813 &v99x8_device::default_border,
1814 &v99x8_device::sprite_mode1,
1815 &v99x8_device::default_draw_sprite
1818 &v99x8_device::mode_graphic23,
1819 &v99x8_device::default_border,
1820 &v99x8_device::sprite_mode1,
1821 &v99x8_device::default_draw_sprite
1824 &v99x8_device::mode_graphic23,
1825 &v99x8_device::default_border,
1826 &v99x8_device::sprite_mode2,
1827 &v99x8_device::default_draw_sprite
1830 &v99x8_device::mode_graphic4,
1831 &v99x8_device::default_border,
1832 &v99x8_device::sprite_mode2,
1833 &v99x8_device::default_draw_sprite
1836 &v99x8_device::mode_graphic5,
1837 &v99x8_device::graphic5_border,
1838 &v99x8_device::sprite_mode2,
1839 &v99x8_device::graphic5_draw_sprite
1842 &v99x8_device::mode_graphic6,
1843 &v99x8_device::default_border,
1844 &v99x8_device::sprite_mode2,
1845 &v99x8_device::default_draw_sprite
1848 &v99x8_device::mode_graphic7,
1849 &v99x8_device::graphic7_border,
1850 &v99x8_device::sprite_mode2,
1851 &v99x8_device::graphic7_draw_sprite
1854 &v99x8_device::mode_text2,
1855 &v99x8_device::default_border,
1860 &v99x8_device::mode_unknown,
1861 &v99x8_device::default_border,
1867 void v99x8_device::set_mode()
1871 n = (((m_cont_reg[0] & 0x0e) << 1) | ((m_cont_reg[1] & 0x18) >> 3));
1874 if ( (s_modes[i].m == n) || (s_modes[i].m == 0xff) ) break;
1879 void v99x8_device::refresh_16(int line)
1881 //const pen_t *pens = m_palette->pens();
1882 const scrntype_t *pens = this->pens;
1883 bool double_lines = false;
1885 scrntype_t *ln, *ln2 = NULL;
1887 if (m_cont_reg[9] & 0x08)
1889 // ln = &m_bitmap.pix16(m_scanline*2+((m_stat_reg[2]>>1)&1));
1890 ln = screen+(m_scanline*2+((m_stat_reg[2]>>1)&1))*_V9938_LONG_WIDTH;
1894 // ln = &m_bitmap.pix16(m_scanline*2);
1895 // ln2 = &m_bitmap.pix16(m_scanline*2+1);
1896 ln = screen+(m_scanline*2)*_V9938_LONG_WIDTH;
1897 ln2 = screen+(m_scanline*2+1)*_V9938_LONG_WIDTH;
1898 double_lines = true;
1901 if ( !(m_cont_reg[1] & 0x40) || (m_stat_reg[2] & 0x40) )
1903 (this->*s_modes[m_mode].border_16)(pens, ln);
1907 (this->*s_modes[m_mode].visible_16)(pens, ln, line);
1908 if (s_modes[m_mode].sprites)
1910 (this->*s_modes[m_mode].sprites)(line, col);
1911 (this->*s_modes[m_mode].draw_sprite_16)(pens, ln, col);
1916 my_memcpy(ln2, ln, (512 + 32) * sizeof(scrntype_t));
1919 void v99x8_device::refresh_line(int line)
1923 ind16 = m_pal_ind16[0];
1924 ind256 = m_pal_ind256[0];
1926 if ( !(m_cont_reg[8] & 0x20) && (m_mode != V9938_MODE_GRAPHIC5) )
1928 m_pal_ind16[0] = m_pal_ind16[(m_cont_reg[7] & 0x0f)];
1929 m_pal_ind256[0] = m_pal_ind256[m_cont_reg[7]];
1934 if ( !(m_cont_reg[8] & 0x20) && (m_mode != V9938_MODE_GRAPHIC5) )
1936 m_pal_ind16[0] = ind16;
1937 m_pal_ind256[0] = ind256;
1943 From: awulms@inter.nl.net (Alex Wulms)
1944 *** About the HR/VR topic: this is how it works according to me:
1947 HR is very straightforward:
1948 -HR=1 during 'display time'
1949 -HR=0 during 'horizontal border, horizontal retrace'
1950 I have put 'display time' and 'horizontal border, horizontal retrace' between
1951 quotes because HR does not only flip between 0 and 1 during the display of
1952 the 192/212 display lines, but also during the vertical border and during the
1956 VR is a little bit tricky
1957 -VR always gets set to 0 when the VDP starts with display line 0
1958 -VR gets set to 1 when the VDP reaches display line (192 if LN=0) or (212 if
1960 -The VDP displays contents of VRAM as long as VR=0
1962 As a consequence of this behaviour, it is possible to program the famous
1963 overscan trick, where VRAM contents is shown in the borders:
1964 Generate an interrupt at line 230 (or so) and on this interrupt: set LN=1
1965 Generate an interrupt at line 200 (or so) and on this interrupt: set LN=0
1966 Repeat the above two steps
1968 *** The top/bottom border contents during overscan:
1970 1) The VDP keeps increasing the name table address pointer during bottom
1971 border, vertical retrace and top border
1972 2) The VDP resets the name table address pointer when the first display line
1975 On the other screens:
1976 1) The VDP keeps increasing the name table address pointer during the bottom
1978 2) The VDP resets the name table address pointer such that the top border
1979 contents connects up with the first display line. E.g., when the top border
1980 is 26 lines high, the VDP will take:
1994 *** About the horizontal interrupt
1996 All relevant definitions on a row:
1997 -FH: Bit 0 of status register 1
1998 -IE1: Bit 4 of mode register 0
1999 -IL: Line number in mode register 19
2000 -DL: The line that the VDP is going to display (corrected for vertical scroll)
2001 -IRQ: Interrupt request line of VDP to Z80
2003 At the *start* of every new line (display, bottom border, part of vertical
2004 display), the VDP does:
2005 -FH = (FH && IE1) || (IL==DL)
2007 After reading of status register 1 by the CPU, the VDP does:
2010 Furthermore, the following is true all the time:
2013 The resulting behaviour:
2015 -FH will be set as soon as display of line IL starts
2016 -FH will be reset as soon as status register 1 is read
2017 -FH will be reset as soon as the next display line is reached
2020 -FH and IRQ will be set as soon as display line IL is reached
2021 -FH and IRQ will be reset as soon as status register 1 is read
2023 Another subtile result:
2024 If, while FH and IRQ are set, IE1 gets reset, the next happens:
2025 -IRQ is reset immediately (since IRQ is always FH && IE1)
2026 -FH will be reset as soon as display of the next line starts (unless the next
2030 *** About the vertical interrupt:
2031 Another relevant definition:
2032 -FV: Bit 7 of status register 0
2033 -IE0: Bit 5 of mode register 1
2035 I only know for sure the behaviour when IE0=1:
2036 -FV and IRQ will be set as soon as VR changes from 0 to 1
2037 -FV and IRQ will be reset as soon as status register 0 is read
2039 A consequence is that NO vertical interrupts will be generated during the
2040 overscan trick, described in the VR section above.
2042 I do not know the behaviour of FV when IE0=0. That is the part that I still
2046 void v99x8_device::interrupt_start_vblank()
2049 if (machine.input().code_pressed (KEYCODE_D) )
2051 for (i=0;i<24;i++) osd_printf_debug ("R#%d = %02x\n", i, m_cont_reg[i]);
2055 // at every frame, vdp switches fields
2056 m_stat_reg[2] = (m_stat_reg[2] & 0xfd) | (~m_stat_reg[2] & 2);
2059 if (!(m_cont_reg[13] & 0xf0))
2061 else if (!(m_cont_reg[13] & 0x0f))
2065 // both on and off counter are non-zero: timed blinking
2072 m_blink_count = (m_cont_reg[13] >> 4) * 10;
2074 m_blink_count = (m_cont_reg[13] & 0x0f) * 10;
2079 /***************************************************************************
2083 ***************************************************************************/
2085 /*************************************************************/
2086 /** Completely rewritten by Alex Wulms: **/
2087 /** - VDP Command execution 'in parallel' with CPU **/
2088 /** - Corrected behaviour of VDP commands **/
2089 /** - Made it easier to implement correct S7/8 mapping **/
2090 /** by concentrating VRAM access in one single place **/
2091 /** - Made use of the 'in parallel' VDP command exec **/
2092 /** and correct timing. You must call the function **/
2093 /** LoopVDP() from LoopZ80 in MSX.c. You must call it **/
2094 /** exactly 256 times per screen refresh. **/
2095 /** Started on : 11-11-1999 **/
2096 /** Beta release 1 on: 9-12-1999 **/
2097 /** Beta release 2 on: 20-01-2000 **/
2098 /** - Corrected behaviour of VRM <-> Z80 transfer **/
2099 /** - Improved performance of the code **/
2100 /** Public release 1.0: 20-04-2000 **/
2101 /*************************************************************/
2103 #define VDP_VRMP5(MX, X, Y) ((!MX) ? (((Y&1023)<<7) + ((X&255)>>1)) : (EXPMEM_OFFSET + ((Y&511)<<7) + ((X&255)>>1)))
2104 #define VDP_VRMP6(MX, X, Y) ((!MX) ? (((Y&1023)<<7) + ((X&511)>>2)) : (EXPMEM_OFFSET + ((Y&511)<<7) + ((X&511)>>2)))
2105 //#define VDP_VRMP7(MX, X, Y) ((!MX) ? (((Y&511)<<8) + ((X&511)>>1)) : (EXPMEM_OFFSET + ((Y&255)<<8) + ((X&511)>>1)))
2106 #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))*/)
2107 //#define VDP_VRMP8(MX, X, Y) ((!MX) ? (((Y&511)<<8) + (X&255)) : (EXPMEM_OFFSET + ((Y&255)<<8) + (X&255)))
2108 #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))*/)
2110 #define VDP_VRMP(M, MX, X, Y) VDPVRMP(M, MX, X, Y)
2111 #define VDP_POINT(M, MX, X, Y) VDPpoint(M, MX, X, Y)
2112 #define VDP_PSET(M, MX, X, Y, C, O) VDPpset(M, MX, X, Y, C, O)
2115 #define CM_POINT 0x4
2128 /*************************************************************
2129 Many VDP commands are executed in some kind of loop but
2130 essentially, there are only a few basic loop structures
2131 that are re-used. We define the loop structures that are
2132 re-used here so that they have to be entered only once
2133 *************************************************************/
2135 while ((cnt-=delta) > 0) {
2140 #define post__x_y(MX) \
2141 if (!--ANX || ((ADX+=TX)&MX)) { \
2142 if (!(--NY&1023) || (DY+=TY)==-1) \
2151 // Loop over DX, SY, DY
2152 #define post__xyy(MX) \
2153 if ((ADX+=TX)&MX) { \
2154 if (!(--NY&1023) || (SY+=TY)==-1 || (DY+=TY)==-1) \
2161 // Loop over SX, DX, SY, DY
2162 #define post_xxyy(MX) \
2163 if (!--ANX || ((ASX+=TX)&MX) || ((ADX+=TX)&MX)) { \
2164 if (!(--NY&1023) || (SY+=TY)==-1 || (DY+=TY)==-1) \
2174 /*************************************************************/
2175 /** Variables visible only in this module **/
2176 /*************************************************************/
2177 static const UINT8 Mask[4] = { 0x0F,0x03,0x0F,0xFF };
2178 static const int PPB[4] = { 2,4,2,1 };
2179 static const int PPL[4] = { 256,512,512,256 };
2181 // SprOn SprOn SprOf SprOf
2182 // ScrOf ScrOn ScrOf ScrOn
2183 static const int srch_timing[8]={
2184 818, 1025, 818, 830, // ntsc
2185 696, 854, 696, 684 // pal
2187 static const int line_timing[8]={
2188 1063, 1259, 1063, 1161,
2191 static const int hmmv_timing[8]={
2195 static const int lmmv_timing[8]={
2196 873, 1135, 873, 1056,
2199 static const int ymmm_timing[8]={
2203 static const int hmmm_timing[8]={
2204 818, 1111, 818, 854,
2207 static const int lmmm_timing[8]={
2208 1160, 1599, 1160, 1172,
2212 /** VDPVRMP() **********************************************/
2213 /** Calculate addr of a pixel in vram **/
2214 /*************************************************************/
2215 inline int v99x8_device::VDPVRMP(UINT8 M,int MX,int X,int Y)
2219 case 0: return VDP_VRMP5(MX,X,Y);
2220 case 1: return VDP_VRMP6(MX,X,Y);
2221 case 2: return VDP_VRMP7(MX,X,Y);
2222 case 3: return VDP_VRMP8(MX,X,Y);
2228 /** VDPpoint5() ***********************************************/
2229 /** Get a pixel on screen 5 **/
2230 /*************************************************************/
2231 inline UINT8 v99x8_device::VDPpoint5(int MXS, int SX, int SY)
2233 return (m_vram_space->read_byte(VDP_VRMP5(MXS, SX, SY)) >>
2238 /** VDPpoint6() ***********************************************/
2239 /** Get a pixel on screen 6 **/
2240 /*************************************************************/
2241 inline UINT8 v99x8_device::VDPpoint6(int MXS, int SX, int SY)
2243 return (m_vram_space->read_byte(VDP_VRMP6(MXS, SX, SY)) >>
2248 /** VDPpoint7() ***********************************************/
2249 /** Get a pixel on screen 7 **/
2250 /*************************************************************/
2251 inline UINT8 v99x8_device::VDPpoint7(int MXS, int SX, int SY)
2253 return (m_vram_space->read_byte(VDP_VRMP7(MXS, SX, SY)) >>
2258 /** VDPpoint8() ***********************************************/
2259 /** Get a pixel on screen 8 **/
2260 /*************************************************************/
2261 inline UINT8 v99x8_device::VDPpoint8(int MXS, int SX, int SY)
2263 return m_vram_space->read_byte(VDP_VRMP8(MXS, SX, SY));
2266 /** VDPpoint() ************************************************/
2267 /** Get a pixel on a screen **/
2268 /*************************************************************/
2269 inline UINT8 v99x8_device::VDPpoint(UINT8 SM, int MXS, int SX, int SY)
2273 case 0: return VDPpoint5(MXS,SX,SY);
2274 case 1: return VDPpoint6(MXS,SX,SY);
2275 case 2: return VDPpoint7(MXS,SX,SY);
2276 case 3: return VDPpoint8(MXS,SX,SY);
2282 /** VDPpsetlowlevel() ****************************************/
2283 /** Low level function to set a pixel on a screen **/
2284 /** Make it inline to make it fast **/
2285 /*************************************************************/
2286 inline void v99x8_device::VDPpsetlowlevel(int addr, UINT8 CL, UINT8 M, UINT8 OP)
2288 // If this turns out to be too slow, get a pointer to the address space
2289 // and work directly on it.
2290 UINT8 val = m_vram_space->read_byte(addr);
2293 case 0: val = (val & M) | CL; break;
2294 case 1: val = val & (CL | M); break;
2295 case 2: val |= CL; break;
2296 case 3: val ^= CL; break;
2297 case 4: val = (val & M) | ~(CL | M); break;
2298 case 8: if (CL) val = (val & M) | CL; break;
2299 case 9: if (CL) val = val & (CL | M); break;
2300 case 10: if (CL) val |= CL; break;
2301 case 11: if (CL) val ^= CL; break;
2302 case 12: if (CL) val = (val & M) | ~(CL|M); break;
2304 LOG(("v9938: invalid operation %d in pset\n", OP));
2307 m_vram_space->write_byte(addr, val);
2310 /** VDPpset5() ***********************************************/
2311 /** Set a pixel on screen 5 **/
2312 /*************************************************************/
2313 inline void v99x8_device::VDPpset5(int MXD, int DX, int DY, UINT8 CL, UINT8 OP)
2315 UINT8 SH = ((~DX)&1)<<2;
2316 VDPpsetlowlevel(VDP_VRMP5(MXD, DX, DY), CL << SH, ~(15<<SH), OP);
2319 /** VDPpset6() ***********************************************/
2320 /** Set a pixel on screen 6 **/
2321 /*************************************************************/
2322 inline void v99x8_device::VDPpset6(int MXD, int DX, int DY, UINT8 CL, UINT8 OP)
2324 UINT8 SH = ((~DX)&3)<<1;
2326 VDPpsetlowlevel(VDP_VRMP6(MXD, DX, DY), CL << SH, ~(3<<SH), OP);
2329 /** VDPpset7() ***********************************************/
2330 /** Set a pixel on screen 7 **/
2331 /*************************************************************/
2332 inline void v99x8_device::VDPpset7(int MXD, int DX, int DY, UINT8 CL, UINT8 OP)
2334 UINT8 SH = ((~DX)&1)<<2;
2336 VDPpsetlowlevel(VDP_VRMP7(MXD, DX, DY), CL << SH, ~(15<<SH), OP);
2339 /** VDPpset8() ***********************************************/
2340 /** Set a pixel on screen 8 **/
2341 /*************************************************************/
2342 inline void v99x8_device::VDPpset8(int MXD, int DX, int DY, UINT8 CL, UINT8 OP)
2344 VDPpsetlowlevel(VDP_VRMP8(MXD, DX, DY), CL, 0, OP);
2347 /** VDPpset() ************************************************/
2348 /** Set a pixel on a screen **/
2349 /*************************************************************/
2350 inline void v99x8_device::VDPpset(UINT8 SM, int MXD, int DX, int DY, UINT8 CL, UINT8 OP)
2353 case 0: VDPpset5(MXD, DX, DY, CL, OP); break;
2354 case 1: VDPpset6(MXD, DX, DY, CL, OP); break;
2355 case 2: VDPpset7(MXD, DX, DY, CL, OP); break;
2356 case 3: VDPpset8(MXD, DX, DY, CL, OP); break;
2360 /** get_vdp_timing_value() **************************************/
2361 /** Get timing value for a certain VDP command **/
2362 /*************************************************************/
2363 int v99x8_device::get_vdp_timing_value(const int *timing_values)
2365 return(timing_values[((m_cont_reg[1]>>6)&1)|(m_cont_reg[8]&2)|((m_cont_reg[9]<<1)&4)]);
2368 /** SrchEgine()** ********************************************/
2369 /** Search a dot **/
2370 /*************************************************************/
2371 void v99x8_device::srch_engine()
2378 int MXD = m_mmc.MXD;
2382 delta = get_vdp_timing_value(srch_timing);
2383 cnt = m_vdp_ops_count;
2385 #define post_srch(MX) \
2386 { m_stat_reg[2]|=0x10; /* Border detected */ break; } \
2387 if ((SX+=TX) & MX) { m_stat_reg[2] &= 0xEF; /* Border not detected */ break; }
2390 case V9938_MODE_GRAPHIC4: pre_loop if ((VDPpoint5(MXD, SX, SY)==CL) ^ANX) post_srch(256) post_loop
2392 case V9938_MODE_GRAPHIC5: pre_loop if ((VDPpoint6(MXD, SX, SY)==CL) ^ANX) post_srch(512) post_loop
2394 case V9938_MODE_GRAPHIC6: pre_loop if ((VDPpoint7(MXD, SX, SY)==CL) ^ANX) post_srch(512) post_loop
2396 case V9938_MODE_GRAPHIC7: pre_loop if ((VDPpoint8(MXD, SX, SY)==CL) ^ANX) post_srch(256) post_loop
2400 if ((m_vdp_ops_count=cnt)>0) {
2401 // Command execution done
2402 m_stat_reg[2] &= 0xFE;
2403 m_vdp_engine = NULL;
2404 // Update SX in VDP registers
2405 m_stat_reg[8] = SX & 0xFF;
2406 m_stat_reg[9] = (SX>>8) | 0xFE;
2413 /** LineEgine()** ********************************************/
2415 /*************************************************************/
2416 void v99x8_device::line_engine()
2428 int MXD = m_mmc.MXD;
2432 delta = get_vdp_timing_value(line_timing);
2433 cnt = m_vdp_ops_count;
2435 #define post_linexmaj(MX) \
2437 if ((ASX-=NY)<0) { \
2441 ASX&=1023; /* Mask to 10 bits range */\
2442 if (ADX++==NX || (DX&MX)) \
2446 #define post_lineymaj(MX) \
2448 if ((ASX-=NY)<0) { \
2452 ASX&=1023; /* Mask to 10 bits range */\
2453 if (ADX++==NX || (DX&MX)) \
2457 if ((m_cont_reg[45]&0x01)==0)
2458 // X-Axis is major direction
2461 case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, DX, DY, CL, LO); post_linexmaj(256)
2463 case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, DX, DY, CL, LO); post_linexmaj(512)
2465 case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, DX, DY, CL, LO); post_linexmaj(512)
2467 case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, DX, DY, CL, LO); post_linexmaj(256)
2471 // Y-Axis is major direction
2474 case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, DX, DY, CL, LO); post_lineymaj(256)
2476 case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, DX, DY, CL, LO); post_lineymaj(512)
2478 case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, DX, DY, CL, LO); post_lineymaj(512)
2480 case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, DX, DY, CL, LO); post_lineymaj(256)
2484 if ((m_vdp_ops_count=cnt)>0) {
2485 // Command execution done
2486 m_stat_reg[2]&=0xFE;
2488 m_cont_reg[38]=DY & 0xFF;
2489 m_cont_reg[39]=(DY>>8) & 0x03;
2499 /** lmmv_engine() *********************************************/
2501 /*************************************************************/
2502 void v99x8_device::lmmv_engine()
2514 int MXD = m_mmc.MXD;
2518 delta = get_vdp_timing_value(lmmv_timing);
2519 cnt = m_vdp_ops_count;
2523 case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, ADX, DY, CL, LO); post__x_y(256)
2525 case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, ADX, DY, CL, LO); post__x_y(512)
2527 case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, ADX, DY, CL, LO); post__x_y(512)
2529 case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, ADX, DY, CL, LO); post__x_y(256)
2533 if ((m_vdp_ops_count=cnt)>0) {
2534 // Command execution done
2535 m_stat_reg[2]&=0xFE;
2539 m_cont_reg[38]=DY & 0xFF;
2540 m_cont_reg[39]=(DY>>8) & 0x03;
2541 m_cont_reg[42]=NY & 0xFF;
2542 m_cont_reg[43]=(NY>>8) & 0x03;
2552 /** lmmm_engine() *********************************************/
2553 /** Vram -> Vram **/
2554 /*************************************************************/
2555 void v99x8_device::lmmm_engine()
2569 int MXS = m_mmc.MXS;
2570 int MXD = m_mmc.MXD;
2574 delta = get_vdp_timing_value(lmmm_timing);
2575 cnt = m_vdp_ops_count;
2579 case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, ADX, DY, VDPpoint5(MXS, ASX, SY), LO); post_xxyy(256)
2581 case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, ADX, DY, VDPpoint6(MXS, ASX, SY), LO); post_xxyy(512)
2583 case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, ADX, DY, VDPpoint7(MXS, ASX, SY), LO); post_xxyy(512)
2585 case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, ADX, DY, VDPpoint8(MXS, ASX, SY), LO); post_xxyy(256)
2589 if ((m_vdp_ops_count=cnt)>0) {
2590 // Command execution done
2591 m_stat_reg[2]&=0xFE;
2600 m_cont_reg[42]=NY & 0xFF;
2601 m_cont_reg[43]=(NY>>8) & 0x03;
2602 m_cont_reg[34]=SY & 0xFF;
2603 m_cont_reg[35]=(SY>>8) & 0x03;
2604 m_cont_reg[38]=DY & 0xFF;
2605 m_cont_reg[39]=(DY>>8) & 0x03;
2617 /** lmcm_engine() *********************************************/
2619 /*************************************************************/
2620 void v99x8_device::lmcm_engine()
2622 if ((m_stat_reg[2]&0x80)!=0x80) {
2623 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);
2624 m_vdp_ops_count-=get_vdp_timing_value(lmmv_timing);
2625 m_stat_reg[2]|=0x80;
2627 if (!--m_mmc.ANX || ((m_mmc.ASX+=m_mmc.TX)&m_mmc.MX)) {
2628 if (!(--m_mmc.NY & 1023) || (m_mmc.SY+=m_mmc.TY)==-1) {
2629 m_stat_reg[2]&=0xFE;
2633 m_cont_reg[42]=m_mmc.NY & 0xFF;
2634 m_cont_reg[43]=(m_mmc.NY>>8) & 0x03;
2635 m_cont_reg[34]=m_mmc.SY & 0xFF;
2636 m_cont_reg[35]=(m_mmc.SY>>8) & 0x03;
2646 /** lmmc_engine() *********************************************/
2648 /*************************************************************/
2649 void v99x8_device::lmmc_engine()
2651 if ((m_stat_reg[2]&0x80)!=0x80) {
2652 UINT8 SM=((m_mode >= 5) && (m_mode <= 8)) ? (m_mode-5) : 0;
2654 m_stat_reg[7]=m_cont_reg[44]&=Mask[SM];
2655 VDP_PSET(SM, m_mmc.MXD, m_mmc.ADX, m_mmc.DY, m_cont_reg[44], m_mmc.LO);
2656 m_vdp_ops_count-=get_vdp_timing_value(lmmv_timing);
2657 m_stat_reg[2]|=0x80;
2659 if (!--m_mmc.ANX || ((m_mmc.ADX+=m_mmc.TX)&m_mmc.MX)) {
2660 if (!(--m_mmc.NY&1023) || (m_mmc.DY+=m_mmc.TY)==-1) {
2661 m_stat_reg[2]&=0xFE;
2665 m_cont_reg[42]=m_mmc.NY & 0xFF;
2666 m_cont_reg[43]=(m_mmc.NY>>8) & 0x03;
2667 m_cont_reg[38]=m_mmc.DY & 0xFF;
2668 m_cont_reg[39]=(m_mmc.DY>>8) & 0x03;
2678 /** hmmv_engine() *********************************************/
2679 /** VDP --> Vram **/
2680 /*************************************************************/
2681 void v99x8_device::hmmv_engine()
2692 int MXD = m_mmc.MXD;
2696 delta = get_vdp_timing_value(hmmv_timing);
2697 cnt = m_vdp_ops_count;
2701 case V9938_MODE_GRAPHIC4: pre_loop m_vram_space->write_byte(VDP_VRMP5(MXD, ADX, DY), CL); post__x_y(256)
2703 case V9938_MODE_GRAPHIC5: pre_loop m_vram_space->write_byte(VDP_VRMP6(MXD, ADX, DY), CL); post__x_y(512)
2705 case V9938_MODE_GRAPHIC6: pre_loop m_vram_space->write_byte(VDP_VRMP7(MXD, ADX, DY), CL); post__x_y(512)
2707 case V9938_MODE_GRAPHIC7: pre_loop m_vram_space->write_byte(VDP_VRMP8(MXD, ADX, DY), CL); post__x_y(256)
2711 if ((m_vdp_ops_count=cnt)>0) {
2712 // Command execution done
2713 m_stat_reg[2]&=0xFE;
2717 m_cont_reg[42]=NY & 0xFF;
2718 m_cont_reg[43]=(NY>>8) & 0x03;
2719 m_cont_reg[38]=DY & 0xFF;
2720 m_cont_reg[39]=(DY>>8) & 0x03;
2730 /** hmmm_engine() *********************************************/
2731 /** Vram -> Vram **/
2732 /*************************************************************/
2733 void v99x8_device::hmmm_engine()
2746 int MXS = m_mmc.MXS;
2747 int MXD = m_mmc.MXD;
2751 delta = get_vdp_timing_value(hmmm_timing);
2752 cnt = m_vdp_ops_count;
2756 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)
2758 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)
2760 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)
2762 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)
2766 if ((m_vdp_ops_count=cnt)>0) {
2767 // Command execution done
2768 m_stat_reg[2]&=0xFE;
2777 m_cont_reg[42]=NY & 0xFF;
2778 m_cont_reg[43]=(NY>>8) & 0x03;
2779 m_cont_reg[34]=SY & 0xFF;
2780 m_cont_reg[35]=(SY>>8) & 0x03;
2781 m_cont_reg[38]=DY & 0xFF;
2782 m_cont_reg[39]=(DY>>8) & 0x03;
2794 /** ymmm_engine() *********************************************/
2795 /** Vram -> Vram **/
2796 /*************************************************************/
2798 void v99x8_device::ymmm_engine()
2807 int MXD = m_mmc.MXD;
2811 delta = get_vdp_timing_value(ymmm_timing);
2812 cnt = m_vdp_ops_count;
2816 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)
2818 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)
2820 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)
2822 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)
2826 if ((m_vdp_ops_count=cnt)>0) {
2827 // Command execution done
2828 m_stat_reg[2]&=0xFE;
2837 m_cont_reg[42]=NY & 0xFF;
2838 m_cont_reg[43]=(NY>>8) & 0x03;
2839 m_cont_reg[34]=SY & 0xFF;
2840 m_cont_reg[35]=(SY>>8) & 0x03;
2841 m_cont_reg[38]=DY & 0xFF;
2842 m_cont_reg[39]=(DY>>8) & 0x03;
2852 /** hmmc_engine() *********************************************/
2854 /*************************************************************/
2855 void v99x8_device::hmmc_engine()
2857 if ((m_stat_reg[2]&0x80)!=0x80) {
2858 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]);
2859 m_vdp_ops_count -= get_vdp_timing_value(hmmv_timing);
2860 m_stat_reg[2]|=0x80;
2862 if (!--m_mmc.ANX || ((m_mmc.ADX+=m_mmc.TX)&m_mmc.MX)) {
2863 if (!(--m_mmc.NY&1023) || (m_mmc.DY+=m_mmc.TY)==-1) {
2864 m_stat_reg[2]&=0xFE;
2868 m_cont_reg[42]=m_mmc.NY & 0xFF;
2869 m_cont_reg[43]=(m_mmc.NY>>8) & 0x03;
2870 m_cont_reg[38]=m_mmc.DY & 0xFF;
2871 m_cont_reg[39]=(m_mmc.DY>>8) & 0x03;
2881 /** VDPWrite() ***********************************************/
2882 /** Use this function to transfer pixel(s) from CPU to m_ **/
2883 /*************************************************************/
2884 void v99x8_device::cpu_to_vdp(UINT8 V)
2886 m_stat_reg[2]&=0x7F;
2887 m_stat_reg[7]=m_cont_reg[44]=V;
2888 if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
2891 /** VDPRead() ************************************************/
2892 /** Use this function to transfer pixel(s) from VDP to CPU. **/
2893 /*************************************************************/
2894 UINT8 v99x8_device::vdp_to_cpu()
2896 m_stat_reg[2]&=0x7F;
2897 if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
2898 return(m_cont_reg[44]);
2901 /** report_vdp_command() ***************************************/
2902 /** Report VDP Command to be executed **/
2903 /*************************************************************/
2904 void v99x8_device::report_vdp_command(UINT8 Op)
2906 static const char *const Ops[16] =
2908 "SET ","AND ","OR ","XOR ","NOT ","NOP ","NOP ","NOP ",
2909 "TSET","TAND","TOR ","TXOR","TNOT","NOP ","NOP ","NOP "
2911 static const char *const Commands[16] =
2913 " ABRT"," ????"," ????"," ????","POINT"," PSET"," SRCH"," LINE",
2914 " LMMV"," LMMM"," LMCM"," LMMC"," HMMV"," HMMM"," YMMM"," HMMC"
2918 int SX,SY, DX,DY, NX,NY;
2921 CL = m_cont_reg[44];
2922 SX = (m_cont_reg[32]+((int)m_cont_reg[33]<<8)) & 511;
2923 SY = (m_cont_reg[34]+((int)m_cont_reg[35]<<8)) & 1023;
2924 DX = (m_cont_reg[36]+((int)m_cont_reg[37]<<8)) & 511;
2925 DY = (m_cont_reg[38]+((int)m_cont_reg[39]<<8)) & 1023;
2926 NX = (m_cont_reg[40]+((int)m_cont_reg[41]<<8)) & 1023;
2927 NY = (m_cont_reg[42]+((int)m_cont_reg[43]<<8)) & 1023;
2931 LOG(("V9938: Opcode %02Xh %s-%s (%d,%d)->(%d,%d),%d [%d,%d]%s\n",
2932 Op, Commands[CM], Ops[LO],
2933 SX,SY, DX,DY, CL, m_cont_reg[45]&0x04? -NX:NX,
2934 m_cont_reg[45]&0x08? -NY:NY,
2935 m_cont_reg[45]&0x70? " on ExtVRAM":""
2939 /** VDPDraw() ************************************************/
2940 /** Perform a given V9938 operation Op. **/
2941 /*************************************************************/
2942 UINT8 v99x8_device::command_unit_w(UINT8 Op)
2946 // V9938 ops only work in SCREENs 5-8
2950 SM = m_mode-5; // Screen mode index 0..3
2953 if ((m_mmc.CM & 0x0C) != 0x0C && m_mmc.CM != 0)
2954 // Dot operation: use only relevant bits of color
2955 m_stat_reg[7]=(m_cont_reg[44]&=Mask[SM]);
2958 report_vdp_command(Op);
2962 m_stat_reg[2]&=0xFE;
2966 m_stat_reg[2]&=0xFE;
2968 m_stat_reg[7]=m_cont_reg[44]=
2969 VDP_POINT(SM, (m_cont_reg[45] & 0x10) != 0,
2970 m_cont_reg[32]+((int)m_cont_reg[33]<<8),
2971 m_cont_reg[34]+((int)m_cont_reg[35]<<8));
2974 m_stat_reg[2]&=0xFE;
2976 VDP_PSET(SM, (m_cont_reg[45] & 0x20) != 0,
2977 m_cont_reg[36]+((int)m_cont_reg[37]<<8),
2978 m_cont_reg[38]+((int)m_cont_reg[39]<<8),
2983 m_vdp_engine=&v99x8_device::srch_engine;
2986 m_vdp_engine=&v99x8_device::line_engine;
2989 m_vdp_engine=&v99x8_device::lmmv_engine;
2992 m_vdp_engine=&v99x8_device::lmmm_engine;
2995 m_vdp_engine=&v99x8_device::lmcm_engine;
2998 m_vdp_engine=&v99x8_device::lmmc_engine;
3001 m_vdp_engine=&v99x8_device::hmmv_engine;
3004 m_vdp_engine=&v99x8_device::hmmm_engine;
3007 m_vdp_engine=&v99x8_device::ymmm_engine;
3010 m_vdp_engine=&v99x8_device::hmmc_engine;
3013 LOG(("V9938: Unrecognized opcode %02Xh\n",Op));
3017 // Fetch unconditional arguments
3018 m_mmc.SX = (m_cont_reg[32]+((int)m_cont_reg[33]<<8)) & 511;
3019 m_mmc.SY = (m_cont_reg[34]+((int)m_cont_reg[35]<<8)) & 1023;
3020 m_mmc.DX = (m_cont_reg[36]+((int)m_cont_reg[37]<<8)) & 511;
3021 m_mmc.DY = (m_cont_reg[38]+((int)m_cont_reg[39]<<8)) & 1023;
3022 m_mmc.NY = (m_cont_reg[42]+((int)m_cont_reg[43]<<8)) & 1023;
3023 m_mmc.TY = m_cont_reg[45]&0x08? -1:1;
3025 m_mmc.CL = m_cont_reg[44];
3027 m_mmc.MXS = (m_cont_reg[45] & 0x10) != 0;
3028 m_mmc.MXD = (m_cont_reg[45] & 0x20) != 0;
3030 // Argument depends on UINT8 or dot operation
3031 if ((m_mmc.CM & 0x0C) == 0x0C) {
3032 m_mmc.TX = m_cont_reg[45]&0x04? -PPB[SM]:PPB[SM];
3033 m_mmc.NX = ((m_cont_reg[40]+((int)m_cont_reg[41]<<8)) & 1023)/PPB[SM];
3036 m_mmc.TX = m_cont_reg[45]&0x04? -1:1;
3037 m_mmc.NX = (m_cont_reg[40]+((int)m_cont_reg[41]<<8)) & 1023;
3040 // X loop variables are treated specially for LINE command
3041 if (m_mmc.CM == CM_LINE) {
3042 m_mmc.ASX=((m_mmc.NX-1)>>1);
3046 m_mmc.ASX = m_mmc.SX;
3047 m_mmc.ADX = m_mmc.DX;
3050 // NX loop variable is treated specially for SRCH command
3051 if (m_mmc.CM == CM_SRCH)
3052 m_mmc.ANX=(m_cont_reg[45]&0x02)!=0; // Do we look for "==" or "!="?
3054 m_mmc.ANX = m_mmc.NX;
3056 // Command execution started
3057 m_stat_reg[2]|=0x01;
3059 // Start execution if we still have time slices
3060 if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
3062 // Operation successfully initiated
3066 /** LoopVDP() ************************************************
3067 Run X steps of active VDP command
3068 *************************************************************/
3069 void v99x8_device::update_command()
3071 if(m_vdp_ops_count<=0)
3073 m_vdp_ops_count+=13662;
3074 if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
3078 m_vdp_ops_count=13662;
3079 if(m_vdp_engine) (this->*m_vdp_engine)();
3083 /*static MACHINE_CONFIG_FRAGMENT( v9938 )
3084 MCFG_PALETTE_ADD("palette", 512)
3085 MCFG_PALETTE_INIT_OWNER(v9938_device, v9938)
3086 MACHINE_CONFIG_END*/
3088 //-------------------------------------------------
3089 // machine_config_additions - return a pointer to
3090 // the device's machine fragment
3091 //-------------------------------------------------
3093 /*machine_config_constructor v9938_device::device_mconfig_additions() const
3095 return MACHINE_CONFIG_NAME( v9938 );
3098 /*static MACHINE_CONFIG_FRAGMENT( v9958 )
3099 MCFG_PALETTE_ADD("palette", 19780)
3100 MCFG_PALETTE_INIT_OWNER(v9958_device, v9958)
3101 MACHINE_CONFIG_END*/
3103 //-------------------------------------------------
3104 // machine_config_additions - return a pointer to
3105 // the device's machine fragment
3106 //-------------------------------------------------
3108 /*machine_config_constructor v9958_device::device_mconfig_additions() const
3110 return MACHINE_CONFIG_NAME( v9958 );
3115 /* for common source code project */
3116 void v99x8_device::draw_screen()
3120 if(osd == NULL) return;
3121 emu->set_vm_screen_lines(__SCREEN_HEIGHT);
3122 for(y=0; y< __SCREEN_HEIGHT; y++) {
3123 if((dst = osd->get_vm_screen_buffer(y)) != NULL) {
3124 my_memcpy(dst, screen+(y+18)*_V9938_LONG_WIDTH+2, __SCREEN_WIDTH*sizeof(scrntype_t));
3129 void v99x8_device::initialize()
3131 DEVICE::initialize();
3132 __SCREEN_WIDTH = osd->get_feature_int_value(_T("SCREEN_WIDTH"));
3133 __SCREEN_HEIGHT = osd->get_feature_int_value(_T("SCREEN_HEIGHT"));
3134 if(__SCREEN_WIDTH <= 0) __SCREEN_WIDTH = 256;
3135 if(__SCREEN_HEIGHT <= 0) __SCREEN_HEIGHT = 192;
3138 register_vline_event(this);
3141 void v99x8_device::reset()
3146 void v99x8_device::event_vline(int v, int clock)
3148 m_stat_reg[2] ^= 0x20;
3152 void v99x8_device::write_signal(int id, uint32_t data, uint32_t mask)
3154 if(id == SIG_VDP_COMMAND_COMPLETION) {
3157 i = m_vdp_ops_count = 13662;
3158 if(m_vdp_engine) (this->*m_vdp_engine)();
3159 if (i == m_vdp_ops_count) break;
3164 #define STATE_VERSION 2
3166 bool v99x8_device::process_state(FILEIO* state_fio, bool loading)
3168 if(!state_fio->StateCheckUint32(STATE_VERSION)) {
3171 if(!state_fio->StateCheckInt32(this_device_id)) {
3174 save_load_state(state_fio, !loading);
3178 void v99x8_device::save_load_state(FILEIO* state_fio, bool is_save)
3180 #define STATE_ENTRY(x) {&(x), sizeof(x)}
3185 t_state_table state_table[] = {
3186 STATE_ENTRY(m_offset_x),
3187 STATE_ENTRY(m_offset_y),
3188 STATE_ENTRY(m_visible_y),
3189 STATE_ENTRY(m_mode),
3190 STATE_ENTRY(m_pal_write_first),
3191 STATE_ENTRY(m_cmd_write_first),
3192 STATE_ENTRY(m_pal_write),
3193 STATE_ENTRY(m_cmd_write),
3194 STATE_ENTRY(m_pal_reg),
3195 STATE_ENTRY(m_stat_reg),
3196 STATE_ENTRY(m_cont_reg),
3197 STATE_ENTRY(m_read_ahead),
3198 STATE_ENTRY(m_int_state),
3199 STATE_ENTRY(m_scanline),
3200 STATE_ENTRY(m_blink),
3201 STATE_ENTRY(m_blink_count),
3202 STATE_ENTRY(m_mx_delta),
3203 STATE_ENTRY(m_my_delta),
3204 STATE_ENTRY(m_button_state),
3205 STATE_ENTRY(m_pal_ind16),
3206 STATE_ENTRY(m_pal_ind256),
3208 STATE_ENTRY(m_vdp_ops_count),
3209 STATE_ENTRY(m_pal_ntsc),
3210 STATE_ENTRY(m_scanline_start),
3211 STATE_ENTRY(m_vblank_start),
3212 STATE_ENTRY(m_scanline_max),
3213 STATE_ENTRY(m_height),
3214 STATE_ENTRY(m_v9958_sp_mode),
3215 STATE_ENTRY(m_address_latch),
3220 for(i=0; state_table[i].size>0; i++) {
3222 state_fio->Fwrite(state_table[i].address, state_table[i].size, 1);
3225 state_fio->Fread(state_table[i].address, state_table[i].size, 1);
3232 Common Source Code Project
3233 MSX Series (experimental)
3235 Origin : mame0172s.zip
3236 mame.zip\src\devices\video\v9938.cpp
3237 modified by umaiboux